resolver.c revision d2358a453e2c69a2348d804c389961f58cc36a69
1633838b8255282d10af15c5c84cee5a51466712Bob Halley/*
ca41b452ede6feaa9d8739ec3cae19389a7b0d03Bob Halley * Copyright (C) 1999 Internet Software Consortium.
1633838b8255282d10af15c5c84cee5a51466712Bob Halley *
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * Permission to use, copy, modify, and distribute this software for any
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * purpose with or without fee is hereby granted, provided that the above
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * copyright notice and this permission notice appear in all copies.
1633838b8255282d10af15c5c84cee5a51466712Bob Halley *
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1633838b8255282d10af15c5c84cee5a51466712Bob Halley * SOFTWARE.
1633838b8255282d10af15c5c84cee5a51466712Bob Halley */
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley
d25afd60ee2286cb171c4960a790f3d7041b6f85Bob Halley#include <config.h>
d25afd60ee2286cb171c4960a790f3d7041b6f85Bob Halley
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <isc/assertions.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <isc/result.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <isc/timer.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <isc/mutex.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <isc/event.h>
9fbefe0ace2ae7dba287f914b278153004bef428Bob Halley#include <isc/task.h>
9fbefe0ace2ae7dba287f914b278153004bef428Bob Halley#include <isc/stdtime.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <dns/types.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <dns/result.h>
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley#include <dns/name.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/db.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/events.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/message.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/dispatch.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/resolver.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/rdata.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/rdataset.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/tsig.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#include <dns/view.h>
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#include <dst/dst.h>
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#include "../isc/util.h" /* XXX */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
ea872078bfa9473cfe9c89b474dae2496377797bDavid Lawrence#define DNS_RESOLVER_TRACE
ea872078bfa9473cfe9c89b474dae2496377797bDavid Lawrence#ifdef DNS_RESOLVER_TRACE
ea872078bfa9473cfe9c89b474dae2496377797bDavid Lawrence#define RTRACE(m) printf("res %p: %s\n", res, (m))
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define RRTRACE(r, m) printf("res %p: %s\n", (r), (m))
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence#define FCTXTRACE(m) printf("fctx %p: %s\n", fctx, (m))
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define FTRACE(m) printf("fetch %p (fctx %p): %s\n", \
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley fetch, fetch->private, (m))
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define QTRACE(m) printf("query %p (res %p fctx %p): %s\n", \
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley query, query->fctx->res, query->fctx, (m))
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#else
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define RTRACE(m)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define RRTRACE(r, m)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define FCTXTRACE(m)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define FTRACE(m)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define QTRACE(m)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#endif
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrencetypedef struct fetchctx fetchctx_t;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halleytypedef struct query {
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley /* Not locked. */
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence unsigned int magic;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley fetchctx_t * fctx;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_dispatch_t * dispatch;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley /* Locked by fctx lock. */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_messageid_t id;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_dispentry_t * dispentry; /* XXX name */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley ISC_LINK(struct query) link;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_buffer_t buffer;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_rdata_any_tsig_t *tsig;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_tsig_key_t *tsigkey;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley unsigned char data[512];
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley} resquery_t;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define QUERY_MAGIC 0x51212121U /* Q!!! */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define VALID_QUERY(query) ((query) != NULL && \
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley (query)->magic == QUERY_MAGIC)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halleytypedef enum {
ea872078bfa9473cfe9c89b474dae2496377797bDavid Lawrence fetchstate_init = 0,
ea872078bfa9473cfe9c89b474dae2496377797bDavid Lawrence fetchstate_active,
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley fetchstate_done
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence} fetchstate;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrencestruct fetchctx {
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence /* Not locked. */
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence unsigned int magic;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence dns_resolver_t * res;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence dns_name_t name;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence dns_rdatatype_t type;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence unsigned int options;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence isc_task_t * task;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence unsigned int bucketnum;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence /* Locked by lock. */
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence fetchstate state;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence isc_boolean_t exiting;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence unsigned int references;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence isc_event_t control_event;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence ISC_LINK(struct fetchctx) link;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence ISC_LIST(dns_fetchevent_t) events;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence /* Only changable by event actions running in the context's task */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_name_t domain;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence dns_rdataset_t nameservers;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_timer_t * timer;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_time_t expires;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_interval_t interval;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_message_t * qmessage;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley dns_message_t * rmessage;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley ISC_LIST(resquery_t) queries;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence ISC_LIST(isc_sockaddr_t) addresses;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence isc_sockaddr_t * address;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley};
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define FCTX_MAGIC 0x46212121U /* F!!! */
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence#define VALID_FCTX(fctx) ((fctx) != NULL && \
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley (fctx)->magic == FCTX_MAGIC)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halleystruct dns_fetch {
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley unsigned int magic;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley void * private;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley};
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence#define DNS_FETCH_MAGIC 0x46746368U /* Ftch */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley#define DNS_FETCH_VALID(fetch) ((fetch) != NULL && \
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley (fetch)->magic == DNS_FETCH_MAGIC)
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halleytypedef struct fctxbucket {
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_task_t * task;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence isc_mutex_t lock;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence ISC_LIST(fetchctx_t) fctxs;
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley isc_boolean_t exiting;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley} fctxbucket_t;
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleystruct dns_resolver {
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley /* Unlocked */
9fbefe0ace2ae7dba287f914b278153004bef428Bob Halley unsigned int magic;
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley isc_mem_t * mctx;
9fbefe0ace2ae7dba287f914b278153004bef428Bob Halley isc_mutex_t lock;
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley dns_rdataclass_t rdclass;
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley isc_socketmgr_t * socketmgr;
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley isc_timermgr_t * timermgr;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence dns_view_t * view;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence /* Locked by lock. */
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence unsigned int references;
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley isc_boolean_t exiting;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence isc_socket_t * udpsocket4;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence isc_socket_t * udpsocket6;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence dns_dispatch_t * dispatch4;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence dns_dispatch_t * dispatch6;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence unsigned int nbuckets;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence unsigned int activebuckets;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence fctxbucket_t * buckets;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence};
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence#define RES_MAGIC 0x52657321U /* Res! */
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence#define VALID_RESOLVER(res) ((res) != NULL && \
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence (res)->magic == RES_MAGIC)
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrencestatic void destroy(dns_resolver_t *res);
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrencestatic void empty_bucket(dns_resolver_t *res);
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrencestatic void query_response(isc_task_t *task, isc_event_t *event);
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence/*
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence * Internal fetch routines. Caller must be holding the proper lock.
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence */
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleystatic inline isc_result_t
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleyfctx_starttimer(fetchctx_t *fctx) {
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley return (isc_timer_reset(fctx->timer, isc_timertype_once,
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley &fctx->expires, &fctx->interval,
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley ISC_FALSE));
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley}
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleystatic inline void
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleyfctx_stoptimer(fetchctx_t *fctx) {
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley isc_result_t result;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence /*
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley * We don't return a result if resetting the timer to inactive fails
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley * since there's nothing to be done about it. Resetting to inactive
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley * should never fail anyway, since the code as currently written
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley * cannot fail in that case.
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley */
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley NULL, NULL, ISC_TRUE);
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley if (result != ISC_R_SUCCESS) {
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley UNEXPECTED_ERROR(__FILE__, __LINE__,
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley "isc_timer_reset(): %s",
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence isc_result_totext(result));
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence }
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence}
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleystatic inline void
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halleyfctx_cancelquery(resquery_t *query, dns_dispatchevent_t **deventp) {
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence fetchctx_t *fctx = query->fctx;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley FCTXTRACE("cancelquery");
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley /*
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley * XXXRTH I don't think that dns_dispatch_removeresponse() will
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley * reclaim events posted to this task. What do we do
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley * about this? Doing what we're doing now is bad, because
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley * we're destroying the query while there may be outstanding
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley * references to it.
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley */
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley dns_dispatch_removeresponse(query->dispatch, &query->dispentry,
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence deventp);
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence ISC_LIST_UNLINK(fctx->queries, query, link);
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence query->magic = 0;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence if (query->tsig != NULL)
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence dns_rdata_freestruct(query->tsig);
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence isc_mem_put(fctx->res->mctx, query, sizeof *query);
0fc87fa2f38df7b293b650deacfa5e6c3d50eff9Bob Halley}
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
d3e7d196cd14fc3095ce97846a66cd49fc6fee6dDavid Lawrencestatic void
d3e7d196cd14fc3095ce97846a66cd49fc6fee6dDavid Lawrencefctx_cancelqueries(fetchctx_t *fctx) {
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence resquery_t *query, *next_query;
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley FCTXTRACE("cancelqueries");
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence for (query = ISC_LIST_HEAD(fctx->queries);
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence query != NULL;
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley query = next_query) {
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence next_query = ISC_LIST_NEXT(query, link);
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley fctx_cancelquery(query, NULL);
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley }
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley}
b592e197fe354d7258dc4166fce0a9425a338b0bBob Halley
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrencestatic void
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrencefctx_done(fetchctx_t *fctx, isc_result_t result) {
d3e7d196cd14fc3095ce97846a66cd49fc6fee6dDavid Lawrence dns_fetchevent_t *event, *next_event;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence isc_task_t *task;
d3e7d196cd14fc3095ce97846a66cd49fc6fee6dDavid Lawrence dns_resolver_t *res;
d5069ac954d067aa45ad395fc338f7e499b834c1David Lawrence
d3e7d196cd14fc3095ce97846a66cd49fc6fee6dDavid Lawrence FCTXTRACE("done");
4d6964d70a114b53a11a3bd778d9b8f5179a7934Bob Halley
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence res = fctx->res;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence /* XXXRTH Free our scaffolding addresses. */
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence {
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence isc_sockaddr_t *address, *next_address;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence for (address = ISC_LIST_HEAD(fctx->addresses);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence address != NULL;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence address = next_address) {
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence next_address = ISC_LIST_NEXT(address, link);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence isc_mem_put(fctx->res->mctx, address, sizeof *address);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence }
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence }
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence fctx_cancelqueries(fctx);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence fctx_stoptimer(fctx);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence LOCK(&res->buckets[fctx->bucketnum].lock);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence fctx->state = fetchstate_done;
585529aaeb95a71cd3d95df2602a4688fc7c3292David Lawrence
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence for (event = ISC_LIST_HEAD(fctx->events);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence event != NULL;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence event = next_event) {
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence next_event = ISC_LIST_NEXT(event, link);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence task = event->sender;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence event->sender = fctx;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence event->result = result;
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence isc_task_sendanddetach(&task, (isc_event_t **)&event);
11d732daac76a24a0f3e5948a2758a4b866a0825David Lawrence }
ISC_LIST_INIT(fctx->events);
/*
* XXXRTH check for finished state.
*/
UNLOCK(&res->buckets[fctx->bucketnum].lock);
}
static void
query_senddone(isc_task_t *task, isc_event_t *event) {
isc_socketevent_t *sevent = (isc_socketevent_t *)event;
resquery_t *query = event->arg;
REQUIRE(event->type == ISC_SOCKEVENT_SENDDONE);
/*
* XXXRTH
*
* Currently we don't wait for the senddone event before retrying
* a query. This means that if we get really behind, we may end
* up doing extra work!
*/
(void)task;
QTRACE("senddone");
printf("query %p: sendto returned %s\n", query,
isc_result_totext(sevent->result));
if (sevent->result != ISC_R_SUCCESS)
fctx_cancelquery(query, NULL);
isc_event_free(&event);
}
static isc_result_t
fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
resquery_t *query;
isc_result_t result;
dns_rdataset_t *qrdataset;
dns_name_t *qname;
isc_region_t r;
dns_resolver_t *res;
isc_task_t *task;
FCTXTRACE("sendquery");
res = fctx->res;
task = res->buckets[fctx->bucketnum].task;
result = fctx_starttimer(fctx);
if (result != ISC_R_SUCCESS)
return (result);
dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
qname = NULL;
result = dns_message_gettempname(fctx->qmessage, &qname);
if (result != ISC_R_SUCCESS)
goto cleanup_temps;
qrdataset = NULL;
result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
if (result != ISC_R_SUCCESS)
goto cleanup_temps;
query = isc_mem_get(res->mctx, sizeof *query);
if (query == NULL)
return (ISC_R_NOMEMORY);
isc_buffer_init(&query->buffer, query->data, sizeof query->data,
ISC_BUFFERTYPE_BINARY);
/*
* If this is a TCP query, then we need to make a socket and
* a dispatch for it here. Otherwise we use the resolver's
* shared dispatch. We do not attach to the resolver's shared
* dispatch if we use it, so the resolver MUST ensure that no
* fetches are running before changing the shared dispatch.
*/
if ((fctx->options & DNS_FETCHOPT_TCP) != 0) {
/* XXXRTH */
result = DNS_R_NOTIMPLEMENTED;
goto cleanup_query;
} else {
switch (isc_sockaddr_pf(address)) {
case AF_INET:
query->dispatch = res->dispatch4;
break;
case AF_INET6:
query->dispatch = res->dispatch6;
break;
default:
result = DNS_R_NOTIMPLEMENTED;
goto cleanup_query;
}
/*
* We should always have a valid dispatcher here. If we
* don't support a protocol family, then its dispatcher
* will be NULL, but we shouldn't be finding addresses for
* protocol types we don't support, so the dispatcher
* we found should never be NULL.
*/
INSIST(query->dispatch != NULL);
}
/*
* Get a query id from the dispatch.
*/
query->dispentry = NULL;
result = dns_dispatch_addresponse(query->dispatch,
address,
task,
query_response,
query,
&query->id,
&query->dispentry);
if (result != ISC_R_SUCCESS)
goto cleanup_query;
query->fctx = fctx;
query->tsig = NULL;
query->tsigkey = NULL;
query->magic = QUERY_MAGIC;
fctx->qmessage->opcode = dns_opcode_query;
/*
* Set up question.
*/
dns_name_init(qname, NULL);
dns_name_clone(&fctx->name, qname);
dns_rdataset_init(qrdataset);
dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
ISC_LIST_APPEND(qname->list, qrdataset, link);
dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
if ((fctx->options & DNS_FETCHOPT_RECURSIVE) != 0)
fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
/*
* We don't have to set opcode because it defaults to query.
*/
fctx->qmessage->id = query->id;
/*
* XXXRTH Add TSIG and/or ENDS0 OPT record tailored to the current
* recipient.
*/
/*
* Convert the question to wire format.
*/
result = dns_message_renderbegin(fctx->qmessage, &query->buffer);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
result = dns_message_rendersection(fctx->qmessage,
DNS_SECTION_QUESTION, 0, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
result = dns_message_rendersection(fctx->qmessage,
DNS_SECTION_ADDITIONAL, 0, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
result = dns_message_renderend(fctx->qmessage);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
if (fctx->qmessage->tsigkey != NULL) {
query->tsigkey = fctx->qmessage->tsigkey;
query->tsig = fctx->qmessage->tsig;
fctx->qmessage->tsig = NULL;
}
/*
* We're now done with the query message.
*/
dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
/*
* Send the query!
*/
isc_buffer_used(&query->buffer, &r);
result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch),
&r, task, query_senddone,
query, address);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
/*
* Finally, we've got everything going!
*/
ISC_LIST_APPEND(fctx->queries, query, link);
QTRACE("sent");
return (ISC_R_SUCCESS);
cleanup_message:
dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
/*
* Stop the dispatcher from listening.
*/
dns_dispatch_removeresponse(query->dispatch,
&query->dispentry,
NULL);
/*
* XXXRTH will need to cleanup a nonshared dispatch and TCP socket
* here.
*/
cleanup_query:
query->magic = 0;
isc_mem_put(res->mctx, query, sizeof *query);
cleanup_temps:
if (qname != NULL)
dns_message_puttempname(fctx->qmessage, &qname);
if (qrdataset != NULL)
dns_message_puttemprdataset(fctx->qmessage, &qrdataset);
fctx_stoptimer(fctx);
return (result);
}
static isc_result_t
fctx_getaddresses(fetchctx_t *fctx) {
isc_boolean_t use_hints;
dns_rdata_t rdata;
isc_region_t r;
dns_name_t name;
dns_rdataset_t rdataset;
isc_sockaddr_t *address;
struct in_addr ina;
isc_result_t result;
FCTXTRACE("getaddresses");
/*
* XXXRTH We don't try to handle forwarding yet.
*/
/*
* XXXRTH This code is a temporary hack until we have working
* address code.
*/
ISC_LIST_INIT(fctx->addresses);
fctx->address = NULL;
use_hints = dns_name_equal(&fctx->domain, dns_rootname);
INSIST(fctx->nameservers.type == dns_rdatatype_ns);
result = dns_rdataset_first(&fctx->nameservers);
while (result == ISC_R_SUCCESS) {
dns_rdataset_current(&fctx->nameservers, &rdata);
dns_rdata_toregion(&rdata, &r);
dns_name_init(&name, NULL);
dns_name_fromregion(&name, &r);
dns_rdataset_init(&rdataset);
result = dns_view_find(fctx->res->view, &name,
dns_rdatatype_a, 0, DNS_DBFIND_GLUEOK,
use_hints, &rdataset, NULL);
if (result == ISC_R_SUCCESS ||
result == DNS_R_GLUE ||
result == DNS_R_HINT) {
result = dns_rdataset_first(&rdataset);
while (result == ISC_R_SUCCESS) {
address = isc_mem_get(fctx->res->mctx,
sizeof *address);
if (address == NULL) {
result = ISC_R_NOMEMORY;
break;
}
dns_rdataset_current(&rdataset, &rdata);
INSIST(rdata.length == 4);
memcpy(&ina.s_addr, rdata.data, 4);
isc_sockaddr_fromin(address, &ina, 53);
ISC_LIST_APPEND(fctx->addresses, address,
link);
result = dns_rdataset_next(&rdataset);
}
}
dns_rdataset_disassociate(&rdataset);
result = dns_rdataset_next(&fctx->nameservers);
}
if (result == DNS_R_NOMORE)
result = ISC_R_SUCCESS;
return (result);
}
static void
fctx_try(fetchctx_t *fctx) {
isc_result_t result;
/*
* Caller must be holding the fetch's lock.
*/
REQUIRE(fctx->state == fetchstate_active);
FCTXTRACE("try");
if (fctx->address != NULL)
fctx->address = ISC_LIST_NEXT(fctx->address, link);
if (fctx->address == NULL) {
result = fctx_getaddresses(fctx);
if (result != ISC_R_SUCCESS) {
fctx_done(fctx, result);
return;
}
fctx->address = ISC_LIST_HEAD(fctx->addresses);
}
if (fctx->address == NULL) {
/*
* XXXRTH No addresses are available...
*/
INSIST(0);
}
/*
* XXXRTH This is the place where a try strategy routine would
* be called to send one or more queries. Instead, we
* just send a single query.
*/
result = fctx_sendquery(fctx, fctx->address);
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;
/*
* Caller must be holding the bucket lock.
*/
REQUIRE(VALID_FCTX(fctx));
REQUIRE(fctx->state == fetchstate_done);
REQUIRE(ISC_LIST_EMPTY(fctx->events));
REQUIRE(ISC_LIST_EMPTY(fctx->queries));
FCTXTRACE("destroy");
res = fctx->res;
bucketnum = fctx->bucketnum;
ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
isc_timer_detach(&fctx->timer);
dns_message_destroy(&fctx->rmessage);
dns_message_destroy(&fctx->qmessage);
if (dns_name_countlabels(&fctx->domain) > 0) {
dns_rdataset_disassociate(&fctx->nameservers);
dns_name_free(&fctx->domain, res->mctx);
}
dns_name_free(&fctx->name, fctx->res->mctx);
isc_mem_put(res->mctx, fctx, sizeof *fctx);
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->arg;
REQUIRE(VALID_FCTX(fctx));
(void)task; /* Keep compiler quiet. */
FCTXTRACE("timeout");
LOCK(&fctx->res->lock);
REQUIRE(fctx->state == fetchstate_active);
if (event->type == ISC_TIMEREVENT_LIFE) {
fctx_cancelqueries(fctx);
fctx_done(fctx, DNS_R_TIMEDOUT);
} else {
/*
* We could cancel the running queries here, or we could let
* them keep going. Right now we choose the latter...
*/
fctx_try(fctx);
}
UNLOCK(&fctx->res->lock);
isc_event_free(&event);
}
static void
fctx_shutdown(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx = event->arg;
isc_boolean_t need_done = ISC_FALSE, bucket_empty = ISC_FALSE;
dns_resolver_t *res;
unsigned int bucketnum;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
bucketnum = fctx->bucketnum;
(void)task; /* Keep compiler quiet. */
FCTXTRACE("shutdown");
LOCK(&res->buckets[bucketnum].lock);
INSIST(fctx->state == fetchstate_active ||
fctx->state == fetchstate_done);
INSIST(fctx->exiting);
if (fctx->state == fetchstate_done) {
if (fctx->references == 0)
bucket_empty = fctx_destroy(fctx);
} else
need_done = ISC_TRUE;
UNLOCK(&res->buckets[bucketnum].lock);
if (need_done)
fctx_done(fctx, ISC_R_CANCELED);
else if (bucket_empty)
empty_bucket(res);
}
static void
fctx_start(isc_task_t *task, isc_event_t *event) {
fetchctx_t *fctx = event->arg;
isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE;
dns_resolver_t *res;
unsigned int bucketnum;
REQUIRE(VALID_FCTX(fctx));
res = fctx->res;
bucketnum = fctx->bucketnum;
(void)task; /* Keep compiler quiet. */
FCTXTRACE("start");
LOCK(&res->buckets[bucketnum].lock);
INSIST(fctx->state == fetchstate_init);
if (fctx->exiting) {
/*
* We haven't started this fctx yet, and we've been requested
* to shut it down.
*
* The events list should be empty, so we INSIST on it.
*/
INSIST(ISC_LIST_EMPTY(fctx->events));
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_shutdown, fctx,
(void *)fctx_shutdown, NULL, NULL);
}
UNLOCK(&res->buckets[bucketnum].lock);
if (!done) {
/*
* All is well. Start working on the fetch.
*/
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_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)
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->tag = fetch;
dns_fixedname_init(&event->foundname);
ISC_LIST_APPEND(fctx->events, event, 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_R_SUCCESS;
isc_result_t iresult;
isc_interval_t interval;
/*
* Caller must be holding the lock for bucket number 'bucketnum'.
*/
REQUIRE(fctxp != NULL && *fctxp == NULL);
fctx = isc_mem_get(res->mctx, sizeof *fctx);
if (fctx == NULL)
return (ISC_R_NOMEMORY);
FCTXTRACE("create");
dns_name_init(&fctx->name, NULL);
result = dns_name_dup(name, res->mctx, &fctx->name);
if (result != ISC_R_SUCCESS)
goto cleanup_fetch;
dns_name_init(&fctx->domain, NULL);
dns_rdataset_init(&fctx->nameservers);
if (domain != NULL) {
result = dns_name_dup(domain, res->mctx, &fctx->domain);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
dns_rdataset_clone(nameservers, &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->exiting = ISC_FALSE;
ISC_LIST_INIT(fctx->queries);
ISC_LIST_INIT(fctx->addresses);
fctx->address = NULL;
fctx->qmessage = NULL;
result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTRENDER,
&fctx->qmessage);
if (result != ISC_R_SUCCESS)
goto cleanup_domain;
fctx->rmessage = NULL;
result = dns_message_create(res->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, 10, 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 = DNS_R_UNEXPECTED;
goto cleanup_rmessage;
}
/*
* XXX Retry interval initialization. Should be setup by the
* transmission strategy routine (when we have one).
*/
isc_interval_set(&fctx->interval, 1, 500000000);
/*
* 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 = DNS_R_UNEXPECTED;
goto cleanup_rmessage;
}
ISC_LIST_INIT(fctx->events);
ISC_LINK_INIT(fctx, link);
fctx->magic = FCTX_MAGIC;
ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
*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_rdataset_disassociate(&fctx->nameservers);
dns_name_free(&fctx->domain, res->mctx);
}
cleanup_name:
dns_name_free(&fctx->name, res->mctx);
cleanup_fetch:
isc_mem_put(res->mctx, fctx, sizeof *fctx);
return (result);
}
/*
* XXXRTH Cleanup
*/
#ifdef obsolete
static void
fctx_cancel(fetchctx_t *fctx) {
isc_result_t iresult;
FCTXTRACE("cancel");
if (fctx->state != fetchstate_done) {
fctx_cancelqueries(fctx);
iresult = isc_timer_reset(fctx->timer, isc_timertype_inactive,
NULL, NULL, ISC_TRUE);
if (iresult != ISC_R_SUCCESS)
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_timer_reset(): %s",
isc_result_totext(iresult));
fctx_done(fctx, DNS_R_CANCELED);
}
}
#endif
/*
* Handle Responses
*/
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
query_response(isc_task_t *task, isc_event_t *event) {
isc_result_t result;
resquery_t *query = event->arg;
dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
isc_boolean_t bad_sender = ISC_FALSE;
dns_message_t *message;
fetchctx_t *fctx;
REQUIRE(VALID_QUERY(query));
fctx = query->fctx;
REQUIRE(VALID_FCTX(fctx));
REQUIRE(event->type == DNS_EVENT_DISPATCH);
(void)task;
QTRACE("response");
LOCK(&fctx->res->lock);
INSIST(fctx->state == fetchstate_active);
message = fctx->rmessage;
message->querytsig = query->tsig;
message->tsigkey = query->tsigkey;
result = dns_message_parse(message, &devent->buffer, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
switch (result) {
case DNS_R_FORMERR:
case DNS_R_UNEXPECTEDEND:
bad_sender = ISC_TRUE;
break;
case DNS_R_MOREDATA:
result = DNS_R_NOTIMPLEMENTED;
break;
}
goto done;
}
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).
*/
/*
* Is it a query response?
*/
if (message->opcode != dns_opcode_query) {
/* XXXRTH Log */
bad_sender = 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) {
bad_sender = ISC_TRUE;
/*
* XXXRTH If we want to catch a FORMERR caused by an EDNS0
* OPT RR, this is the place to do it.
*/
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)
bad_sender = ISC_TRUE;
goto done;
}
/*
* Did we get any answers?
*/
if (message->counts[DNS_SECTION_ANSWER] > 0) {
/*
* We've got answers.
*/
result = DNS_R_NOTIMPLEMENTED;
goto foo;
} else if (message->counts[DNS_SECTION_AUTHORITY] > 0) {
/*
* NXDOMAIN, NXRDATASET, or referral.
*/
result = DNS_R_NOTIMPLEMENTED;
goto foo;
} else {
/*
* The sender is insane.
*/
/* XXXRTH Log */
bad_sender = ISC_TRUE;
goto done;
}
foo:
query->tsig = NULL;
fctx_stoptimer(fctx);
fctx_cancelquery(query, &devent);
result = DNS_R_SERVFAIL;
done:
/*
* XXXRTH Record round-trip statistics here.
*/
if (bad_sender) {
/*
* XXXRTH We will mark the sender as bad here instead
* of doing the printf().
*/
printf("bad sender\n");
dns_dispatch_freeevent(query->dispatch, query->dispentry,
&devent);
/*
* Keep trying.
*/
fctx_try(fctx);
} else {
/*
* All is well, or we got an error fatal to the fetch.
* In either case, we're done.
*/
fctx_done(fctx, result);
}
UNLOCK(&fctx->res->lock);
}
/***
*** Resolver Methods
***/
static void
destroy(dns_resolver_t *res) {
unsigned int i;
REQUIRE(res->references == 0);
RTRACE("destroy");
isc_mutex_destroy(&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);
isc_mutex_destroy(&res->buckets[i].lock);
}
isc_mem_put(res->mctx, res->buckets,
res->nbuckets * sizeof (fctxbucket_t));
if (res->dispatch4 != NULL)
dns_dispatch_detach(&res->dispatch4);
if (res->udpsocket4 != NULL)
isc_socket_detach(&res->udpsocket4);
if (res->dispatch6 != NULL)
dns_dispatch_detach(&res->dispatch6);
if (res->udpsocket6 != NULL)
isc_socket_detach(&res->udpsocket6);
res->magic = 0;
isc_mem_put(res->mctx, res, sizeof *res);
}
static void
empty_bucket(dns_resolver_t *res) {
isc_boolean_t need_destroy = ISC_FALSE;
RTRACE("empty_bucket");
LOCK(&res->lock);
INSIST(res->activebuckets > 0);
res->activebuckets--;
if (res->activebuckets == 0)
need_destroy = ISC_TRUE;
UNLOCK(&res->lock);
if (need_destroy)
destroy(res);
}
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,
dns_dispatch_t *dispatch, dns_resolver_t **resp)
{
dns_resolver_t *res;
isc_result_t result = ISC_R_SUCCESS;
unsigned int i, buckets_created = 0;
REQUIRE(resp != NULL && *resp == NULL);
REQUIRE(ntasks > 0);
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->view = view;
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, view->mctx, 0,
&res->buckets[i].task);
if (result != ISC_R_SUCCESS) {
isc_mutex_destroy(&res->buckets[i].lock);
goto cleanup_buckets;
}
ISC_LIST_INIT(res->buckets[i].fctxs);
res->buckets[i].exiting = ISC_FALSE;
buckets_created++;
}
/*
* IPv4 Dispatcher.
*/
res->dispatch4 = NULL;
res->udpsocket4 = NULL;
if (dispatch != NULL) {
dns_dispatch_attach(dispatch, &res->dispatch4);
} else if (isc_net_probeipv4() == ISC_R_SUCCESS) {
struct in_addr ina;
isc_sockaddr_t sa;
/*
* Create an IPv4 UDP socket and a dispatcher for it.
*/
result = isc_socket_create(socketmgr, AF_INET,
isc_sockettype_udp,
&res->udpsocket4);
if (result != ISC_R_SUCCESS)
goto cleanup_buckets;
/*
* XXXRTH Temporarily bind() to 5353 to make things
* easier for Bob's firewalls.
*/
ina.s_addr = htonl(INADDR_ANY);
isc_sockaddr_fromin(&sa, &ina, 5353);
result = isc_socket_bind(res->udpsocket4, &sa);
if (result != ISC_R_SUCCESS)
goto cleanup_buckets;
result = dns_dispatch_create(res->mctx, res->udpsocket4,
res->buckets[0].task, 4096,
50, 50, 14, &res->dispatch4);
if (result != ISC_R_SUCCESS)
goto cleanup_udpsocket4;
}
/*
* IPv6 Dispatcher.
*/
res->dispatch6 = NULL;
res->udpsocket6 = NULL;
if (isc_net_probeipv6() == ISC_R_SUCCESS) {
/*
* Create an IPv6 UDP socket and a dispatcher for it.
*/
result = isc_socket_create(socketmgr, AF_INET,
isc_sockettype_udp,
&res->udpsocket6);
if (result != ISC_R_SUCCESS)
goto cleanup_dispatch4;
result = dns_dispatch_create(res->mctx, res->udpsocket6,
res->buckets[0].task, 4096,
50, 50, 14, &res->dispatch6);
if (result != ISC_R_SUCCESS)
goto cleanup_udpsocket6;
}
res->references = 1;
res->exiting = ISC_FALSE;
result = isc_mutex_init(&res->lock);
if (result != ISC_R_SUCCESS)
goto cleanup_dispatch6;
res->magic = RES_MAGIC;
*resp = res;
return (ISC_R_SUCCESS);
cleanup_dispatch6:
if (res->dispatch6 != NULL)
dns_dispatch_detach(&res->dispatch6);
cleanup_udpsocket6:
if (res->udpsocket6 != NULL)
isc_socket_detach(&res->udpsocket6);
cleanup_dispatch4:
if (res->dispatch4 != NULL)
dns_dispatch_detach(&res->dispatch4);
cleanup_udpsocket4:
if (res->udpsocket4 != NULL)
isc_socket_detach(&res->udpsocket4);
cleanup_buckets:
for (i = 0; i < buckets_created; i++) {
(void)isc_mutex_destroy(&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);
}
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_detach(dns_resolver_t **resp) {
dns_resolver_t *res;
isc_boolean_t need_destroy = ISC_FALSE;
unsigned int i;
REQUIRE(resp != NULL);
res = *resp;
REQUIRE(VALID_RESOLVER(res));
RTRACE("detach");
LOCK(&res->lock);
INSIST(res->references > 0);
res->references--;
if (res->references == 0) {
RTRACE("exiting");
res->exiting = ISC_TRUE;
for (i = 0; i < res->nbuckets; i++) {
/*
* XXXRTH Post shutdown events?
*/
LOCK(&res->buckets[i].lock);
if (res->udpsocket4 != NULL)
isc_socket_cancel(res->udpsocket4,
res->buckets[i].task,
ISC_SOCKCANCEL_ALL);
if (res->udpsocket6 != NULL)
isc_socket_cancel(res->udpsocket6,
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)
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));
}
/*
* XXXRTH This routine takes an unconscionable number of arguments!
*
* Maybe caller should allocate an event and pass that in? Something must
* be done!
*/
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)
{
dns_fetch_t *fetch;
fetchctx_t *fctx = NULL;
isc_result_t result;
unsigned int bucketnum;
isc_boolean_t new_fctx = ISC_FALSE;
isc_event_t *event;
(void)forwarders;
REQUIRE(VALID_RESOLVER(res));
REQUIRE(fetchp != NULL && *fetchp == NULL);
RTRACE("createfetch");
/* XXXRTH */
if ((options & DNS_FETCHOPT_TCP) != 0)
return (DNS_R_NOTIMPLEMENTED);
/*
* XXXRTH use a mempool?
*/
fetch = isc_mem_get(res->mctx, sizeof *fetch);
if (fetch == NULL)
return (ISC_R_NOMEMORY);
bucketnum = dns_name_hash(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;
}
}
if (fctx == NULL || fctx->state == fetchstate_done) {
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, 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, (void *)fctx_create,
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_destroyfetch(dns_resolver_t *res, dns_fetch_t **fetchp) {
dns_fetch_t *fetch;
dns_fetchevent_t *event, *next_event;
isc_event_t *cevent;
fetchctx_t *fctx;
isc_task_t *etask;
REQUIRE(fetchp != NULL);
fetch = *fetchp;
REQUIRE(DNS_FETCH_VALID(fetch));
fctx = fetch->private;
FTRACE("destroyfetch");
LOCK(&res->buckets[fctx->bucketnum].lock);
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, link);
if (event->tag == fetch) {
ISC_LIST_UNLINK(fctx->events, event,
link);
FTRACE("found");
break;
}
}
}
if (event != NULL) {
etask = event->sender;
event->result = ISC_R_CANCELED;
isc_task_sendanddetach(&etask, (isc_event_t **)&event);
}
INSIST(fctx->references > 0);
fctx->references--;
if (fctx->references == 0) {
/*
* No one cares about the result of this fetch anymore.
* Shut it down.
*/
fctx->exiting = 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(res->buckets[fctx->bucketnum].task,
&cevent);
}
}
UNLOCK(&res->buckets[fctx->bucketnum].lock);
isc_mem_put(res->mctx, fetch, sizeof *fetch);
*fetchp = NULL;
}