dighost.c revision 8cd54a7461ad183f9e839d96cedb7b7ad03fb244
/*
* Copyright (C) 2000 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.
*/
/* $Id: dighost.c,v 1.87 2000/07/14 17:57:25 mws Exp $ */
/*
* Notice to programmers: Do not use this code as an example of how to
* use the ISC library to perform DNS lookups. Dig and Host both operate
* on the request level, since they allow fine-tuning of output and are
* intended as debugging tools. As a result, they perform many of the
* functions which could be better handled using the dns_resolver
* functions in most applications.
*/
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <limits.h>
#if (!(defined(HAVE_ADDRINFO) && defined(HAVE_GETADDRINFO)))
extern int h_errno;
#endif
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
unsigned int timeout = 0;
int sendcount = 0;
int recvcount = 0;
int sockcount = 0;
int ndots = -1;
int tries = 3;
int lookup_counter = 0;
int exitcode = 9;
char keynametext[MXNAME];
extern isc_boolean_t isc_mem_debugging;
/*
* Apply and clear locks at the event level in global task.
*/
#define LOCK_LOOKUP {\
debug("success");\
}
#define UNLOCK_LOOKUP {\
"isc_mutex_unlock");\
}
static void
static int
count_dots(char *string) {
char *s;
int i = 0;
s = string;
while (*s != '\0') {
if (*s == '.')
i++;
s++;
}
return (i);
}
static void
hex_dump(isc_buffer_t *b) {
unsigned int len;
isc_region_t r;
isc_buffer_remainingregion(b, &r);
printf("\n");
}
if (len % 16 != 0)
printf("\n");
}
void
if (exitcode == 0)
exitcode = 8;
#ifdef NEVER
if (isc_mem_debugging)
}
#endif
}
void
if (debugging) {
}
}
void
if (result != ISC_R_SUCCESS) {
exitcode = 1;
}
}
/*
* Tests if a field is a class, without needing isc libs
* initialized. This list will have to be manually kept in
* sync with what the libs support.
*/
const int numclasses = 3;
int i;
for (i = 0; i < numclasses; i++)
return (ISC_TRUE);
return (ISC_FALSE);
}
/*
* Tests if a field is a type, without needing isc libs
* initialized. This list will have to be manually kept in
* sync with what the libs support.
*/
"soa", "mb", "mg", "mr", "null",
"wks", "ptr", "hinfo", "minfo",
"mx", "txt", "rp", "afsdb",
"x25", "isdn", "rt", "nsap",
"nsap_ptr", "sig", "key", "px",
"gpos", "aaaa", "loc", "nxt",
"srv", "naptr", "kx", "cert",
"a6", "dname", "opt", "unspec",
"tkey", "tsig", "axfr", "any"};
const int numtypes = 42;
int i;
for (i = 0; i < numtypes; i++) {
return (ISC_TRUE);
}
return (ISC_FALSE);
}
make_server(const char *servname) {
fatal("Memory allocation failure in %s:%d",
return (srv);
}
void
{
debug("clone_server_list()");
}
}
make_empty_lookup(void) {
debug("make_lookup()");
fatal("Memory allocation failure in %s:%d",
ISC_LIST_INIT(looknew->q);
return (looknew);
}
/*
* Caution: If you don't clone the servers, you MUST clone the server
* list seperately from somewhere else, or construct it by hand.
*/
debug("clone_lookup()");
looknew = make_empty_lookup();
if (servers)
return (looknew);
}
debug("requeue_lookup()");
if (lookup_counter > LOOKUP_LIMIT)
fatal("Too many lookups");
debug("before insertion, init@%p "
"-> %p, new@%p -> %p",
debug("after insertion, init -> "
"%p, new = %p, new -> %p",
return (looknew);
}
void
setup_system(void) {
char *ptr;
dig_lookup_t *l;
int secretsize;
unsigned char *secretstore;
debug("setup_system()");
if (fixeddomain[0] != 0) {
fatal("Memory allocation failure in %s:%d",
}
if (get_servers &&
debug("got a nameserver line");
}
6) == 0) &&
(ndots == -1)) {
&ptr[6]);
debug("ndots is "
"%d.",
ndots);
}
}
&& usesearch){
!= NULL) {
mctx, sizeof(struct
dig_server));
fatal("Memory "
"allocation "
"failure in %s:"
"%d", __FILE__,
__LINE__);
ptr,
MXNAME - 1);
link);
}
(fixeddomain[0] == 0 )){
!= NULL) {
mctx, sizeof(struct
dig_server));
fatal("Memory "
"allocation "
"failure in %s:"
"%d", __FILE__,
__LINE__);
ptr,
MXNAME - 1);
link);
}
}
}
}
}
if (ndots == -1)
ndots = 1;
}
for (l = ISC_LIST_HEAD(lookup_list) ;
l != NULL;
l = ISC_LIST_NEXT(l, link) ) {
}
if (keysecret[0] != 0) {
debug("keyring");
debug("buffer");
debug("name");
debug("secretstore");
if (secretstore == NULL)
fatal("Memory allocation failure in %s:%d",
debug("lex");
if (result != ISC_R_SUCCESS) {
printf(";; Couldn't create key %s: %s\n",
goto SYSSETUP_FAIL;
}
debug("close");
debug("namefromtext");
namebuf);
if (result != ISC_R_SUCCESS) {
printf(";; Couldn't create key %s: %s\n",
goto SYSSETUP_FAIL;
}
debug("tsigkey");
if (result != ISC_R_SUCCESS) {
printf(";; Couldn't create key %s: %s\n",
}
return;
return;
}
}
void
setup_libs(void) {
debug("setup_libs()");
/*
* Warning: This is not particularly good randomness. We'll
* just use random() now for getting id values, but doing so
* does NOT insure that id's cann't be guessed.
*/
result = isc_app_start();
result = isc_net_probeipv4();
result = isc_net_probeipv6();
if (result == ISC_R_SUCCESS)
/*
* 6 and 2 set as reasonable parameters for 3 or 4 nameserver
* systems.
*/
}
static void
debug("add_opt()");
}
static void
{
debug("add_question()");
}
/*
* We're done iff all the counts are zero and the lookup list is empty
*/
static void
check_if_done(void) {
debug("check_if_done()");
debug("sockcount=%d, recvcount=%d, sendcount=%d, list %s",
&& ISC_LIST_EMPTY(lookup_list)) {
debug("shutting down");
}
}
/*
* Clear out a query when we're done with it. WARNING: This routine
* WILL invalidate the query pointer.
*/
static void
link);
link);
sockcount--;
}
}
static isc_boolean_t
dig_server_t *s;
dig_query_t *q;
void *ptr;
if (debugging) {
q = ISC_LIST_HEAD(lookup->q);
while (q != NULL) {
debug ("query to %s still pending",
q->servname);
q = ISC_LIST_NEXT(q, link);
}
return (ISC_FALSE);
}
}
/*
* At this point, we know there are no queries on the lookup,
* so can make it go away also.
*/
while (s != NULL) {
debug("freeing server %p belonging to %p",
s, lookup);
ptr = s;
s = ISC_LIST_NEXT(s, link);
}
}
return (ISC_TRUE);
}
/*
* If we can, start the next lookup in the queue running.
* This assumes that the lookup on the head of the queue hasn't been
* started yet.
*/
static void
begin_next_lookup(void) {
debug("begin_next_lookup()");
if (cancel_now)
return;
} else {
}
}
/*
* WARNING: The following routine may invalidate the lookup pointer.
* Never depend on being able to reference lookup or query pointers on the
* current lookup after calling this.
*/
static void
}
}
if (still_working)
return;
if (try_clear_lookup(lookup))
}
static void
isc_buffer_t *b = NULL;
isc_region_t r;
int len;
debug("followup_lookup()");
if (result != ISC_R_SUCCESS) {
debug("firstname returned %s",
if ((section == DNS_SECTION_ANSWER) &&
return;
}
for (;;) {
while (loopresult == ISC_R_SUCCESS) {
debug("got rdata with type %d",
{
BUFSIZE);
"isc_buffer_allocate");
NULL,
b);
"dns_rdata_totext");
isc_buffer_usedregion(b, &r);
/* Initialize lookup if we've not yet */
debug("found NS %d %.*s",
(char *)r.base);
if (!success) {
if (section ==
}
else {
query->
query->
}
}
debug("adding server %s",
srv->servername);
isc_buffer_free(&b);
}
}
}
if (result != ISC_R_SUCCESS)
break;
}
}
static void
debug("next_origin()");
/*
* Then we just did rootorg; there's nothing left.
*/
debug("made it to the root with nowhere to go");
return;
}
}
static void
debug("insert_soa()");
MXNAME);
}
void
int len;
isc_region_t r;
isc_buffer_t b;
debug("setting up for looking up %s @%p->%p",
if (lookup->new_search) {
debug("resetting lookup counter.");
lookup_counter = 0;
}
debug("cloning server list");
}
sizeof(lookup->onamespace));
isc_buffer_add(&b, len);
if (result != ISC_R_SUCCESS) {
fatal("%s is not a legal name syntax (%s)",
}
if (!lookup->trace_root) {
isc_buffer_add(&b, len);
} else {
}
if (result != ISC_R_SUCCESS) {
fatal("%s is not a legal name syntax (%s)",
}
} else {
debug("using root origin");
if (!lookup->trace_root) {
isc_buffer_add(&b, len);
} else {
}
if (result != ISC_R_SUCCESS) {
isc_buffer_usedregion(&b, &r);
fatal("%s/%.*s is not a legal name syntax "
}
}
isc_buffer_usedregion(&b, &r);
lookup->msgcounter = 0;
/*
* If this is a trace request, completely disallow recursion, since
* it's meaningless for traces.
*/
debug("recursive query");
}
debug("AA query");
}
debug("AD query");
}
debug("CD query");
}
if (lookup->trace_root) {
debug("doing trace_root");
} else {
}
if ((rdtype == dns_rdatatype_axfr) ||
(rdtype == dns_rdatatype_ixfr)) {
/*
* Force TCP mode if we're doing an xfr.
*/
}
if (lookup->trace_root) {
} else {
}
if (rdtype == dns_rdatatype_ixfr)
debug("initializing keys");
}
fatal("memory allocation failure");
debug("starting to render the message");
}
DNS_SECTION_QUESTION, 0);
debug("done rendering");
fatal("Memory allocation failure in %s:%d",
debug("create query %p linked to lookup %p",
query->second_rr_serial = 0;
fatal("memory allocation failure");
}
ISC_TRUE);
}
}
static void
dig_lookup_t *l;
debug("send_done()");
sendcount--;
debug("in send cancel handler");
}
}
void
debug("cancel_lookup()");
debug("cancelling a worker");
}
}
}
}
static void
static void
void
unsigned int local_timeout;
debug("send_udp()");
if (timeout == 0) {
else
} else
}
debug("working on lookup %p, query %p",
debug("recving with lookup=%p, query=%p, sock=%p",
recvcount++;
debug("sending a request");
sendcount++;
}
}
/*
* connect_timeout is used for both UDP recieves and TCP connects.
*/
static void
dig_query_t *q=NULL;
isc_buffer_t *b=NULL;
isc_region_t r;
debug("connect_timeout()");
debug("buffer allocate connect_timeout");
for (q = ISC_LIST_HEAD(lookup->q);
q != NULL;
q = ISC_LIST_NEXT(q, link)) {
if (q->working) {
isc_buffer_clear(b);
isc_buffer_usedregion(b, &r);
printf(";; Connection to %.*s(%s) "
"for %s timed out. "
"Retrying %d.\n",
q->servname,
else {
printf(";; Connection to "
"%.*s(%s) "
"for %s timed out. "
"Giving up.\n",
q->servname,
}
}
}
isc_buffer_free(&b);
debug("done with connect_timeout()");
}
static void
isc_buffer_t *b=NULL;
isc_region_t r;
dig_lookup_t *l;
debug("tcp_length_done()");
recvcount--;
return;
}
debug("buffer allocate connect_timeout");
isc_buffer_usedregion(b, &r);
isc_buffer_free(&b);
sockcount--;
return;
}
length = isc_buffer_getuint16(b);
fatal("Length of %X was longer than I can handle!",
length);
}
/*
* Even though the buffer was already init'ed, we need
* to redo it now, to force the length we want.
*/
debug("recving with lookup=%p, query=%p",
recvcount++;
debug("resubmitted recv request with length %d, recvcount=%d",
}
static void
dig_lookup_t *l;
debug("launch_next_query()");
debug("ignoring launch_next_query because !pending");
sockcount--;
return;
}
if (include_question) {
link);
}
recvcount++;
if (!query->first_soa_rcvd) {
debug("sending a request");
sendcount++;
}
return;
}
static void
dig_lookup_t *l;
isc_buffer_t *b = NULL;
isc_region_t r;
debug("connect_done()");
debug("unsuccessful connection: %s",
isc_buffer_usedregion(b, &r);
printf(";; Connection to %.*s(%s) for %s failed: "
sockcount--;
if (exitcode < 9)
exitcode = 9;
isc_buffer_free(&b);
return;
}
}
#if 0
static isc_boolean_t
debug("msg_contains_soa()");
if (result == ISC_R_SUCCESS) {
return (ISC_TRUE);
} else {
debug("didn't find SOA, result=%d:%s",
return (ISC_FALSE);
}
}
#endif
/*
* Returns true if we should call cancel_lookup(). This is a hack.
*/
static isc_boolean_t
{
isc_buffer_t b;
isc_region_t r;
debug("check_for_more_data()");
/*
* By the time we're in this routine, we know we're doing
* either an AXFR or IXFR. If there's no second_rr_type,
* then we don't yet know which kind of answer we got back
* from the server. Here, we're going to walk through the
* rr's in the message, acting as necessary whenever we hit
* an SOA rr.
*/
if (result != ISC_R_SUCCESS) {
puts("; Transfer failed.");
return (ISC_TRUE);
}
#ifdef NEVER
#endif
do {
&name);
if (result != ISC_R_SUCCESS)
continue;
do {
/*
* If this is the first rr, make sure
* it's an SOA
*/
if ((!query->first_soa_rcvd) &&
puts("; Transfer failed. "
"Didn't start with "
"SOA answer.");
return (ISC_TRUE);
}
if ((!query->second_rr_rcvd) &&
query->second_rr_serial = 0;
debug("got the second rr as nonsoa");
continue;
}
/*
* If the record is anything except an SOA
* now, just continue on...
*/
goto next_rdata;
/* Now we have an SOA. Work with it. */
debug("got an SOA");
&soa,
mctx);
"dns_rdata_tostruct");
if (!query->first_soa_rcvd) {
debug("this is the first %d",
goto xfr_done;
}
goto next_rdata;
}
if (!query->second_rr_rcvd) {
debug("this is the second %d",
goto next_rdata;
}
if (query->second_rr_serial == 0) {
/*
* If the second RR was a non-SOA
* record, and we're getting any
* other SOA, then this is an
* AXFR, and we're done.
*/
debug("done, since axfr");
&b);
"isc_sockaddr_totext");
isc_buffer_usedregion(&b, &r);
return (ISC_TRUE);
}
/*
* If we get to this point, we're doing an
* IXFR and have to start really looking
* at serial numbers.
*/
debug("got a match for ixfr");
if (!query->first_repeat_rcvd) {
goto next_rdata;
}
debug("done with ixfr");
goto xfr_done;
}
debug("meaningless soa %d",
} while (result == ISC_R_SUCCESS);
}
} while (result == ISC_R_SUCCESS);
return (ISC_FALSE);
}
static void
isc_buffer_t *b = NULL;
isc_region_t r;
dig_lookup_t *n, *l;
unsigned int local_timeout;
debug("recv_done()");
recvcount--;
isc_timer_touch(l->timer);
if ((!l->pending && !l->ns_search_only)
|| cancel_now) {
debug("no longer pending. Got %s",
return;
}
&msg);
debug("getting initial querysig");
l->sendmsg,
"dns_message_getquerytsig");
}
l->querysig);
if (l->msgcounter != 0)
l->msgcounter++;
}
debug("before parse starts");
if (result != ISC_R_SUCCESS) {
printf(";; Got bad UDP packet:\n");
hex_dump(b);
if (!l->tcp_mode) {
printf(";; Retrying in TCP mode.\n");
n = requeue_lookup(l, ISC_TRUE);
}
cancel_lookup(l);
return;
}
debug("before verify");
debug("after verify");
if (result != ISC_R_SUCCESS) {
printf(";; Couldn't verify signature: %s\n",
}
debug("freeing querysig buffer %p",
l->querysig);
isc_buffer_free(&l->querysig);
}
&l->querysig);
}
debug("after parse");
/*
* Once we are in the XFR message, increase
* the timeout to much longer, so brief network
* outages won't cause the XFR to abort
*/
if (timeout == 0) {
if (l->tcp_mode)
else
} else {
else
}
debug("have local timeout of %d",
isc_interval_set(&l->interval,
local_timeout, 0);
NULL,
&l->interval,
}
}
if ((l->trace)||
(l->ns_search_only)) {
debug("in TRACE code");
if (show_details ||
== ISC_R_SUCCESS)) &&
!l->trace_root)) {
}
} else {
if ((result != ISC_R_SUCCESS) ||
l->trace_root)
}
if (show_details) {
}
} else {
if (query->first_soa_rcvd &&
l->doing_xfr)
else
}
== ISC_R_SUCCESS) &&
l->ns_search_only &&
!l->trace_root ) {
}
if (l->pending)
debug("still pending.");
if (l->doing_xfr) {
isc_event_free (&event);
return;
}
if (docancel) {
cancel_lookup(l);
}
}
else {
&ab);
isc_buffer_usedregion(&ab, &r);
if ((dns_message_firstname(msg,
== ISC_R_SUCCESS) ||
(char *)r.base,
query);
}
}
cancel_lookup(l);
}
}
return;
}
/*
* In truth, we should never get into the CANCELED routine, since
* the cancel_lookup() routine clears the pending flag.
*/
debug("in recv cancel handler");
return;
}
fatal("recv_done got result %s",
}
void
#if defined(HAVE_ADDRINFO) && defined(HAVE_GETADDRINFO)
int result;
#else
#endif
debug("get_address()");
else {
#if defined(HAVE_ADDRINFO) && defined(HAVE_GETADDRINFO)
if (result != 0) {
fatal("Couldn't find server '%s': %s",
}
#else
fatal("Couldn't find server '%s' (h_errno=%d)",
port);
#endif
}
}
static void
unsigned int local_timeout;
debug("do_lookup_tcp()");
if (timeout == 0) {
else
} else
}
sockcount++;
if (specified_source)
else {
else
}
}
}
static void
debug("do_lookup_udp()");
sockcount++;
if (specified_source)
else {
else
}
}
}
void
debug("do_lookup()");
else
}
void
start_lookup(void) {
debug("start_lookup()");
}
}
void
start_lookup();
}
/*
* This will be used in the SIGINT handler, and perhaps other places.
*/
void
cancel_all(void) {
dig_lookup_t *l;
dig_query_t *q;
debug("cancel_all()");
if (free_now) {
return;
}
l = ISC_LIST_HEAD(lookup_list);
while (l != NULL) {
isc_timer_detach(&l->timer);
q = ISC_LIST_HEAD(l->q);
while (q != NULL) {
debug("cancelling query %p, belonging to %p",
q, l);
}
q = ISC_LIST_NEXT(q, link);
}
l = ISC_LIST_NEXT(l, link);
}
}
static void
xfree_lists(void) {
void *ptr;
dig_server_t *s;
dig_searchlist_t *o;
debug("free_lists()");
s = ISC_LIST_HEAD(server_list);
while (s != NULL) {
debug("freeing global server %p", s);
ptr = s;
s = ISC_LIST_NEXT(s, link);
}
o = ISC_LIST_HEAD(search_list);
while (o != NULL) {
debug("freeing search %p", o);
ptr = o;
o = ISC_LIST_NEXT(o, link);
}
debug("freeing commctx");
}
debug("freeing socketmgr");
}
debug("freeing timermgr");
}
}
}
if (is_dst_up) {
debug("destroy DST lib");
}
debug("detach from entropy");
}
}
void
destroy_libs(void) {
debug("destroy_libs()");
if (global_task != NULL) {
debug("freeing task");
}
debug("Freeing taskmgr");
}
xfree_lists();
if (isc_mem_debugging)
}
/*
* Dummy function, soon to go away
*/
void
free_lists(void) {
}