Mostly, several functions that take pointers as arguments, almost always char * pointers, had those pointers qualified with "const". Those that returned pointers to previously const-qualified arguments had their return values qualified as const. Some structure members were qualified as const to retain that attribute from the variables from which they were assigned. Minor other ISC style cleanups.
1726 lines
44 KiB
C
1726 lines
44 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* 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>
|
|
|
|
extern int h_errno;
|
|
|
|
#include <isc/app.h>
|
|
#include <isc/netdb.h>
|
|
#include <isc/string.h>
|
|
#include <isc/task.h>
|
|
#include <isc/timer.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/message.h>
|
|
#include <dns/name.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdataclass.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/rdatatype.h>
|
|
#include <dns/rdatalist.h>
|
|
#include <dns/result.h>
|
|
|
|
#include <dig/dig.h>
|
|
|
|
ISC_LIST(dig_lookup_t) lookup_list;
|
|
ISC_LIST(dig_server_t) server_list;
|
|
ISC_LIST(dig_searchlist_t) search_list;
|
|
|
|
isc_boolean_t have_ipv6 = ISC_FALSE, specified_source = ISC_FALSE,
|
|
free_now = ISC_FALSE, show_details = ISC_FALSE, usesearch=ISC_TRUE,
|
|
qr = ISC_FALSE;
|
|
#ifdef TWIDDLE
|
|
isc_boolean_t twiddle = ISC_FALSE;
|
|
#endif
|
|
in_port_t port = 53;
|
|
unsigned int timeout = 5;
|
|
isc_mem_t *mctx = NULL;
|
|
isc_taskmgr_t *taskmgr = NULL;
|
|
isc_task_t *global_task = NULL;
|
|
isc_timermgr_t *timermgr = NULL;
|
|
isc_socketmgr_t *socketmgr = NULL;
|
|
isc_sockaddr_t bind_address;
|
|
char *rootspace[BUFSIZE];
|
|
isc_buffer_t rootbuf;
|
|
int sendcount = 0;
|
|
int sockcount = 0;
|
|
int ndots = -1;
|
|
int tries = 3;
|
|
int lookup_counter = 0;
|
|
char fixeddomain[MXNAME]="";
|
|
int exitcode = 9;
|
|
|
|
static void
|
|
cancel_lookup(dig_lookup_t *lookup);
|
|
|
|
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("Printing a buffer with length %d\n", r.length);
|
|
for (len = 0 ; len < r.length ; len++) {
|
|
printf("%02x ", r.base[len]);
|
|
if (len != 0 && len % 16 == 0)
|
|
printf("\n");
|
|
}
|
|
if (len % 16 != 0)
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void
|
|
fatal(const char *format, ...) {
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
if (exitcode == 0)
|
|
exitcode = 8;
|
|
#ifdef NEVER
|
|
dighost_shutdown();
|
|
free_lists(exitcode);
|
|
if (mctx != NULL) {
|
|
#ifdef MEMDEBUG
|
|
isc_mem_stats(mctx, stderr);
|
|
#endif
|
|
isc_mem_destroy(&mctx);
|
|
}
|
|
#endif
|
|
exit(exitcode);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
debug(const char *format, ...) {
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#else
|
|
void
|
|
debug(const char *format, ...) {
|
|
UNUSED(format);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
check_result(isc_result_t result, const char *msg) {
|
|
if (result != ISC_R_SUCCESS) {
|
|
exitcode = 1;
|
|
fatal("%s: %s", msg, isc_result_totext(result));
|
|
}
|
|
}
|
|
|
|
isc_boolean_t
|
|
isclass(char *text) {
|
|
/* 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 char *classlist[] = {"in", "hs", "chaos"};
|
|
const int numclasses = 3;
|
|
int i;
|
|
|
|
for (i = 0; i < numclasses; i++)
|
|
if (strcasecmp(text, classlist[i]) == 0)
|
|
return ISC_TRUE;
|
|
|
|
return ISC_FALSE;
|
|
}
|
|
|
|
isc_boolean_t
|
|
istype(char *text) {
|
|
/* 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.
|
|
*/
|
|
const char *typelist[] = {"a", "ns", "md", "mf", "cname",
|
|
"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++) {
|
|
if (strcasecmp(text, typelist[i]) == 0)
|
|
return ISC_TRUE;
|
|
}
|
|
return ISC_FALSE;
|
|
}
|
|
|
|
|
|
#ifdef TWIDDLE
|
|
void
|
|
twiddlebuf(isc_buffer_t buf) {
|
|
isc_region_t r;
|
|
int len, pos, bit;
|
|
unsigned char bitfield;
|
|
int i, tw;
|
|
|
|
hex_dump(&buf);
|
|
tw=TWIDDLE;
|
|
printf ("Twiddling %d bits: ", tw);
|
|
for (i=0;i<tw;i++) {
|
|
isc_buffer_usedregion (&buf, &r);
|
|
len = r.length;
|
|
pos=(int)random();
|
|
pos = pos%len;
|
|
bit = (int)random()%8;
|
|
bitfield = 1 << bit;
|
|
printf ("%d@%03x ", bit, pos);
|
|
r.base[pos] ^= bitfield;
|
|
}
|
|
puts ("");
|
|
hex_dump(&buf);
|
|
}
|
|
#endif
|
|
|
|
dig_lookup_t
|
|
*requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
|
|
dig_lookup_t *looknew;
|
|
dig_server_t *s, *srv;
|
|
|
|
debug("requeue_lookup()");
|
|
|
|
if (free_now)
|
|
return(ISC_R_SUCCESS);
|
|
|
|
lookup_counter++;
|
|
if (lookup_counter > LOOKUP_LIMIT)
|
|
fatal ("Too many lookups.");
|
|
looknew = isc_mem_allocate
|
|
(mctx, sizeof(struct dig_lookup));
|
|
if (looknew == NULL)
|
|
fatal ("Memory allocation failure in %s:%d",
|
|
__FILE__, __LINE__);
|
|
looknew->pending = ISC_FALSE;
|
|
strncpy (looknew->textname, lookold-> textname, MXNAME);
|
|
strncpy (looknew->rttext, lookold-> rttext, 32);
|
|
strncpy (looknew->rctext, lookold-> rctext, 32);
|
|
looknew->namespace[0]=0;
|
|
looknew->sendspace[0]=0;
|
|
looknew->sendmsg=NULL;
|
|
looknew->name=NULL;
|
|
looknew->oname=NULL;
|
|
looknew->timer = NULL;
|
|
looknew->xfr_q = NULL;
|
|
looknew->doing_xfr = lookold->doing_xfr;
|
|
looknew->defname = lookold->defname;
|
|
looknew->trace = lookold->trace;
|
|
looknew->trace_root = lookold->trace_root;
|
|
looknew->identify = lookold->identify;
|
|
looknew->udpsize = lookold->udpsize;
|
|
looknew->recurse = lookold->recurse;
|
|
looknew->aaonly = lookold->aaonly;
|
|
looknew->ns_search_only = lookold->ns_search_only;
|
|
looknew->origin = NULL;
|
|
looknew->retries = tries;
|
|
looknew->nsfound = 0;
|
|
looknew->tcp_mode = lookold->tcp_mode;
|
|
looknew->comments = lookold->comments;
|
|
looknew->stats = lookold->stats;
|
|
looknew->section_question = lookold->section_question;
|
|
looknew->section_answer = lookold->section_answer;
|
|
looknew->section_authority = lookold->section_authority;
|
|
looknew->section_additional = lookold->section_additional;
|
|
looknew->new_search = ISC_FALSE;
|
|
ISC_LIST_INIT(looknew->my_server_list);
|
|
ISC_LIST_INIT(looknew->q);
|
|
|
|
looknew->use_my_server_list = ISC_FALSE;
|
|
if (servers) {
|
|
looknew->use_my_server_list = lookold->use_my_server_list;
|
|
if (looknew->use_my_server_list) {
|
|
s = ISC_LIST_HEAD(lookold->my_server_list);
|
|
while (s != NULL) {
|
|
srv = isc_mem_allocate (mctx, sizeof(struct
|
|
dig_server));
|
|
if (srv == NULL)
|
|
fatal("Memory allocation failure "
|
|
"in %s:%d", __FILE__, __LINE__);
|
|
strncpy(srv->servername, s->servername,
|
|
MXNAME);
|
|
ISC_LIST_ENQUEUE(looknew->my_server_list, srv,
|
|
link);
|
|
s = ISC_LIST_NEXT(s, link);
|
|
}
|
|
}
|
|
}
|
|
debug ("Before insertion, init@%lx "
|
|
"-> %lx, new@%lx "
|
|
"-> %lx", (long int)lookold,
|
|
(long int)lookold->link.next,
|
|
(long int)looknew, (long int)looknew->
|
|
link.next);
|
|
ISC_LIST_INSERTAFTER(lookup_list, lookold, looknew, link);
|
|
debug ("After insertion, init -> "
|
|
"%lx, new = %lx, "
|
|
"new -> %lx", (long int)lookold,
|
|
(long int)looknew, (long int)looknew->
|
|
link.next);
|
|
return (looknew);
|
|
}
|
|
|
|
void
|
|
setup_system(void) {
|
|
char rcinput[MXNAME];
|
|
FILE *fp;
|
|
char *ptr;
|
|
dig_server_t *srv;
|
|
dig_searchlist_t *search;
|
|
dig_lookup_t *l;
|
|
isc_boolean_t get_servers;
|
|
|
|
|
|
if (fixeddomain[0]!=0) {
|
|
search = isc_mem_allocate( mctx, sizeof(struct dig_server));
|
|
if (search == NULL)
|
|
fatal("Memory allocation failure in %s:%d",
|
|
__FILE__, __LINE__);
|
|
strncpy(search->origin, fixeddomain, MXNAME - 1);
|
|
ISC_LIST_PREPEND(search_list, search, link);
|
|
}
|
|
|
|
debug ("setup_system()");
|
|
|
|
free_now = ISC_FALSE;
|
|
get_servers = ISC_TF(server_list.head == NULL);
|
|
fp = fopen (RESOLVCONF, "r");
|
|
if (fp != NULL) {
|
|
while (fgets(rcinput, MXNAME, fp) != 0) {
|
|
ptr = strtok (rcinput, " \t\r\n");
|
|
if (ptr != NULL) {
|
|
if (get_servers &&
|
|
strcasecmp(ptr, "nameserver") == 0) {
|
|
debug ("Got a nameserver line");
|
|
ptr = strtok (NULL, " \t\r\n");
|
|
if (ptr != NULL) {
|
|
srv = isc_mem_allocate(mctx,
|
|
sizeof(struct dig_server));
|
|
if (srv == NULL)
|
|
fatal("Memory "
|
|
"allocation "
|
|
"failure in "
|
|
"%s:%d",
|
|
__FILE__,
|
|
__LINE__);
|
|
strncpy((char *)srv->
|
|
servername,
|
|
ptr,
|
|
MXNAME - 1);
|
|
ISC_LIST_APPEND
|
|
(server_list,
|
|
srv, link);
|
|
}
|
|
} else if (strcasecmp(ptr, "options") == 0) {
|
|
ptr = strtok(NULL, " \t\r\n");
|
|
if (ptr != NULL) {
|
|
if ((strncasecmp(ptr, "ndots:",
|
|
6) == 0) &&
|
|
(ndots == -1)) {
|
|
ndots = atoi(
|
|
&ptr[6]);
|
|
debug ("ndots is "
|
|
"%d.",
|
|
ndots);
|
|
}
|
|
}
|
|
} else if ((strcasecmp(ptr, "search") == 0)
|
|
&& usesearch){
|
|
while ((ptr = strtok(NULL, " \t\r\n"))
|
|
!= NULL) {
|
|
search = isc_mem_allocate(
|
|
mctx, sizeof(struct
|
|
dig_server));
|
|
if (search == NULL)
|
|
fatal("Memory "
|
|
"allocation "
|
|
"failure in %s:"
|
|
"%d", __FILE__,
|
|
__LINE__);
|
|
strncpy(search->
|
|
origin,
|
|
ptr,
|
|
MXNAME - 1);
|
|
ISC_LIST_APPEND
|
|
(search_list,
|
|
search,
|
|
link);
|
|
}
|
|
} else if ((strcasecmp(ptr, "domain") == 0) &&
|
|
(fixeddomain[0] == 0 )){
|
|
while ((ptr = strtok(NULL, " \t\r\n"))
|
|
!= NULL) {
|
|
search = isc_mem_allocate(
|
|
mctx, sizeof(struct
|
|
dig_server));
|
|
if (search == NULL)
|
|
fatal("Memory "
|
|
"allocation "
|
|
"failure in %s:"
|
|
"%d", __FILE__,
|
|
__LINE__);
|
|
strncpy(search->
|
|
origin,
|
|
ptr,
|
|
MXNAME - 1);
|
|
ISC_LIST_PREPEND
|
|
(search_list,
|
|
search,
|
|
link);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
fclose (fp);
|
|
}
|
|
|
|
if (ndots == -1)
|
|
ndots = 1;
|
|
|
|
if (server_list.head == NULL) {
|
|
srv = isc_mem_allocate(mctx, sizeof(dig_server_t));
|
|
if (srv == NULL)
|
|
fatal("Memory allocation failure");
|
|
strcpy(srv->servername, "127.0.0.1");
|
|
ISC_LIST_APPEND(server_list, srv, link);
|
|
}
|
|
|
|
for (l = ISC_LIST_HEAD(lookup_list) ;
|
|
l != NULL;
|
|
l = ISC_LIST_NEXT(l, link) ) {
|
|
l -> origin = ISC_LIST_HEAD(search_list);
|
|
}
|
|
}
|
|
|
|
void
|
|
setup_libs(void) {
|
|
isc_result_t result;
|
|
isc_buffer_t b;
|
|
|
|
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.
|
|
*/
|
|
srandom (getpid() + (int)&setup_libs);
|
|
|
|
result = isc_app_start();
|
|
check_result(result, "isc_app_start");
|
|
|
|
result = isc_net_probeipv4();
|
|
check_result(result, "isc_net_probeipv4");
|
|
|
|
result = isc_net_probeipv6();
|
|
if (result == ISC_R_SUCCESS)
|
|
have_ipv6=ISC_TRUE;
|
|
|
|
result = isc_mem_create(0, 0, &mctx);
|
|
check_result(result, "isc_mem_create");
|
|
|
|
result = isc_taskmgr_create (mctx, 1, 0, &taskmgr);
|
|
check_result(result, "isc_taskmgr_create");
|
|
|
|
result = isc_task_create (taskmgr, 0, &global_task);
|
|
check_result(result, "isc_task_create");
|
|
|
|
result = isc_timermgr_create (mctx, &timermgr);
|
|
check_result(result, "isc_timermgr_create");
|
|
|
|
result = isc_socketmgr_create (mctx, &socketmgr);
|
|
check_result(result, "isc_socketmgr_create");
|
|
isc_buffer_init(&b, ".", 1);
|
|
isc_buffer_add(&b, 1);
|
|
}
|
|
|
|
static void
|
|
add_opt (dns_message_t *msg, isc_uint16_t udpsize) {
|
|
dns_rdataset_t *rdataset = NULL;
|
|
dns_rdatalist_t *rdatalist = NULL;
|
|
dns_rdata_t *rdata = NULL;
|
|
isc_result_t result;
|
|
|
|
debug ("add_opt()");
|
|
result = dns_message_gettemprdataset(msg, &rdataset);
|
|
check_result (result, "dns_message_gettemprdataset");
|
|
dns_rdataset_init (rdataset);
|
|
result = dns_message_gettemprdatalist(msg, &rdatalist);
|
|
check_result (result, "dns_message_gettemprdatalist");
|
|
result = dns_message_gettemprdata(msg, &rdata);
|
|
check_result (result, "dns_message_gettemprdata");
|
|
|
|
debug ("Setting udp size of %d", udpsize);
|
|
rdatalist->type = dns_rdatatype_opt;
|
|
rdatalist->covers = 0;
|
|
rdatalist->rdclass = udpsize;
|
|
rdatalist->ttl = 0;
|
|
rdata->data = NULL;
|
|
rdata->length = 0;
|
|
ISC_LIST_INIT(rdatalist->rdata);
|
|
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
|
|
dns_rdatalist_tordataset(rdatalist, rdataset);
|
|
result = dns_message_setopt(msg, rdataset);
|
|
check_result (result, "dns_message_setopt");
|
|
}
|
|
|
|
static void
|
|
add_type(dns_message_t *message, dns_name_t *name, dns_rdataclass_t rdclass,
|
|
dns_rdatatype_t rdtype)
|
|
{
|
|
dns_rdataset_t *rdataset;
|
|
isc_result_t result;
|
|
|
|
debug ("add_type()");
|
|
rdataset = NULL;
|
|
result = dns_message_gettemprdataset(message, &rdataset);
|
|
check_result(result, "dns_message_gettemprdataset()");
|
|
dns_rdataset_init(rdataset);
|
|
dns_rdataset_makequestion(rdataset, rdclass, rdtype);
|
|
ISC_LIST_APPEND(name->list, rdataset, link);
|
|
}
|
|
|
|
static void
|
|
check_next_lookup(dig_lookup_t *lookup) {
|
|
dig_lookup_t *next;
|
|
dig_query_t *query;
|
|
isc_boolean_t still_working=ISC_FALSE;
|
|
|
|
if (free_now)
|
|
return;
|
|
|
|
debug("check_next_lookup(%lx)", (long int)lookup);
|
|
for (query = ISC_LIST_HEAD(lookup->q);
|
|
query != NULL;
|
|
query = ISC_LIST_NEXT(query, link)) {
|
|
if (query->working) {
|
|
debug("Still have a worker.", stderr);
|
|
still_working=ISC_TRUE;
|
|
}
|
|
}
|
|
if (still_working)
|
|
return;
|
|
|
|
debug ("Have %d retries left for %s",
|
|
lookup->retries-1, lookup->textname);
|
|
debug ("Lookup %s pending", lookup->pending?"is":"is not");
|
|
|
|
next = ISC_LIST_NEXT(lookup, link);
|
|
|
|
if (lookup->tcp_mode) {
|
|
if (next == NULL) {
|
|
debug("Shutting Down.", stderr);
|
|
dighost_shutdown();
|
|
return;
|
|
}
|
|
if (next->sendmsg == NULL) {
|
|
debug ("Setting up for TCP");
|
|
setup_lookup(next);
|
|
do_lookup(next);
|
|
}
|
|
} else {
|
|
if (!lookup->pending) {
|
|
if (next == NULL) {
|
|
debug("Shutting Down.", stderr);
|
|
dighost_shutdown();
|
|
return;
|
|
}
|
|
if (next->sendmsg == NULL) {
|
|
debug ("Setting up for UDP");
|
|
setup_lookup(next);
|
|
do_lookup(next);
|
|
}
|
|
} else {
|
|
if (lookup->retries > 1) {
|
|
debug ("Retrying");
|
|
lookup->retries --;
|
|
if (lookup->timer != NULL)
|
|
isc_timer_detach(&lookup->timer);
|
|
send_udp(lookup);
|
|
} else {
|
|
debug ("Cancelling");
|
|
cancel_lookup(lookup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
followup_lookup(dns_message_t *msg, dig_query_t *query,
|
|
dns_section_t section) {
|
|
dig_lookup_t *lookup = NULL;
|
|
dig_server_t *srv = NULL;
|
|
dns_rdataset_t *rdataset = NULL;
|
|
dns_rdata_t rdata;
|
|
dns_name_t *name = NULL;
|
|
isc_result_t result, loopresult;
|
|
isc_buffer_t *b = NULL;
|
|
isc_region_t r;
|
|
isc_boolean_t success = ISC_FALSE;
|
|
int len;
|
|
|
|
debug ("followup_lookup()");
|
|
if (free_now)
|
|
return;
|
|
result = dns_message_firstname (msg,section);
|
|
if (result != ISC_R_SUCCESS) {
|
|
debug ("Firstname returned %s",
|
|
isc_result_totext(result));
|
|
if ((section == DNS_SECTION_ANSWER) &&
|
|
query->lookup->trace)
|
|
followup_lookup (msg, query, DNS_SECTION_AUTHORITY);
|
|
return;
|
|
}
|
|
|
|
debug ("Following up %s", query->lookup->textname);
|
|
|
|
for (;;) {
|
|
name = NULL;
|
|
dns_message_currentname(msg, section, &name);
|
|
for (rdataset = ISC_LIST_HEAD(name->list);
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
loopresult = dns_rdataset_first(rdataset);
|
|
while (loopresult == ISC_R_SUCCESS) {
|
|
dns_rdataset_current(rdataset, &rdata);
|
|
debug ("Got rdata with type %d",
|
|
rdata.type);
|
|
if ((rdata.type == dns_rdatatype_ns) &&
|
|
(!query->lookup->trace_root ||
|
|
(query->lookup->nsfound < ROOTNS)))
|
|
{
|
|
query->lookup->nsfound++;
|
|
result = isc_buffer_allocate(mctx, &b,
|
|
BUFSIZE);
|
|
check_result (result,
|
|
"isc_buffer_allocate");
|
|
result = dns_rdata_totext (&rdata,
|
|
NULL,
|
|
b);
|
|
check_result (result,
|
|
"dns_rdata_totext");
|
|
isc_buffer_usedregion(b, &r);
|
|
len = r.length-1;
|
|
if (len >= MXNAME)
|
|
len = MXNAME-1;
|
|
/* Initialize lookup if we've not yet */
|
|
debug ("Found NS %d %.*s",
|
|
(int)r.length, (int)r.length,
|
|
(char *)r.base);
|
|
if (!success) {
|
|
success = ISC_TRUE;
|
|
lookup_counter++;
|
|
lookup = requeue_lookup
|
|
(query->lookup,
|
|
ISC_FALSE);
|
|
lookup->doing_xfr = ISC_FALSE;
|
|
lookup->defname = ISC_FALSE;
|
|
lookup->use_my_server_list =
|
|
ISC_TRUE;
|
|
if (section ==
|
|
DNS_SECTION_ANSWER)
|
|
lookup->trace =
|
|
ISC_FALSE;
|
|
else
|
|
lookup->trace =
|
|
query->
|
|
lookup->trace;
|
|
lookup->trace_root = ISC_FALSE;
|
|
ISC_LIST_INIT(lookup->
|
|
my_server_list);
|
|
}
|
|
srv = isc_mem_allocate (mctx,
|
|
sizeof(
|
|
struct
|
|
dig_server));
|
|
if (srv == NULL)
|
|
fatal("Memory allocation "
|
|
"failure in %s:%d",
|
|
__FILE__, __LINE__);
|
|
strncpy(srv->servername,
|
|
(char *)r.base, len);
|
|
srv->servername[len]=0;
|
|
debug ("Adding server %s",
|
|
srv->servername);
|
|
ISC_LIST_APPEND
|
|
(lookup->my_server_list,
|
|
srv, link);
|
|
isc_buffer_free (&b);
|
|
}
|
|
loopresult = dns_rdataset_next(rdataset);
|
|
}
|
|
}
|
|
result = dns_message_nextname (msg, section);
|
|
if (result != ISC_R_SUCCESS)
|
|
break;
|
|
}
|
|
if ((lookup == NULL) && (section == DNS_SECTION_ANSWER) &&
|
|
query->lookup->trace)
|
|
followup_lookup(msg, query, DNS_SECTION_AUTHORITY);
|
|
}
|
|
|
|
static void
|
|
next_origin(dns_message_t *msg, dig_query_t *query) {
|
|
dig_lookup_t *lookup;
|
|
|
|
UNUSED (msg);
|
|
|
|
debug ("next_origin()");
|
|
if (free_now)
|
|
return;
|
|
debug ("Following up %s", query->lookup->textname);
|
|
|
|
if (query->lookup->origin == NULL) { /*Then we just did rootorg;
|
|
there's nothing left. */
|
|
debug ("Made it to the root whith nowhere to go.");
|
|
return;
|
|
}
|
|
lookup = requeue_lookup(query->lookup, ISC_TRUE);
|
|
lookup->defname = ISC_FALSE;
|
|
lookup->origin = ISC_LIST_NEXT(query->lookup->origin, link);
|
|
}
|
|
|
|
|
|
void
|
|
setup_lookup(dig_lookup_t *lookup) {
|
|
isc_result_t result, res2;
|
|
int len;
|
|
dns_rdatatype_t rdtype;
|
|
dns_rdataclass_t rdclass;
|
|
dig_server_t *serv;
|
|
dig_query_t *query;
|
|
isc_region_t r;
|
|
isc_constregion_t tr;
|
|
isc_buffer_t b;
|
|
char store[MXNAME];
|
|
|
|
REQUIRE (lookup != NULL);
|
|
|
|
debug("setup_lookup(%lx)",(long int)lookup);
|
|
|
|
if (free_now)
|
|
return;
|
|
|
|
debug("Setting up for looking up %s @%lx->%lx",
|
|
lookup->textname, (long int)lookup,
|
|
(long int)lookup->link.next);
|
|
|
|
result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
|
|
&lookup->sendmsg);
|
|
check_result(result, "dns_message_create");
|
|
|
|
if (lookup->new_search) {
|
|
debug ("Resetting lookup counter.");
|
|
lookup_counter = 0;
|
|
}
|
|
|
|
result = dns_message_gettempname(lookup->sendmsg, &lookup->name);
|
|
check_result(result, "dns_message_gettempname");
|
|
dns_name_init(lookup->name, NULL);
|
|
|
|
isc_buffer_init(&lookup->namebuf, lookup->namespace, BUFSIZE);
|
|
isc_buffer_init(&lookup->onamebuf, lookup->onamespace, BUFSIZE);
|
|
|
|
if ((count_dots(lookup->textname) >= ndots) || lookup->defname)
|
|
lookup->origin = NULL; /* Force root lookup */
|
|
debug ("lookup->origin = %lx", (long int)lookup->origin);
|
|
if (lookup->origin != NULL) {
|
|
debug ("Trying origin %s", lookup->origin->origin);
|
|
result = dns_message_gettempname(lookup->sendmsg,
|
|
&lookup->oname);
|
|
check_result(result, "dns_message_gettempname");
|
|
dns_name_init(lookup->oname, NULL);
|
|
len=strlen(lookup->origin->origin);
|
|
isc_buffer_init(&b, lookup->origin->origin, len);
|
|
isc_buffer_add(&b, len);
|
|
result = dns_name_fromtext(lookup->oname, &b, dns_rootname,
|
|
ISC_FALSE, &lookup->onamebuf);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_message_puttempname(lookup->sendmsg,
|
|
&lookup->name);
|
|
dns_message_puttempname(lookup->sendmsg,
|
|
&lookup->oname);
|
|
fatal("Aborting: %s is not a legal name syntax. (%s)",
|
|
lookup->origin->origin,
|
|
dns_result_totext(result));
|
|
}
|
|
if (!lookup->trace_root) {
|
|
len=strlen(lookup->textname);
|
|
isc_buffer_init(&b, lookup->textname, len);
|
|
isc_buffer_add(&b, len);
|
|
result = dns_name_fromtext(lookup->name, &b,
|
|
lookup->oname, ISC_FALSE,
|
|
&lookup->namebuf);
|
|
} else {
|
|
isc_buffer_init(&b, ". ", 1);
|
|
isc_buffer_add(&b, 1);
|
|
result = dns_name_fromtext(lookup->name, &b,
|
|
lookup->oname, ISC_FALSE,
|
|
&lookup->namebuf);
|
|
}
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_message_puttempname(lookup->sendmsg,
|
|
&lookup->name);
|
|
dns_message_puttempname(lookup->sendmsg,
|
|
&lookup->oname);
|
|
fatal("Aborting: %s is not a legal name syntax. (%s)",
|
|
lookup->textname, dns_result_totext(result));
|
|
}
|
|
dns_message_puttempname(lookup->sendmsg, &lookup->oname);
|
|
} else {
|
|
debug ("Using root origin.");
|
|
if (!lookup->trace_root) {
|
|
len = strlen (lookup->textname);
|
|
isc_buffer_init(&b, lookup->textname, len);
|
|
isc_buffer_add(&b, len);
|
|
result = dns_name_fromtext(lookup->name, &b,
|
|
dns_rootname,
|
|
ISC_FALSE,
|
|
&lookup->namebuf);
|
|
} else {
|
|
isc_buffer_init(&b, ". ", 1);
|
|
isc_buffer_add(&b, 1);
|
|
result = dns_name_fromtext(lookup->name, &b,
|
|
dns_rootname,
|
|
ISC_FALSE,
|
|
&lookup->namebuf);
|
|
}
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_message_puttempname(lookup->sendmsg,
|
|
&lookup->name);
|
|
isc_buffer_init(&b, store, MXNAME);
|
|
res2 = dns_name_totext(dns_rootname, ISC_FALSE, &b);
|
|
check_result (res2, "dns_name_totext");
|
|
isc_buffer_usedregion (&b, &r);
|
|
fatal("Aborting: %s/%.*s is not a legal name syntax. "
|
|
"(%s)", lookup->textname, (int)r.length,
|
|
(char *)r.base, dns_result_totext(result));
|
|
}
|
|
}
|
|
isc_buffer_init (&b, store, MXNAME);
|
|
dns_name_totext(lookup->name, ISC_FALSE, &b);
|
|
isc_buffer_usedregion (&b, &r);
|
|
trying((int)r.length, (char *)r.base, lookup);
|
|
#ifdef DEBUG
|
|
if (dns_name_isabsolute(lookup->name))
|
|
debug ("This is an absolute name.");
|
|
else
|
|
debug ("This is a relative name (which is wrong).");
|
|
#endif
|
|
|
|
if (lookup->rctext[0] == 0)
|
|
strcpy(lookup->rctext, "IN");
|
|
if (lookup->rttext[0] == 0)
|
|
strcpy(lookup->rttext, "A");
|
|
|
|
lookup->sendmsg->id = (unsigned short)(random() & 0xFFFF);
|
|
lookup->sendmsg->opcode = dns_opcode_query;
|
|
/*
|
|
* If this is a trace request, completely disallow recursion, since
|
|
* it's meaningless for traces.
|
|
*/
|
|
if (lookup->recurse && !lookup->trace) {
|
|
debug ("Recursive query");
|
|
lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RD;
|
|
}
|
|
|
|
if (lookup->aaonly) {
|
|
debug ("AA query");
|
|
lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AA;
|
|
}
|
|
|
|
dns_message_addname(lookup->sendmsg, lookup->name,
|
|
DNS_SECTION_QUESTION);
|
|
|
|
|
|
if (lookup->trace_root) {
|
|
tr.base="SOA";
|
|
tr.length=3;
|
|
} else {
|
|
tr.base=lookup->rttext;
|
|
tr.length=strlen(lookup->rttext);
|
|
}
|
|
result = dns_rdatatype_fromtext(&rdtype, (isc_textregion_t *)&tr);
|
|
check_result(result, "dns_rdatatype_fromtext");
|
|
if (rdtype == dns_rdatatype_axfr) {
|
|
lookup->doing_xfr = ISC_TRUE;
|
|
/*
|
|
* Force TCP mode if we're doing an xfr.
|
|
*/
|
|
lookup->tcp_mode = ISC_TRUE;
|
|
}
|
|
if (lookup->trace_root) {
|
|
tr.base="IN";
|
|
tr.length=2;
|
|
} else {
|
|
tr.base=lookup->rctext;
|
|
tr.length=strlen(lookup->rctext);
|
|
}
|
|
result = dns_rdataclass_fromtext(&rdclass, (isc_textregion_t *)&tr);
|
|
check_result(result, "dns_rdataclass_fromtext");
|
|
add_type(lookup->sendmsg, lookup->name, rdclass, rdtype);
|
|
|
|
isc_buffer_init(&lookup->sendbuf, lookup->sendspace, COMMSIZE);
|
|
debug ("Starting to render the message");
|
|
result = dns_message_renderbegin(lookup->sendmsg, &lookup->sendbuf);
|
|
check_result(result, "dns_message_renderbegin");
|
|
if (lookup->udpsize > 0) {
|
|
add_opt(lookup->sendmsg, lookup->udpsize);
|
|
}
|
|
result = dns_message_rendersection(lookup->sendmsg,
|
|
DNS_SECTION_QUESTION, 0);
|
|
check_result(result, "dns_message_rendersection");
|
|
result = dns_message_renderend(lookup->sendmsg);
|
|
check_result(result, "dns_message_renderend");
|
|
debug ("Done rendering.");
|
|
|
|
lookup->pending = ISC_FALSE;
|
|
|
|
if (lookup->use_my_server_list)
|
|
serv = ISC_LIST_HEAD(lookup->my_server_list);
|
|
else
|
|
serv = ISC_LIST_HEAD(server_list);
|
|
for (; serv != NULL;
|
|
serv = ISC_LIST_NEXT(serv, link)) {
|
|
query = isc_mem_allocate(mctx, sizeof(dig_query_t));
|
|
if (query == NULL)
|
|
fatal("Memory allocation failure in %s:%d",
|
|
__FILE__, __LINE__);
|
|
debug ("Create query %lx linked to lookup %lx",
|
|
(long int)query, (long int)lookup);
|
|
query->lookup = lookup;
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
query->first_pass = ISC_TRUE;
|
|
query->first_soa_rcvd = ISC_FALSE;
|
|
query->servname = serv->servername;
|
|
ISC_LIST_INIT(query->sendlist);
|
|
ISC_LIST_INIT(query->recvlist);
|
|
ISC_LIST_INIT(query->lengthlist);
|
|
query->sock = NULL;
|
|
|
|
isc_buffer_init(&query->recvbuf, query->recvspace, COMMSIZE);
|
|
isc_buffer_init(&query->lengthbuf, query->lengthspace, 2);
|
|
isc_buffer_init(&query->slbuf, query->slspace, 2);
|
|
|
|
ISC_LIST_ENQUEUE(lookup->q, query, link);
|
|
}
|
|
if (!ISC_LIST_EMPTY(lookup->q) && qr) {
|
|
printmessage (ISC_LIST_HEAD(lookup->q), lookup->sendmsg,
|
|
ISC_TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
send_done(isc_task_t *task, isc_event_t *event) {
|
|
UNUSED(task);
|
|
|
|
isc_event_free(&event);
|
|
|
|
debug("send_done()");
|
|
}
|
|
|
|
static void
|
|
cancel_lookup(dig_lookup_t *lookup) {
|
|
dig_query_t *query=NULL;
|
|
|
|
debug("cancel_lookup()");
|
|
for (query = ISC_LIST_HEAD(lookup->q);
|
|
query != NULL;
|
|
query = ISC_LIST_NEXT(query, link)) {
|
|
if (query->working) {
|
|
debug ("Cancelling a worker.");
|
|
}
|
|
if (query->sock != NULL) {
|
|
isc_socket_cancel(query->sock, global_task,
|
|
ISC_SOCKCANCEL_ALL);
|
|
isc_socket_detach(&query->sock);
|
|
sockcount--;
|
|
debug ("Socket = %d",sockcount);
|
|
}
|
|
}
|
|
lookup->pending = ISC_FALSE;
|
|
lookup->retries = 0;
|
|
check_next_lookup(lookup);
|
|
}
|
|
|
|
static void
|
|
recv_done(isc_task_t *task, isc_event_t *event);
|
|
|
|
static void
|
|
connect_timeout(isc_task_t *task, isc_event_t *event);
|
|
|
|
void
|
|
send_udp(dig_lookup_t *lookup) {
|
|
dig_query_t *query;
|
|
isc_result_t result;
|
|
|
|
debug ("send_udp()");
|
|
|
|
isc_interval_set(&lookup->interval, timeout, 0);
|
|
result = isc_timer_create(timermgr, isc_timertype_once, NULL,
|
|
&lookup->interval, global_task,
|
|
connect_timeout, lookup, &lookup->timer);
|
|
check_result(result, "isc_timer_create");
|
|
for (query = ISC_LIST_HEAD(lookup->q);
|
|
query != NULL;
|
|
query = ISC_LIST_NEXT(query, link)) {
|
|
debug ("Working on lookup %lx, query %lx",
|
|
(long int)query->lookup, (long int)query);
|
|
ISC_LIST_ENQUEUE(query->recvlist, &query->recvbuf, link);
|
|
query->working = ISC_TRUE;
|
|
debug ("recving with lookup=%lx, query=%lx, sock=%lx",
|
|
(long int)query->lookup, (long int)query,
|
|
(long int)query->sock);
|
|
result = isc_socket_recvv(query->sock, &query->recvlist, 1,
|
|
global_task, recv_done, query);
|
|
check_result(result, "isc_socket_recvv");
|
|
sendcount++;
|
|
debug("Sent count number %d", sendcount);
|
|
#ifdef TWIDDLE
|
|
if (twiddle) {
|
|
twiddlebuf(lookup->sendbuf);
|
|
}
|
|
#endif
|
|
ISC_LIST_ENQUEUE(query->sendlist, &lookup->sendbuf, link);
|
|
debug("Sending a request.");
|
|
result = isc_time_now(&query->time_sent);
|
|
check_result(result, "isc_time_now");
|
|
ENSURE (query->sock != NULL);
|
|
result = isc_socket_sendtov(query->sock, &query->sendlist,
|
|
global_task, send_done, query,
|
|
&query->sockaddr, NULL);
|
|
check_result(result, "isc_socket_sendtov");
|
|
}
|
|
}
|
|
|
|
/* connect_timeout is used for both UDP recieves and TCP connects. */
|
|
static void
|
|
connect_timeout(isc_task_t *task, isc_event_t *event) {
|
|
dig_lookup_t *lookup=NULL, *next=NULL;
|
|
dig_query_t *q=NULL;
|
|
isc_result_t result;
|
|
isc_buffer_t *b=NULL;
|
|
isc_region_t r;
|
|
|
|
REQUIRE(event->ev_type == ISC_TIMEREVENT_IDLE);
|
|
|
|
debug("connect_timeout()");
|
|
lookup = event->ev_arg;
|
|
|
|
isc_event_free(&event);
|
|
|
|
debug ("Buffer Allocate connect_timeout");
|
|
result = isc_buffer_allocate(mctx, &b, 256);
|
|
check_result(result, "isc_buffer_allocate");
|
|
for (q = ISC_LIST_HEAD(lookup->q);
|
|
q != NULL;
|
|
q = ISC_LIST_NEXT(q, link)) {
|
|
if (q->working) {
|
|
if (!free_now) {
|
|
isc_buffer_clear(b);
|
|
result = isc_sockaddr_totext(&q->sockaddr, b);
|
|
check_result(result, "isc_sockaddr_totext");
|
|
isc_buffer_usedregion(b, &r);
|
|
if (q->lookup->retries > 1)
|
|
printf(";; Connection to server %.*s "
|
|
"for %s timed out. "
|
|
"Retrying %d.\n",
|
|
(int)r.length, r.base,
|
|
q->lookup->textname,
|
|
q->lookup->retries-1);
|
|
else {
|
|
if (lookup->tcp_mode) {
|
|
printf(";; Connection to "
|
|
"server %.*s "
|
|
"for %s timed out. "
|
|
"Giving up.\n",
|
|
(int)r.length, r.base,
|
|
q->lookup->textname);
|
|
} else {
|
|
printf(";; Connection to "
|
|
"server %.*s "
|
|
"for %s timed out. "
|
|
"Trying TCP.\n",
|
|
(int)r.length, r.base,
|
|
q->lookup->textname);
|
|
next = requeue_lookup
|
|
(lookup,ISC_TRUE);
|
|
next->tcp_mode = ISC_TRUE;
|
|
}
|
|
}
|
|
}
|
|
isc_socket_cancel(q->sock, task,
|
|
ISC_SOCKCANCEL_ALL);
|
|
}
|
|
}
|
|
ENSURE(lookup->timer != NULL);
|
|
isc_timer_detach(&lookup->timer);
|
|
isc_buffer_free(&b);
|
|
debug ("Done with connect_timeout()");
|
|
}
|
|
|
|
static void
|
|
tcp_length_done(isc_task_t *task, isc_event_t *event) {
|
|
isc_socketevent_t *sevent;
|
|
isc_buffer_t *b=NULL;
|
|
isc_region_t r;
|
|
isc_result_t result;
|
|
dig_query_t *query=NULL;
|
|
isc_uint16_t length;
|
|
|
|
REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
|
|
|
|
UNUSED(task);
|
|
|
|
debug("tcp_length_done()");
|
|
|
|
if (free_now) {
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
sevent = (isc_socketevent_t *)event;
|
|
|
|
query = event->ev_arg;
|
|
|
|
if (sevent->result == ISC_R_CANCELED) {
|
|
query->working = ISC_FALSE;
|
|
check_next_lookup(query->lookup);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
if (sevent->result != ISC_R_SUCCESS) {
|
|
debug ("Buffer Allocate connect_timeout");
|
|
result = isc_buffer_allocate(mctx, &b, 256);
|
|
check_result(result, "isc_buffer_allocate");
|
|
result = isc_sockaddr_totext(&query->sockaddr, b);
|
|
check_result(result, "isc_sockaddr_totext");
|
|
isc_buffer_usedregion(b, &r);
|
|
printf("%.*s: %s\n", (int)r.length, r.base,
|
|
isc_result_totext(sevent->result));
|
|
isc_buffer_free(&b);
|
|
query->working = ISC_FALSE;
|
|
sockcount--;
|
|
debug ("Socket = %d",sockcount);
|
|
isc_socket_detach(&query->sock);
|
|
check_next_lookup(query->lookup);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
b = ISC_LIST_HEAD(sevent->bufferlist);
|
|
ISC_LIST_DEQUEUE(sevent->bufferlist, &query->lengthbuf, link);
|
|
length = isc_buffer_getuint16(b);
|
|
if (length > COMMSIZE) {
|
|
isc_event_free (&event);
|
|
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.
|
|
*/
|
|
isc_buffer_invalidate(&query->recvbuf);
|
|
isc_buffer_init(&query->recvbuf, query->recvspace, length);
|
|
ENSURE(ISC_LIST_EMPTY(query->recvlist));
|
|
ISC_LIST_ENQUEUE(query->recvlist, &query->recvbuf, link);
|
|
debug ("recving with lookup=%lx, query=%lx",
|
|
(long int)query->lookup, (long int)query);
|
|
result = isc_socket_recvv(query->sock, &query->recvlist, length, task,
|
|
recv_done, query);
|
|
check_result(result, "isc_socket_recvv");
|
|
debug("Resubmitted recv request with length %d", length);
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
launch_next_query(dig_query_t *query, isc_boolean_t include_question) {
|
|
isc_result_t result;
|
|
|
|
debug("launch_next_query()");
|
|
|
|
if (free_now)
|
|
return;
|
|
|
|
if (!query->lookup->pending) {
|
|
debug("Ignoring launch_next_query because !pending.");
|
|
sockcount--;
|
|
debug ("Socket = %d",sockcount);
|
|
isc_socket_detach(&query->sock);
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
check_next_lookup(query->lookup);
|
|
return;
|
|
}
|
|
|
|
isc_buffer_clear(&query->slbuf);
|
|
isc_buffer_clear(&query->lengthbuf);
|
|
isc_buffer_putuint16(&query->slbuf, query->lookup->sendbuf.used);
|
|
ISC_LIST_ENQUEUE(query->sendlist, &query->slbuf, link);
|
|
if (include_question) {
|
|
#ifdef TWIDDLE
|
|
if (twiddle) {
|
|
twiddlebuf(query->lookup->sendbuf);
|
|
}
|
|
#endif
|
|
ISC_LIST_ENQUEUE(query->sendlist, &query->lookup->sendbuf,
|
|
link);
|
|
}
|
|
ISC_LIST_ENQUEUE(query->lengthlist, &query->lengthbuf, link);
|
|
|
|
result = isc_socket_recvv(query->sock, &query->lengthlist, 0,
|
|
global_task, tcp_length_done, query);
|
|
check_result(result, "isc_socket_recvv");
|
|
sendcount++;
|
|
if (!query->first_soa_rcvd) {
|
|
debug("Sending a request.");
|
|
result = isc_time_now(&query->time_sent);
|
|
check_result(result, "isc_time_now");
|
|
result = isc_socket_sendv(query->sock, &query->sendlist,
|
|
global_task, send_done, query);
|
|
check_result(result, "isc_socket_recvv");
|
|
}
|
|
query->waiting_connect = ISC_FALSE;
|
|
check_next_lookup(query->lookup);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
connect_done(isc_task_t *task, isc_event_t *event) {
|
|
isc_result_t result;
|
|
isc_socketevent_t *sevent=NULL;
|
|
dig_query_t *query=NULL;
|
|
isc_buffer_t *b=NULL;
|
|
isc_region_t r;
|
|
|
|
UNUSED(task);
|
|
|
|
REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
|
|
|
|
debug ("connect_done()");
|
|
|
|
if (free_now) {
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
sevent = (isc_socketevent_t *)event;
|
|
query = sevent->ev_arg;
|
|
|
|
REQUIRE(query->waiting_connect);
|
|
|
|
query->waiting_connect = ISC_FALSE;
|
|
|
|
if (sevent->result != ISC_R_SUCCESS) {
|
|
debug ("Buffer Allocate connect_timeout");
|
|
result = isc_buffer_allocate(mctx, &b, 256);
|
|
check_result(result, "isc_buffer_allocate");
|
|
result = isc_sockaddr_totext(&query->sockaddr, b);
|
|
check_result(result, "isc_sockaddr_totext");
|
|
isc_buffer_usedregion(b, &r);
|
|
printf(";; Connection to server %.*s for %s failed: %s.\n",
|
|
(int)r.length, r.base, query->lookup->textname,
|
|
isc_result_totext(sevent->result));
|
|
if (exitcode < 9)
|
|
exitcode = 9;
|
|
isc_buffer_free(&b);
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
check_next_lookup(query->lookup);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
launch_next_query(query, ISC_TRUE);
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static isc_boolean_t
|
|
msg_contains_soa(dns_message_t *msg, dig_query_t *query) {
|
|
isc_result_t result;
|
|
dns_name_t *name=NULL;
|
|
|
|
debug("msg_contains_soa()");
|
|
|
|
result = dns_message_findname(msg, DNS_SECTION_ANSWER,
|
|
query->lookup->name, dns_rdatatype_soa,
|
|
0, &name, NULL);
|
|
if (result == ISC_R_SUCCESS) {
|
|
debug("Found SOA", stderr);
|
|
return (ISC_TRUE);
|
|
} else {
|
|
debug("Didn't find SOA, result=%d:%s",
|
|
result, dns_result_totext(result));
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
recv_done(isc_task_t *task, isc_event_t *event) {
|
|
isc_socketevent_t *sevent = NULL;
|
|
dig_query_t *query = NULL;
|
|
isc_buffer_t *b = NULL;
|
|
dns_message_t *msg = NULL;
|
|
isc_result_t result;
|
|
isc_buffer_t ab;
|
|
char abspace[MXNAME];
|
|
isc_region_t r;
|
|
dig_lookup_t *n;
|
|
|
|
UNUSED (task);
|
|
|
|
debug ("recv_done()");
|
|
|
|
if (free_now) {
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
query = event->ev_arg;
|
|
debug("(lookup=%lx, query=%lx)",
|
|
(long int)query->lookup, (long int)query);
|
|
|
|
if (free_now) {
|
|
debug("Bailing out, since freeing now.");
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
sendcount--;
|
|
debug("In recv_done, counter down to %d", sendcount);
|
|
REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
|
|
sevent = (isc_socketevent_t *)event;
|
|
|
|
if (!query->lookup->pending && !query->lookup->ns_search_only) {
|
|
|
|
debug("No longer pending. Got %s",
|
|
isc_result_totext(sevent->result));
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
|
|
cancel_lookup(query->lookup);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
if (sevent->result == ISC_R_SUCCESS) {
|
|
b = ISC_LIST_HEAD(sevent->bufferlist);
|
|
ISC_LIST_DEQUEUE(sevent->bufferlist, &query->recvbuf, link);
|
|
result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
|
|
&msg);
|
|
check_result(result, "dns_message_create");
|
|
debug ("Before parse starts");
|
|
result = dns_message_parse(msg, b, ISC_TRUE);
|
|
if (result != ISC_R_SUCCESS) {
|
|
printf (";; Got bad UDP packet:\n");
|
|
hex_dump(b);
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
if (!query->lookup->tcp_mode) {
|
|
printf (";; Retrying in TCP mode.\n");
|
|
n = requeue_lookup(query->lookup, ISC_TRUE);
|
|
n->tcp_mode = ISC_TRUE;
|
|
}
|
|
cancel_lookup(query->lookup);
|
|
dns_message_destroy(&msg);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
debug ("After parse has started");
|
|
if (query->lookup->xfr_q == NULL)
|
|
query->lookup->xfr_q = query;
|
|
if (query->lookup->xfr_q == query) {
|
|
if (query->lookup->trace) {
|
|
if (show_details ||
|
|
((dns_message_firstname(msg,
|
|
DNS_SECTION_ANSWER)
|
|
== ISC_R_SUCCESS) &&
|
|
!query->lookup->trace_root)) {
|
|
printmessage(query, msg, ISC_TRUE);
|
|
}
|
|
if ((msg->rcode != 0) &&
|
|
(query->lookup->origin != NULL)) {
|
|
next_origin(msg, query);
|
|
} else {
|
|
result = dns_message_firstname
|
|
(msg,DNS_SECTION_ANSWER);
|
|
if ((result != ISC_R_SUCCESS) ||
|
|
query->lookup->trace_root)
|
|
followup_lookup(msg, query,
|
|
DNS_SECTION_AUTHORITY);
|
|
}
|
|
} else if ((msg->rcode != 0) &&
|
|
(query->lookup->origin != NULL)) {
|
|
next_origin(msg, query);
|
|
if (show_details) {
|
|
printmessage(query, msg, ISC_TRUE);
|
|
}
|
|
} else {
|
|
if (query->first_soa_rcvd &&
|
|
query->lookup->doing_xfr)
|
|
printmessage(query, msg, ISC_FALSE);
|
|
else
|
|
printmessage(query, msg, ISC_TRUE);
|
|
}
|
|
} else if (( dns_message_firstname(msg, DNS_SECTION_ANSWER)
|
|
== ISC_R_SUCCESS) &&
|
|
query->lookup->ns_search_only &&
|
|
!query->lookup->trace_root ) {
|
|
printmessage (query, msg, ISC_TRUE);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (query->lookup->pending)
|
|
debug("Still pending.");
|
|
#endif
|
|
if (query->lookup->doing_xfr) {
|
|
if (query != query->lookup->xfr_q) {
|
|
dns_message_destroy (&msg);
|
|
isc_event_free (&event);
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
return;
|
|
}
|
|
if (!query->first_soa_rcvd) {
|
|
debug("Not yet got first SOA");
|
|
if (!msg_contains_soa(msg, query)) {
|
|
puts("; Transfer failed. "
|
|
"Didn't start with SOA answer.");
|
|
query->working = ISC_FALSE;
|
|
cancel_lookup(query->lookup);
|
|
dns_message_destroy (&msg);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
else {
|
|
query->first_soa_rcvd = ISC_TRUE;
|
|
launch_next_query(query, ISC_FALSE);
|
|
}
|
|
} else {
|
|
if (msg_contains_soa(msg, query)) {
|
|
isc_buffer_init(&ab, abspace, MXNAME);
|
|
result = isc_sockaddr_totext(&sevent->
|
|
address,
|
|
&ab);
|
|
check_result(result,
|
|
"isc_sockaddr_totext");
|
|
isc_buffer_usedregion(&ab, &r);
|
|
received(b->used, r.length,
|
|
(char *)r.base, query);
|
|
query->working = ISC_FALSE;
|
|
cancel_lookup(query->lookup);
|
|
dns_message_destroy (&msg);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
else {
|
|
launch_next_query(query, ISC_FALSE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ((msg->rcode == 0) ||
|
|
(query->lookup->origin == NULL)) {
|
|
isc_buffer_init(&ab, abspace, MXNAME);
|
|
result = isc_sockaddr_totext(&sevent->address,
|
|
&ab);
|
|
check_result(result, "isc_sockaddr_totext");
|
|
isc_buffer_usedregion(&ab, &r);
|
|
received(b->used, r.length, (char *)r.base,
|
|
query);
|
|
}
|
|
query->working = ISC_FALSE;
|
|
query->lookup->pending = ISC_FALSE;
|
|
if (!query->lookup->ns_search_only ||
|
|
query->lookup->trace_root ) {
|
|
cancel_lookup(query->lookup);
|
|
}
|
|
check_next_lookup(query->lookup);
|
|
}
|
|
dns_message_destroy(&msg);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
/* In truth, we should never get into the CANCELED routine, since
|
|
the cancel_lookup() routine clears the pending flag. */
|
|
if (sevent->result == ISC_R_CANCELED) {
|
|
debug ("In cancel handler");
|
|
query->working = ISC_FALSE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
check_next_lookup(query->lookup);
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
fatal("recv_done got result %s",
|
|
isc_result_totext(sevent->result));
|
|
}
|
|
|
|
void
|
|
get_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
|
|
struct in_addr in4;
|
|
struct in6_addr in6;
|
|
struct hostent *he;
|
|
|
|
debug("get_address()");
|
|
|
|
if (have_ipv6 && inet_pton(AF_INET6, host, &in6) == 1)
|
|
isc_sockaddr_fromin6(sockaddr, &in6, port);
|
|
else if (inet_pton(AF_INET, host, &in4) == 1)
|
|
isc_sockaddr_fromin(sockaddr, &in4, port);
|
|
else {
|
|
he = gethostbyname(host);
|
|
if (he == NULL)
|
|
fatal("Couldn't look up your server host %s. errno=%d",
|
|
host, h_errno);
|
|
INSIST(he->h_addrtype == AF_INET);
|
|
isc_sockaddr_fromin(sockaddr,
|
|
(struct in_addr *)(he->h_addr_list[0]),
|
|
port);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_lookup_tcp(dig_lookup_t *lookup) {
|
|
dig_query_t *query;
|
|
isc_result_t result;
|
|
|
|
debug("do_lookup_tcp()");
|
|
lookup->pending = ISC_TRUE;
|
|
isc_interval_set(&lookup->interval, timeout, 0);
|
|
result = isc_timer_create(timermgr, isc_timertype_once, NULL,
|
|
&lookup->interval, global_task,
|
|
connect_timeout, lookup, &lookup->timer);
|
|
check_result(result, "isc_timer_create");
|
|
|
|
for (query = ISC_LIST_HEAD(lookup->q);
|
|
query != NULL;
|
|
query = ISC_LIST_NEXT(query, link)) {
|
|
query->working = ISC_TRUE;
|
|
query->waiting_connect = ISC_TRUE;
|
|
get_address(query->servname, port, &query->sockaddr);
|
|
|
|
sockcount++;
|
|
debug ("Socket = %d",sockcount);
|
|
ENSURE (query->sock == NULL);
|
|
result = isc_socket_create(socketmgr,
|
|
isc_sockaddr_pf(&query->sockaddr),
|
|
isc_sockettype_tcp, &query->sock) ;
|
|
check_result(result, "isc_socket_create");
|
|
if (specified_source) {
|
|
result = isc_socket_bind(query->sock, &bind_address);
|
|
check_result(result, "isc_socket_bind");
|
|
}
|
|
result = isc_socket_connect(query->sock, &query->sockaddr,
|
|
global_task, connect_done, query);
|
|
check_result (result, "isc_socket_connect");
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_lookup_udp(dig_lookup_t *lookup) {
|
|
dig_query_t *query;
|
|
isc_result_t result;
|
|
|
|
#ifdef DEBUG
|
|
debug("do_lookup_udp()");
|
|
if (lookup->tcp_mode)
|
|
debug("I'm starting UDP with tcp_mode set!!!");
|
|
#endif
|
|
lookup->pending = ISC_TRUE;
|
|
|
|
for (query = ISC_LIST_HEAD(lookup->q);
|
|
query != NULL;
|
|
query = ISC_LIST_NEXT(query, link)) {
|
|
query->working = ISC_TRUE;
|
|
query->waiting_connect = ISC_FALSE;
|
|
get_address(query->servname, port, &query->sockaddr);
|
|
|
|
sockcount++;
|
|
debug ("Socket = %d",sockcount);
|
|
result = isc_socket_create(socketmgr,
|
|
isc_sockaddr_pf(&query->sockaddr),
|
|
isc_sockettype_udp, &query->sock) ;
|
|
check_result(result, "isc_socket_create");
|
|
if (specified_source) {
|
|
result = isc_socket_bind(query->sock, &bind_address);
|
|
check_result(result, "isc_socket_bind");
|
|
}
|
|
}
|
|
|
|
send_udp(lookup);
|
|
}
|
|
|
|
void
|
|
do_lookup(dig_lookup_t *lookup) {
|
|
|
|
REQUIRE (lookup != NULL);
|
|
|
|
debug ("do_lookup()");
|
|
if (lookup->tcp_mode)
|
|
do_lookup_tcp(lookup);
|
|
else
|
|
do_lookup_udp(lookup);
|
|
}
|
|
|
|
void
|
|
start_lookup(void) {
|
|
dig_lookup_t *lookup;
|
|
|
|
debug ("start_lookup()");
|
|
|
|
if (free_now)
|
|
return;
|
|
|
|
lookup = ISC_LIST_HEAD(lookup_list);
|
|
if (lookup != NULL) {
|
|
setup_lookup(lookup);
|
|
do_lookup(lookup);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_lists(int _exitcode) {
|
|
void *ptr;
|
|
dig_lookup_t *l;
|
|
dig_query_t *q;
|
|
dig_server_t *s;
|
|
dig_searchlist_t *o;
|
|
|
|
debug("free_lists()");
|
|
|
|
if (free_now)
|
|
return;
|
|
|
|
free_now = ISC_TRUE;
|
|
|
|
l = ISC_LIST_HEAD(lookup_list);
|
|
while (l != NULL) {
|
|
q = ISC_LIST_HEAD(l->q);
|
|
while (q != NULL) {
|
|
if (q->sock != NULL) {
|
|
isc_socket_cancel(q->sock, NULL,
|
|
ISC_SOCKCANCEL_ALL);
|
|
isc_socket_detach(&q->sock);
|
|
sockcount--;
|
|
debug ("Socket = %d",sockcount);
|
|
}
|
|
if (ISC_LINK_LINKED(&q->recvbuf, link))
|
|
ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
|
|
link);
|
|
if (ISC_LINK_LINKED(&q->lengthbuf, link))
|
|
ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
|
|
link);
|
|
isc_buffer_invalidate(&q->recvbuf);
|
|
isc_buffer_invalidate(&q->lengthbuf);
|
|
ptr = q;
|
|
q = ISC_LIST_NEXT(q, link);
|
|
isc_mem_free(mctx, ptr);
|
|
}
|
|
if (l->use_my_server_list) {
|
|
s = ISC_LIST_HEAD(l->my_server_list);
|
|
while (s != NULL) {
|
|
ptr = s;
|
|
s = ISC_LIST_NEXT(s, link);
|
|
isc_mem_free(mctx, ptr);
|
|
|
|
}
|
|
}
|
|
if (l->sendmsg != NULL)
|
|
dns_message_destroy (&l->sendmsg);
|
|
if (l->timer != NULL)
|
|
isc_timer_detach (&l->timer);
|
|
ptr = l;
|
|
l = ISC_LIST_NEXT(l, link);
|
|
isc_mem_free(mctx, ptr);
|
|
}
|
|
s = ISC_LIST_HEAD(server_list);
|
|
while (s != NULL) {
|
|
ptr = s;
|
|
s = ISC_LIST_NEXT(s, link);
|
|
isc_mem_free(mctx, ptr);
|
|
}
|
|
o = ISC_LIST_HEAD(search_list);
|
|
while (o != NULL) {
|
|
ptr = o;
|
|
o = ISC_LIST_NEXT(o, link);
|
|
isc_mem_free(mctx, ptr);
|
|
}
|
|
if (socketmgr != NULL)
|
|
isc_socketmgr_destroy(&socketmgr);
|
|
if (timermgr != NULL)
|
|
isc_timermgr_destroy(&timermgr);
|
|
if (global_task != NULL)
|
|
isc_task_detach(&global_task);
|
|
if (taskmgr != NULL)
|
|
isc_taskmgr_destroy(&taskmgr);
|
|
|
|
#ifdef MEMDEBUG
|
|
isc_mem_stats(mctx, stderr);
|
|
#endif
|
|
isc_app_finish();
|
|
if (mctx != NULL)
|
|
isc_mem_destroy(&mctx);
|
|
|
|
debug("Getting ready to exit, code=%d",_exitcode);
|
|
if (_exitcode != 0)
|
|
exit(_exitcode);
|
|
}
|