resolver.c revision a473029e76dfc68db00505f40065607b4ea1e0f5
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews/*
499b34cea04a46823d003d4c0520c8b03e8513cbBrian Wellington * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * Copyright (C) 1999-2003 Internet Software Consortium.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews *
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * Permission to use, copy, modify, and distribute this software for any
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * purpose with or without fee is hereby granted, provided that the above
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * copyright notice and this permission notice appear in all copies.
15a44745412679c30a6d022733925af70a38b715David Lawrence *
15a44745412679c30a6d022733925af70a38b715David Lawrence * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
15a44745412679c30a6d022733925af70a38b715David Lawrence * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15a44745412679c30a6d022733925af70a38b715David Lawrence * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
15a44745412679c30a6d022733925af70a38b715David Lawrence * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15a44745412679c30a6d022733925af70a38b715David Lawrence * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15a44745412679c30a6d022733925af70a38b715David Lawrence * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15a44745412679c30a6d022733925af70a38b715David Lawrence * PERFORMANCE OF THIS SOFTWARE.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff/* $Id: resolver.c,v 1.336 2006/10/18 04:18:54 marka Exp $ */
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews/*! \file */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
92ef1a9b9dbd48ecb507b42ac62c15afefdaf838David Lawrence#include <config.h>
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <isc/print.h>
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <isc/string.h>
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <isc/task.h>
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <isc/timer.h>
e21d199dca95aff5d50f133d6b064309e209af00Brian Wellington#include <isc/util.h>
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <dns/acl.h>
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <dns/adb.h>
8b61d2012063306528286680bd9f086fa868d86eMark Andrews#include <dns/cache.h>
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#include <dns/db.h>
19d1b1667d073850d4366352aaf8319efc5debeeBrian Wellington#include <dns/dispatch.h>
19d1b1667d073850d4366352aaf8319efc5debeeBrian Wellington#include <dns/ds.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/events.h>
09f22ac5b09e70bc526015f37168ba33e21ea91fDavid Lawrence#include <dns/forward.h>
19d1b1667d073850d4366352aaf8319efc5debeeBrian Wellington#include <dns/keytable.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/log.h>
92ef1a9b9dbd48ecb507b42ac62c15afefdaf838David Lawrence#include <dns/message.h>
92ef1a9b9dbd48ecb507b42ac62c15afefdaf838David Lawrence#include <dns/ncache.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/opcode.h>
92ef1a9b9dbd48ecb507b42ac62c15afefdaf838David Lawrence#include <dns/peer.h>
92ef1a9b9dbd48ecb507b42ac62c15afefdaf838David Lawrence#include <dns/rbt.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/rcode.h>
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#include <dns/rdata.h>
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#include <dns/rdataclass.h>
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews#include <dns/rdatalist.h>
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews#include <dns/rdataset.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/rdatastruct.h>
28fc90e6c81338c5f34e065fdda49d320e362583Mark Andrews#include <dns/rdatatype.h>
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#include <dns/resolver.h>
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#include <dns/result.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/rootns.h>
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#include <dns/tsig.h>
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#include <dns/validator.h>
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#define DNS_RESOLVER_TRACE
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#ifdef DNS_RESOLVER_TRACE
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff#define RTRACE(m) isc_log_write(dns_lctx, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGCATEGORY_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGMODULE_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff ISC_LOG_DEBUG(3), \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff "res %p: %s", res, (m))
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#define RRTRACE(r, m) isc_log_write(dns_lctx, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGCATEGORY_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGMODULE_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff ISC_LOG_DEBUG(3), \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews "res %p: %s", (r), (m))
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTXTRACE(m) isc_log_write(dns_lctx, \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews DNS_LOGCATEGORY_RESOLVER, \
28fc90e6c81338c5f34e065fdda49d320e362583Mark Andrews DNS_LOGMODULE_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff ISC_LOG_DEBUG(3), \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff "fctx %p(%s'): %s", fctx, fctx->info, (m))
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#define FCTXTRACE2(m1, m2) \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff isc_log_write(dns_lctx, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGCATEGORY_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGMODULE_RESOLVER, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff ISC_LOG_DEBUG(3), \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff "fctx %p(%s): %s %s", \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff fctx, fctx->info, (m1), (m2))
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff#define FTRACE(m) isc_log_write(dns_lctx, \
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff DNS_LOGCATEGORY_RESOLVER, \
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington DNS_LOGMODULE_RESOLVER, \
19d1b1667d073850d4366352aaf8319efc5debeeBrian Wellington ISC_LOG_DEBUG(3), \
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson "fetch %p (fctx %p(%s)): %s", \
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson fetch, fetch->private, \
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews fetch->private->info, (m))
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff#define QTRACE(m) isc_log_write(dns_lctx, \
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews DNS_LOGCATEGORY_RESOLVER, \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews DNS_LOGMODULE_RESOLVER, \
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews ISC_LOG_DEBUG(3), \
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews "resquery %p (fctx %p(%s)): %s", \
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson query, query->fctx, \
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson query->fctx->info, (m))
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews#else
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews#define RTRACE(m)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define RRTRACE(r, m)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define FCTXTRACE(m)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define FTRACE(m)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define QTRACE(m)
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews#endif
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson/*%
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson * Maximum EDNS0 input packet size.
f53848e17123569387b279578f0100dca5407da5Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews/*%
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * This defines the maximum number of timeouts we will permit before we
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * disable EDNS0 on the query.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define MAX_EDNS0_TIMEOUTS 3
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewstypedef struct fetchctx fetchctx_t;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
59abb512d344bfa09012cc11b7d814966f035da4Mark Andrewstypedef struct query {
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews /* Locked by task event serialization. */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews unsigned int magic;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fetchctx_t * fctx;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_mem_t * mctx;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_dispatchmgr_t * dispatchmgr;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_dispatch_t * dispatch;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_adbaddrinfo_t * addrinfo;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_socket_t * tcpsocket;
76c8294c81fb48b1da6e1fc5b83322a4cedb8e58Andreas Gustafsson isc_time_t start;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson dns_messageid_t id;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_dispentry_t * dispentry;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews ISC_LINK(struct query) link;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_buffer_t buffer;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_buffer_t *tsig;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_tsigkey_t *tsigkey;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int options;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews unsigned int attributes;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews unsigned int sends;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews unsigned int connects;
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff unsigned char data[512];
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff} resquery_t;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!')
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define RESQUERY_ATTR_CANCELED 0x02
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#define RESQUERY_CONNECTING(q) ((q)->connects > 0)
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews#define RESQUERY_CANCELED(q) (((q)->attributes & \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews RESQUERY_ATTR_CANCELED) != 0)
8b61d2012063306528286680bd9f086fa868d86eMark Andrews#define RESQUERY_SENDING(q) ((q)->sends > 0)
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewstypedef enum {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fetchstate_init = 0, /*%< Start event has not run yet. */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fetchstate_active,
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff fetchstate_done /*%< FETCHDONE events posted. */
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff} fetchstate;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstruct fetchctx {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*% Not locked. */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int magic;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_resolver_t * res;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_name_t name;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_rdatatype_t type;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int options;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int bucketnum;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews char * info;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews /*% Locked by appropriate bucket lock. */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews fetchstate state;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_boolean_t want_shutdown;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_boolean_t cloned;
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff isc_boolean_t spilled;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int references;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_event_t control_event;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews ISC_LINK(struct fetchctx) link;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews ISC_LIST(dns_fetchevent_t) events;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews /*% Locked by task event serialization. */
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff dns_name_t domain;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews dns_rdataset_t nameservers;
5e387b9ce6bafdfadedb5b34e4c33a4404e5d589Brian Wellington unsigned int attributes;
5e387b9ce6bafdfadedb5b34e4c33a4404e5d589Brian Wellington isc_timer_t * timer;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_time_t expires;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews isc_interval_t interval;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews dns_message_t * qmessage;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews dns_message_t * rmessage;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews ISC_LIST(resquery_t) queries;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_adbfindlist_t finds;
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff dns_adbfind_t * find;
ca9739800f045cd4d39014f98b920d4354b5bd14Michael Graff dns_adbfindlist_t altfinds;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_adbfind_t * altfind;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_adbaddrinfolist_t forwaddrs;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_adbaddrinfolist_t altaddrs;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_sockaddrlist_t forwarders;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_fwdpolicy_t fwdpolicy;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_sockaddrlist_t bad;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_sockaddrlist_t edns;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_sockaddrlist_t edns512;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews ISC_LIST(dns_validator_t) validators;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_db_t * cache;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_adb_t * adb;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews /*%
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews * The number of events we're waiting for.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int pending;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrews /*%
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * The number of times we've "restarted" the current
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * nameserver set. This acts as a failsafe to prevent
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * us from pounding constantly on a particular set of
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * servers that, for whatever reason, are not giving
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * us useful responses, but are responding in such a
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * way that they are not marked "bad".
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int restarts;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington /*%
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * The number of timeouts that have occurred since we
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * last successfully received a response packet. This
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * is used for EDNS0 black hole detection.
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington */
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington unsigned int timeouts;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington /*%
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington * Look aside state for DS lookups.
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington */
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington dns_name_t nsname;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington dns_fetch_t * nsfetch;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington dns_rdataset_t nsrrset;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington};
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC)
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_HAVEANSWER 0x0001
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_GLUING 0x0002
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_ADDRWAIT 0x0004
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_SHUTTINGDOWN 0x0008
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_WANTCACHE 0x0010
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington#define FCTX_ATTR_WANTNCACHE 0x0020
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTX_ATTR_NEEDEDNS0 0x0040
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTX_ATTR_TRIEDFIND 0x0080
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTX_ATTR_TRIEDALT 0x0100
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews 0)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington 0)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \
8b61d2012063306528286680bd9f086fa868d86eMark Andrews 0)
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0)
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewstypedef struct {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_adbaddrinfo_t * addrinfo;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fetchctx_t * fctx;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews} dns_valarg_t;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstruct dns_fetch {
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int magic;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fetchctx_t * private;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews};
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h')
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC)
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrewstypedef struct fctxbucket {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_task_t * task;
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews isc_mutex_t lock;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews ISC_LIST(fetchctx_t) fctxs;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_boolean_t exiting;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mem_t * mctx;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews} fctxbucket_t;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewstypedef struct alternate {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_boolean_t isaddress;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews union {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_sockaddr_t addr;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews struct {
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington dns_name_t name;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington in_port_t port;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews } _n;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews } _u;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews ISC_LINK(struct alternate) link;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews} alternate_t;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrewsstruct dns_resolver {
8b61d2012063306528286680bd9f086fa868d86eMark Andrews /* Unlocked. */
8b61d2012063306528286680bd9f086fa868d86eMark Andrews unsigned int magic;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mem_t * mctx;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mutex_t lock;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mutex_t nlock;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mutex_t primelock;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_rdataclass_t rdclass;
f53848e17123569387b279578f0100dca5407da5Mark Andrews isc_socketmgr_t * socketmgr;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_timermgr_t * timermgr;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_taskmgr_t * taskmgr;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_view_t * view;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_boolean_t frozen;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews unsigned int options;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_dispatchmgr_t * dispatchmgr;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_dispatch_t * dispatchv4;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_dispatch_t * dispatchv6;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int nbuckets;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews fctxbucket_t * buckets;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_uint32_t lame_ttl;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews ISC_LIST(alternate_t) alternates;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_uint16_t udpsize;
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews#if USE_ALGLOCK
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews isc_rwlock_t alglock;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#endif
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews dns_rbt_t * algorithms;
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews#if USE_MBSLOCK
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_rwlock_t mbslock;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews#endif
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_rbt_t * mustbesecure;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int spillatmax;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int spillatmin;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_timer_t * spillattimer;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_boolean_t zero_no_soa_ttl;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews /* Locked by lock. */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int references;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_boolean_t exiting;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington isc_eventlist_t whenshutdown;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington unsigned int activebuckets;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_boolean_t priming;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews unsigned int spillat;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews /* Locked by primelock. */
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews dns_fetch_t * primefetch;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews /* Locked by nlock. */
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int nfctx;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews};
8b61d2012063306528286680bd9f086fa868d86eMark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrews#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!')
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews/*%
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * which we also use as an addrinfo flag.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTX_ADDRINFO_MARK 0x0001
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define FCTX_ADDRINFO_FORWARDER 0x1000
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews == 0)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews#define ISFORWARDER(a) (((a)->flags & \
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews FCTX_ADDRINFO_FORWARDER) != 0)
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
8b61d2012063306528286680bd9f086fa868d86eMark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrewsstatic void destroy(dns_resolver_t *res);
8b61d2012063306528286680bd9f086fa868d86eMark Andrewsstatic void empty_bucket(dns_resolver_t *res);
8b61d2012063306528286680bd9f086fa868d86eMark Andrewsstatic isc_result_t resquery_send(resquery_t *query);
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewsstatic void resquery_response(isc_task_t *task, isc_event_t *event);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsstatic void resquery_connected(isc_task_t *task, isc_event_t *event);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsstatic void fctx_try(fetchctx_t *fctx);
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewsstatic isc_boolean_t fctx_destroy(fetchctx_t *fctx);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstatic isc_result_t ncache_adderesult(dns_message_t *message,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_db_t *cache, dns_dbnode_t *node,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_rdatatype_t covers,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_stdtime_t now, dns_ttl_t maxttl,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_rdataset_t *ardataset,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_result_t *eresultp);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstatic void validated(isc_task_t *task, isc_event_t *event);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstatic isc_result_t
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsvalcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_rdatatype_t type, dns_rdataset_t *rdataset,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_rdataset_t *sigrdataset, unsigned int valoptions,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_task_t *task)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews{
8b61d2012063306528286680bd9f086fa868d86eMark Andrews dns_validator_t *validator = NULL;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews dns_valarg_t *valarg;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_result_t result;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews valarg = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews sizeof(*valarg));
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews if (valarg == NULL)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews return (ISC_R_NOMEMORY);
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff valarg->fctx = fctx;
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff valarg->addrinfo = addrinfo;
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews result = dns_validator_create(fctx->res->view, name, type, rdataset,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews sigrdataset, fctx->rmessage,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews valoptions, task, validated, valarg,
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews &validator);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews if (result == ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews ISC_LIST_APPEND(fctx->validators, validator, link);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews else
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews valarg, sizeof(*valarg));
8b61d2012063306528286680bd9f086fa868d86eMark Andrews return (result);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews}
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewsstatic isc_boolean_t
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsfix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
5e387b9ce6bafdfadedb5b34e4c33a4404e5d589Brian Wellington dns_name_t *name;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews dns_name_t *domain = &fctx->domain;
5e387b9ce6bafdfadedb5b34e4c33a4404e5d589Brian Wellington dns_rdataset_t *rdataset;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_rdatatype_t type;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_result_t result;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews isc_boolean_t keep_auth = ISC_FALSE;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (message->rcode == dns_rcode_nxdomain)
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews return (ISC_FALSE);
f3ca27e9fe307b55e35ea8d7b37351650630e5a3Andreas Gustafsson
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews /*
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * Look for BIND 8 style delegations.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * Also look for answers to ANY queries where the duplicate NS RRset
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews * may have been stripped from the authority section.
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews */
8b61d2012063306528286680bd9f086fa868d86eMark Andrews if (message->counts[DNS_SECTION_ANSWER] != 0 &&
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews (fctx->type == dns_rdatatype_ns ||
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews fctx->type == dns_rdatatype_any)) {
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews result = dns_message_firstname(message, DNS_SECTION_ANSWER);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews while (result == ISC_R_SUCCESS) {
668eb43f1f24c887b6972f6a1610b4b38b281080Brian Wellington name = NULL;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews dns_message_currentname(message, DNS_SECTION_ANSWER,
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews &name);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews for (rdataset = ISC_LIST_HEAD(name->list);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdataset != NULL;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdataset = ISC_LIST_NEXT(rdataset, link)) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews type = rdataset->type;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews if (type != dns_rdatatype_ns)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews continue;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews if (dns_name_issubdomain(name, domain))
8b61d2012063306528286680bd9f086fa868d86eMark Andrews return (ISC_FALSE);
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews }
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews result = dns_message_nextname(message,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence DNS_SECTION_ANSWER);
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews }
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews }
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews /* Look for referral. */
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews if (message->counts[DNS_SECTION_AUTHORITY] == 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto munge;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington while (result == ISC_R_SUCCESS) {
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington name = NULL;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington for (rdataset = ISC_LIST_HEAD(name->list);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington rdataset != NULL;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington rdataset = ISC_LIST_NEXT(rdataset, link)) {
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington type = rdataset->type;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (type == dns_rdatatype_soa &&
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington dns_name_equal(name, domain))
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington keep_auth = ISC_TRUE;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (type != dns_rdatatype_ns &&
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington type != dns_rdatatype_soa)
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington continue;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (dns_name_equal(name, domain))
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington goto munge;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (dns_name_issubdomain(name, domain))
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington return (ISC_FALSE);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington }
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington }
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington munge:
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington message->rcode = dns_rcode_nxdomain;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson message->counts[DNS_SECTION_ANSWER] = 0;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson if (!keep_auth)
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson message->counts[DNS_SECTION_AUTHORITY] = 0;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson message->counts[DNS_SECTION_ADDITIONAL] = 0;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff return (ISC_TRUE);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington}
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellingtonstatic inline isc_result_t
ac6afcd0caf72aaa2a537e0003de30b363b4a68bBrian Wellingtonfctx_starttimer(fetchctx_t *fctx) {
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington /*
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington * Start the lifetime timer for fctx.
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington *
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington * This is also used for stopping the idle timer; in that
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington * case we must purge events already posted to ensure that
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * no further idle events are delivered.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
97f26b88f62b10a9c26a91ebe8387d2e498c2d00Andreas Gustafsson return (isc_timer_reset(fctx->timer, isc_timertype_once,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews &fctx->expires, NULL, ISC_TRUE));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews}
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsstatic inline void
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsfctx_stoptimer(fetchctx_t *fctx) {
97f26b88f62b10a9c26a91ebe8387d2e498c2d00Andreas Gustafsson isc_result_t result;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * We don't return a result if resetting the timer to inactive fails
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * since there's nothing to be done about it. Resetting to inactive
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * should never fail anyway, since the code as currently written
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * cannot fail in that case.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews NULL, NULL, ISC_TRUE);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (result != ISC_R_SUCCESS) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews UNEXPECTED_ERROR(__FILE__, __LINE__,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews "isc_timer_reset(): %s",
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews isc_result_totext(result));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews }
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews}
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsstatic inline isc_result_t
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsfctx_startidletimer(fetchctx_t *fctx) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * Start the idle timer for fctx. The lifetime timer continues
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * to be in effect.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews return (isc_timer_reset(fctx->timer, isc_timertype_once,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews &fctx->expires, &fctx->interval,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews ISC_FALSE));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews}
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews/*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * Stopping the idle timer is equivalent to calling fctx_starttimer(), but
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * we use fctx_stopidletimer for readability in the code below.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews#define fctx_stopidletimer fctx_starttimer
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsstatic inline void
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsresquery_destroy(resquery_t **queryp) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews resquery_t *query;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews REQUIRE(queryp != NULL);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews query = *queryp;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews REQUIRE(!ISC_LINK_LINKED(query, link));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews INSIST(query->tcpsocket == NULL);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews query->magic = 0;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews isc_mem_put(query->mctx, query, sizeof(*query));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews *queryp = NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews}
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsstatic void
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrewsfctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews isc_time_t *finish, isc_boolean_t no_response)
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews{
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews fetchctx_t *fctx;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews resquery_t *query;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews unsigned int rtt;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews unsigned int factor;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adbfind_t *find;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adbaddrinfo_t *addrinfo;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews query = *queryp;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews fctx = query->fctx;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews FCTXTRACE("cancelquery");
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews REQUIRE(!RESQUERY_CANCELED(query));
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews query->attributes |= RESQUERY_ATTR_CANCELED;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * Should we update the RTT?
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (finish != NULL || no_response) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (finish != NULL) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * We have both the start and finish times for this
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * packet, so we can compute a real RTT.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews rtt = (unsigned int)isc_time_microdiff(finish,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews &query->start);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews factor = DNS_ADB_RTTADJDEFAULT;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews } else {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * We don't have an RTT for this query. Maybe the
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * packet was lost, or maybe this server is very
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * slow. We don't know. Increase the RTT.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews INSIST(no_response);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews rtt = query->addrinfo->srtt + 200000;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (rtt > 10000000)
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews rtt = 10000000;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * Replace the current RTT with our value.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews factor = DNS_ADB_RTTADJREPLACE;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews }
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews }
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews /*
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews * Age RTTs of servers not tried.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews factor = DNS_ADB_RTTADJAGE;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (finish != NULL)
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo = ISC_LIST_NEXT(addrinfo, publink))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (UNMARKED(addrinfo))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_adjustsrtt(fctx->adb, addrinfo,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews 0, factor);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (finish != NULL && TRIEDFIND(fctx))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews for (find = ISC_LIST_HEAD(fctx->finds);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews find != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews find = ISC_LIST_NEXT(find, publink))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews for (addrinfo = ISC_LIST_HEAD(find->list);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo = ISC_LIST_NEXT(addrinfo, publink))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (UNMARKED(addrinfo))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_adjustsrtt(fctx->adb, addrinfo,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews 0, factor);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (finish != NULL && TRIEDALT(fctx)) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo != NULL;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews addrinfo = ISC_LIST_NEXT(addrinfo, publink))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (UNMARKED(addrinfo))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_adjustsrtt(fctx->adb, addrinfo,
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews 0, factor);
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews for (find = ISC_LIST_HEAD(fctx->altfinds);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews find != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews find = ISC_LIST_NEXT(find, publink))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews for (addrinfo = ISC_LIST_HEAD(find->list);
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews addrinfo != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addrinfo = ISC_LIST_NEXT(addrinfo, publink))
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews if (UNMARKED(addrinfo))
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_adjustsrtt(fctx->adb, addrinfo,
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews 0, factor);
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews }
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (query->dispentry != NULL)
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_dispatch_removeresponse(&query->dispentry, deventp);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews ISC_LIST_UNLINK(fctx->queries, query, link);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews if (query->tsig != NULL)
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_buffer_free(&query->tsig);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews if (query->tsigkey != NULL)
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff dns_tsigkey_detach(&query->tsigkey);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * Check for any outstanding socket events. If they exist, cancel
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * them and let the event handlers finish the cleanup. The resolver
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * only needs to worry about managing the connect and send events;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * the dispatcher manages the recv events.
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews */
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff if (RESQUERY_CONNECTING(query))
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * Cancel the connect.
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews */
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff isc_socket_cancel(query->tcpsocket, NULL,
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff ISC_SOCKCANCEL_CONNECT);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff else if (RESQUERY_SENDING(query))
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * Cancel the pending send.
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff */
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff isc_socket_cancel(dns_dispatch_getsocket(query->dispatch),
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff NULL, ISC_SOCKCANCEL_SEND);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff if (query->dispatch != NULL)
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff dns_dispatch_detach(&query->dispatch);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query)))
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * It's safe to destroy the query now.
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff */
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff resquery_destroy(&query);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff}
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews resquery_t *query, *next_query;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews FCTXTRACE("cancelqueries");
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews for (query = ISC_LIST_HEAD(fctx->queries);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews query != NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews query = next_query) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews next_query = ISC_LIST_NEXT(query, link);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cancelquery(&query, NULL, NULL, no_response);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews}
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_cleanupfinds(fetchctx_t *fctx) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_adbfind_t *find, *next_find;
6ef15459b8fd3fc8b5672da4ad72c19a755dbe45Mark Andrews
6ef15459b8fd3fc8b5672da4ad72c19a755dbe45Mark Andrews REQUIRE(ISC_LIST_EMPTY(fctx->queries));
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews for (find = ISC_LIST_HEAD(fctx->finds);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews find != NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews find = next_find) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews next_find = ISC_LIST_NEXT(find, publink);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews ISC_LIST_UNLINK(fctx->finds, find, publink);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_destroyfind(&find);
e21d199dca95aff5d50f133d6b064309e209af00Brian Wellington }
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington fctx->find = NULL;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington}
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellingtonstatic void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_cleanupaltfinds(fetchctx_t *fctx) {
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff dns_adbfind_t *find, *next_find;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff REQUIRE(ISC_LIST_EMPTY(fctx->queries));
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff for (find = ISC_LIST_HEAD(fctx->altfinds);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff find != NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews find = next_find) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews next_find = ISC_LIST_NEXT(find, publink);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews ISC_LIST_UNLINK(fctx->altfinds, find, publink);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_adb_destroyfind(&find);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->altfind = NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews}
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_cleanupforwaddrs(fetchctx_t *fctx) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_adbaddrinfo_t *addr, *next_addr;
f3ca27e9fe307b55e35ea8d7b37351650630e5a3Andreas Gustafsson
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews REQUIRE(ISC_LIST_EMPTY(fctx->queries));
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews for (addr = ISC_LIST_HEAD(fctx->forwaddrs);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews addr != NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews addr = next_addr) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews next_addr = ISC_LIST_NEXT(addr, publink);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_adb_freeaddrinfo(fctx->adb, &addr);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews}
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_cleanupaltaddrs(fetchctx_t *fctx) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_adbaddrinfo_t *addr, *next_addr;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews REQUIRE(ISC_LIST_EMPTY(fctx->queries));
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews for (addr = ISC_LIST_HEAD(fctx->altaddrs);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addr != NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews addr = next_addr) {
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews next_addr = ISC_LIST_NEXT(addr, publink);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews dns_adb_freeaddrinfo(fctx->adb, &addr);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews}
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic inline void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews FCTXTRACE("stopeverything");
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cancelqueries(fctx, no_response);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cleanupfinds(fctx);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cleanupaltfinds(fctx);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cleanupforwaddrs(fctx);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_cleanupaltaddrs(fctx);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx_stoptimer(fctx);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews}
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsstatic inline void
056141f2878d1046306ef0ba035263a00de57f98Mark Andrewsfctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews dns_fetchevent_t *event, *next_event;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_task_t *task;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews unsigned int count = 0;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_interval_t i;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_boolean_t logit = ISC_FALSE;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews /*
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews * Caller must be holding the appropriate bucket lock.
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews */
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews REQUIRE(fctx->state == fetchstate_done);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews FCTXTRACE("sendevents");
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews for (event = ISC_LIST_HEAD(fctx->events);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews event != NULL;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews event = next_event) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews next_event = ISC_LIST_NEXT(event, ev_link);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews ISC_LIST_UNLINK(fctx->events, event, ev_link);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews task = event->ev_sender;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews event->ev_sender = fctx;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff if (!HAVE_ANSWER(fctx))
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews event->result = result;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews INSIST(result != ISC_R_SUCCESS ||
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews dns_rdataset_isassociated(event->rdataset) ||
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews fctx->type == dns_rdatatype_any ||
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->type == dns_rdatatype_rrsig ||
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->type == dns_rdatatype_sig);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews count++;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 &&
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->spilled &&
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) {
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews LOCK(&fctx->res->lock);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (count == fctx->res->spillat && !fctx->res->exiting) {
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington fctx->res->spillat += 5;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews if (fctx->res->spillat > fctx->res->spillatmax &&
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->res->spillatmax != 0)
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews fctx->res->spillat = fctx->res->spillatmax;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_interval_set(&i, 20 * 60, 0);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews result = isc_timer_reset(fctx->res->spillattimer,
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews isc_timertype_ticker, NULL,
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews &i, ISC_TRUE);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews RUNTIME_CHECK(result == ISC_R_SUCCESS);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews logit = ISC_TRUE;
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews UNLOCK(&fctx->res->lock);
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington if (logit)
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews "clients-per-query increased to %u",
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews count + 1);
056141f2878d1046306ef0ba035263a00de57f98Mark Andrews }
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews}
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrewsstatic void
19d1b1667d073850d4366352aaf8319efc5debeeBrian Wellingtonfctx_done(fetchctx_t *fctx, isc_result_t result) {
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_resolver_t *res;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_boolean_t no_response;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews FCTXTRACE("done");
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff res = fctx->res;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews if (result == ISC_R_SUCCESS)
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews no_response = ISC_TRUE;
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews else
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews no_response = ISC_FALSE;
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews fctx_stopeverything(fctx, no_response);
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews LOCK(&res->buckets[fctx->bucketnum].lock);
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews fctx->state = fetchstate_done;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff fctx_sendevents(fctx, result);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff UNLOCK(&res->buckets[fctx->bucketnum].lock);
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews}
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrewsstatic void
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrewsresquery_senddone(isc_task_t *task, isc_event_t *event) {
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews isc_socketevent_t *sevent = (isc_socketevent_t *)event;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews resquery_t *query = event->ev_arg;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff isc_boolean_t retry = ISC_FALSE;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews isc_result_t result;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews fetchctx_t *fctx;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff QTRACE("senddone");
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * XXXRTH
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff *
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * Currently we don't wait for the senddone event before retrying
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * a query. This means that if we get really behind, we may end
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * up doing extra work!
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff */
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff UNUSED(task);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff INSIST(RESQUERY_SENDING(query));
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff query->sends--;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff fctx = query->fctx;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (RESQUERY_CANCELED(query)) {
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (query->sends == 0) {
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews * This query was canceled while the
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews * isc_socket_sendto() was in progress.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (query->tcpsocket != NULL)
94baac869a70b529a24ff23d8dc899faa5d4fdc4Brian Wellington isc_socket_detach(&query->tcpsocket);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews resquery_destroy(&query);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews }
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews } else
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews switch (sevent->result) {
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews case ISC_R_SUCCESS:
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews break;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews case ISC_R_HOSTUNREACH:
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews case ISC_R_NETUNREACH:
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews case ISC_R_NOPERM:
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews case ISC_R_ADDRNOTAVAIL:
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews case ISC_R_CONNREFUSED:
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington /*
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * No route to remote.
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews */
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
e21d199dca95aff5d50f133d6b064309e209af00Brian Wellington retry = ISC_TRUE;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington break;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington default:
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews break;
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff }
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff isc_event_free(&event);
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff if (retry) {
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * Behave as if the idle timer has expired. For TCP
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * this may not actually reflect the latest timer.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews result = fctx_stopidletimer(fctx);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx_done(fctx, result);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews else
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx_try(fctx);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews}
f3ca27e9fe307b55e35ea8d7b37351650630e5a3Andreas Gustafsson
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsstatic inline isc_result_t
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrewsfctx_addopt(dns_message_t *message, unsigned int version, isc_uint16_t udpsize)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews{
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_rdataset_t *rdataset;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_rdatalist_t *rdatalist;
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff dns_rdata_t *rdata;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_result_t result;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
c27767a7b946f4c6f08d33129691e2d6339e8350Brian Wellington rdatalist = NULL;
c27767a7b946f4c6f08d33129691e2d6339e8350Brian Wellington result = dns_message_gettemprdatalist(message, &rdatalist);
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews return (result);
dcc7ea97174501f0409c0c919b3ca04083e4e1b8Andreas Gustafsson rdata = NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews result = dns_message_gettemprdata(message, &rdata);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews if (result != ISC_R_SUCCESS)
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews return (result);
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews rdataset = NULL;
7c2dce3c4d2c863ff268576f13c4ddd6f29d67edMark Andrews result = dns_message_gettemprdataset(message, &rdataset);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews return (result);
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews dns_rdataset_init(rdataset);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews rdatalist->type = dns_rdatatype_opt;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews rdatalist->covers = 0;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * Set Maximum UDP buffer size.
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson */
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson rdatalist->rdclass = udpsize;
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson /*
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson * Set EXTENDED-RCODE and Z to 0, DO to 1.
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdatalist->ttl = (version << 16);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * No EDNS options.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff rdata->data = NULL;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdata->length = 0;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdata->rdclass = rdatalist->rdclass;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews rdata->type = rdatalist->type;
94baac869a70b529a24ff23d8dc899faa5d4fdc4Brian Wellington rdata->flags = 0;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews ISC_LIST_INIT(rdatalist->rdata);
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS);
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington return (dns_message_setopt(message, rdataset));
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington}
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellingtonstatic inline void
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsfctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int seconds;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int us;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews /*
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * We retry every .5 seconds the first two times through the address
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * list, and then we do exponential back-off.
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (fctx->restarts < 3)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews us = 500000;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews else
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff us = (500000 << (fctx->restarts - 2));
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * Double the round-trip time.
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews */
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews rtt *= 2;
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * Always wait for at least the doubled round-trip time.
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews */
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews if (us < rtt)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews us = rtt;
04c22ceaf2d3812eaab69d79958d0e0d62048cd2Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * But don't ever wait for more than 10 seconds.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (us > 10000000)
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington us = 10000000;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews seconds = us / 1000000;
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews us -= seconds * 1000000;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_interval_set(&fctx->interval, seconds, us * 1000);
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews}
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewsstatic isc_result_t
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrewsfctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int options)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews{
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_resolver_t *res;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_task_t *task;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington isc_result_t result;
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington resquery_t *query;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_sockaddr_t addr;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_boolean_t have_addr = ISC_FALSE;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews FCTXTRACE("query");
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews res = fctx->res;
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews task = res->buckets[fctx->bucketnum].task;
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews fctx_setretryinterval(fctx, addrinfo->srtt);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews result = fctx_startidletimer(fctx);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result != ISC_R_SUCCESS)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews return (result);
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query = isc_mem_get(res->buckets[fctx->bucketnum].mctx,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews sizeof(*query));
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (query == NULL) {
8b61d2012063306528286680bd9f086fa868d86eMark Andrews result = ISC_R_NOMEMORY;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto stop_idle_timer;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->mctx = res->buckets[fctx->bucketnum].mctx;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->options = options;
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence query->attributes = 0;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->sends = 0;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->connects = 0;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington * Note that the caller MUST guarantee that 'addrinfo' will remain
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington * valid until this query is canceled.
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington */
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington query->addrinfo = addrinfo;
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington TIME_NOW(&query->start);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews * If this is a TCP query, then we need to make a socket and
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington * a dispatch for it here. Otherwise we use the resolver's
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff * shared dispatch.
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews */
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->dispatchmgr = res->dispatchmgr;
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff query->dispatch = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->tcpsocket = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (res->view->peers != NULL) {
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff dns_peer_t *peer = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_netaddr_t dstip;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr);
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff result = dns_peerlist_peerbyaddr(res->view->peers,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews &dstip, &peer);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result == ISC_R_SUCCESS) {
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff result = dns_peer_getquerysource(peer, &addr);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result == ISC_R_SUCCESS)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews have_addr = ISC_TRUE;
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington if ((query->options & DNS_FETCHOPT_TCP) != 0) {
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington int pf;
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews pf = isc_sockaddr_pf(&addrinfo->sockaddr);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (!have_addr) {
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews switch (pf) {
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence case PF_INET:
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews result =
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews dns_dispatch_getlocaladdress(res->dispatchv4,
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews &addr);
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews break;
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews case PF_INET6:
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews result =
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews dns_dispatch_getlocaladdress(res->dispatchv6,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews &addr);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews break;
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews default:
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews result = ISC_R_NOTIMPLEMENTED;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews break;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result != ISC_R_SUCCESS)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews goto cleanup_query;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_sockaddr_setport(&addr, 0);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews result = isc_socket_create(res->socketmgr, pf,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_sockettype_tcp,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews &query->tcpsocket);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result != ISC_R_SUCCESS)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews goto cleanup_query;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews result = isc_socket_bind(query->tcpsocket, &addr);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (result != ISC_R_SUCCESS)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews goto cleanup_socket;
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington#endif
e43b9a20054cdda6946ab758e1c2005f2b25641aBrian Wellington
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews * A dispatch will be created once the connect succeeds.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson } else {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson if (have_addr) {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson unsigned int attrs, attrmask;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrs = DNS_DISPATCHATTR_UDP;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson switch (isc_sockaddr_pf(&addr)) {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson case AF_INET:
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrs |= DNS_DISPATCHATTR_IPV4;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson break;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson case AF_INET6:
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrs |= DNS_DISPATCHATTR_IPV6;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson break;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson default:
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson result = ISC_R_NOTIMPLEMENTED;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson goto cleanup_query;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson }
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrmask = DNS_DISPATCHATTR_UDP;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrmask |= DNS_DISPATCHATTR_TCP;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrmask |= DNS_DISPATCHATTR_IPV4;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson attrmask |= DNS_DISPATCHATTR_IPV6;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson result = dns_dispatch_getudp(res->dispatchmgr,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson res->socketmgr,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson res->taskmgr, &addr,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson 4096, 1000, 32768, 16411,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson 16433, attrs, attrmask,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson &query->dispatch);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson if (result != ISC_R_SUCCESS)
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson goto cleanup_query;
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson } else {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson case PF_INET:
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson dns_dispatch_attach(res->dispatchv4,
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson &query->dispatch);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson break;
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson case PF_INET6:
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews dns_dispatch_attach(res->dispatchv6,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews &query->dispatch);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews break;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews default:
8b61d2012063306528286680bd9f086fa868d86eMark Andrews result = ISC_R_NOTIMPLEMENTED;
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews goto cleanup_query;
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews }
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews }
8f17525ebc8ab06446b613f56972dcf063548141Andreas Gustafsson /*
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson * We should always have a valid dispatcher here. If we
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson * don't support a protocol family, then its dispatcher
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson * will be NULL, but we shouldn't be finding addresses for
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * protocol types we don't support, so the dispatcher
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews * we found should never be NULL.
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews */
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews INSIST(query->dispatch != NULL);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews }
59abb512d344bfa09012cc11b7d814966f035da4Mark Andrews
6850cdd4497424c9d42ade487edfde9fb9a47de9Brian Wellington query->dispentry = NULL;
59abb512d344bfa09012cc11b7d814966f035da4Mark Andrews query->fctx = fctx;
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington query->tsig = NULL;
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington query->tsigkey = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews ISC_LINK_INIT(query, link);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews query->magic = QUERY_MAGIC;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrews if ((query->options & DNS_FETCHOPT_TCP) != 0) {
8b61d2012063306528286680bd9f086fa868d86eMark Andrews /*
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * Connect to the remote server.
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson *
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson * XXXRTH Should we attach to the socket?
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson */
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson result = isc_socket_connect(query->tcpsocket,
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson &addrinfo->sockaddr, task,
1f1d36a87b65186d9f89aac7f456ab1fd2a39ef6Andreas Gustafsson resquery_connected, query);
6850cdd4497424c9d42ade487edfde9fb9a47de9Brian Wellington if (result != ISC_R_SUCCESS)
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington goto cleanup_socket;
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington query->connects++;
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington QTRACE("connecting via TCP");
2483a850e8b82230331defb0e22c67f6b46cfb38Brian Wellington } else {
84a47e20aedd16ba86feb25848732338ad618b16Brian Wellington result = resquery_send(query);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews if (result != ISC_R_SUCCESS)
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews goto cleanup_dispatch;
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews }
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews ISC_LIST_APPEND(fctx->queries, query, link);
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews return (ISC_R_SUCCESS);
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews
7ed73313b1c129c7134d7d33beb82c85bd6d1df4Mark Andrews cleanup_socket:
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews isc_socket_detach(&query->tcpsocket);
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews cleanup_dispatch:
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence if (query->dispatch != NULL)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_dispatch_detach(&query->dispatch);
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews cleanup_query:
8b61d2012063306528286680bd9f086fa868d86eMark Andrews query->magic = 0;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_mem_put(res->buckets[fctx->bucketnum].mctx,
8b61d2012063306528286680bd9f086fa868d86eMark Andrews query, sizeof(*query));
9cb39bbe40998ee14df86609da806441b9e144ceAndreas Gustafsson
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews stop_idle_timer:
9cb39bbe40998ee14df86609da806441b9e144ceAndreas Gustafsson RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews return (result);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews}
9cb39bbe40998ee14df86609da806441b9e144ceAndreas Gustafsson
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrewsstatic isc_boolean_t
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafssontriededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson isc_sockaddr_t *sa;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson for (sa = ISC_LIST_HEAD(fctx->edns);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson sa != NULL;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson sa = ISC_LIST_NEXT(sa, link)) {
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson if (isc_sockaddr_equal(sa, address))
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson return (ISC_TRUE);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson }
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews return (ISC_FALSE);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews}
b54630c4518a1a173fee3478f4bf51dff450b6dcMark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsstatic void
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsadd_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_sockaddr_t *sa;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if (triededns(fctx, address))
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews return;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews sizeof(*sa));
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff if (sa == NULL)
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews return;
2c12768b4306035c208afc92bace4f3501f051f7Michael Sawyer
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews *sa = *address;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews}
8b61d2012063306528286680bd9f086fa868d86eMark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrewsstatic isc_boolean_t
f53848e17123569387b279578f0100dca5407da5Mark Andrewstriededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_sockaddr_t *sa;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews for (sa = ISC_LIST_HEAD(fctx->edns512);
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson sa != NULL;
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson sa = ISC_LIST_NEXT(sa, link)) {
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson if (isc_sockaddr_equal(sa, address))
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson return (ISC_TRUE);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson }
0ad8ee89c532951a55b7de25317eeca2c3b2ed63Andreas Gustafsson
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson return (ISC_FALSE);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews}
559bb1016f1b00a3661cb2790dc837a977057b86Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsstatic void
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsadd_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_sockaddr_t *sa;
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence
84d982f168ca104186490a93190c8e28d333026dMark Andrews if (triededns512(fctx, address))
f53848e17123569387b279578f0100dca5407da5Mark Andrews return;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson
f53848e17123569387b279578f0100dca5407da5Mark Andrews sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews sizeof(*sa));
f53848e17123569387b279578f0100dca5407da5Mark Andrews if (sa == NULL)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews return;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews *sa = *address;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews}
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graffstatic isc_result_t
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrewsresquery_send(resquery_t *query) {
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff fetchctx_t *fctx;
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews isc_result_t result;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews dns_name_t *qname = NULL;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_rdataset_t *qrdataset = NULL;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_region_t r;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews dns_resolver_t *res;
e02884167b7c969b56413f76c48c3802c4dca14dAndreas Gustafsson isc_task_t *task;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_socket_t *socket;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_buffer_t tcpbuffer;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_sockaddr_t *address;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_buffer_t *buffer;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_netaddr_t ipaddr;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews dns_tsigkey_t *tsigkey = NULL;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews dns_peer_t *peer = NULL;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_boolean_t useedns;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews dns_compress_t cctx;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson isc_boolean_t cleanup_cctx = ISC_FALSE;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews isc_boolean_t secure_domain;
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews fctx = query->fctx;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews QTRACE("send");
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews res = fctx->res;
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews task = res->buckets[fctx->bucketnum].task;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews address = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews if ((query->options & DNS_FETCHOPT_TCP) != 0) {
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews * Reserve space for the TCP message length.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data));
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff isc_buffer_init(&query->buffer, query->data + 2,
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews sizeof(query->data) - 2);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews buffer = &tcpbuffer;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews } else {
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews isc_buffer_init(&query->buffer, query->data,
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff sizeof(query->data));
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews buffer = &query->buffer;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews }
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence
8b61d2012063306528286680bd9f086fa868d86eMark Andrews result = dns_message_gettempname(fctx->qmessage, &qname);
8b61d2012063306528286680bd9f086fa868d86eMark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto cleanup_temps;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto cleanup_temps;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews /*
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews * Get a query id from the dispatch.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews result = dns_dispatch_addresponse(query->dispatch,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence &query->addrinfo->sockaddr,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence task,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence resquery_response,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews query,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews &query->id,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews &query->dispentry);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto cleanup_temps;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx->qmessage->opcode = dns_opcode_query;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff * Set up question.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_name_init(qname, NULL);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_name_clone(&fctx->name, qname);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_rdataset_init(qrdataset);
8126e45e8cc3fd54517c034dd30a42928f5206e3Andreas Gustafsson dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews ISC_LIST_APPEND(qname->list, qrdataset, link);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews qname = NULL;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews qrdataset = NULL;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff /*
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews * Set RD if the client has requested that we do a recursive query,
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence * or if we're sending to a forwarder.
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews */
8b61d2012063306528286680bd9f086fa868d86eMark Andrews if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 ||
8b61d2012063306528286680bd9f086fa868d86eMark Andrews ISFORWARDER(query->addrinfo))
4be19dcd14cea678511f1d1b269ab89273e987eeMark Andrews fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews /*
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * Set CD if the client says don't validate or the question is
e903df2f012869e36251e9a76b13a9aa228fb1acMichael Graff * under a secure entry point.
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson */
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson } else if (res->view->enablevalidation) {
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson result = dns_keytable_issecuredomain(res->view->secroots,
897f762e3caf052688d4ec7b725746cfef09b058Andreas Gustafsson &fctx->name,
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews &secure_domain);
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews if (result != ISC_R_SUCCESS)
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews secure_domain = ISC_FALSE;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews if (res->view->dlv != NULL)
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews secure_domain = ISC_TRUE;
cf70df7d0e24401a358f0b9c1a616ad0e8c783a6Mark Andrews if (secure_domain)
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews }
34ee961fa2f0f5f2ee3cff40fdb4d7d7b48b7728Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * We don't have to set opcode because it defaults to query.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx->qmessage->id = query->id;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews /*
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * Convert the question to wire format.
8b61d2012063306528286680bd9f086fa868d86eMark Andrews */
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews result = dns_compress_init(&cctx, -1, fctx->res->mctx);
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews if (result != ISC_R_SUCCESS)
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews goto cleanup_message;
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews cleanup_cctx = ISC_TRUE;
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff result = dns_message_renderbegin(fctx->qmessage, &cctx,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews &query->buffer);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if (result != ISC_R_SUCCESS)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews goto cleanup_message;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews result = dns_message_rendersection(fctx->qmessage,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews DNS_SECTION_QUESTION, 0);
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews if (result != ISC_R_SUCCESS)
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews goto cleanup_message;
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews
8b61d2012063306528286680bd9f086fa868d86eMark Andrews peer = NULL;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer);
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews /*
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * The ADB does not know about servers with "edns no". Check this,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * and then inform the ADB for future use.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews */
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 &&
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews peer != NULL &&
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS &&
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff !useedns)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews query->options |= DNS_FETCHOPT_NOEDNS0;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews dns_adb_changeflags(fctx->adb,
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews query->addrinfo,
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington DNS_FETCHOPT_NOEDNS0,
0f80bfec687db08a6e6ce945ef1d818da06c7ca9Brian Wellington DNS_FETCHOPT_NOEDNS0);
c27767a7b946f4c6f08d33129691e2d6339e8350Brian Wellington }
c27767a7b946f4c6f08d33129691e2d6339e8350Brian Wellington
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington /*
dac1e1dd18b62be8cc3bec1a3656968b7b8633e6Brian Wellington * Use EDNS0, unless the caller doesn't want it, or we know that
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * the remote server doesn't like it.
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews */
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if ((triededns512(fctx, &query->addrinfo->sockaddr) ||
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) &&
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson query->options |= DNS_FETCHOPT_NOEDNS0;
8068ceb2a9cc56d18016c3cd94a09e4bb0bc7b0dAndreas Gustafsson FCTXTRACE("too many timeouts, disabling EDNS0");
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews } else if ((triededns(fctx, &query->addrinfo->sockaddr) ||
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews fctx->timeouts >= MAX_EDNS0_TIMEOUTS) &&
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews query->options |= DNS_FETCHOPT_EDNS512;
390b2077fc751105e40174ceaa1ce34ef06e7dd4Mark Andrews FCTXTRACE("too many timeouts, setting EDNS size to 512");
8b61d2012063306528286680bd9f086fa868d86eMark Andrews }
8b61d2012063306528286680bd9f086fa868d86eMark Andrews
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews unsigned int version = 0; /* Default version. */
440164d3e36353a4b9801fcc05fe66b6cf1fb8ceMark Andrews unsigned int flags;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews isc_uint16_t udpsize = res->udpsize;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews flags = query->addrinfo->flags;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews version = flags & DNS_FETCHOPT_EDNSVERSIONMASK;
46993e1d9d18410a5852b7d990338b70b158855cMichael Graff version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews }
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews udpsize = 512;
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews else if (peer != NULL)
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews (void)dns_peer_getudpsize(peer, &udpsize);
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews result = fctx_addopt(fctx->qmessage, version, udpsize);
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews if (result != ISC_R_SUCCESS) {
046a9aca49bdc25bd57d75fd0dd34c021722f095Mark Andrews /*
a5c30de2601a1d130a15a78cf3dc7610a02b2d85Mark Andrews * We couldn't add the OPT, but we'll press on.
78da321b437bbb690ef570ccf17dcc8583a5a4a0Mark Andrews * We're not using EDNS0, so set the NOEDNS0
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * bit.
8b61d2012063306528286680bd9f086fa868d86eMark Andrews */
8b61d2012063306528286680bd9f086fa868d86eMark Andrews query->options |= DNS_FETCHOPT_NOEDNS0;
8b61d2012063306528286680bd9f086fa868d86eMark Andrews }
8b61d2012063306528286680bd9f086fa868d86eMark Andrews } else {
8b61d2012063306528286680bd9f086fa868d86eMark Andrews /*
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * We know this server doesn't like EDNS0, so we
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * won't use it. Set the NOEDNS0 bit since we're
8b61d2012063306528286680bd9f086fa868d86eMark Andrews * not using EDNS0.
8b61d2012063306528286680bd9f086fa868d86eMark Andrews */
query->options |= DNS_FETCHOPT_NOEDNS0;
}
}
/*
* If we need EDNS0 to do this query and aren't using it, we lose.
*/
if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) {
result = DNS_R_SERVFAIL;
goto cleanup_message;
}
if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
add_triededns(fctx, &query->addrinfo->sockaddr);
if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
add_triededns512(fctx, &query->addrinfo->sockaddr);
/*
* Clear CD if EDNS is not in use.
*/
if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0)
fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD;
/*
* Add TSIG record tailored to the current recipient.
*/
result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
goto cleanup_message;
if (tsigkey != NULL) {
result = dns_message_settsigkey(fctx->qmessage, tsigkey);
dns_tsigkey_detach(&tsigkey);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
}
result = dns_message_rendersection(fctx->qmessage,
DNS_SECTION_ADDITIONAL, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
result = dns_message_renderend(fctx->qmessage);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
dns_compress_invalidate(&cctx);
cleanup_cctx = ISC_FALSE;
if (dns_message_gettsigkey(fctx->qmessage) != NULL) {
dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage),
&query->tsigkey);
result = dns_message_getquerytsig(fctx->qmessage,
fctx->res->mctx,
&query->tsig);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
}
/*
* If using TCP, write the length of the message at the beginning
* of the buffer.
*/
if ((query->options & DNS_FETCHOPT_TCP) != 0) {
isc_buffer_usedregion(&query->buffer, &r);
isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length);
isc_buffer_add(&tcpbuffer, r.length);
}
/*
* We're now done with the query message.
*/
dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
socket = dns_dispatch_getsocket(query->dispatch);
/*
* Send the query!
*/
if ((query->options & DNS_FETCHOPT_TCP) == 0)
address = &query->addrinfo->sockaddr;
isc_buffer_usedregion(buffer, &r);
/*
* XXXRTH Make sure we don't send to ourselves! We should probably
* prune out these addresses when we get them from the ADB.
*/
result = isc_socket_sendto(socket, &r, task, resquery_senddone,
query, address, NULL);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
query->sends++;
QTRACE("sent");
return (ISC_R_SUCCESS);
cleanup_message:
if (cleanup_cctx)
dns_compress_invalidate(&cctx);
dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
/*
* Stop the dispatcher from listening.
*/
dns_dispatch_removeresponse(&query->dispentry, NULL);
cleanup_temps:
if (qname != NULL)
dns_message_puttempname(fctx->qmessage, &qname);
if (qrdataset != NULL)
dns_message_puttemprdataset(fctx->qmessage, &qrdataset);
return (result);
}
static void
resquery_connected(isc_task_t *task, isc_event_t *event) {
isc_socketevent_t *sevent = (isc_socketevent_t *)event;
resquery_t *query = event->ev_arg;
isc_boolean_t retry = ISC_FALSE;
isc_result_t result;
unsigned int attrs;
fetchctx_t *fctx;
REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
REQUIRE(VALID_QUERY(query));
QTRACE("connected");
UNUSED(task);
/*
* XXXRTH
*
* Currently we don't wait for the connect event before retrying
* a query. This means that if we get really behind, we may end
* up doing extra work!
*/
query->connects--;
fctx = query->fctx;
if (RESQUERY_CANCELED(query)) {
/*
* This query was canceled while the connect() was in
* progress.
*/
isc_socket_detach(&query->tcpsocket);
resquery_destroy(&query);
} else {
switch (sevent->result) {
case ISC_R_SUCCESS:
/*
* We are connected. Create a dispatcher and
* send the query.
*/
attrs = 0;
attrs |= DNS_DISPATCHATTR_TCP;
attrs |= DNS_DISPATCHATTR_PRIVATE;
attrs |= DNS_DISPATCHATTR_CONNECTED;
if (isc_sockaddr_pf(&query->addrinfo->sockaddr) ==
AF_INET)
attrs |= DNS_DISPATCHATTR_IPV4;
else
attrs |= DNS_DISPATCHATTR_IPV6;
attrs |= DNS_DISPATCHATTR_MAKEQUERY;
result = dns_dispatch_createtcp(query->dispatchmgr,
query->tcpsocket,
query->fctx->res->taskmgr,
4096, 2, 1, 1, 3, attrs,
&query->dispatch);
/*
* Regardless of whether dns_dispatch_create()
* succeeded or not, we don't need our reference
* to the socket anymore.
*/
isc_socket_detach(&query->tcpsocket);
if (result == ISC_R_SUCCESS)
result = resquery_send(query);
if (result != ISC_R_SUCCESS) {
fctx_cancelquery(&query, NULL, NULL,
ISC_FALSE);
fctx_done(fctx, result);
}
break;
case ISC_R_NETUNREACH:
case ISC_R_HOSTUNREACH:
case ISC_R_CONNREFUSED:
case ISC_R_NOPERM:
case ISC_R_ADDRNOTAVAIL:
case ISC_R_CONNECTIONRESET:
/*
* No route to remote.
*/
isc_socket_detach(&query->tcpsocket);
fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
retry = ISC_TRUE;
break;
default:
isc_socket_detach(&query->tcpsocket);
fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
break;
}
}
isc_event_free(&event);
if (retry) {
/*
* Behave as if the idle timer has expired. For TCP
* connections this may not actually reflect the latest timer.
*/
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
result = fctx_stopidletimer(fctx);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
else
fctx_try(fctx);
}
}
static void
fctx_finddone(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx;
dns_adbfind_t *find;
dns_resolver_t *res;
isc_boolean_t want_try = ISC_FALSE;
isc_boolean_t want_done = ISC_FALSE;
isc_boolean_t bucket_empty = ISC_FALSE;
unsigned int bucketnum;
find = event->ev_sender;
fctx = event->ev_arg;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
UNUSED(task);
FCTXTRACE("finddone");
INSIST(fctx->pending > 0);
fctx->pending--;
if (ADDRWAIT(fctx)) {
/*
* The fetch is waiting for a name to be found.
*/
INSIST(!SHUTTINGDOWN(fctx));
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES)
want_try = ISC_TRUE;
else if (fctx->pending == 0) {
/*
* We've got nothing else to wait for and don't
* know the answer. There's nothing to do but
* fail the fctx.
*/
want_done = ISC_TRUE;
}
} else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 &&
ISC_LIST_EMPTY(fctx->validators)) {
bucketnum = fctx->bucketnum;
LOCK(&res->buckets[bucketnum].lock);
/*
* Note that we had to wait until we had the lock before
* looking at fctx->references.
*/
if (fctx->references == 0)
bucket_empty = fctx_destroy(fctx);
UNLOCK(&res->buckets[bucketnum].lock);
}
isc_event_free(&event);
dns_adb_destroyfind(&find);
if (want_try)
fctx_try(fctx);
else if (want_done)
fctx_done(fctx, ISC_R_FAILURE);
else if (bucket_empty)
empty_bucket(res);
}
static inline isc_boolean_t
bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) {
isc_sockaddr_t *sa;
for (sa = ISC_LIST_HEAD(fctx->bad);
sa != NULL;
sa = ISC_LIST_NEXT(sa, link)) {
if (isc_sockaddr_equal(sa, address))
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static inline isc_boolean_t
mark_bad(fetchctx_t *fctx) {
dns_adbfind_t *curr;
dns_adbaddrinfo_t *addrinfo;
isc_boolean_t all_bad = ISC_TRUE;
/*
* Mark all known bad servers, so we don't try to talk to them
* again.
*/
/*
* Mark any bad nameservers.
*/
for (curr = ISC_LIST_HEAD(fctx->finds);
curr != NULL;
curr = ISC_LIST_NEXT(curr, publink)) {
for (addrinfo = ISC_LIST_HEAD(curr->list);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (bad_server(fctx, &addrinfo->sockaddr))
addrinfo->flags |= FCTX_ADDRINFO_MARK;
else
all_bad = ISC_FALSE;
}
}
/*
* Mark any bad forwarders.
*/
for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (bad_server(fctx, &addrinfo->sockaddr))
addrinfo->flags |= FCTX_ADDRINFO_MARK;
else
all_bad = ISC_FALSE;
}
/*
* Mark any bad alternates.
*/
for (curr = ISC_LIST_HEAD(fctx->altfinds);
curr != NULL;
curr = ISC_LIST_NEXT(curr, publink)) {
for (addrinfo = ISC_LIST_HEAD(curr->list);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (bad_server(fctx, &addrinfo->sockaddr))
addrinfo->flags |= FCTX_ADDRINFO_MARK;
else
all_bad = ISC_FALSE;
}
}
for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (bad_server(fctx, &addrinfo->sockaddr))
addrinfo->flags |= FCTX_ADDRINFO_MARK;
else
all_bad = ISC_FALSE;
}
return (all_bad);
}
static void
add_bad(fetchctx_t *fctx, isc_sockaddr_t *address, isc_result_t reason) {
char namebuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
char classbuf[64];
char typebuf[64];
char code[64];
isc_buffer_t b;
isc_sockaddr_t *sa;
const char *sep1, *sep2;
if (bad_server(fctx, address)) {
/*
* We already know this server is bad.
*/
return;
}
FCTXTRACE("add_bad");
sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
sizeof(*sa));
if (sa == NULL)
return;
*sa = *address;
ISC_LIST_INITANDAPPEND(fctx->bad, sa, link);
if (reason == DNS_R_LAME) /* already logged */
return;
if (reason == DNS_R_UNEXPECTEDRCODE) {
isc_buffer_init(&b, code, sizeof(code) - 1);
dns_rcode_totext(fctx->rmessage->rcode, &b);
code[isc_buffer_usedlength(&b)] = '\0';
sep1 = "(";
sep2 = ") ";
} else if (reason == DNS_R_UNEXPECTEDOPCODE) {
isc_buffer_init(&b, code, sizeof(code) - 1);
dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b);
code[isc_buffer_usedlength(&b)] = '\0';
sep1 = "(";
sep2 = ") ";
} else {
code[0] = '\0';
sep1 = "";
sep2 = "";
}
dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf));
isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
"%s %s%s%sresolving '%s/%s/%s': %s",
dns_result_totext(reason), sep1, code, sep2,
namebuf, typebuf, classbuf, addrbuf);
}
static void
sort_adbfind(dns_adbfind_t *find) {
dns_adbaddrinfo_t *best, *curr;
dns_adbaddrinfolist_t sorted;
/*
* Lame N^2 bubble sort.
*/
ISC_LIST_INIT(sorted);
while (!ISC_LIST_EMPTY(find->list)) {
best = ISC_LIST_HEAD(find->list);
curr = ISC_LIST_NEXT(best, publink);
while (curr != NULL) {
if (curr->srtt < best->srtt)
best = curr;
curr = ISC_LIST_NEXT(curr, publink);
}
ISC_LIST_UNLINK(find->list, best, publink);
ISC_LIST_APPEND(sorted, best, publink);
}
find->list = sorted;
}
static void
sort_finds(fetchctx_t *fctx) {
dns_adbfind_t *best, *curr;
dns_adbfindlist_t sorted;
dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
/*
* Lame N^2 bubble sort.
*/
ISC_LIST_INIT(sorted);
while (!ISC_LIST_EMPTY(fctx->finds)) {
best = ISC_LIST_HEAD(fctx->finds);
bestaddrinfo = ISC_LIST_HEAD(best->list);
INSIST(bestaddrinfo != NULL);
curr = ISC_LIST_NEXT(best, publink);
while (curr != NULL) {
addrinfo = ISC_LIST_HEAD(curr->list);
INSIST(addrinfo != NULL);
if (addrinfo->srtt < bestaddrinfo->srtt) {
best = curr;
bestaddrinfo = addrinfo;
}
curr = ISC_LIST_NEXT(curr, publink);
}
ISC_LIST_UNLINK(fctx->finds, best, publink);
ISC_LIST_APPEND(sorted, best, publink);
}
fctx->finds = sorted;
ISC_LIST_INIT(sorted);
while (!ISC_LIST_EMPTY(fctx->altfinds)) {
best = ISC_LIST_HEAD(fctx->altfinds);
bestaddrinfo = ISC_LIST_HEAD(best->list);
INSIST(bestaddrinfo != NULL);
curr = ISC_LIST_NEXT(best, publink);
while (curr != NULL) {
addrinfo = ISC_LIST_HEAD(curr->list);
INSIST(addrinfo != NULL);
if (addrinfo->srtt < bestaddrinfo->srtt) {
best = curr;
bestaddrinfo = addrinfo;
}
curr = ISC_LIST_NEXT(curr, publink);
}
ISC_LIST_UNLINK(fctx->altfinds, best, publink);
ISC_LIST_APPEND(sorted, best, publink);
}
fctx->altfinds = sorted;
}
static void
findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
unsigned int options, unsigned int flags, isc_stdtime_t now,
isc_boolean_t *need_alternate)
{
dns_adbaddrinfo_t *ai;
dns_adbfind_t *find;
dns_resolver_t *res;
isc_boolean_t unshared;
isc_result_t result;
res = fctx->res;
unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0);
/*
* If this name is a subdomain of the query domain, tell
* the ADB to start looking using zone/hint data. This keeps us
* from getting stuck if the nameserver is beneath the zone cut
* and we don't know its address (e.g. because the A record has
* expired).
*/
if (dns_name_issubdomain(name, &fctx->domain))
options |= DNS_ADBFIND_STARTATZONE;
options |= DNS_ADBFIND_GLUEOK;
options |= DNS_ADBFIND_HINTOK;
/*
* See what we know about this address.
*/
find = NULL;
result = dns_adb_createfind(fctx->adb,
res->buckets[fctx->bucketnum].task,
fctx_finddone, fctx, name,
&fctx->name, fctx->type,
options, now, NULL,
res->view->dstport, &find);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_ALIAS) {
/*
* XXXRTH Follow the CNAME/DNAME chain?
*/
dns_adb_destroyfind(&find);
}
} else if (!ISC_LIST_EMPTY(find->list)) {
/*
* We have at least some of the addresses for the
* name.
*/
INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
sort_adbfind(find);
if (flags != 0 || port != 0) {
for (ai = ISC_LIST_HEAD(find->list);
ai != NULL;
ai = ISC_LIST_NEXT(ai, publink)) {
ai->flags |= flags;
if (port != 0)
isc_sockaddr_setport(&ai->sockaddr,
port);
}
}
if ((flags & FCTX_ADDRINFO_FORWARDER) != 0)
ISC_LIST_APPEND(fctx->altfinds, find, publink);
else
ISC_LIST_APPEND(fctx->finds, find, publink);
} else {
/*
* We don't know any of the addresses for this
* name.
*/
if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
/*
* We're looking for them and will get an
* event about it later.
*/
fctx->pending++;
/*
* Bootstrap.
*/
if (need_alternate != NULL &&
!*need_alternate && unshared &&
((res->dispatchv4 == NULL &&
find->result_v6 != DNS_R_NXDOMAIN) ||
(res->dispatchv6 == NULL &&
find->result_v4 != DNS_R_NXDOMAIN)))
*need_alternate = ISC_TRUE;
} else {
/*
* If we know there are no addresses for
* the family we are using then try to add
* an alternative server.
*/
if (need_alternate != NULL && !*need_alternate &&
((res->dispatchv4 == NULL &&
find->result_v6 == DNS_R_NXRRSET) ||
(res->dispatchv6 == NULL &&
find->result_v4 == DNS_R_NXRRSET)))
*need_alternate = ISC_TRUE;
dns_adb_destroyfind(&find);
}
}
}
static isc_result_t
fctx_getaddresses(fetchctx_t *fctx) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
dns_resolver_t *res;
isc_stdtime_t now;
unsigned int stdoptions;
isc_sockaddr_t *sa;
dns_adbaddrinfo_t *ai;
isc_boolean_t all_bad;
dns_rdata_ns_t ns;
isc_boolean_t need_alternate = ISC_FALSE;
FCTXTRACE("getaddresses");
/*
* Don't pound on remote servers. (Failsafe!)
*/
fctx->restarts++;
if (fctx->restarts > 10) {
FCTXTRACE("too many restarts");
return (DNS_R_SERVFAIL);
}
res = fctx->res;
stdoptions = 0; /* Keep compiler happy. */
/*
* Forwarders.
*/
INSIST(ISC_LIST_EMPTY(fctx->forwaddrs));
INSIST(ISC_LIST_EMPTY(fctx->altaddrs));
/*
* If this fctx has forwarders, use them; otherwise use any
* selective forwarders specified in the view; otherwise use the
* resolver's forwarders (if any).
*/
sa = ISC_LIST_HEAD(fctx->forwarders);
if (sa == NULL) {
dns_forwarders_t *forwarders = NULL;
dns_name_t *name = &fctx->name;
dns_name_t suffix;
unsigned int labels;
/*
* DS records are found in the parent server.
* Strip label to get the correct forwarder (if any).
*/
if (fctx->type == dns_rdatatype_ds &&
dns_name_countlabels(name) > 1) {
dns_name_init(&suffix, NULL);
labels = dns_name_countlabels(name);
dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
name = &suffix;
}
result = dns_fwdtable_find(fctx->res->view->fwdtable, name,
&forwarders);
if (result == ISC_R_SUCCESS) {
sa = ISC_LIST_HEAD(forwarders->addrs);
fctx->fwdpolicy = forwarders->fwdpolicy;
}
}
while (sa != NULL) {
ai = NULL;
result = dns_adb_findaddrinfo(fctx->adb,
sa, &ai, 0); /* XXXMLG */
if (result == ISC_R_SUCCESS) {
dns_adbaddrinfo_t *cur;
ai->flags |= FCTX_ADDRINFO_FORWARDER;
cur = ISC_LIST_HEAD(fctx->forwaddrs);
while (cur != NULL && cur->srtt < ai->srtt)
cur = ISC_LIST_NEXT(cur, publink);
if (cur != NULL)
ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur,
ai, publink);
else
ISC_LIST_APPEND(fctx->forwaddrs, ai, publink);
}
sa = ISC_LIST_NEXT(sa, link);
}
/*
* If the forwarding policy is "only", we don't need the addresses
* of the nameservers.
*/
if (fctx->fwdpolicy == dns_fwdpolicy_only)
goto out;
/*
* Normal nameservers.
*/
stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT;
if (fctx->restarts == 1) {
/*
* To avoid sending out a flood of queries likely to
* result in NXRRSET, we suppress fetches for address
* families we don't have the first time through,
* provided that we have addresses in some family we
* can use.
*
* We don't want to set this option all the time, since
* if fctx->restarts > 1, we've clearly been having trouble
* with the addresses we had, so getting more could help.
*/
stdoptions |= DNS_ADBFIND_AVOIDFETCHES;
}
if (res->dispatchv4 != NULL)
stdoptions |= DNS_ADBFIND_INET;
if (res->dispatchv6 != NULL)
stdoptions |= DNS_ADBFIND_INET6;
isc_stdtime_get(&now);
INSIST(ISC_LIST_EMPTY(fctx->finds));
INSIST(ISC_LIST_EMPTY(fctx->altfinds));
for (result = dns_rdataset_first(&fctx->nameservers);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&fctx->nameservers))
{
dns_rdataset_current(&fctx->nameservers, &rdata);
/*
* Extract the name from the NS record.
*/
result = dns_rdata_tostruct(&rdata, &ns, NULL);
if (result != ISC_R_SUCCESS)
continue;
findname(fctx, &ns.name, 0, stdoptions, 0, now,
&need_alternate);
dns_rdata_reset(&rdata);
dns_rdata_freestruct(&ns);
}
if (result != ISC_R_NOMORE)
return (result);
/*
* Do we need to use 6 to 4?
*/
if (need_alternate) {
int family;
alternate_t *a;
family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET;
for (a = ISC_LIST_HEAD(fctx->res->alternates);
a != NULL;
a = ISC_LIST_NEXT(a, link)) {
if (!a->isaddress) {
findname(fctx, &a->_u._n.name, a->_u._n.port,
stdoptions, FCTX_ADDRINFO_FORWARDER,
now, NULL);
continue;
}
if (isc_sockaddr_pf(&a->_u.addr) != family)
continue;
ai = NULL;
result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr,
&ai, 0);
if (result == ISC_R_SUCCESS) {
dns_adbaddrinfo_t *cur;
ai->flags |= FCTX_ADDRINFO_FORWARDER;
cur = ISC_LIST_HEAD(fctx->altaddrs);
while (cur != NULL && cur->srtt < ai->srtt)
cur = ISC_LIST_NEXT(cur, publink);
if (cur != NULL)
ISC_LIST_INSERTBEFORE(fctx->altaddrs,
cur, ai, publink);
else
ISC_LIST_APPEND(fctx->altaddrs, ai,
publink);
}
}
}
out:
/*
* Mark all known bad servers.
*/
all_bad = mark_bad(fctx);
/*
* How are we doing?
*/
if (all_bad) {
/*
* We've got no addresses.
*/
if (fctx->pending > 0) {
/*
* We're fetching the addresses, but don't have any
* yet. Tell the caller to wait for an answer.
*/
result = DNS_R_WAIT;
} else {
/*
* We've lost completely. We don't know any
* addresses, and the ADB has told us it can't get
* them.
*/
FCTXTRACE("no addresses");
result = ISC_R_FAILURE;
}
} else {
/*
* We've found some addresses. We might still be looking
* for more addresses.
*/
sort_finds(fctx);
result = ISC_R_SUCCESS;
}
return (result);
}
static inline void
possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr)
{
isc_netaddr_t na;
char buf[ISC_NETADDR_FORMATSIZE];
isc_sockaddr_t *sa;
isc_boolean_t aborted = ISC_FALSE;
isc_boolean_t bogus;
dns_acl_t *blackhole;
isc_netaddr_t ipaddr;
dns_peer_t *peer = NULL;
dns_resolver_t *res;
const char *msg = NULL;
sa = &addr->sockaddr;
res = fctx->res;
isc_netaddr_fromsockaddr(&ipaddr, sa);
blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr);
(void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer);
if (blackhole != NULL) {
int match;
if (dns_acl_match(&ipaddr, NULL, blackhole,
&res->view->aclenv,
&match, NULL) == ISC_R_SUCCESS &&
match > 0)
aborted = ISC_TRUE;
}
if (peer != NULL &&
dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS &&
bogus)
aborted = ISC_TRUE;
if (aborted) {
addr->flags |= FCTX_ADDRINFO_MARK;
msg = "ignoring blackholed / bogus server: ";
} else if (isc_sockaddr_ismulticast(sa)) {
addr->flags |= FCTX_ADDRINFO_MARK;
msg = "ignoring multicast address: ";
} else if (isc_sockaddr_isexperimental(sa)) {
addr->flags |= FCTX_ADDRINFO_MARK;
msg = "ignoring experimental address: ";
} else if (sa->type.sa.sa_family != AF_INET6) {
return;
} else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) {
addr->flags |= FCTX_ADDRINFO_MARK;
msg = "ignoring IPv6 mapped IPV4 address: ";
} else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) {
addr->flags |= FCTX_ADDRINFO_MARK;
msg = "ignoring IPv6 compatibility IPV4 address: ";
} else
return;
if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3)))
return;
isc_netaddr_fromsockaddr(&na, sa);
isc_netaddr_format(&na, buf, sizeof(buf));
FCTXTRACE2(msg, buf);
}
static inline dns_adbaddrinfo_t *
fctx_nextaddress(fetchctx_t *fctx) {
dns_adbfind_t *find, *start;
dns_adbaddrinfo_t *addrinfo;
dns_adbaddrinfo_t *faddrinfo;
/*
* Return the next untried address, if any.
*/
/*
* Find the first unmarked forwarder (if any).
*/
for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (!UNMARKED(addrinfo))
continue;
possibly_mark(fctx, addrinfo);
if (UNMARKED(addrinfo)) {
addrinfo->flags |= FCTX_ADDRINFO_MARK;
fctx->find = NULL;
return (addrinfo);
}
}
/*
* No forwarders. Move to the next find.
*/
fctx->attributes |= FCTX_ATTR_TRIEDFIND;
find = fctx->find;
if (find == NULL)
find = ISC_LIST_HEAD(fctx->finds);
else {
find = ISC_LIST_NEXT(find, publink);
if (find == NULL)
find = ISC_LIST_HEAD(fctx->finds);
}
/*
* Find the first unmarked addrinfo.
*/
addrinfo = NULL;
if (find != NULL) {
start = find;
do {
for (addrinfo = ISC_LIST_HEAD(find->list);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (!UNMARKED(addrinfo))
continue;
possibly_mark(fctx, addrinfo);
if (UNMARKED(addrinfo)) {
addrinfo->flags |= FCTX_ADDRINFO_MARK;
break;
}
}
if (addrinfo != NULL)
break;
find = ISC_LIST_NEXT(find, publink);
if (find == NULL)
find = ISC_LIST_HEAD(fctx->finds);
} while (find != start);
}
fctx->find = find;
if (addrinfo != NULL)
return (addrinfo);
/*
* No nameservers left. Try alternates.
*/
fctx->attributes |= FCTX_ATTR_TRIEDALT;
find = fctx->altfind;
if (find == NULL)
find = ISC_LIST_HEAD(fctx->altfinds);
else {
find = ISC_LIST_NEXT(find, publink);
if (find == NULL)
find = ISC_LIST_HEAD(fctx->altfinds);
}
/*
* Find the first unmarked addrinfo.
*/
addrinfo = NULL;
if (find != NULL) {
start = find;
do {
for (addrinfo = ISC_LIST_HEAD(find->list);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (!UNMARKED(addrinfo))
continue;
possibly_mark(fctx, addrinfo);
if (UNMARKED(addrinfo)) {
addrinfo->flags |= FCTX_ADDRINFO_MARK;
break;
}
}
if (addrinfo != NULL)
break;
find = ISC_LIST_NEXT(find, publink);
if (find == NULL)
find = ISC_LIST_HEAD(fctx->altfinds);
} while (find != start);
}
faddrinfo = addrinfo;
/*
* See if we have a better alternate server by address.
*/
for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
addrinfo != NULL;
addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
if (!UNMARKED(addrinfo))
continue;
possibly_mark(fctx, addrinfo);
if (UNMARKED(addrinfo) &&
(faddrinfo == NULL ||
addrinfo->srtt < faddrinfo->srtt)) {
if (faddrinfo != NULL)
faddrinfo->flags &= ~FCTX_ADDRINFO_MARK;
addrinfo->flags |= FCTX_ADDRINFO_MARK;
break;
}
}
if (addrinfo == NULL) {
addrinfo = faddrinfo;
fctx->altfind = find;
}
return (addrinfo);
}
static void
fctx_try(fetchctx_t *fctx) {
isc_result_t result;
dns_adbaddrinfo_t *addrinfo;
FCTXTRACE("try");
REQUIRE(!ADDRWAIT(fctx));
addrinfo = fctx_nextaddress(fctx);
if (addrinfo == NULL) {
/*
* We have no more addresses. Start over.
*/
fctx_cancelqueries(fctx, ISC_TRUE);
fctx_cleanupfinds(fctx);
fctx_cleanupaltfinds(fctx);
fctx_cleanupforwaddrs(fctx);
fctx_cleanupaltaddrs(fctx);
result = fctx_getaddresses(fctx);
if (result == DNS_R_WAIT) {
/*
* Sleep waiting for addresses.
*/
FCTXTRACE("addrwait");
fctx->attributes |= FCTX_ATTR_ADDRWAIT;
return;
} else if (result != ISC_R_SUCCESS) {
/*
* Something bad happened.
*/
fctx_done(fctx, result);
return;
}
addrinfo = fctx_nextaddress(fctx);
/*
* While we may have addresses from the ADB, they
* might be bad ones. In this case, return SERVFAIL.
*/
if (addrinfo == NULL) {
fctx_done(fctx, DNS_R_SERVFAIL);
return;
}
}
result = fctx_query(fctx, addrinfo, fctx->options);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
}
static isc_boolean_t
fctx_destroy(fetchctx_t *fctx) {
dns_resolver_t *res;
unsigned int bucketnum;
isc_sockaddr_t *sa, *next_sa;
/*
* Caller must be holding the bucket lock.
*/
REQUIRE(VALID_FCTX(fctx));
REQUIRE(fctx->state == fetchstate_done ||
fctx->state == fetchstate_init);
REQUIRE(ISC_LIST_EMPTY(fctx->events));
REQUIRE(ISC_LIST_EMPTY(fctx->queries));
REQUIRE(ISC_LIST_EMPTY(fctx->finds));
REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
REQUIRE(fctx->pending == 0);
REQUIRE(ISC_LIST_EMPTY(fctx->validators));
REQUIRE(fctx->references == 0);
FCTXTRACE("destroy");
res = fctx->res;
bucketnum = fctx->bucketnum;
ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
/*
* Free bad.
*/
for (sa = ISC_LIST_HEAD(fctx->bad);
sa != NULL;
sa = next_sa) {
next_sa = ISC_LIST_NEXT(sa, link);
ISC_LIST_UNLINK(fctx->bad, sa, link);
isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
}
for (sa = ISC_LIST_HEAD(fctx->edns);
sa != NULL;
sa = next_sa) {
next_sa = ISC_LIST_NEXT(sa, link);
ISC_LIST_UNLINK(fctx->edns, sa, link);
isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
}
for (sa = ISC_LIST_HEAD(fctx->edns512);
sa != NULL;
sa = next_sa) {
next_sa = ISC_LIST_NEXT(sa, link);
ISC_LIST_UNLINK(fctx->edns512, sa, link);
isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
}
isc_timer_detach(&fctx->timer);
dns_message_destroy(&fctx->rmessage);
dns_message_destroy(&fctx->qmessage);
if (dns_name_countlabels(&fctx->domain) > 0)
dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
dns_db_detach(&fctx->cache);
dns_adb_detach(&fctx->adb);
isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
LOCK(&res->nlock);
res->nfctx--;
UNLOCK(&res->nlock);
if (res->buckets[bucketnum].exiting &&
ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
return (ISC_TRUE);
return (ISC_FALSE);
}
/*
* Fetch event handlers.
*/
static void
fctx_timeout(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx = event->ev_arg;
REQUIRE(VALID_FCTX(fctx));
UNUSED(task);
FCTXTRACE("timeout");
if (event->ev_type == ISC_TIMEREVENT_LIFE) {
fctx_done(fctx, ISC_R_TIMEDOUT);
} else {
isc_result_t result;
fctx->timeouts++;
/*
* We could cancel the running queries here, or we could let
* them keep going. Right now we choose the latter...
*/
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
/*
* Our timer has triggered. Reestablish the fctx lifetime
* timer.
*/
result = fctx_starttimer(fctx);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
else
/*
* Keep trying.
*/
fctx_try(fctx);
}
isc_event_free(&event);
}
static void
fctx_shutdown(fetchctx_t *fctx) {
isc_event_t *cevent;
/*
* Start the shutdown process for fctx, if it isn't already underway.
*/
FCTXTRACE("shutdown");
/*
* The caller must be holding the appropriate bucket lock.
*/
if (fctx->want_shutdown)
return;
fctx->want_shutdown = ISC_TRUE;
/*
* Unless we're still initializing (in which case the
* control event is still outstanding), we need to post
* the control event to tell the fetch we want it to
* exit.
*/
if (fctx->state != fetchstate_init) {
cevent = &fctx->control_event;
isc_task_send(fctx->res->buckets[fctx->bucketnum].task,
&cevent);
}
}
static void
fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx = event->ev_arg;
isc_boolean_t bucket_empty = ISC_FALSE;
dns_resolver_t *res;
unsigned int bucketnum;
dns_validator_t *validator;
REQUIRE(VALID_FCTX(fctx));
UNUSED(task);
res = fctx->res;
bucketnum = fctx->bucketnum;
FCTXTRACE("doshutdown");
/*
* An fctx that is shutting down is no longer in ADDRWAIT mode.
*/
fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
/*
* Cancel all pending validators. Note that this must be done
* without the bucket lock held, since that could cause deadlock.
*/
validator = ISC_LIST_HEAD(fctx->validators);
while (validator != NULL) {
dns_validator_cancel(validator);
validator = ISC_LIST_NEXT(validator, link);
}
if (fctx->nsfetch != NULL)
dns_resolver_cancelfetch(fctx->nsfetch);
/*
* Shut down anything that is still running on behalf of this
* fetch. To avoid deadlock with the ADB, we must do this
* before we lock the bucket lock.
*/
fctx_stopeverything(fctx, ISC_FALSE);
LOCK(&res->buckets[bucketnum].lock);
fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
INSIST(fctx->state == fetchstate_active ||
fctx->state == fetchstate_done);
INSIST(fctx->want_shutdown);
if (fctx->state != fetchstate_done) {
fctx->state = fetchstate_done;
fctx_sendevents(fctx, ISC_R_CANCELED);
}
if (fctx->references == 0 && fctx->pending == 0 &&
ISC_LIST_EMPTY(fctx->validators))
bucket_empty = fctx_destroy(fctx);
UNLOCK(&res->buckets[bucketnum].lock);
if (bucket_empty)
empty_bucket(res);
}
static void
fctx_start(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx = event->ev_arg;
isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE;
dns_resolver_t *res;
unsigned int bucketnum;
REQUIRE(VALID_FCTX(fctx));
UNUSED(task);
res = fctx->res;
bucketnum = fctx->bucketnum;
FCTXTRACE("start");
LOCK(&res->buckets[bucketnum].lock);
INSIST(fctx->state == fetchstate_init);
if (fctx->want_shutdown) {
/*
* We haven't started this fctx yet, and we've been requested
* to shut it down.
*/
fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
fctx->state = fetchstate_done;
fctx_sendevents(fctx, ISC_R_CANCELED);
/*
* Since we haven't started, we INSIST that we have no
* pending ADB finds and no pending validations.
*/
INSIST(fctx->pending == 0);
INSIST(ISC_LIST_EMPTY(fctx->validators));
if (fctx->references == 0) {
/*
* It's now safe to destroy this fctx.
*/
bucket_empty = fctx_destroy(fctx);
}
done = ISC_TRUE;
} else {
/*
* Normal fctx startup.
*/
fctx->state = fetchstate_active;
/*
* Reset the control event for later use in shutting down
* the fctx.
*/
ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx,
NULL, NULL, NULL);
}
UNLOCK(&res->buckets[bucketnum].lock);
if (!done) {
isc_result_t result;
/*
* All is well. Start working on the fetch.
*/
result = fctx_starttimer(fctx);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
else
fctx_try(fctx);
} else if (bucket_empty)
empty_bucket(res);
}
/*
* Fetch Creation, Joining, and Cancelation.
*/
static inline isc_result_t
fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client,
dns_messageid_t id, isc_taskaction_t action, void *arg,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
dns_fetch_t *fetch)
{
isc_task_t *clone;
dns_fetchevent_t *event;
FCTXTRACE("join");
/*
* We store the task we're going to send this event to in the
* sender field. We'll make the fetch the sender when we actually
* send the event.
*/
clone = NULL;
isc_task_attach(task, &clone);
event = (dns_fetchevent_t *)
isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE,
action, arg, sizeof(*event));
if (event == NULL) {
isc_task_detach(&clone);
return (ISC_R_NOMEMORY);
}
event->result = DNS_R_SERVFAIL;
event->qtype = fctx->type;
event->db = NULL;
event->node = NULL;
event->rdataset = rdataset;
event->sigrdataset = sigrdataset;
event->fetch = fetch;
event->client = client;
event->id = id;
dns_fixedname_init(&event->foundname);
/*
* Make sure that we can store the sigrdataset in the
* first event if it is needed by any of the events.
*/
if (event->sigrdataset != NULL)
ISC_LIST_PREPEND(fctx->events, event, ev_link);
else
ISC_LIST_APPEND(fctx->events, event, ev_link);
fctx->references++;
fetch->magic = DNS_FETCH_MAGIC;
fetch->private = fctx;
return (ISC_R_SUCCESS);
}
static isc_result_t
fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
dns_name_t *domain, dns_rdataset_t *nameservers,
unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp)
{
fetchctx_t *fctx;
isc_result_t result;
isc_result_t iresult;
isc_interval_t interval;
dns_fixedname_t fixed;
unsigned int findoptions = 0;
char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
dns_name_t suffix;
/*
* Caller must be holding the lock for bucket number 'bucketnum'.
*/
REQUIRE(fctxp != NULL && *fctxp == NULL);
fctx = isc_mem_get(res->buckets[bucketnum].mctx, sizeof(*fctx));
if (fctx == NULL)
return (ISC_R_NOMEMORY);
dns_name_format(name, buf, sizeof(buf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
strcat(buf, "/"); /* checked */
strcat(buf, typebuf); /* checked */
fctx->info = isc_mem_strdup(res->buckets[bucketnum].mctx, buf);
if (fctx->info == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup_fetch;
}
FCTXTRACE("create");
dns_name_init(&fctx->name, NULL);
result = dns_name_dup(name, res->buckets[bucketnum].mctx, &fctx->name);
if (result != ISC_R_SUCCESS)
goto cleanup_info;
dns_name_init(&fctx->domain, NULL);
dns_rdataset_init(&fctx->nameservers);
fctx->type = type;
fctx->options = options;
/*
* Note! We do not attach to the task. We are relying on the
* resolver to ensure that this task doesn't go away while we are
* using it.
*/
fctx->res = res;
fctx->references = 0;
fctx->bucketnum = bucketnum;
fctx->state = fetchstate_init;
fctx->want_shutdown = ISC_FALSE;
fctx->cloned = ISC_FALSE;
ISC_LIST_INIT(fctx->queries);
ISC_LIST_INIT(fctx->finds);
ISC_LIST_INIT(fctx->altfinds);
ISC_LIST_INIT(fctx->forwaddrs);
ISC_LIST_INIT(fctx->altaddrs);
ISC_LIST_INIT(fctx->forwarders);
fctx->fwdpolicy = dns_fwdpolicy_none;
ISC_LIST_INIT(fctx->bad);
ISC_LIST_INIT(fctx->edns);
ISC_LIST_INIT(fctx->edns512);
ISC_LIST_INIT(fctx->validators);
fctx->find = NULL;
fctx->altfind = NULL;
fctx->pending = 0;
fctx->restarts = 0;
fctx->timeouts = 0;
fctx->attributes = 0;
fctx->spilled = ISC_FALSE;
dns_name_init(&fctx->nsname, NULL);
fctx->nsfetch = NULL;
dns_rdataset_init(&fctx->nsrrset);
if (domain == NULL) {
dns_forwarders_t *forwarders = NULL;
unsigned int labels;
/*
* DS records are found in the parent server.
* Strip label to get the correct forwarder (if any).
*/
if (fctx->type == dns_rdatatype_ds &&
dns_name_countlabels(name) > 1) {
dns_name_init(&suffix, NULL);
labels = dns_name_countlabels(name);
dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
name = &suffix;
}
dns_fixedname_init(&fixed);
domain = dns_fixedname_name(&fixed);
result = dns_fwdtable_find2(fctx->res->view->fwdtable, name,
domain, &forwarders);
if (result == ISC_R_SUCCESS)
fctx->fwdpolicy = forwarders->fwdpolicy;
if (fctx->fwdpolicy != dns_fwdpolicy_only) {
/*
* The caller didn't supply a query domain and
* nameservers, and we're not in forward-only mode,
* so find the best nameservers to use.
*/
if (dns_rdatatype_atparent(type))
findoptions |= DNS_DBFIND_NOEXACT;
result = dns_view_findzonecut(res->view, name, domain,
0, findoptions, ISC_TRUE,
&fctx->nameservers,
NULL);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
result = dns_name_dup(domain,
res->buckets[bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS) {
dns_rdataset_disassociate(&fctx->nameservers);
goto cleanup_name;
}
} else {
/*
* We're in forward-only mode. Set the query domain.
*/
result = dns_name_dup(domain,
res->buckets[bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
}
} else {
result = dns_name_dup(domain,
res->buckets[bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
dns_rdataset_clone(nameservers, &fctx->nameservers);
}
INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
fctx->qmessage = NULL;
result = dns_message_create(res->buckets[bucketnum].mctx,
DNS_MESSAGE_INTENTRENDER,
&fctx->qmessage);
if (result != ISC_R_SUCCESS)
goto cleanup_domain;
fctx->rmessage = NULL;
result = dns_message_create(res->buckets[bucketnum].mctx,
DNS_MESSAGE_INTENTPARSE,
&fctx->rmessage);
if (result != ISC_R_SUCCESS)
goto cleanup_qmessage;
/*
* Compute an expiration time for the entire fetch.
*/
isc_interval_set(&interval, 30, 0); /* XXXRTH constant */
iresult = isc_time_nowplusinterval(&fctx->expires, &interval);
if (iresult != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_time_nowplusinterval: %s",
isc_result_totext(iresult));
result = ISC_R_UNEXPECTED;
goto cleanup_rmessage;
}
/*
* Default retry interval initialization. We set the interval now
* mostly so it won't be uninitialized. It will be set to the
* correct value before a query is issued.
*/
isc_interval_set(&fctx->interval, 2, 0);
/*
* Create an inactive timer. It will be made active when the fetch
* is actually started.
*/
fctx->timer = NULL;
iresult = isc_timer_create(res->timermgr, isc_timertype_inactive,
NULL, NULL,
res->buckets[bucketnum].task, fctx_timeout,
fctx, &fctx->timer);
if (iresult != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_timer_create: %s",
isc_result_totext(iresult));
result = ISC_R_UNEXPECTED;
goto cleanup_rmessage;
}
/*
* Attach to the view's cache and adb.
*/
fctx->cache = NULL;
dns_db_attach(res->view->cachedb, &fctx->cache);
fctx->adb = NULL;
dns_adb_attach(res->view->adb, &fctx->adb);
ISC_LIST_INIT(fctx->events);
ISC_LINK_INIT(fctx, link);
fctx->magic = FCTX_MAGIC;
ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
LOCK(&res->nlock);
res->nfctx++;
UNLOCK(&res->nlock);
*fctxp = fctx;
return (ISC_R_SUCCESS);
cleanup_rmessage:
dns_message_destroy(&fctx->rmessage);
cleanup_qmessage:
dns_message_destroy(&fctx->qmessage);
cleanup_domain:
if (dns_name_countlabels(&fctx->domain) > 0)
dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
cleanup_name:
dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
cleanup_info:
isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
cleanup_fetch:
isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
return (result);
}
/*
* Handle Responses
*/
static inline isc_boolean_t
is_lame(fetchctx_t *fctx) {
dns_message_t *message = fctx->rmessage;
dns_name_t *name;
dns_rdataset_t *rdataset;
isc_result_t result;
if (message->rcode != dns_rcode_noerror &&
message->rcode != dns_rcode_nxdomain)
return (ISC_FALSE);
if (message->counts[DNS_SECTION_ANSWER] != 0)
return (ISC_FALSE);
if (message->counts[DNS_SECTION_AUTHORITY] == 0)
return (ISC_FALSE);
result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
dns_namereln_t namereln;
int order;
unsigned int labels;
if (rdataset->type != dns_rdatatype_ns)
continue;
namereln = dns_name_fullcompare(name, &fctx->domain,
&order, &labels);
if (namereln == dns_namereln_equal &&
(message->flags & DNS_MESSAGEFLAG_AA) != 0)
return (ISC_FALSE);
if (namereln == dns_namereln_subdomain)
return (ISC_FALSE);
return (ISC_TRUE);
}
result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
}
return (ISC_FALSE);
}
static inline void
log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) {
char namebuf[DNS_NAME_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
"lame server resolving '%s' (in '%s'?): %s",
namebuf, domainbuf, addrbuf);
}
static inline isc_result_t
same_question(fetchctx_t *fctx) {
isc_result_t result;
dns_message_t *message = fctx->rmessage;
dns_name_t *name;
dns_rdataset_t *rdataset;
/*
* Caller must be holding the fctx lock.
*/
/*
* XXXRTH Currently we support only one question.
*/
if (message->counts[DNS_SECTION_QUESTION] != 1)
return (DNS_R_FORMERR);
result = dns_message_firstname(message, DNS_SECTION_QUESTION);
if (result != ISC_R_SUCCESS)
return (result);
name = NULL;
dns_message_currentname(message, DNS_SECTION_QUESTION, &name);
rdataset = ISC_LIST_HEAD(name->list);
INSIST(rdataset != NULL);
INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
if (fctx->type != rdataset->type ||
fctx->res->rdclass != rdataset->rdclass ||
!dns_name_equal(&fctx->name, name))
return (DNS_R_FORMERR);
return (ISC_R_SUCCESS);
}
static void
clone_results(fetchctx_t *fctx) {
dns_fetchevent_t *event, *hevent;
isc_result_t result;
dns_name_t *name, *hname;
FCTXTRACE("clone_results");
/*
* Set up any other events to have the same data as the first
* event.
*
* Caller must be holding the appropriate lock.
*/
fctx->cloned = ISC_TRUE;
hevent = ISC_LIST_HEAD(fctx->events);
if (hevent == NULL)
return;
hname = dns_fixedname_name(&hevent->foundname);
for (event = ISC_LIST_NEXT(hevent, ev_link);
event != NULL;
event = ISC_LIST_NEXT(event, ev_link)) {
name = dns_fixedname_name(&event->foundname);
result = dns_name_copy(hname, name, NULL);
if (result != ISC_R_SUCCESS)
event->result = result;
else
event->result = hevent->result;
dns_db_attach(hevent->db, &event->db);
dns_db_attachnode(hevent->db, hevent->node, &event->node);
INSIST(hevent->rdataset != NULL);
INSIST(event->rdataset != NULL);
if (dns_rdataset_isassociated(hevent->rdataset))
dns_rdataset_clone(hevent->rdataset, event->rdataset);
INSIST(! (hevent->sigrdataset == NULL &&
event->sigrdataset != NULL));
if (hevent->sigrdataset != NULL &&
dns_rdataset_isassociated(hevent->sigrdataset) &&
event->sigrdataset != NULL)
dns_rdataset_clone(hevent->sigrdataset,
event->sigrdataset);
}
}
#define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0)
#define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0)
#define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0)
#define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0)
#define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0)
#define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0)
#define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0)
/*
* Destroy '*fctx' if it is ready to be destroyed (i.e., if it has
* no references and is no longer waiting for any events). If this
* was the last fctx in the resolver, destroy the resolver.
*
* Requires:
* '*fctx' is shutting down.
*/
static void
maybe_destroy(fetchctx_t *fctx) {
unsigned int bucketnum;
isc_boolean_t bucket_empty = ISC_FALSE;
dns_resolver_t *res = fctx->res;
REQUIRE(SHUTTINGDOWN(fctx));
if (fctx->pending != 0 || !ISC_LIST_EMPTY(fctx->validators))
return;
bucketnum = fctx->bucketnum;
LOCK(&res->buckets[bucketnum].lock);
if (fctx->references == 0)
bucket_empty = fctx_destroy(fctx);
UNLOCK(&res->buckets[bucketnum].lock);
if (bucket_empty)
empty_bucket(res);
}
/*
* The validator has finished.
*/
static void
validated(isc_task_t *task, isc_event_t *event) {
isc_result_t result = ISC_R_SUCCESS;
isc_result_t eresult = ISC_R_SUCCESS;
isc_stdtime_t now;
fetchctx_t *fctx;
dns_validatorevent_t *vevent;
dns_fetchevent_t *hevent;
dns_rdataset_t *ardataset = NULL;
dns_rdataset_t *asigrdataset = NULL;
dns_dbnode_t *node = NULL;
isc_boolean_t negative;
isc_boolean_t chaining;
isc_boolean_t sentresponse;
isc_uint32_t ttl;
dns_dbnode_t *nsnode = NULL;
dns_name_t *name;
dns_rdataset_t *rdataset;
dns_rdataset_t *sigrdataset;
dns_valarg_t *valarg;
dns_adbaddrinfo_t *addrinfo;
UNUSED(task); /* for now */
REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE);
valarg = event->ev_arg;
fctx = valarg->fctx;
addrinfo = valarg->addrinfo;
REQUIRE(VALID_FCTX(fctx));
REQUIRE(!ISC_LIST_EMPTY(fctx->validators));
vevent = (dns_validatorevent_t *)event;
FCTXTRACE("received validation completion event");
ISC_LIST_UNLINK(fctx->validators, vevent->validator, link);
/*
* Destroy the validator early so that we can
* destroy the fctx if necessary.
*/
dns_validator_destroy(&vevent->validator);
isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
valarg, sizeof(*valarg));
negative = ISC_TF(vevent->rdataset == NULL);
sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0);
/*
* If shutting down, ignore the results. Check to see if we're
* done waiting for validator completions and ADB pending events; if
* so, destroy the fctx.
*/
if (SHUTTINGDOWN(fctx) && !sentresponse) {
maybe_destroy(fctx); /* Locks bucket. */
goto cleanup_event;
}
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
/*
* If chaining, we need to make sure that the right result code is
* returned, and that the rdatasets are bound.
*/
if (vevent->result == ISC_R_SUCCESS &&
!negative &&
vevent->rdataset != NULL &&
CHAINING(vevent->rdataset))
{
if (vevent->rdataset->type == dns_rdatatype_cname)
eresult = DNS_R_CNAME;
else {
INSIST(vevent->rdataset->type == dns_rdatatype_dname);
eresult = DNS_R_DNAME;
}
chaining = ISC_TRUE;
} else
chaining = ISC_FALSE;
/*
* Either we're not shutting down, or we are shutting down but want
* to cache the result anyway (if this was a validation started by
* a query with cd set)
*/
hevent = ISC_LIST_HEAD(fctx->events);
if (hevent != NULL) {
if (!negative && !chaining &&
(fctx->type == dns_rdatatype_any ||
fctx->type == dns_rdatatype_rrsig ||
fctx->type == dns_rdatatype_sig)) {
/*
* Don't bind rdatasets; the caller
* will iterate the node.
*/
} else {
ardataset = hevent->rdataset;
asigrdataset = hevent->sigrdataset;
}
}
if (vevent->result != ISC_R_SUCCESS) {
FCTXTRACE("validation failed");
result = ISC_R_NOTFOUND;
if (vevent->rdataset != NULL)
result = dns_db_findnode(fctx->cache, vevent->name,
ISC_TRUE, &node);
if (result == ISC_R_SUCCESS)
(void)dns_db_deleterdataset(fctx->cache, node, NULL,
vevent->type, 0);
if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL)
(void)dns_db_deleterdataset(fctx->cache, node, NULL,
dns_rdatatype_rrsig,
vevent->type);
if (result == ISC_R_SUCCESS)
dns_db_detachnode(fctx->cache, &node);
result = vevent->result;
add_bad(fctx, &addrinfo->sockaddr, result);
isc_event_free(&event);
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
if (sentresponse)
fctx_done(fctx, result); /* Locks bucket. */
else
fctx_try(fctx); /* Locks bucket. */
return;
}
isc_stdtime_get(&now);
if (negative) {
dns_rdatatype_t covers;
FCTXTRACE("nonexistence validation OK");
if (fctx->rmessage->rcode == dns_rcode_nxdomain)
covers = dns_rdatatype_any;
else
covers = fctx->type;
result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE,
&node);
if (result != ISC_R_SUCCESS)
goto noanswer_response;
/*
* If we are asking for a SOA record set the cache time
* to zero to facilitate locating the containing zone of
* a arbitary zone.
*/
ttl = fctx->res->view->maxncachettl;
if (fctx->type == dns_rdatatype_soa &&
covers == dns_rdatatype_any &&
fctx->res->zero_no_soa_ttl)
ttl = 0;
result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
covers, now, ttl,
ardataset, &eresult);
if (result != ISC_R_SUCCESS)
goto noanswer_response;
goto answer_response;
}
FCTXTRACE("validation OK");
if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) {
result = dns_rdataset_addnoqname(vevent->rdataset,
vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
INSIST(vevent->sigrdataset != NULL);
vevent->sigrdataset->ttl = vevent->rdataset->ttl;
}
/*
* The data was already cached as pending data.
* Re-cache it as secure and bind the cached
* rdatasets to the first event on the fetch
* event list.
*/
result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node);
if (result != ISC_R_SUCCESS)
goto noanswer_response;
result = dns_db_addrdataset(fctx->cache, node, NULL, now,
vevent->rdataset, 0, ardataset);
if (result != ISC_R_SUCCESS &&
result != DNS_R_UNCHANGED)
goto noanswer_response;
if (vevent->sigrdataset != NULL) {
result = dns_db_addrdataset(fctx->cache, node, NULL, now,
vevent->sigrdataset, 0,
asigrdataset);
if (result != ISC_R_SUCCESS &&
result != DNS_R_UNCHANGED)
goto noanswer_response;
}
if (sentresponse) {
/*
* If we only deferred the destroy because we wanted to cache
* the data, destroy now.
*/
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
if (SHUTTINGDOWN(fctx))
maybe_destroy(fctx); /* Locks bucket. */
goto cleanup_event;
}
if (!ISC_LIST_EMPTY(fctx->validators)) {
INSIST(!negative);
INSIST(fctx->type == dns_rdatatype_any ||
fctx->type == dns_rdatatype_rrsig ||
fctx->type == dns_rdatatype_sig);
/*
* Don't send a response yet - we have
* more rdatasets that still need to
* be validated.
*/
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
goto cleanup_event;
}
answer_response:
/*
* Cache any NS/NSEC records that happened to be validated.
*/
result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
&name);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if ((rdataset->type != dns_rdatatype_ns &&
rdataset->type != dns_rdatatype_nsec) ||
rdataset->trust != dns_trust_secure)
continue;
for (sigrdataset = ISC_LIST_HEAD(name->list);
sigrdataset != NULL;
sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
if (sigrdataset->type != dns_rdatatype_rrsig ||
sigrdataset->covers != rdataset->type)
continue;
break;
}
if (sigrdataset == NULL ||
sigrdataset->trust != dns_trust_secure)
continue;
result = dns_db_findnode(fctx->cache, name, ISC_TRUE,
&nsnode);
if (result != ISC_R_SUCCESS)
continue;
result = dns_db_addrdataset(fctx->cache, nsnode, NULL,
now, rdataset, 0, NULL);
if (result == ISC_R_SUCCESS)
result = dns_db_addrdataset(fctx->cache, nsnode,
NULL, now,
sigrdataset, 0,
NULL);
dns_db_detachnode(fctx->cache, &nsnode);
}
result = dns_message_nextname(fctx->rmessage,
DNS_SECTION_AUTHORITY);
}
result = ISC_R_SUCCESS;
/*
* Respond with an answer, positive or negative,
* as opposed to an error. 'node' must be non-NULL.
*/
fctx->attributes |= FCTX_ATTR_HAVEANSWER;
if (hevent != NULL) {
hevent->result = eresult;
RUNTIME_CHECK(dns_name_copy(vevent->name,
dns_fixedname_name(&hevent->foundname), NULL)
== ISC_R_SUCCESS);
dns_db_attach(fctx->cache, &hevent->db);
hevent->node = node;
node = NULL;
clone_results(fctx);
}
noanswer_response:
if (node != NULL)
dns_db_detachnode(fctx->cache, &node);
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
fctx_done(fctx, result); /* Locks bucket. */
cleanup_event:
isc_event_free(&event);
}
static inline isc_result_t
cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
isc_stdtime_t now) {
dns_rdataset_t *rdataset, *sigrdataset;
dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset;
dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL;
dns_dbnode_t *node, **anodep;
dns_db_t **adbp;
dns_name_t *aname;
dns_resolver_t *res;
isc_boolean_t need_validation, secure_domain, have_answer;
isc_result_t result, eresult;
dns_fetchevent_t *event;
unsigned int options;
isc_task_t *task;
isc_boolean_t fail;
unsigned int valoptions = 0;
/*
* The appropriate bucket lock must be held.
*/
res = fctx->res;
need_validation = ISC_FALSE;
secure_domain = ISC_FALSE;
have_answer = ISC_FALSE;
eresult = ISC_R_SUCCESS;
task = res->buckets[fctx->bucketnum].task;
/*
* Is DNSSEC validation required for this name?
*/
if (res->view->enablevalidation) {
result = dns_keytable_issecuredomain(res->view->secroots, name,
&secure_domain);
if (result != ISC_R_SUCCESS)
return (result);
if (!secure_domain && res->view->dlv != NULL) {
valoptions = DNS_VALIDATOR_DLV;
secure_domain = ISC_TRUE;
}
}
if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
need_validation = ISC_FALSE;
else
need_validation = secure_domain;
adbp = NULL;
aname = NULL;
anodep = NULL;
ardataset = NULL;
asigrdataset = NULL;
event = NULL;
if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 &&
!need_validation) {
have_answer = ISC_TRUE;
event = ISC_LIST_HEAD(fctx->events);
if (event != NULL) {
adbp = &event->db;
aname = dns_fixedname_name(&event->foundname);
result = dns_name_copy(name, aname, NULL);
if (result != ISC_R_SUCCESS)
return (result);
anodep = &event->node;
/*
* If this is an ANY, SIG or RRSIG query, we're not
* going to return any rdatasets, unless we encountered
* a CNAME or DNAME as "the answer". In this case,
* we're going to return DNS_R_CNAME or DNS_R_DNAME
* and we must set up the rdatasets.
*/
if ((fctx->type != dns_rdatatype_any &&
fctx->type != dns_rdatatype_rrsig &&
fctx->type != dns_rdatatype_sig) ||
(name->attributes & DNS_NAMEATTR_CHAINING) != 0) {
ardataset = event->rdataset;
asigrdataset = event->sigrdataset;
}
}
}
/*
* Find or create the cache node.
*/
node = NULL;
result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Cache or validate each cacheable rdataset.
*/
fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (!CACHE(rdataset))
continue;
if (CHECKNAMES(rdataset)) {
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
char classbuf[DNS_RDATATYPE_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(rdataset->type, typebuf,
sizeof(typebuf));
dns_rdataclass_format(rdataset->rdclass, classbuf,
sizeof(classbuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
"check-names %s %s/%s/%s",
fail ? "failure" : "warning",
namebuf, typebuf, classbuf);
if (fail) {
if (ANSWER(rdataset))
return (DNS_R_BADNAME);
continue;
}
}
/*
* Enforce the configure maximum cache TTL.
*/
if (rdataset->ttl > res->view->maxcachettl)
rdataset->ttl = res->view->maxcachettl;
/*
* If this rrset is in a secure domain, do DNSSEC validation
* for it, unless it is glue.
*/
if (secure_domain && rdataset->trust != dns_trust_glue) {
/*
* RRSIGs are validated as part of validating the
* type they cover.
*/
if (rdataset->type == dns_rdatatype_rrsig)
continue;
/*
* Find the SIG for this rdataset, if we have it.
*/
for (sigrdataset = ISC_LIST_HEAD(name->list);
sigrdataset != NULL;
sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
if (sigrdataset->type == dns_rdatatype_rrsig &&
sigrdataset->covers == rdataset->type)
break;
}
if (sigrdataset == NULL) {
if (!ANSWER(rdataset) && need_validation) {
/*
* Ignore non-answer rdatasets that
* are missing signatures.
*/
continue;
}
}
/*
* Normalize the rdataset and sigrdataset TTLs.
*/
if (sigrdataset != NULL) {
rdataset->ttl = ISC_MIN(rdataset->ttl,
sigrdataset->ttl);
sigrdataset->ttl = rdataset->ttl;
}
/*
* Cache this rdataset/sigrdataset pair as
* pending data.
*/
rdataset->trust = dns_trust_pending;
if (sigrdataset != NULL)
sigrdataset->trust = dns_trust_pending;
if (!need_validation)
addedrdataset = ardataset;
else
addedrdataset = NULL;
result = dns_db_addrdataset(fctx->cache, node, NULL,
now, rdataset, 0,
addedrdataset);
if (result == DNS_R_UNCHANGED)
result = ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
break;
if (sigrdataset != NULL) {
if (!need_validation)
addedrdataset = asigrdataset;
else
addedrdataset = NULL;
result = dns_db_addrdataset(fctx->cache,
node, NULL, now,
sigrdataset, 0,
addedrdataset);
if (result == DNS_R_UNCHANGED)
result = ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
break;
} else if (!ANSWER(rdataset))
continue;
if (ANSWER(rdataset) && need_validation) {
if (fctx->type != dns_rdatatype_any &&
fctx->type != dns_rdatatype_rrsig &&
fctx->type != dns_rdatatype_sig) {
/*
* This is The Answer. We will
* validate it, but first we cache
* the rest of the response - it may
* contain useful keys.
*/
INSIST(valrdataset == NULL &&
valsigrdataset == NULL);
valrdataset = rdataset;
valsigrdataset = sigrdataset;
} else {
/*
* This is one of (potentially)
* multiple answers to an ANY
* or SIG query. To keep things
* simple, we just start the
* validator right away rather
* than caching first and
* having to remember which
* rdatasets needed validation.
*/
result = valcreate(fctx, addrinfo,
name, rdataset->type,
rdataset,
sigrdataset,
valoptions, task);
}
} else if (CHAINING(rdataset)) {
if (rdataset->type == dns_rdatatype_cname)
eresult = DNS_R_CNAME;
else {
INSIST(rdataset->type ==
dns_rdatatype_dname);
eresult = DNS_R_DNAME;
}
}
} else if (!EXTERNAL(rdataset)) {
/*
* It's OK to cache this rdataset now.
*/
if (ANSWER(rdataset))
addedrdataset = ardataset;
else if (ANSWERSIG(rdataset))
addedrdataset = asigrdataset;
else
addedrdataset = NULL;
if (CHAINING(rdataset)) {
if (rdataset->type == dns_rdatatype_cname)
eresult = DNS_R_CNAME;
else {
INSIST(rdataset->type ==
dns_rdatatype_dname);
eresult = DNS_R_DNAME;
}
}
if (rdataset->trust == dns_trust_glue &&
(rdataset->type == dns_rdatatype_ns ||
(rdataset->type == dns_rdatatype_rrsig &&
rdataset->covers == dns_rdatatype_ns))) {
/*
* If the trust level is 'dns_trust_glue'
* then we are adding data from a referral
* we got while executing the search algorithm.
* New referral data always takes precedence
* over the existing cache contents.
*/
options = DNS_DBADD_FORCE;
} else
options = 0;
/*
* Now we can add the rdataset.
*/
result = dns_db_addrdataset(fctx->cache,
node, NULL, now,
rdataset,
options,
addedrdataset);
if (result == DNS_R_UNCHANGED) {
if (ANSWER(rdataset) &&
ardataset != NULL &&
ardataset->type == 0) {
/*
* The answer in the cache is better
* than the answer we found, and is
* a negative cache entry, so we
* must set eresult appropriately.
*/
if (NXDOMAIN(ardataset))
eresult =
DNS_R_NCACHENXDOMAIN;
else
eresult =
DNS_R_NCACHENXRRSET;
}
result = ISC_R_SUCCESS;
} else if (result != ISC_R_SUCCESS)
break;
}
}
if (valrdataset != NULL)
result = valcreate(fctx, addrinfo, name, fctx->type,
valrdataset, valsigrdataset, valoptions,
task);
if (result == ISC_R_SUCCESS && have_answer) {
fctx->attributes |= FCTX_ATTR_HAVEANSWER;
if (event != NULL) {
event->result = eresult;
dns_db_attach(fctx->cache, adbp);
*anodep = node;
node = NULL;
clone_results(fctx);
}
}
if (node != NULL)
dns_db_detachnode(fctx->cache, &node);
return (result);
}
static inline isc_result_t
cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now)
{
isc_result_t result;
dns_section_t section;
dns_name_t *name;
FCTXTRACE("cache_message");
fctx->attributes &= ~FCTX_ATTR_WANTCACHE;
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
for (section = DNS_SECTION_ANSWER;
section <= DNS_SECTION_ADDITIONAL;
section++) {
result = dns_message_firstname(fctx->rmessage, section);
while (result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(fctx->rmessage, section,
&name);
if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) {
result = cache_name(fctx, name, addrinfo, now);
if (result != ISC_R_SUCCESS)
break;
}
result = dns_message_nextname(fctx->rmessage, section);
}
if (result != ISC_R_NOMORE)
break;
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
return (result);
}
/*
* Do what dns_ncache_add() does, and then compute an appropriate eresult.
*/
static isc_result_t
ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
dns_rdataset_t *ardataset,
isc_result_t *eresultp)
{
isc_result_t result;
dns_rdataset_t rdataset;
if (ardataset == NULL) {
dns_rdataset_init(&rdataset);
ardataset = &rdataset;
}
result = dns_ncache_add(message, cache, node, covers, now,
maxttl, ardataset);
if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
/*
* If the cache now contains a negative entry and we
* care about whether it is DNS_R_NCACHENXDOMAIN or
* DNS_R_NCACHENXRRSET then extract it.
*/
if (ardataset->type == 0) {
/*
* The cache data is a negative cache entry.
*/
if (NXDOMAIN(ardataset))
*eresultp = DNS_R_NCACHENXDOMAIN;
else
*eresultp = DNS_R_NCACHENXRRSET;
} else {
/*
* Either we don't care about the nature of the
* cache rdataset (because no fetch is interested
* in the outcome), or the cache rdataset is not
* a negative cache entry. Whichever case it is,
* we can return success.
*
* XXXRTH There's a CNAME/DNAME problem here.
*/
*eresultp = ISC_R_SUCCESS;
}
result = ISC_R_SUCCESS;
}
if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset))
dns_rdataset_disassociate(ardataset);
return (result);
}
static inline isc_result_t
ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
dns_rdatatype_t covers, isc_stdtime_t now)
{
isc_result_t result, eresult;
dns_name_t *name;
dns_resolver_t *res;
dns_db_t **adbp;
dns_dbnode_t *node, **anodep;
dns_rdataset_t *ardataset;
isc_boolean_t need_validation, secure_domain;
dns_name_t *aname;
dns_fetchevent_t *event;
isc_uint32_t ttl;
unsigned int valoptions = 0;
FCTXTRACE("ncache_message");
fctx->attributes &= ~FCTX_ATTR_WANTNCACHE;
res = fctx->res;
need_validation = ISC_FALSE;
secure_domain = ISC_FALSE;
eresult = ISC_R_SUCCESS;
name = &fctx->name;
node = NULL;
/*
* XXXMPA remove when we follow cnames and adjust the setting
* of FCTX_ATTR_WANTNCACHE in noanswer_response().
*/
INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0);
/*
* Is DNSSEC validation required for this name?
*/
if (fctx->res->view->enablevalidation) {
result = dns_keytable_issecuredomain(res->view->secroots, name,
&secure_domain);
if (result != ISC_R_SUCCESS)
return (result);
if (!secure_domain && res->view->dlv != NULL) {
valoptions = DNS_VALIDATOR_DLV;
secure_domain = ISC_TRUE;
}
}
if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
need_validation = ISC_FALSE;
else
need_validation = secure_domain;
if (secure_domain) {
/*
* Mark all rdatasets as pending.
*/
dns_rdataset_t *trdataset;
dns_name_t *tname;
result = dns_message_firstname(fctx->rmessage,
DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) {
tname = NULL;
dns_message_currentname(fctx->rmessage,
DNS_SECTION_AUTHORITY,
&tname);
for (trdataset = ISC_LIST_HEAD(tname->list);
trdataset != NULL;
trdataset = ISC_LIST_NEXT(trdataset, link))
trdataset->trust = dns_trust_pending;
result = dns_message_nextname(fctx->rmessage,
DNS_SECTION_AUTHORITY);
}
if (result != ISC_R_NOMORE)
return (result);
}
if (need_validation) {
/*
* Do negative response validation.
*/
result = valcreate(fctx, addrinfo, name, fctx->type,
NULL, NULL, valoptions,
res->buckets[fctx->bucketnum].task);
/*
* If validation is necessary, return now. Otherwise continue
* to process the message, letting the validation complete
* in its own good time.
*/
return (result);
}
LOCK(&res->buckets[fctx->bucketnum].lock);
adbp = NULL;
aname = NULL;
anodep = NULL;
ardataset = NULL;
if (!HAVE_ANSWER(fctx)) {
event = ISC_LIST_HEAD(fctx->events);
if (event != NULL) {
adbp = &event->db;
aname = dns_fixedname_name(&event->foundname);
result = dns_name_copy(name, aname, NULL);
if (result != ISC_R_SUCCESS)
goto unlock;
anodep = &event->node;
ardataset = event->rdataset;
}
} else
event = NULL;
result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
if (result != ISC_R_SUCCESS)
goto unlock;
/*
* If we are asking for a SOA record set the cache time
* to zero to facilitate locating the containing zone of
* a arbitary zone.
*/
ttl = fctx->res->view->maxncachettl;
if (fctx->type == dns_rdatatype_soa &&
covers == dns_rdatatype_any)
ttl = 0;
result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
covers, now, ttl, ardataset, &eresult);
if (result != ISC_R_SUCCESS)
goto unlock;
if (!HAVE_ANSWER(fctx)) {
fctx->attributes |= FCTX_ATTR_HAVEANSWER;
if (event != NULL) {
event->result = eresult;
dns_db_attach(fctx->cache, adbp);
*anodep = node;
node = NULL;
clone_results(fctx);
}
}
unlock:
UNLOCK(&res->buckets[fctx->bucketnum].lock);
if (node != NULL)
dns_db_detachnode(fctx->cache, &node);
return (result);
}
static inline void
mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
isc_boolean_t external, isc_boolean_t gluing)
{
name->attributes |= DNS_NAMEATTR_CACHE;
if (gluing) {
rdataset->trust = dns_trust_glue;
/*
* Glue with 0 TTL causes problems. We force the TTL to
* 1 second to prevent this.
*/
if (rdataset->ttl == 0)
rdataset->ttl = 1;
} else
rdataset->trust = dns_trust_additional;
/*
* Avoid infinite loops by only marking new rdatasets.
*/
if (!CACHE(rdataset)) {
name->attributes |= DNS_NAMEATTR_CHASE;
rdataset->attributes |= DNS_RDATASETATTR_CHASE;
}
rdataset->attributes |= DNS_RDATASETATTR_CACHE;
if (external)
rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
}
static isc_result_t
check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
fetchctx_t *fctx = arg;
isc_result_t result;
dns_name_t *name;
dns_rdataset_t *rdataset;
isc_boolean_t external;
dns_rdatatype_t rtype;
isc_boolean_t gluing;
REQUIRE(VALID_FCTX(fctx));
if (GLUING(fctx))
gluing = ISC_TRUE;
else
gluing = ISC_FALSE;
name = NULL;
rdataset = NULL;
result = dns_message_findname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
addname, dns_rdatatype_any, 0, &name,
NULL);
if (result == ISC_R_SUCCESS) {
external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
if (type == dns_rdatatype_a) {
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (rdataset->type == dns_rdatatype_rrsig)
rtype = rdataset->covers;
else
rtype = rdataset->type;
if (rtype == dns_rdatatype_a ||
rtype == dns_rdatatype_aaaa)
mark_related(name, rdataset, external,
gluing);
}
} else {
result = dns_message_findtype(name, type, 0,
&rdataset);
if (result == ISC_R_SUCCESS) {
mark_related(name, rdataset, external, gluing);
/*
* Do we have its SIG too?
*/
rdataset = NULL;
result = dns_message_findtype(name,
dns_rdatatype_rrsig,
type, &rdataset);
if (result == ISC_R_SUCCESS)
mark_related(name, rdataset, external,
gluing);
}
}
}
return (ISC_R_SUCCESS);
}
static void
chase_additional(fetchctx_t *fctx) {
isc_boolean_t rescan;
dns_section_t section = DNS_SECTION_ADDITIONAL;
isc_result_t result;
again:
rescan = ISC_FALSE;
for (result = dns_message_firstname(fctx->rmessage, section);
result == ISC_R_SUCCESS;
result = dns_message_nextname(fctx->rmessage, section)) {
dns_name_t *name = NULL;
dns_rdataset_t *rdataset;
dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
&name);
if ((name->attributes & DNS_NAMEATTR_CHASE) == 0)
continue;
name->attributes &= ~DNS_NAMEATTR_CHASE;
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (CHASE(rdataset)) {
rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
(void)dns_rdataset_additionaldata(rdataset,
check_related,
fctx);
rescan = ISC_TRUE;
}
}
}
if (rescan)
goto again;
}
static inline isc_result_t
cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_cname_t cname;
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &cname, NULL);
if (result != ISC_R_SUCCESS)
return (result);
dns_name_init(tname, NULL);
dns_name_clone(&cname.cname, tname);
dns_rdata_freestruct(&cname);
return (ISC_R_SUCCESS);
}
static inline isc_result_t
dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname,
dns_fixedname_t *fixeddname)
{
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
unsigned int nlabels;
int order;
dns_namereln_t namereln;
dns_rdata_dname_t dname;
dns_fixedname_t prefix;
/*
* Get the target name of the DNAME.
*/
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &dname, NULL);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Get the prefix of qname.
*/
namereln = dns_name_fullcompare(qname, oname, &order, &nlabels);
if (namereln != dns_namereln_subdomain) {
dns_rdata_freestruct(&dname);
return (DNS_R_FORMERR);
}
dns_fixedname_init(&prefix);
dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
dns_fixedname_init(fixeddname);
result = dns_name_concatenate(dns_fixedname_name(&prefix),
&dname.dname,
dns_fixedname_name(fixeddname), NULL);
dns_rdata_freestruct(&dname);
return (result);
}
/*
* Handle a no-answer response (NXDOMAIN, NXRRSET, or referral).
* If bind8_ns_resp is ISC_TRUE, this is a suspected BIND 8
* response to an NS query that should be treated as a referral
* even though the NS records occur in the answer section
* rather than the authority section.
*/
static isc_result_t
noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
isc_boolean_t bind8_ns_resp)
{
isc_result_t result;
dns_message_t *message;
dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name;
dns_rdataset_t *rdataset, *ns_rdataset;
isc_boolean_t done, aa, negative_response;
dns_rdatatype_t type;
dns_section_t section =
bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY;
FCTXTRACE("noanswer_response");
message = fctx->rmessage;
/*
* Setup qname.
*/
if (oqname == NULL) {
/*
* We have a normal, non-chained negative response or
* referral.
*/
if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
aa = ISC_TRUE;
else
aa = ISC_FALSE;
qname = &fctx->name;
} else {
/*
* We're being invoked by answer_response() after it has
* followed a CNAME/DNAME chain.
*/
qname = oqname;
aa = ISC_FALSE;
/*
* If the current qname is not a subdomain of the query
* domain, there's no point in looking at the authority
* section without doing DNSSEC validation.
*
* Until we do that validation, we'll just return success
* in this case.
*/
if (!dns_name_issubdomain(qname, &fctx->domain))
return (ISC_R_SUCCESS);
}
/*
* We have to figure out if this is a negative response, or a
* referral.
*/
/*
* Sometimes we can tell if its a negative response by looking at
* the message header.
*/
negative_response = ISC_FALSE;
if (message->rcode == dns_rcode_nxdomain ||
(message->counts[DNS_SECTION_ANSWER] == 0 &&
message->counts[DNS_SECTION_AUTHORITY] == 0))
negative_response = ISC_TRUE;
/*
* Process the authority section.
*/
done = ISC_FALSE;
ns_name = NULL;
ns_rdataset = NULL;
soa_name = NULL;
ds_name = NULL;
result = dns_message_firstname(message, section);
while (!done && result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(message, section, &name);
if (dns_name_issubdomain(name, &fctx->domain)) {
/*
* Look for NS/SOA RRsets first.
*/
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
type = rdataset->type;
if (type == dns_rdatatype_rrsig)
type = rdataset->covers;
if (((type == dns_rdatatype_ns ||
type == dns_rdatatype_soa) &&
!dns_name_issubdomain(qname, name)))
return (DNS_R_FORMERR);
if (type == dns_rdatatype_ns) {
/*
* NS or RRSIG NS.
*
* Only one set of NS RRs is allowed.
*/
if (rdataset->type ==
dns_rdatatype_ns) {
if (ns_name != NULL &&
name != ns_name)
return (DNS_R_FORMERR);
ns_name = name;
ns_rdataset = rdataset;
}
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
rdataset->trust = dns_trust_glue;
}
if (type == dns_rdatatype_soa) {
/*
* SOA, or RRSIG SOA.
*
* Only one SOA is allowed.
*/
if (rdataset->type ==
dns_rdatatype_soa) {
if (soa_name != NULL &&
name != soa_name)
return (DNS_R_FORMERR);
soa_name = name;
}
name->attributes |=
DNS_NAMEATTR_NCACHE;
rdataset->attributes |=
DNS_RDATASETATTR_NCACHE;
if (aa)
rdataset->trust =
dns_trust_authauthority;
else
rdataset->trust =
dns_trust_additional;
}
}
/*
* A negative response has a SOA record (Type 2)
* and a optional NS RRset (Type 1) or it has neither
* a SOA or a NS RRset (Type 3, handled above) or
* rcode is NXDOMAIN (handled above) in which case
* the NS RRset is allowed (Type 4).
*/
if (soa_name != NULL)
negative_response = ISC_TRUE;
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
type = rdataset->type;
if (type == dns_rdatatype_rrsig)
type = rdataset->covers;
if (type == dns_rdatatype_nsec) {
/*
* NSEC or RRSIG NSEC.
*/
if (negative_response) {
name->attributes |=
DNS_NAMEATTR_NCACHE;
rdataset->attributes |=
DNS_RDATASETATTR_NCACHE;
} else {
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
}
if (aa)
rdataset->trust =
dns_trust_authauthority;
else
rdataset->trust =
dns_trust_additional;
/*
* No additional data needs to be
* marked.
*/
} else if (type == dns_rdatatype_ds) {
/*
* DS or SIG DS.
*
* These should only be here if
* this is a referral, and there
* should only be one DS.
*/
if (ns_name == NULL)
return (DNS_R_FORMERR);
if (rdataset->type ==
dns_rdatatype_ds) {
if (ds_name != NULL &&
name != ds_name)
return (DNS_R_FORMERR);
ds_name = name;
}
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
if (aa)
rdataset->trust =
dns_trust_authauthority;
else
rdataset->trust =
dns_trust_additional;
}
}
}
result = dns_message_nextname(message, section);
if (result == ISC_R_NOMORE)
break;
else if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Trigger lookups for DNS nameservers.
*/
if (negative_response && message->rcode == dns_rcode_noerror &&
fctx->type == dns_rdatatype_ds && soa_name != NULL &&
dns_name_equal(soa_name, qname) &&
!dns_name_equal(qname, dns_rootname))
return (DNS_R_CHASEDSSERVERS);
/*
* Did we find anything?
*/
if (!negative_response && ns_name == NULL) {
/*
* Nope.
*/
if (oqname != NULL) {
/*
* We've already got a partial CNAME/DNAME chain,
* and haven't found else anything useful here, but
* no error has occurred since we have an answer.
*/
return (ISC_R_SUCCESS);
} else {
/*
* The responder is insane.
*/
return (DNS_R_FORMERR);
}
}
/*
* If we found both NS and SOA, they should be the same name.
*/
if (ns_name != NULL && soa_name != NULL && ns_name != soa_name)
return (DNS_R_FORMERR);
/*
* Do we have a referral? (We only want to follow a referral if
* we're not following a chain.)
*/
if (!negative_response && ns_name != NULL && oqname == NULL) {
/*
* We already know ns_name is a subdomain of fctx->domain.
* If ns_name is equal to fctx->domain, we're not making
* progress. We return DNS_R_FORMERR so that we'll keep
* trying other servers.
*/
if (dns_name_equal(ns_name, &fctx->domain))
return (DNS_R_FORMERR);
/*
* If the referral name is not a parent of the query
* name, consider the responder insane.
*/
if (! dns_name_issubdomain(&fctx->name, ns_name)) {
FCTXTRACE("referral to non-parent");
return (DNS_R_FORMERR);
}
/*
* Mark any additional data related to this rdataset.
* It's important that we do this before we change the
* query domain.
*/
INSIST(ns_rdataset != NULL);
fctx->attributes |= FCTX_ATTR_GLUING;
(void)dns_rdataset_additionaldata(ns_rdataset, check_related,
fctx);
fctx->attributes &= ~FCTX_ATTR_GLUING;
/*
* NS rdatasets with 0 TTL cause problems.
* dns_view_findzonecut() will not find them when we
* try to follow the referral, and we'll SERVFAIL
* because the best nameservers are now above QDOMAIN.
* We force the TTL to 1 second to prevent this.
*/
if (ns_rdataset->ttl == 0)
ns_rdataset->ttl = 1;
/*
* Set the current query domain to the referral name.
*
* XXXRTH We should check if we're in forward-only mode, and
* if so we should bail out.
*/
INSIST(dns_name_countlabels(&fctx->domain) > 0);
dns_name_free(&fctx->domain,
fctx->res->buckets[fctx->bucketnum].mctx);
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
dns_name_init(&fctx->domain, NULL);
result = dns_name_dup(ns_name,
fctx->res->buckets[fctx->bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS)
return (result);
fctx->attributes |= FCTX_ATTR_WANTCACHE;
return (DNS_R_DELEGATION);
}
/*
* Since we're not doing a referral, we don't want to cache any
* NS RRs we may have found.
*/
if (ns_name != NULL)
ns_name->attributes &= ~DNS_NAMEATTR_CACHE;
if (negative_response && oqname == NULL)
fctx->attributes |= FCTX_ATTR_WANTNCACHE;
return (ISC_R_SUCCESS);
}
static isc_result_t
answer_response(fetchctx_t *fctx) {
isc_result_t result;
dns_message_t *message;
dns_name_t *name, *qname, tname;
dns_rdataset_t *rdataset;
isc_boolean_t done, external, chaining, aa, found, want_chaining;
isc_boolean_t have_answer, found_cname, found_type, wanted_chaining;
unsigned int aflag;
dns_rdatatype_t type;
dns_fixedname_t dname, fqname;
FCTXTRACE("answer_response");
message = fctx->rmessage;
/*
* Examine the answer section, marking those rdatasets which are
* part of the answer and should be cached.
*/
done = ISC_FALSE;
found_cname = ISC_FALSE;
found_type = ISC_FALSE;
chaining = ISC_FALSE;
have_answer = ISC_FALSE;
want_chaining = ISC_FALSE;
if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
aa = ISC_TRUE;
else
aa = ISC_FALSE;
qname = &fctx->name;
type = fctx->type;
result = dns_message_firstname(message, DNS_SECTION_ANSWER);
while (!done && result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
if (dns_name_equal(name, qname)) {
wanted_chaining = ISC_FALSE;
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
found = ISC_FALSE;
want_chaining = ISC_FALSE;
aflag = 0;
if (rdataset->type == type && !found_cname) {
/*
* We've found an ordinary answer.
*/
found = ISC_TRUE;
found_type = ISC_TRUE;
done = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWER;
} else if (type == dns_rdatatype_any) {
/*
* We've found an answer matching
* an ANY query. There may be
* more.
*/
found = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWER;
} else if (rdataset->type == dns_rdatatype_rrsig
&& rdataset->covers == type
&& !found_cname) {
/*
* We've found a signature that
* covers the type we're looking for.
*/
found = ISC_TRUE;
found_type = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWERSIG;
} else if (rdataset->type ==
dns_rdatatype_cname
&& !found_type) {
/*
* We're looking for something else,
* but we found a CNAME.
*
* Getting a CNAME response for some
* query types is an error.
*/
if (type == dns_rdatatype_rrsig ||
type == dns_rdatatype_dnskey ||
type == dns_rdatatype_nsec)
return (DNS_R_FORMERR);
found = ISC_TRUE;
found_cname = ISC_TRUE;
want_chaining = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWER;
result = cname_target(rdataset,
&tname);
if (result != ISC_R_SUCCESS)
return (result);
} else if (rdataset->type == dns_rdatatype_rrsig
&& rdataset->covers ==
dns_rdatatype_cname
&& !found_type) {
/*
* We're looking for something else,
* but we found a SIG CNAME.
*/
found = ISC_TRUE;
found_cname = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWERSIG;
}
if (found) {
/*
* We've found an answer to our
* question.
*/
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
rdataset->trust = dns_trust_answer;
if (!chaining) {
/*
* This data is "the" answer
* to our question only if
* we're not chaining (i.e.
* if we haven't followed
* a CNAME or DNAME).
*/
INSIST(!external);
if (aflag ==
DNS_RDATASETATTR_ANSWER)
have_answer = ISC_TRUE;
name->attributes |=
DNS_NAMEATTR_ANSWER;
rdataset->attributes |= aflag;
if (aa)
rdataset->trust =
dns_trust_authanswer;
} else if (external) {
/*
* This data is outside of
* our query domain, and
* may only be cached if it
* comes from a secure zone
* and validates.
*/
rdataset->attributes |=
DNS_RDATASETATTR_EXTERNAL;
}
/*
* Mark any additional data related
* to this rdataset.
*/
(void)dns_rdataset_additionaldata(
rdataset,
check_related,
fctx);
/*
* CNAME chaining.
*/
if (want_chaining) {
wanted_chaining = ISC_TRUE;
name->attributes |=
DNS_NAMEATTR_CHAINING;
rdataset->attributes |=
DNS_RDATASETATTR_CHAINING;
qname = &tname;
}
}
/*
* We could add an "else" clause here and
* log that we're ignoring this rdataset.
*/
}
/*
* If wanted_chaining is true, we've done
* some chaining as the result of processing
* this node, and thus we need to set
* chaining to true.
*
* We don't set chaining inside of the
* rdataset loop because doing that would
* cause us to ignore the signatures of
* CNAMEs.
*/
if (wanted_chaining)
chaining = ISC_TRUE;
} else {
/*
* Look for a DNAME (or its SIG). Anything else is
* ignored.
*/
wanted_chaining = ISC_FALSE;
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
isc_boolean_t found_dname = ISC_FALSE;
found = ISC_FALSE;
aflag = 0;
if (rdataset->type == dns_rdatatype_dname) {
/*
* We're looking for something else,
* but we found a DNAME.
*
* If we're not chaining, then the
* DNAME should not be external.
*/
if (!chaining && external)
return (DNS_R_FORMERR);
found = ISC_TRUE;
want_chaining = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWER;
result = dname_target(rdataset,
qname, name,
&dname);
if (result == ISC_R_NOSPACE) {
/*
* We can't construct the
* DNAME target. Do not
* try to continue.
*/
want_chaining = ISC_FALSE;
} else if (result != ISC_R_SUCCESS)
return (result);
else
found_dname = ISC_TRUE;
} else if (rdataset->type == dns_rdatatype_rrsig
&& rdataset->covers ==
dns_rdatatype_dname) {
/*
* We've found a signature that
* covers the DNAME.
*/
found = ISC_TRUE;
aflag = DNS_RDATASETATTR_ANSWERSIG;
}
if (found) {
/*
* We've found an answer to our
* question.
*/
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
rdataset->trust = dns_trust_answer;
if (!chaining) {
/*
* This data is "the" answer
* to our question only if
* we're not chaining.
*/
INSIST(!external);
if (aflag ==
DNS_RDATASETATTR_ANSWER)
have_answer = ISC_TRUE;
name->attributes |=
DNS_NAMEATTR_ANSWER;
rdataset->attributes |= aflag;
if (aa)
rdataset->trust =
dns_trust_authanswer;
} else if (external) {
rdataset->attributes |=
DNS_RDATASETATTR_EXTERNAL;
}
/*
* DNAME chaining.
*/
if (found_dname) {
/*
* Copy the the dname into the
* qname fixed name.
*
* Although we check for
* failure of the copy
* operation, in practice it
* should never fail since
* we already know that the
* result fits in a fixedname.
*/
dns_fixedname_init(&fqname);
result = dns_name_copy(
dns_fixedname_name(&dname),
dns_fixedname_name(&fqname),
NULL);
if (result != ISC_R_SUCCESS)
return (result);
wanted_chaining = ISC_TRUE;
name->attributes |=
DNS_NAMEATTR_CHAINING;
rdataset->attributes |=
DNS_RDATASETATTR_CHAINING;
qname = dns_fixedname_name(
&fqname);
}
}
}
if (wanted_chaining)
chaining = ISC_TRUE;
}
result = dns_message_nextname(message, DNS_SECTION_ANSWER);
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
return (result);
/*
* We should have found an answer.
*/
if (!have_answer)
return (DNS_R_FORMERR);
/*
* This response is now potentially cacheable.
*/
fctx->attributes |= FCTX_ATTR_WANTCACHE;
/*
* Did chaining end before we got the final answer?
*/
if (chaining) {
/*
* Yes. This may be a negative reply, so hand off
* authority section processing to the noanswer code.
* If it isn't a noanswer response, no harm will be
* done.
*/
return (noanswer_response(fctx, qname, ISC_FALSE));
}
/*
* We didn't end with an incomplete chain, so the rcode should be
* "no error".
*/
if (message->rcode != dns_rcode_noerror)
return (DNS_R_FORMERR);
/*
* Examine the authority section (if there is one).
*
* We expect there to be only one owner name for all the rdatasets
* in this section, and we expect that it is not external.
*/
done = ISC_FALSE;
result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
while (!done && result == ISC_R_SUCCESS) {
name = NULL;
dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
if (!external) {
/*
* We expect to find NS or SIG NS rdatasets, and
* nothing else.
*/
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (rdataset->type == dns_rdatatype_ns ||
(rdataset->type == dns_rdatatype_rrsig &&
rdataset->covers == dns_rdatatype_ns)) {
name->attributes |=
DNS_NAMEATTR_CACHE;
rdataset->attributes |=
DNS_RDATASETATTR_CACHE;
if (aa && !chaining)
rdataset->trust =
dns_trust_authauthority;
else
rdataset->trust =
dns_trust_additional;
/*
* Mark any additional data related
* to this rdataset.
*/
(void)dns_rdataset_additionaldata(
rdataset,
check_related,
fctx);
done = ISC_TRUE;
}
}
}
result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
return (result);
}
static void
resume_dslookup(isc_task_t *task, isc_event_t *event) {
dns_fetchevent_t *fevent;
dns_resolver_t *res;
fetchctx_t *fctx;
isc_result_t result;
isc_boolean_t bucket_empty = ISC_FALSE;
isc_boolean_t locked = ISC_FALSE;
unsigned int bucketnum;
dns_rdataset_t nameservers;
dns_fixedname_t fixed;
dns_name_t *domain;
REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
fevent = (dns_fetchevent_t *)event;
fctx = event->ev_arg;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
UNUSED(task);
FCTXTRACE("resume_dslookup");
if (fevent->node != NULL)
dns_db_detachnode(fevent->db, &fevent->node);
if (fevent->db != NULL)
dns_db_detach(&fevent->db);
dns_rdataset_init(&nameservers);
bucketnum = fctx->bucketnum;
if (fevent->result == ISC_R_CANCELED) {
dns_resolver_destroyfetch(&fctx->nsfetch);
fctx_done(fctx, ISC_R_CANCELED);
} else if (fevent->result == ISC_R_SUCCESS) {
FCTXTRACE("resuming DS lookup");
dns_resolver_destroyfetch(&fctx->nsfetch);
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
dns_rdataset_clone(fevent->rdataset, &fctx->nameservers);
dns_name_free(&fctx->domain,
fctx->res->buckets[bucketnum].mctx);
dns_name_init(&fctx->domain, NULL);
result = dns_name_dup(&fctx->nsname,
fctx->res->buckets[bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS) {
fctx_done(fctx, DNS_R_SERVFAIL);
goto cleanup;
}
/*
* Try again.
*/
fctx_try(fctx);
} else {
unsigned int n;
dns_rdataset_t *nsrdataset = NULL;
/*
* Retrieve state from fctx->nsfetch before we destroy it.
*/
dns_fixedname_init(&fixed);
domain = dns_fixedname_name(&fixed);
dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL);
if (dns_name_equal(&fctx->nsname, domain)) {
fctx_done(fctx, DNS_R_SERVFAIL);
dns_resolver_destroyfetch(&fctx->nsfetch);
goto cleanup;
}
if (dns_rdataset_isassociated(
&fctx->nsfetch->private->nameservers)) {
dns_rdataset_clone(
&fctx->nsfetch->private->nameservers,
&nameservers);
nsrdataset = &nameservers;
} else
domain = NULL;
dns_resolver_destroyfetch(&fctx->nsfetch);
n = dns_name_countlabels(&fctx->nsname);
dns_name_getlabelsequence(&fctx->nsname, 1, n - 1,
&fctx->nsname);
if (dns_rdataset_isassociated(fevent->rdataset))
dns_rdataset_disassociate(fevent->rdataset);
FCTXTRACE("continuing to look for parent's NS records");
result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
dns_rdatatype_ns, domain,
nsrdataset, NULL, 0, task,
resume_dslookup, fctx,
&fctx->nsrrset, NULL,
&fctx->nsfetch);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
else {
LOCK(&res->buckets[bucketnum].lock);
locked = ISC_TRUE;
fctx->references++;
}
}
cleanup:
if (dns_rdataset_isassociated(&nameservers))
dns_rdataset_disassociate(&nameservers);
if (dns_rdataset_isassociated(fevent->rdataset))
dns_rdataset_disassociate(fevent->rdataset);
INSIST(fevent->sigrdataset == NULL);
isc_event_free(&event);
if (!locked)
LOCK(&res->buckets[bucketnum].lock);
fctx->references--;
if (fctx->references == 0)
bucket_empty = fctx_destroy(fctx);
UNLOCK(&res->buckets[bucketnum].lock);
if (bucket_empty)
empty_bucket(res);
}
static inline void
checknamessection(dns_message_t *message, dns_section_t section) {
isc_result_t result;
dns_name_t *name;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_t *rdataset;
for (result = dns_message_firstname(message, section);
result == ISC_R_SUCCESS;
result = dns_message_nextname(message, section))
{
name = NULL;
dns_message_currentname(message, section, &name);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
for (result = dns_rdataset_first(rdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(rdataset)) {
dns_rdataset_current(rdataset, &rdata);
if (!dns_rdata_checkowner(name, rdata.rdclass,
rdata.type,
ISC_FALSE) ||
!dns_rdata_checknames(&rdata, name, NULL))
{
rdataset->attributes |=
DNS_RDATASETATTR_CHECKNAMES;
}
dns_rdata_reset(&rdata);
}
}
}
}
static void
checknames(dns_message_t *message) {
checknamessection(message, DNS_SECTION_ANSWER);
checknamessection(message, DNS_SECTION_AUTHORITY);
checknamessection(message, DNS_SECTION_ADDITIONAL);
}
static void
log_packet(dns_message_t *message, int level, isc_mem_t *mctx) {
isc_buffer_t buffer;
char *buf = NULL;
int len = 1024;
isc_result_t result;
if (! isc_log_wouldlog(dns_lctx, level))
return;
/*
* Note that these are multiline debug messages. We want a newline
* to appear in the log after each message.
*/
do {
buf = isc_mem_get(mctx, len);
if (buf == NULL)
break;
isc_buffer_init(&buffer, buf, len);
result = dns_message_totext(message, &dns_master_style_debug,
0, &buffer);
if (result == ISC_R_NOSPACE) {
isc_mem_put(mctx, buf, len);
len += 1024;
} else if (result == ISC_R_SUCCESS)
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, level,
"received packet:\n%.*s",
(int)isc_buffer_usedlength(&buffer),
buf);
} while (result == ISC_R_NOSPACE);
if (buf != NULL)
isc_mem_put(mctx, buf, len);
}
static void
resquery_response(isc_task_t *task, isc_event_t *event) {
isc_result_t result = ISC_R_SUCCESS;
resquery_t *query = event->ev_arg;
dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
isc_boolean_t keep_trying, get_nameservers, resend;
isc_boolean_t truncated;
dns_message_t *message;
fetchctx_t *fctx;
dns_name_t *fname;
dns_fixedname_t foundname;
isc_stdtime_t now;
isc_time_t tnow, *finish;
dns_adbaddrinfo_t *addrinfo;
unsigned int options;
unsigned int findoptions;
isc_result_t broken_server;
REQUIRE(VALID_QUERY(query));
fctx = query->fctx;
options = query->options;
REQUIRE(VALID_FCTX(fctx));
REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
QTRACE("response");
(void)isc_timer_touch(fctx->timer);
keep_trying = ISC_FALSE;
broken_server = ISC_R_SUCCESS;
get_nameservers = ISC_FALSE;
resend = ISC_FALSE;
truncated = ISC_FALSE;
finish = NULL;
if (fctx->res->exiting) {
result = ISC_R_SHUTTINGDOWN;
goto done;
}
fctx->timeouts = 0;
/*
* XXXRTH We should really get the current time just once. We
* need a routine to convert from an isc_time_t to an
* isc_stdtime_t.
*/
TIME_NOW(&tnow);
finish = &tnow;
isc_stdtime_get(&now);
/*
* Did the dispatcher have a problem?
*/
if (devent->result != ISC_R_SUCCESS) {
if (devent->result == ISC_R_EOF &&
(query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
options |= DNS_FETCHOPT_NOEDNS0;
resend = ISC_TRUE;
/*
* Remember that they don't like EDNS0.
*/
dns_adb_changeflags(fctx->adb,
query->addrinfo,
DNS_FETCHOPT_NOEDNS0,
DNS_FETCHOPT_NOEDNS0);
} else {
/*
* There's no hope for this query.
*/
keep_trying = ISC_TRUE;
}
goto done;
}
message = fctx->rmessage;
if (query->tsig != NULL) {
result = dns_message_setquerytsig(message, query->tsig);
if (result != ISC_R_SUCCESS)
goto done;
}
if (query->tsigkey) {
result = dns_message_settsigkey(message, query->tsigkey);
if (result != ISC_R_SUCCESS)
goto done;
}
result = dns_message_parse(message, &devent->buffer, 0);
if (result != ISC_R_SUCCESS) {
switch (result) {
case ISC_R_UNEXPECTEDEND:
if (!message->question_ok ||
(message->flags & DNS_MESSAGEFLAG_TC) == 0 ||
(options & DNS_FETCHOPT_TCP) != 0) {
/*
* Either the message ended prematurely,
* and/or wasn't marked as being truncated,
* and/or this is a response to a query we
* sent over TCP. In all of these cases,
* something is wrong with the remote
* server and we don't want to retry using
* TCP.
*/
if ((query->options & DNS_FETCHOPT_NOEDNS0)
== 0) {
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
options |= DNS_FETCHOPT_NOEDNS0;
resend = ISC_TRUE;
/*
* Remember that they don't like EDNS0.
*/
dns_adb_changeflags(
fctx->adb,
query->addrinfo,
DNS_FETCHOPT_NOEDNS0,
DNS_FETCHOPT_NOEDNS0);
} else {
broken_server = result;
keep_trying = ISC_TRUE;
}
goto done;
}
/*
* We defer retrying via TCP for a bit so we can
* check out this message further.
*/
truncated = ISC_TRUE;
break;
case DNS_R_FORMERR:
if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
options |= DNS_FETCHOPT_NOEDNS0;
resend = ISC_TRUE;
/*
* Remember that they don't like EDNS0.
*/
dns_adb_changeflags(fctx->adb,
query->addrinfo,
DNS_FETCHOPT_NOEDNS0,
DNS_FETCHOPT_NOEDNS0);
} else {
broken_server = DNS_R_UNEXPECTEDRCODE;
keep_trying = ISC_TRUE;
}
goto done;
default:
/*
* Something bad has happened.
*/
goto done;
}
}
/*
* Log the incoming packet.
*/
log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx);
/*
* If the message is signed, check the signature. If not, this
* returns success anyway.
*/
result = dns_message_checksig(message, fctx->res->view);
if (result != ISC_R_SUCCESS)
goto done;
/*
* The dispatcher should ensure we only get responses with QR set.
*/
INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0);
/*
* INSIST() that the message comes from the place we sent it to,
* since the dispatch code should ensure this.
*
* INSIST() that the message id is correct (this should also be
* ensured by the dispatch code).
*/
/*
* Deal with truncated responses by retrying using TCP.
*/
if ((message->flags & DNS_MESSAGEFLAG_TC) != 0)
truncated = ISC_TRUE;
if (truncated) {
if ((options & DNS_FETCHOPT_TCP) != 0) {
broken_server = DNS_R_TRUNCATEDTCP;
keep_trying = ISC_TRUE;
} else {
options |= DNS_FETCHOPT_TCP;
resend = ISC_TRUE;
}
goto done;
}
/*
* Is it a query response?
*/
if (message->opcode != dns_opcode_query) {
/* XXXRTH Log */
broken_server = DNS_R_UNEXPECTEDOPCODE;
keep_trying = ISC_TRUE;
goto done;
}
/*
* Is the remote server broken, or does it dislike us?
*/
if (message->rcode != dns_rcode_noerror &&
message->rcode != dns_rcode_nxdomain) {
if ((message->rcode == dns_rcode_formerr ||
message->rcode == dns_rcode_notimp ||
message->rcode == dns_rcode_servfail) &&
(query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
/*
* It's very likely they don't like EDNS0.
*
* XXXRTH We should check if the question
* we're asking requires EDNS0, and
* if so, we should bail out.
*/
options |= DNS_FETCHOPT_NOEDNS0;
resend = ISC_TRUE;
/*
* Remember that they don't like EDNS0.
*/
if (message->rcode != dns_rcode_servfail)
dns_adb_changeflags(fctx->adb, query->addrinfo,
DNS_FETCHOPT_NOEDNS0,
DNS_FETCHOPT_NOEDNS0);
} else if (message->rcode == dns_rcode_formerr) {
if (ISFORWARDER(query->addrinfo)) {
/*
* This forwarder doesn't understand us,
* but other forwarders might. Keep trying.
*/
broken_server = DNS_R_REMOTEFORMERR;
keep_trying = ISC_TRUE;
} else {
/*
* The server doesn't understand us. Since
* all servers for a zone need similar
* capabilities, we assume that we will get
* FORMERR from all servers, and thus we
* cannot make any more progress with this
* fetch.
*/
result = DNS_R_FORMERR;
}
} else if (message->rcode == dns_rcode_yxdomain) {
/*
* DNAME mapping failed because the new name
* was too long. There's no chance of success
* for this fetch.
*/
result = DNS_R_YXDOMAIN;
} else if (message->rcode == dns_rcode_badvers) {
dns_rdataset_t *opt;
unsigned int flags, mask;
unsigned int version;
resend = ISC_TRUE;
opt = dns_message_getopt(message);
version = (opt->ttl >> 16) & 0xff;
flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) |
DNS_FETCHOPT_EDNSVERSIONSET;
mask = DNS_FETCHOPT_EDNSVERSIONMASK |
DNS_FETCHOPT_EDNSVERSIONSET;
switch (version) {
case 0:
dns_adb_changeflags(fctx->adb, query->addrinfo,
flags, mask);
break;
default:
broken_server = DNS_R_BADVERS;
keep_trying = ISC_TRUE;
break;
}
} else {
/*
* XXXRTH log.
*/
broken_server = DNS_R_UNEXPECTEDRCODE;
INSIST(broken_server != ISC_R_SUCCESS);
keep_trying = ISC_TRUE;
}
goto done;
}
/*
* Is the question the same as the one we asked?
*/
result = same_question(fctx);
if (result != ISC_R_SUCCESS) {
/* XXXRTH Log */
if (result == DNS_R_FORMERR)
keep_trying = ISC_TRUE;
goto done;
}
/*
* Is the server lame?
*/
if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) &&
is_lame(fctx)) {
log_lame(fctx, query->addrinfo);
result = dns_adb_marklame(fctx->adb, query->addrinfo,
&fctx->name, fctx->type,
now + fctx->res->lame_ttl);
if (result != ISC_R_SUCCESS)
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
"could not mark server as lame: %s",
isc_result_totext(result));
broken_server = DNS_R_LAME;
keep_trying = ISC_TRUE;
goto done;
}
/*
* Enforce delegations only zones like NET and COM.
*/
if (!ISFORWARDER(query->addrinfo) &&
dns_view_isdelegationonly(fctx->res->view, &fctx->domain) &&
!dns_name_equal(&fctx->domain, &fctx->name) &&
fix_mustbedelegationornxdomain(message, fctx)) {
char namebuf[DNS_NAME_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
char classbuf[64];
char typebuf[64];
dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
dns_rdataclass_format(fctx->res->rdclass, classbuf,
sizeof(classbuf));
isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
sizeof(addrbuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY,
DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
"enforced delegation-only for '%s' (%s/%s/%s) "
"from %s",
domainbuf, namebuf, typebuf, classbuf, addrbuf);
}
if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0)
checknames(message);
/*
* Clear cache bits.
*/
fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE);
/*
* Did we get any answers?
*/
if (message->counts[DNS_SECTION_ANSWER] > 0 &&
(message->rcode == dns_rcode_noerror ||
message->rcode == dns_rcode_nxdomain)) {
/*
* We've got answers. However, if we sent
* a BIND 8 server an NS query, it may have
* incorrectly responded with a non-authoritative
* answer instead of a referral. Since this
* answer lacks the SIGs necessary to do DNSSEC
* validation, we must invoke the following special
* kludge to treat it as a referral.
*/
if (fctx->type == dns_rdatatype_ns &&
(message->flags & DNS_MESSAGEFLAG_AA) == 0 &&
!ISFORWARDER(query->addrinfo))
{
result = noanswer_response(fctx, NULL, ISC_TRUE);
if (result != DNS_R_DELEGATION) {
/*
* The answer section must have contained
* something other than the NS records
* we asked for. Since AA is not set
* and the server is not a forwarder,
* it is technically lame and it's easier
* to treat it as such than to figure out
* some more elaborate course of action.
*/
broken_server = DNS_R_LAME;
keep_trying = ISC_TRUE;
goto done;
}
goto force_referral;
}
result = answer_response(fctx);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_FORMERR)
keep_trying = ISC_TRUE;
goto done;
}
} else if (message->counts[DNS_SECTION_AUTHORITY] > 0 ||
message->rcode == dns_rcode_noerror ||
message->rcode == dns_rcode_nxdomain) {
/*
* NXDOMAIN, NXRDATASET, or referral.
*/
result = noanswer_response(fctx, NULL, ISC_FALSE);
if (result == DNS_R_CHASEDSSERVERS) {
} else if (result == DNS_R_DELEGATION) {
force_referral:
/*
* We don't have the answer, but we know a better
* place to look.
*/
get_nameservers = ISC_TRUE;
keep_trying = ISC_TRUE;
/*
* We have a new set of name servers, and it
* has not experienced any restarts yet.
*/
fctx->restarts = 0;
result = ISC_R_SUCCESS;
} else if (result != ISC_R_SUCCESS) {
/*
* Something has gone wrong.
*/
if (result == DNS_R_FORMERR)
keep_trying = ISC_TRUE;
goto done;
}
} else {
/*
* The server is insane.
*/
/* XXXRTH Log */
broken_server = DNS_R_UNEXPECTEDRCODE;
keep_trying = ISC_TRUE;
goto done;
}
/*
* Follow additional section data chains.
*/
chase_additional(fctx);
/*
* Cache the cacheable parts of the message. This may also cause
* work to be queued to the DNSSEC validator.
*/
if (WANTCACHE(fctx)) {
result = cache_message(fctx, query->addrinfo, now);
if (result != ISC_R_SUCCESS)
goto done;
}
/*
* Ncache the negatively cacheable parts of the message. This may
* also cause work to be queued to the DNSSEC validator.
*/
if (WANTNCACHE(fctx)) {
dns_rdatatype_t covers;
if (message->rcode == dns_rcode_nxdomain)
covers = dns_rdatatype_any;
else
covers = fctx->type;
/*
* Cache any negative cache entries in the message.
*/
result = ncache_message(fctx, query->addrinfo, covers, now);
}
done:
/*
* Remember the query's addrinfo, in case we need to mark the
* server as broken.
*/
addrinfo = query->addrinfo;
/*
* Cancel the query.
*
* XXXRTH Don't cancel the query if waiting for validation?
*/
fctx_cancelquery(&query, &devent, finish, ISC_FALSE);
if (keep_trying) {
if (result == DNS_R_FORMERR)
broken_server = DNS_R_FORMERR;
if (broken_server != ISC_R_SUCCESS) {
/*
* Add this server to the list of bad servers for
* this fctx.
*/
add_bad(fctx, &addrinfo->sockaddr, broken_server);
}
if (get_nameservers) {
dns_name_t *name;
dns_fixedname_init(&foundname);
fname = dns_fixedname_name(&foundname);
if (result != ISC_R_SUCCESS) {
fctx_done(fctx, DNS_R_SERVFAIL);
return;
}
findoptions = 0;
if (dns_rdatatype_atparent(fctx->type))
findoptions |= DNS_DBFIND_NOEXACT;
if ((options & DNS_FETCHOPT_UNSHARED) == 0)
name = &fctx->name;
else
name = &fctx->domain;
result = dns_view_findzonecut(fctx->res->view,
name, fname,
now, findoptions,
ISC_TRUE,
&fctx->nameservers,
NULL);
if (result != ISC_R_SUCCESS) {
FCTXTRACE("couldn't find a zonecut");
fctx_done(fctx, DNS_R_SERVFAIL);
return;
}
if (!dns_name_issubdomain(fname, &fctx->domain)) {
/*
* The best nameservers are now above our
* QDOMAIN.
*/
FCTXTRACE("nameservers now above QDOMAIN");
fctx_done(fctx, DNS_R_SERVFAIL);
return;
}
dns_name_free(&fctx->domain,
fctx->res->buckets[fctx->bucketnum].mctx);
dns_name_init(&fctx->domain, NULL);
result = dns_name_dup(fname,
fctx->res->buckets[fctx->bucketnum].mctx,
&fctx->domain);
if (result != ISC_R_SUCCESS) {
fctx_done(fctx, DNS_R_SERVFAIL);
return;
}
fctx_cancelqueries(fctx, ISC_TRUE);
fctx_cleanupfinds(fctx);
fctx_cleanupaltfinds(fctx);
fctx_cleanupforwaddrs(fctx);
fctx_cleanupaltaddrs(fctx);
}
/*
* Try again.
*/
fctx_try(fctx);
} else if (resend) {
/*
* Resend (probably with changed options).
*/
FCTXTRACE("resend");
result = fctx_query(fctx, addrinfo, options);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
} else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) {
/*
* All has gone well so far, but we are waiting for the
* DNSSEC validator to validate the answer.
*/
FCTXTRACE("wait for validator");
fctx_cancelqueries(fctx, ISC_TRUE);
/*
* We must not retransmit while the validator is working;
* it has references to the current rmessage.
*/
result = fctx_stopidletimer(fctx);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
} else if (result == DNS_R_CHASEDSSERVERS) {
unsigned int n;
add_bad(fctx, &addrinfo->sockaddr, result);
fctx_cancelqueries(fctx, ISC_TRUE);
fctx_cleanupfinds(fctx);
fctx_cleanupforwaddrs(fctx);
n = dns_name_countlabels(&fctx->name);
dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname);
FCTXTRACE("suspending DS lookup to find parent's NS records");
result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
dns_rdatatype_ns,
NULL, NULL, NULL, 0, task,
resume_dslookup, fctx,
&fctx->nsrrset, NULL,
&fctx->nsfetch);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
fctx->references++;
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
result = fctx_stopidletimer(fctx);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result);
} else {
/*
* We're done.
*/
fctx_done(fctx, result);
}
}
/***
*** Resolver Methods
***/
static void
destroy(dns_resolver_t *res) {
unsigned int i;
alternate_t *a;
REQUIRE(res->references == 0);
REQUIRE(!res->priming);
REQUIRE(res->primefetch == NULL);
RTRACE("destroy");
INSIST(res->nfctx == 0);
DESTROYLOCK(&res->primelock);
DESTROYLOCK(&res->nlock);
DESTROYLOCK(&res->lock);
for (i = 0; i < res->nbuckets; i++) {
INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs));
isc_task_shutdown(res->buckets[i].task);
isc_task_detach(&res->buckets[i].task);
DESTROYLOCK(&res->buckets[i].lock);
isc_mem_detach(&res->buckets[i].mctx);
}
isc_mem_put(res->mctx, res->buckets,
res->nbuckets * sizeof(fctxbucket_t));
if (res->dispatchv4 != NULL)
dns_dispatch_detach(&res->dispatchv4);
if (res->dispatchv6 != NULL)
dns_dispatch_detach(&res->dispatchv6);
while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
ISC_LIST_UNLINK(res->alternates, a, link);
if (!a->isaddress)
dns_name_free(&a->_u._n.name, res->mctx);
isc_mem_put(res->mctx, a, sizeof(*a));
}
dns_resolver_reset_algorithms(res);
dns_resolver_resetmustbesecure(res);
#if USE_ALGLOCK
isc_rwlock_destroy(&res->alglock);
#endif
#if USE_MBSLOCK
isc_rwlock_destroy(&res->mbslock);
#endif
isc_timer_detach(&res->spillattimer);
res->magic = 0;
isc_mem_put(res->mctx, res, sizeof(*res));
}
static void
send_shutdown_events(dns_resolver_t *res) {
isc_event_t *event, *next_event;
isc_task_t *etask;
/*
* Caller must be holding the resolver lock.
*/
for (event = ISC_LIST_HEAD(res->whenshutdown);
event != NULL;
event = next_event) {
next_event = ISC_LIST_NEXT(event, ev_link);
ISC_LIST_UNLINK(res->whenshutdown, event, ev_link);
etask = event->ev_sender;
event->ev_sender = res;
isc_task_sendanddetach(&etask, &event);
}
}
static void
empty_bucket(dns_resolver_t *res) {
RTRACE("empty_bucket");
LOCK(&res->lock);
INSIST(res->activebuckets > 0);
res->activebuckets--;
if (res->activebuckets == 0)
send_shutdown_events(res);
UNLOCK(&res->lock);
}
static void
spillattimer_countdown(isc_task_t *task, isc_event_t *event) {
dns_resolver_t *res = event->ev_arg;
isc_result_t result;
unsigned int count;
isc_boolean_t logit = ISC_FALSE;
REQUIRE(VALID_RESOLVER(res));
UNUSED(task);
LOCK(&res->lock);
INSIST(!res->exiting);
if (res->spillat > res->spillatmin) {
res->spillat--;
logit = ISC_TRUE;
}
if (res->spillat <= res->spillatmin) {
result = isc_timer_reset(res->spillattimer,
isc_timertype_inactive, NULL,
NULL, ISC_TRUE);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
count = res->spillat;
UNLOCK(&res->lock);
if (logit)
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
"clients-per-query decreased to %u", count);
isc_event_free(&event);
}
isc_result_t
dns_resolver_create(dns_view_t *view,
isc_taskmgr_t *taskmgr, unsigned int ntasks,
isc_socketmgr_t *socketmgr,
isc_timermgr_t *timermgr,
unsigned int options,
dns_dispatchmgr_t *dispatchmgr,
dns_dispatch_t *dispatchv4,
dns_dispatch_t *dispatchv6,
dns_resolver_t **resp)
{
dns_resolver_t *res;
isc_result_t result = ISC_R_SUCCESS;
unsigned int i, buckets_created = 0;
isc_task_t *task = NULL;
char name[16];
/*
* Create a resolver.
*/
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE(ntasks > 0);
REQUIRE(resp != NULL && *resp == NULL);
REQUIRE(dispatchmgr != NULL);
REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL);
res = isc_mem_get(view->mctx, sizeof(*res));
if (res == NULL)
return (ISC_R_NOMEMORY);
RTRACE("create");
res->mctx = view->mctx;
res->rdclass = view->rdclass;
res->socketmgr = socketmgr;
res->timermgr = timermgr;
res->taskmgr = taskmgr;
res->dispatchmgr = dispatchmgr;
res->view = view;
res->options = options;
res->lame_ttl = 0;
ISC_LIST_INIT(res->alternates);
res->udpsize = RECV_BUFFER_SIZE;
res->algorithms = NULL;
res->mustbesecure = NULL;
res->spillatmin = res->spillat = 10;
res->spillatmax = 100;
res->spillattimer = NULL;
res->zero_no_soa_ttl = ISC_FALSE;
res->nbuckets = ntasks;
res->activebuckets = ntasks;
res->buckets = isc_mem_get(view->mctx,
ntasks * sizeof(fctxbucket_t));
if (res->buckets == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup_res;
}
for (i = 0; i < ntasks; i++) {
result = isc_mutex_init(&res->buckets[i].lock);
if (result != ISC_R_SUCCESS)
goto cleanup_buckets;
res->buckets[i].task = NULL;
result = isc_task_create(taskmgr, 0, &res->buckets[i].task);
if (result != ISC_R_SUCCESS) {
DESTROYLOCK(&res->buckets[i].lock);
goto cleanup_buckets;
}
res->buckets[i].mctx = NULL;
result = isc_mem_create(0, 0, &res->buckets[i].mctx);
if (result != ISC_R_SUCCESS) {
isc_task_detach(&res->buckets[i].task);
DESTROYLOCK(&res->buckets[i].lock);
goto cleanup_buckets;
}
snprintf(name, sizeof(name), "res%u", i);
isc_task_setname(res->buckets[i].task, name, res);
ISC_LIST_INIT(res->buckets[i].fctxs);
res->buckets[i].exiting = ISC_FALSE;
buckets_created++;
}
res->dispatchv4 = NULL;
if (dispatchv4 != NULL)
dns_dispatch_attach(dispatchv4, &res->dispatchv4);
res->dispatchv6 = NULL;
if (dispatchv6 != NULL)
dns_dispatch_attach(dispatchv6, &res->dispatchv6);
res->references = 1;
res->exiting = ISC_FALSE;
res->frozen = ISC_FALSE;
ISC_LIST_INIT(res->whenshutdown);
res->priming = ISC_FALSE;
res->primefetch = NULL;
res->nfctx = 0;
result = isc_mutex_init(&res->lock);
if (result != ISC_R_SUCCESS)
goto cleanup_dispatches;
result = isc_mutex_init(&res->nlock);
if (result != ISC_R_SUCCESS)
goto cleanup_lock;
result = isc_mutex_init(&res->primelock);
if (result != ISC_R_SUCCESS)
goto cleanup_nlock;
task = NULL;
result = isc_task_create(taskmgr, 0, &task);
if (result != ISC_R_SUCCESS)
goto cleanup_primelock;
result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
task, spillattimer_countdown, res,
&res->spillattimer);
isc_task_detach(&task);
if (result != ISC_R_SUCCESS)
goto cleanup_primelock;
#if USE_ALGLOCK
result = isc_rwlock_init(&res->alglock, 0, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_spillattimer;
#endif
#if USE_MBSLOCK
result = isc_rwlock_init(&res->mbslock, 0, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_alglock;
#endif
res->magic = RES_MAGIC;
*resp = res;
return (ISC_R_SUCCESS);
#if USE_MBSLOCK
cleanup_alglock:
#if USE_ALGLOCK
isc_rwlock_destroy(&res->alglock);
#endif
#endif
#if USE_ALGLOCK || USE_MBSLOCK
cleanup_spillattimer:
isc_timer_detach(&res->spillattimer);
#endif
cleanup_primelock:
DESTROYLOCK(&res->primelock);
cleanup_nlock:
DESTROYLOCK(&res->nlock);
cleanup_lock:
DESTROYLOCK(&res->lock);
cleanup_dispatches:
if (res->dispatchv6 != NULL)
dns_dispatch_detach(&res->dispatchv6);
if (res->dispatchv4 != NULL)
dns_dispatch_detach(&res->dispatchv4);
cleanup_buckets:
for (i = 0; i < buckets_created; i++) {
isc_mem_detach(&res->buckets[i].mctx);
DESTROYLOCK(&res->buckets[i].lock);
isc_task_shutdown(res->buckets[i].task);
isc_task_detach(&res->buckets[i].task);
}
isc_mem_put(view->mctx, res->buckets,
res->nbuckets * sizeof(fctxbucket_t));
cleanup_res:
isc_mem_put(view->mctx, res, sizeof(*res));
return (result);
}
static void
prime_done(isc_task_t *task, isc_event_t *event) {
dns_resolver_t *res;
dns_fetchevent_t *fevent;
dns_fetch_t *fetch;
dns_db_t *db = NULL;
REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
fevent = (dns_fetchevent_t *)event;
res = event->ev_arg;
REQUIRE(VALID_RESOLVER(res));
UNUSED(task);
LOCK(&res->lock);
INSIST(res->priming);
res->priming = ISC_FALSE;
LOCK(&res->primelock);
fetch = res->primefetch;
res->primefetch = NULL;
UNLOCK(&res->primelock);
UNLOCK(&res->lock);
if (fevent->result == ISC_R_SUCCESS &&
res->view->cache != NULL && res->view->hints != NULL) {
dns_cache_attachdb(res->view->cache, &db);
dns_root_checkhints(res->view, res->view->hints, db);
dns_db_detach(&db);
}
if (fevent->node != NULL)
dns_db_detachnode(fevent->db, &fevent->node);
if (fevent->db != NULL)
dns_db_detach(&fevent->db);
if (dns_rdataset_isassociated(fevent->rdataset))
dns_rdataset_disassociate(fevent->rdataset);
INSIST(fevent->sigrdataset == NULL);
isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset));
isc_event_free(&event);
dns_resolver_destroyfetch(&fetch);
}
void
dns_resolver_prime(dns_resolver_t *res) {
isc_boolean_t want_priming = ISC_FALSE;
dns_rdataset_t *rdataset;
isc_result_t result;
REQUIRE(VALID_RESOLVER(res));
REQUIRE(res->frozen);
RTRACE("dns_resolver_prime");
LOCK(&res->lock);
if (!res->exiting && !res->priming) {
INSIST(res->primefetch == NULL);
res->priming = ISC_TRUE;
want_priming = ISC_TRUE;
}
UNLOCK(&res->lock);
if (want_priming) {
/*
* To avoid any possible recursive locking problems, we
* start the priming fetch like any other fetch, and holding
* no resolver locks. No one else will try to start it
* because we're the ones who set res->priming to true.
* Any other callers of dns_resolver_prime() while we're
* running will see that res->priming is already true and
* do nothing.
*/
RTRACE("priming");
rdataset = isc_mem_get(res->mctx, sizeof(*rdataset));
if (rdataset == NULL) {
LOCK(&res->lock);
INSIST(res->priming);
INSIST(res->primefetch == NULL);
res->priming = ISC_FALSE;
UNLOCK(&res->lock);
return;
}
dns_rdataset_init(rdataset);
LOCK(&res->primelock);
result = dns_resolver_createfetch(res, dns_rootname,
dns_rdatatype_ns,
NULL, NULL, NULL, 0,
res->buckets[0].task,
prime_done,
res, rdataset, NULL,
&res->primefetch);
UNLOCK(&res->primelock);
if (result != ISC_R_SUCCESS) {
LOCK(&res->lock);
INSIST(res->priming);
res->priming = ISC_FALSE;
UNLOCK(&res->lock);
}
}
}
void
dns_resolver_freeze(dns_resolver_t *res) {
/*
* Freeze resolver.
*/
REQUIRE(VALID_RESOLVER(res));
REQUIRE(!res->frozen);
res->frozen = ISC_TRUE;
}
void
dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) {
REQUIRE(VALID_RESOLVER(source));
REQUIRE(targetp != NULL && *targetp == NULL);
RRTRACE(source, "attach");
LOCK(&source->lock);
REQUIRE(!source->exiting);
INSIST(source->references > 0);
source->references++;
INSIST(source->references != 0);
UNLOCK(&source->lock);
*targetp = source;
}
void
dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task,
isc_event_t **eventp)
{
isc_task_t *clone;
isc_event_t *event;
REQUIRE(VALID_RESOLVER(res));
REQUIRE(eventp != NULL);
event = *eventp;
*eventp = NULL;
LOCK(&res->lock);
if (res->exiting && res->activebuckets == 0) {
/*
* We're already shutdown. Send the event.
*/
event->ev_sender = res;
isc_task_send(task, &event);
} else {
clone = NULL;
isc_task_attach(task, &clone);
event->ev_sender = clone;
ISC_LIST_APPEND(res->whenshutdown, event, ev_link);
}
UNLOCK(&res->lock);
}
void
dns_resolver_shutdown(dns_resolver_t *res) {
unsigned int i;
fetchctx_t *fctx;
isc_socket_t *sock;
isc_result_t result;
REQUIRE(VALID_RESOLVER(res));
RTRACE("shutdown");
LOCK(&res->lock);
if (!res->exiting) {
RTRACE("exiting");
res->exiting = ISC_TRUE;
for (i = 0; i < res->nbuckets; i++) {
LOCK(&res->buckets[i].lock);
for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs);
fctx != NULL;
fctx = ISC_LIST_NEXT(fctx, link))
fctx_shutdown(fctx);
if (res->dispatchv4 != NULL) {
sock = dns_dispatch_getsocket(res->dispatchv4);
isc_socket_cancel(sock, res->buckets[i].task,
ISC_SOCKCANCEL_ALL);
}
if (res->dispatchv6 != NULL) {
sock = dns_dispatch_getsocket(res->dispatchv6);
isc_socket_cancel(sock, res->buckets[i].task,
ISC_SOCKCANCEL_ALL);
}
res->buckets[i].exiting = ISC_TRUE;
if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) {
INSIST(res->activebuckets > 0);
res->activebuckets--;
}
UNLOCK(&res->buckets[i].lock);
}
if (res->activebuckets == 0)
send_shutdown_events(res);
result = isc_timer_reset(res->spillattimer,
isc_timertype_inactive, NULL,
NULL, ISC_TRUE);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
UNLOCK(&res->lock);
}
void
dns_resolver_detach(dns_resolver_t **resp) {
dns_resolver_t *res;
isc_boolean_t need_destroy = ISC_FALSE;
REQUIRE(resp != NULL);
res = *resp;
REQUIRE(VALID_RESOLVER(res));
RTRACE("detach");
LOCK(&res->lock);
INSIST(res->references > 0);
res->references--;
if (res->references == 0) {
INSIST(res->exiting && res->activebuckets == 0);
need_destroy = ISC_TRUE;
}
UNLOCK(&res->lock);
if (need_destroy)
destroy(res);
*resp = NULL;
}
static inline isc_boolean_t
fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
unsigned int options)
{
if (fctx->type != type || fctx->options != options)
return (ISC_FALSE);
return (dns_name_equal(&fctx->name, name));
}
static inline void
log_fetch(dns_name_t *name, dns_rdatatype_t type) {
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
int level = ISC_LOG_DEBUG(1);
if (! isc_log_wouldlog(dns_lctx, level))
return;
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, level,
"createfetch: %s %s", namebuf, typebuf);
}
isc_result_t
dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
dns_rdatatype_t type,
dns_name_t *domain, dns_rdataset_t *nameservers,
dns_forwarders_t *forwarders,
unsigned int options, isc_task_t *task,
isc_taskaction_t action, void *arg,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp)
{
return (dns_resolver_createfetch2(res, name, type, domain,
nameservers, forwarders, NULL, 0,
options, task, action, arg,
rdataset, sigrdataset, fetchp));
}
isc_result_t
dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
dns_rdatatype_t type,
dns_name_t *domain, dns_rdataset_t *nameservers,
dns_forwarders_t *forwarders,
isc_sockaddr_t *client, dns_messageid_t id,
unsigned int options, isc_task_t *task,
isc_taskaction_t action, void *arg,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset,
dns_fetch_t **fetchp)
{
dns_fetch_t *fetch;
fetchctx_t *fctx = NULL;
isc_result_t result = ISC_R_SUCCESS;
unsigned int bucketnum;
isc_boolean_t new_fctx = ISC_FALSE;
isc_event_t *event;
unsigned int count = 0;
UNUSED(forwarders);
REQUIRE(VALID_RESOLVER(res));
REQUIRE(res->frozen);
/* XXXRTH Check for meta type */
if (domain != NULL) {
REQUIRE(DNS_RDATASET_VALID(nameservers));
REQUIRE(nameservers->type == dns_rdatatype_ns);
} else
REQUIRE(nameservers == NULL);
REQUIRE(forwarders == NULL);
REQUIRE(!dns_rdataset_isassociated(rdataset));
REQUIRE(sigrdataset == NULL ||
!dns_rdataset_isassociated(sigrdataset));
REQUIRE(fetchp != NULL && *fetchp == NULL);
log_fetch(name, type);
/*
* XXXRTH use a mempool?
*/
fetch = isc_mem_get(res->mctx, sizeof(*fetch));
if (fetch == NULL)
return (ISC_R_NOMEMORY);
bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets;
LOCK(&res->buckets[bucketnum].lock);
if (res->buckets[bucketnum].exiting) {
result = ISC_R_SHUTTINGDOWN;
goto unlock;
}
if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs);
fctx != NULL;
fctx = ISC_LIST_NEXT(fctx, link)) {
if (fctx_match(fctx, name, type, options))
break;
}
}
/*
* Is this a duplicate?
*/
if (fctx != NULL && client != NULL) {
dns_fetchevent_t *fevent;
for (fevent = ISC_LIST_HEAD(fctx->events);
fevent != NULL;
fevent = ISC_LIST_NEXT(fevent, ev_link)) {
if (fevent->client != NULL && fevent->id == id &&
isc_sockaddr_equal(fevent->client, client)) {
result = DNS_R_DUPLICATE;
goto unlock;
}
count++;
}
}
if (count >= res->spillatmin && res->spillatmin != 0) {
if (!fctx->spilled) {
LOCK(&fctx->res->lock);
if (count >= res->spillat)
fctx->spilled = ISC_TRUE;
UNLOCK(&fctx->res->lock);
}
if (fctx->spilled) {
result = DNS_R_DROP;
goto unlock;
}
}
/*
* If we didn't have a fetch, would attach to a done fetch, this
* fetch has already cloned its results, or if the fetch has gone
* "idle" (no one was interested in it), we need to start a new
* fetch instead of joining with the existing one.
*/
if (fctx == NULL ||
fctx->state == fetchstate_done ||
fctx->cloned ||
ISC_LIST_EMPTY(fctx->events)) {
fctx = NULL;
result = fctx_create(res, name, type, domain, nameservers,
options, bucketnum, &fctx);
if (result != ISC_R_SUCCESS)
goto unlock;
new_fctx = ISC_TRUE;
}
result = fctx_join(fctx, task, client, id, action, arg,
rdataset, sigrdataset, fetch);
if (new_fctx) {
if (result == ISC_R_SUCCESS) {
/*
* Launch this fctx.
*/
event = &fctx->control_event;
ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
DNS_EVENT_FETCHCONTROL,
fctx_start, fctx, NULL,
NULL, NULL);
isc_task_send(res->buckets[bucketnum].task, &event);
} else {
/*
* We don't care about the result of fctx_destroy()
* since we know we're not exiting.
*/
(void)fctx_destroy(fctx);
}
}
unlock:
UNLOCK(&res->buckets[bucketnum].lock);
if (result == ISC_R_SUCCESS) {
FTRACE("created");
*fetchp = fetch;
} else
isc_mem_put(res->mctx, fetch, sizeof(*fetch));
return (result);
}
void
dns_resolver_cancelfetch(dns_fetch_t *fetch) {
fetchctx_t *fctx;
dns_resolver_t *res;
dns_fetchevent_t *event, *next_event;
isc_task_t *etask;
REQUIRE(DNS_FETCH_VALID(fetch));
fctx = fetch->private;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
FTRACE("cancelfetch");
LOCK(&res->buckets[fctx->bucketnum].lock);
/*
* Find the completion event for this fetch (as opposed
* to those for other fetches that have joined the same
* fctx) and send it with result = ISC_R_CANCELED.
*/
event = NULL;
if (fctx->state != fetchstate_done) {
for (event = ISC_LIST_HEAD(fctx->events);
event != NULL;
event = next_event) {
next_event = ISC_LIST_NEXT(event, ev_link);
if (event->fetch == fetch) {
ISC_LIST_UNLINK(fctx->events, event, ev_link);
break;
}
}
}
if (event != NULL) {
etask = event->ev_sender;
event->ev_sender = fctx;
event->result = ISC_R_CANCELED;
isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event));
}
/*
* The fctx continues running even if no fetches remain;
* the answer is still cached.
*/
UNLOCK(&res->buckets[fctx->bucketnum].lock);
}
void
dns_resolver_destroyfetch(dns_fetch_t **fetchp) {
dns_fetch_t *fetch;
dns_resolver_t *res;
dns_fetchevent_t *event, *next_event;
fetchctx_t *fctx;
unsigned int bucketnum;
isc_boolean_t bucket_empty = ISC_FALSE;
REQUIRE(fetchp != NULL);
fetch = *fetchp;
REQUIRE(DNS_FETCH_VALID(fetch));
fctx = fetch->private;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
FTRACE("destroyfetch");
bucketnum = fctx->bucketnum;
LOCK(&res->buckets[bucketnum].lock);
/*
* Sanity check: the caller should have gotten its event before
* trying to destroy the fetch.
*/
event = NULL;
if (fctx->state != fetchstate_done) {
for (event = ISC_LIST_HEAD(fctx->events);
event != NULL;
event = next_event) {
next_event = ISC_LIST_NEXT(event, ev_link);
RUNTIME_CHECK(event->fetch != fetch);
}
}
INSIST(fctx->references > 0);
fctx->references--;
if (fctx->references == 0) {
/*
* No one cares about the result of this fetch anymore.
*/
if (fctx->pending == 0 && ISC_LIST_EMPTY(fctx->validators) &&
SHUTTINGDOWN(fctx)) {
/*
* This fctx is already shutdown; we were just
* waiting for the last reference to go away.
*/
bucket_empty = fctx_destroy(fctx);
} else {
/*
* Initiate shutdown.
*/
fctx_shutdown(fctx);
}
}
UNLOCK(&res->buckets[bucketnum].lock);
isc_mem_put(res->mctx, fetch, sizeof(*fetch));
*fetchp = NULL;
if (bucket_empty)
empty_bucket(res);
}
dns_dispatchmgr_t *
dns_resolver_dispatchmgr(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->dispatchmgr);
}
dns_dispatch_t *
dns_resolver_dispatchv4(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->dispatchv4);
}
dns_dispatch_t *
dns_resolver_dispatchv6(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->dispatchv6);
}
isc_socketmgr_t *
dns_resolver_socketmgr(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->socketmgr);
}
isc_taskmgr_t *
dns_resolver_taskmgr(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->taskmgr);
}
isc_uint32_t
dns_resolver_getlamettl(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->lame_ttl);
}
void
dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) {
REQUIRE(VALID_RESOLVER(resolver));
resolver->lame_ttl = lame_ttl;
}
unsigned int
dns_resolver_nrunning(dns_resolver_t *resolver) {
unsigned int n;
LOCK(&resolver->nlock);
n = resolver->nfctx;
UNLOCK(&resolver->nlock);
return (n);
}
isc_result_t
dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
dns_name_t *name, in_port_t port) {
alternate_t *a;
isc_result_t result;
REQUIRE(VALID_RESOLVER(resolver));
REQUIRE(!resolver->frozen);
REQUIRE((alt == NULL) ^ (name == NULL));
a = isc_mem_get(resolver->mctx, sizeof(*a));
if (a == NULL)
return (ISC_R_NOMEMORY);
if (alt != NULL) {
a->isaddress = ISC_TRUE;
a->_u.addr = *alt;
} else {
a->isaddress = ISC_FALSE;
a->_u._n.port = port;
dns_name_init(&a->_u._n.name, NULL);
result = dns_name_dup(name, resolver->mctx, &a->_u._n.name);
if (result != ISC_R_SUCCESS) {
isc_mem_put(resolver->mctx, a, sizeof(*a));
return (result);
}
}
ISC_LINK_INIT(a, link);
ISC_LIST_APPEND(resolver->alternates, a, link);
return (ISC_R_SUCCESS);
}
void
dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) {
REQUIRE(VALID_RESOLVER(resolver));
resolver->udpsize = udpsize;
}
isc_uint16_t
dns_resolver_getudpsize(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->udpsize);
}
static void
free_algorithm(void *node, void *arg) {
unsigned char *algorithms = node;
isc_mem_t *mctx = arg;
isc_mem_put(mctx, algorithms, *algorithms);
}
void
dns_resolver_reset_algorithms(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
#if USE_ALGLOCK
RWLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
if (resolver->algorithms != NULL)
dns_rbt_destroy(&resolver->algorithms);
#if USE_ALGLOCK
RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
}
isc_result_t
dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
unsigned int alg)
{
unsigned int len, mask;
unsigned char *new;
unsigned char *algorithms;
isc_result_t result;
dns_rbtnode_t *node = NULL;
REQUIRE(VALID_RESOLVER(resolver));
if (alg > 255)
return (ISC_R_RANGE);
#if USE_ALGLOCK
RWLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
if (resolver->algorithms == NULL) {
result = dns_rbt_create(resolver->mctx, free_algorithm,
resolver->mctx, &resolver->algorithms);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
len = alg/8 + 2;
mask = 1 << (alg%8);
result = dns_rbt_addnode(resolver->algorithms, name, &node);
if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
algorithms = node->data;
if (algorithms == NULL || len > *algorithms) {
new = isc_mem_get(resolver->mctx, len);
if (new == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
memset(new, 0, len);
if (algorithms != NULL)
memcpy(new, algorithms, *algorithms);
new[len-1] |= mask;
*new = len;
node->data = new;
if (algorithms != NULL)
isc_mem_put(resolver->mctx, algorithms,
*algorithms);
} else
algorithms[len-1] |= mask;
}
result = ISC_R_SUCCESS;
cleanup:
#if USE_ALGLOCK
RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
#endif
return (result);
}
isc_boolean_t
dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
unsigned int alg)
{
unsigned int len, mask;
unsigned char *algorithms;
void *data = NULL;
isc_result_t result;
isc_boolean_t found = ISC_FALSE;
REQUIRE(VALID_RESOLVER(resolver));
#if USE_ALGLOCK
RWLOCK(&resolver->alglock, isc_rwlocktype_read);
#endif
if (resolver->algorithms == NULL)
goto unlock;
result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
len = alg/8 + 2;
mask = 1 << (alg%8);
algorithms = data;
if (len <= *algorithms && (algorithms[len-1] & mask) != 0)
found = ISC_TRUE;
}
unlock:
#if USE_ALGLOCK
RWUNLOCK(&resolver->alglock, isc_rwlocktype_read);
#endif
if (found)
return (ISC_FALSE);
return (dst_algorithm_supported(alg));
}
isc_boolean_t
dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) {
UNUSED(resolver);
return (dns_ds_digest_supported(digest));
}
void
dns_resolver_resetmustbesecure(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
#if USE_MBSLOCK
RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
#endif
if (resolver->mustbesecure != NULL)
dns_rbt_destroy(&resolver->mustbesecure);
#if USE_MBSLOCK
RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
#endif
}
static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE;
isc_result_t
dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
isc_boolean_t value)
{
isc_result_t result;
REQUIRE(VALID_RESOLVER(resolver));
#if USE_MBSLOCK
RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
#endif
if (resolver->mustbesecure == NULL) {
result = dns_rbt_create(resolver->mctx, NULL, NULL,
&resolver->mustbesecure);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
result = dns_rbt_addname(resolver->mustbesecure, name,
value ? &yes : &no);
cleanup:
#if USE_MBSLOCK
RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
#endif
return (result);
}
isc_boolean_t
dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) {
void *data = NULL;
isc_boolean_t value = ISC_FALSE;
isc_result_t result;
REQUIRE(VALID_RESOLVER(resolver));
#if USE_MBSLOCK
RWLOCK(&resolver->mbslock, isc_rwlocktype_read);
#endif
if (resolver->mustbesecure == NULL)
goto unlock;
result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
value = *(isc_boolean_t*)data;
unlock:
#if USE_MBSLOCK
RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read);
#endif
return (value);
}
void
dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
isc_uint32_t *min, isc_uint32_t *max)
{
REQUIRE(VALID_RESOLVER(resolver));
LOCK(&resolver->lock);
if (cur != NULL)
*cur = resolver->spillat;
if (min != NULL)
*min = resolver->spillatmin;
if (max != NULL)
*max = resolver->spillatmax;
UNLOCK(&resolver->lock);
}
void
dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min,
isc_uint32_t max)
{
REQUIRE(VALID_RESOLVER(resolver));
LOCK(&resolver->lock);
resolver->spillatmin = resolver->spillat = min;
resolver->spillatmax = max;
UNLOCK(&resolver->lock);
}
isc_boolean_t
dns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
return (resolver->zero_no_soa_ttl);
}
void
dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) {
REQUIRE(VALID_RESOLVER(resolver));
resolver->zero_no_soa_ttl = state;
}