resolver.c revision 5f0e2c8913fed44e1629f1367ce54e74ce2a2eb3
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include <config.h>
#include <isc/assertions.h>
#include <dns/dispatch.h>
#include <dns/resolver.h>
#include <dns/rdataset.h>
#define DNS_RESOLVER_TRACE
#ifdef DNS_RESOLVER_TRACE
#else
#define RTRACE(m)
#define RRTRACE(r, m)
#define FCTXTRACE(m)
#define FTRACE(m)
#define QTRACE(m)
#endif
typedef struct fetchctx fetchctx_t;
typedef struct query {
/* Not locked. */
unsigned int magic;
fetchctx_t * fctx;
/* Locked by fctx lock. */
unsigned char data[512];
} resquery_t;
typedef enum {
fetchstate_init = 0,
} fetchstate;
struct fetchctx {
/* Not locked. */
unsigned int magic;
unsigned int references;
unsigned int locknum;
unsigned int options;
isc_task_t * task;
/* Locked by lock. */
isc_timer_t * timer;
};
struct dns_resolver {
/* Unlocked */
unsigned int magic;
/* Locked by lock. */
unsigned int references;
unsigned int ntasks;
unsigned int next_task;
isc_task_t ** tasks;
};
/*
* Internal fetch routines. Caller must be holding the proper lock.
*/
static inline dns_result_t
if (iresult != ISC_R_SUCCESS) {
"isc_timer_reset(): %s",
return (DNS_R_UNEXPECTED);
}
return (DNS_R_SUCCESS);
}
static inline void
/*
* We don't return a result if resetting the timer to inactive fails
* since there's nothing to be done about it. Resetting to inactive
* should never fail anyway, since the code as currently written
* cannot fail in that case.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_timer_reset(): %s",
}
}
static void
/*
* The caller must be holding the proper lock.
*/
FCTXTRACE("done");
event = next_event) {
if (iresult != ISC_R_SUCCESS) {
"isc_task_send(): %s",
}
}
}
static void
(void)task;
QTRACE("senddone");
}
static dns_result_t
isc_region_t r;
FCTXTRACE("sendquery");
if (result != DNS_R_SUCCESS)
return (result);
return (DNS_R_NOMEMORY);
/*
* 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 chaning the shared dispatch.
*/
/* XXXRTH */
goto cleanup_query;
} else
/*
* Get a query id from the dispatch.
*/
if (result != DNS_R_SUCCESS)
goto cleanup_query;
/*
* Set up question.
*/
/*
* We don't have to set opcode because it defaults to query.
*/
/*
* recipient.
*/
/*
* Convert the question to wire format.
*/
if (result != DNS_R_SUCCESS)
goto cleanup_message;
DNS_SECTION_QUESTION, 0, 0);
if (result != DNS_R_SUCCESS)
goto cleanup_message;
DNS_SECTION_ADDITIONAL, 0, 0);
if (result != DNS_R_SUCCESS)
goto cleanup_message;
if (result != DNS_R_SUCCESS)
goto cleanup_message;
}
/*
* We're now done with the query message.
*
* It's imperative that we reset the message before we return,
* because the rdataset used for the question is on our stack,
* and won't be valid after we return.
*/
/*
* Send the query!
*/
if (result != ISC_R_SUCCESS)
goto cleanup_message;
/*
* Finally, we've got everything going!
*/
QTRACE("sent");
return (DNS_R_SUCCESS);
/*
* It's imperative that we reset the message here, because
* the rdataset used for the question is on our stack, and won't
* be valid after we return.
*/
/*
* Stop the dispatch from listening.
*/
NULL);
/*
* XXXRTH will need to cleanup a nonshared dispatch and TCP socket
* here.
*/
return (result);
}
static inline void
FCTXTRACE("cancelquery");
deventp);
}
static void
FCTXTRACE("cancelqueries");
query = next_query) {
}
}
static void
FCTXTRACE("getaddresses");
}
static void
/*
* Caller must be holding the fetch's lock.
*/
FCTXTRACE("try");
}
/* 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.
*/
if (result != DNS_R_SUCCESS)
}
static void
FCTXTRACE("destroy");
}
}
/*
* Fetch event handlers.
*/
static void
(void)task; /* Keep compiler quiet. */
FCTXTRACE("timeout");
} else {
/*
* We could cancel the running queries here, or we could let
* them keep going. Right now we choose the latter...
*/
}
}
static void
(void)task; /* Keep compiler quiet. */
FCTXTRACE("start");
} else {
/*
* The events list should be empty, so we INSIST on it.
* Since the event list is empty, the result code we pass
* to fctx_done doesn't matter.
*/
}
if (need_fctx_destroy)
}
/*
* Fetch Creation, Joining, and Cancelation.
*/
static inline dns_result_t
{
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.
*/
event = (dns_fetchdoneevent_t *)
return (DNS_R_NOMEMORY);
/*
* XXX other event initialization here.
*/
fctx->references++;
return (DNS_R_SUCCESS);
}
static dns_result_t
unsigned int options,
{
return (DNS_R_NOMEMORY);
FCTXTRACE("create");
if (result != DNS_R_SUCCESS)
goto cleanup_fetch;
if (result != DNS_R_SUCCESS)
goto cleanup_name;
}
/*
* 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->references = 0;
if (result != DNS_R_SUCCESS)
goto cleanup_domain;
if (result != DNS_R_SUCCESS)
goto cleanup_qmessage;
/*
* Compute an expiration time for the entire fetch.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_stdtime_get: %s",
goto cleanup_rmessage;
}
/*
* XXX Retry interval initialization. Should be setup by the
* transmission strategy routine (when we have one).
*/
/*
* Create an inactive timer. It will be made active when the fetch
* is actually started.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_timer_create: %s",
goto cleanup_rmessage;
}
if (result != DNS_R_SUCCESS)
goto cleanup_timer;
/*
* The fetch is now ready to go. We send the start event to its
* task to get the ball rolling.
*
* XXX we should really send this event from dns_resolver_fetch(),
* after we've unlocked the fetch's lock, otherwise the other task
* could well block on the lock we're about to release.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_task_send: %s",
goto cleanup_timer;
}
return (DNS_R_SUCCESS);
}
return (result);
}
static void
FCTXTRACE("cancel");
if (iresult != ISC_R_SUCCESS)
"isc_timer_reset(): %s",
}
}
/*
* Handle Responses
*/
static inline dns_result_t
/*
* Caller must be holding the fctx lock.
*/
/*
* XXXRTH Currently we support only one question.
*/
return (DNS_R_FORMERR);
if (result != DNS_R_SUCCESS)
return (result);
return (DNS_R_FORMERR);
return (DNS_R_SUCCESS);
}
static void
(void)task;
QTRACE("response");
if (result != DNS_R_SUCCESS) {
switch (result) {
case DNS_R_FORMERR:
case DNS_R_UNEXPECTEDEND:
break;
case DNS_R_MOREDATA:
break;
}
goto done;
}
/*
* 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?
*/
/* XXXRTH Log */
goto done;
}
/*
* Is the remote server broken, or does it dislike us?
*/
/*
* XXXRTH If we want to catch a FORMERR caused by an EDNS0
* OPT RR, this is the place to do it.
*/
goto done;
}
/*
* Does the it answer the question we asked?
*/
if (result != DNS_R_SUCCESS) {
/* XXXRTH Log */
if (result == DNS_R_FORMERR)
goto done;
}
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");
&devent);
/*
* Keep trying.
*/
} else {
/*
* All is well, or we got an error fatal to the fetch.
* In either case, we're done.
*/
}
}
/***
*** Resolver Methods
***/
static void
unsigned int i;
RTRACE("destroy");
}
}
{
unsigned int i, tasks_created = 0;
return (DNS_R_NOMEMORY);
RTRACE("create");
goto cleanup_res;
}
for (i = 0; i < ntasks; i++) {
if (iresult != ISC_R_SUCCESS) {
"isc_task_create() failed: %s",
goto cleanup_tasks;
}
}
if (iresult != ISC_R_SUCCESS) {
"isc_mutex_init() failed: %s",
goto cleanup_tasks;
}
return (DNS_R_SUCCESS);
for (i = 0; i < tasks_created; i++) {
}
return (result);
}
void
source->references++;
}
void
RTRACE("detach");
res->references--;
if (res->references == 0) {
RTRACE("exiting");
}
if (need_destroy)
}
static inline isc_boolean_t
unsigned int options)
{
return (ISC_FALSE);
}
{
(void)forwarders;
/*
* We require !res->exiting, since if it were exiting, that would
* mean that the reference count was wrong!
*/
RTRACE("createfetch");
/* XXXRTH */
if ((options & DNS_FETCHOPT_TCP) != 0)
return (DNS_R_NOTIMPLEMENTED);
return (DNS_R_NOMEMORY);
/*
* XXXRTH This is for correctness, and doesn't represent the final
* way of assigning tasks, or the final form of the fetch table.
*/
if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
break;
}
}
fetch);
if (result == DNS_R_SUCCESS) {
}
} else
if (result == DNS_R_SUCCESS) {
FTRACE("created");
} else
return (result);
}
void
/*
* XXXRTH We could make it so that even if all the clients detach
* from the fetch, the fctx keeps going. Perhaps this should be
* a resolver option? Right now if they all go away the fctx will
* be destroyed too.
*/
FTRACE("destroyfetch");
event = next_event) {
FTRACE("found");
break;
}
}
fctx->references--;
if (fctx->references == 0) {
/*
* The fctx is still initializing, which means that
* the start event either hasn't been delivered, or
* is being processed right now, but is blocked waiting
* for the lock.
*
* Rather than try to purge the event, we simply
* wait for it to happen, deferring further destruction
* until it has been processed.
*/
} else {
}
}
}
if (need_fctx_destroy)
}
void
}