/*
* Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
/*
* Copyright (c) 1989
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* 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, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION 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.
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Portions Copyright (c) 1996-1999 by 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 ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
*/
/*********************** Notes for the BIND 4.9 release (Paul Vixie, DEC)
* dig 2.0 was written by copying sections of libresolv.a and nslookup
* and modifying them to be more useful for a general lookup utility.
* as of BIND 4.9, the changes needed to support dig have mostly been
* incorporated into libresolv.a and nslookup; dig now links against
* some of nslookup's .o files rather than #including them or maintaining
* local copies of them.
*
* while merging dig back into the BIND release, i made a number of
* structural changes. for one thing, i put all of dig's private
* library routines into this file rather than maintaining them in
* separate, #included, files. i don't like to #include ".c" files.
* i removed all calls to "bcopy", replacing them with structure
* assignments. i removed all "extern"'s of standard functions,
* replacing them with #include's of standard header files. this
* version of dig is probably as portable as the rest of BIND.
*
* i had to remove the query-time and packet-count statistics since
* the current libresolv.a is a lot harder to modify to maintain these
* than the 4.8 one (used in the original dig) was. for consolation,
* i added a "usage" message with extensive help text.
*
* to save my (limited, albeit) sanity, i ran "indent" over the source.
* i also added the standard berkeley/DEC copyrights, since this file now
* contains a fair amount of non-USC code. note that the berkeley and
* DEC copyrights do not prohibit redistribution, with or without fee;
* we add them only to protect ourselves (you have to claim copyright
* in order to disclaim liability and warranty).
*
* Paul Vixie, Palo Alto, CA, April 1993
****************************************************************************
******************************************************************
* DiG -- Domain Information Groper *
* *
* dig.c - Version 2.1 (7/12/94) ("BIND takeover") *
* *
* Developed by: Steve Hotz & Paul Mockapetris *
* USC Information Sciences Institute (USC-ISI) *
* Marina del Rey, California *
* 1989 *
* *
* dig.c - *
* Version 2.0 (9/1/90) *
* o renamed difftime() difftv() to avoid *
* clash with ANSI C *
* o fixed incorrect # args to strcmp,gettimeofday *
* o incorrect length specified to strncmp *
* o fixed broken -sticky -envsa -envset functions *
* o print options/flags redefined & modified *
* *
* Version 2.0.beta (5/9/90) *
* o output format - helpful to `doc` *
* o minor cleanup *
* o release to beta testers *
* *
* Version 1.1.beta (10/26/89) *
* o hanging zone transer (when REFUSED) fixed *
* o trailing dot added to domain names in RDATA *
* o ISI internal *
* *
* Version 1.0.tmp (8/27/89) *
* o Error in prnttime() fixed *
* o no longer dumps core on large pkts *
* o zone transfer (axfr) added *
* o -x added for inverse queries *
* (i.e. "dig -x 128.9.0.32") *
* o give address of default server *
* o accept broadcast to server @255.255.255.255 *
* *
* Version 1.0 (3/27/89) *
* o original release *
* *
* DiG is Public Domain, and may be used for any purpose as *
* long as this notice is not removed. *
******************************************************************/
/*
* If LOCALDEF in environment, should point to file
* containing local favourite defaults. Also look for file
* DiG.env (i.e. SAVEENV) in local directory.
*/
if (strcmp(*vtmp, "-f") == 0) {
dofile++; once=0;
if ((qfp = fopen(*++vtmp, "r")) == NULL) {
fflush(stdout);
perror("file open");
fflush(stderr);
exit(10);
}
} else {
if (ax - args == DIG_MAXARGS) {
fprintf(stderr, "dig: too many arguments\n");
exit(10);
}
*ax++ = *vtmp;
}
vtmp++;
}
gettimeofday(&tv1, NULL);
/*
* Main section: once if cmd-line query
* while !EOF if batch mode
*/
*fileq = '\0';
while ((dofile && fgets(fileq, sizeof fileq, qfp) != NULL) ||
(!dofile && once--))
{
if (*fileq == '\n' || *fileq == '#' || *fileq==';') {
printf("%s", fileq); /* echo but otherwise ignore */
continue; /* blank lines and comments */
}
/*
* "Sticky" requests that before current parsing args
* return to current "working" environment (X******).
*/
if (sticky) {
printf(";; (using sticky settings)\n");
res = res_x;
}
/*
* Concat cmd-line and file args.
*/
stackarg(fileq, ax);
/* process key options */
if (keyfile) {
#ifdef PARSE_KEYFILE
int i, n1;
char buf[BUFSIZ], *p;
FILE *fp = NULL;
int file_major, file_minor, alg;
fp = fopen(keyfile, "r");
if (fp == NULL) {
perror(keyfile);
exit(1);
}
/* Now read the header info from the file. */
i = fread(buf, 1, BUFSIZ, fp);
if (i < 5) {
fclose(fp);
exit(1);
}
fclose(fp);
p = buf;
n=strlen(p); /* get length of strings */
n1=strlen("Private-key-format: v");
if (n1 > n ||
strncmp(buf, "Private-key-format: v", n1)) {
fprintf(stderr, "Invalid key file format\n");
exit(1); /* not a match */
}
p+=n1; /* advance pointer */
sscanf((char *)p, "%d.%d", &file_major, &file_minor);
/* should do some error checking with these someday */
while (*p++!='\n'); /* skip to end of line */
n=strlen(p); /* get length of strings */
n1=strlen("Algorithm: ");
if (n1 > n || strncmp(p, "Algorithm: ", n1)) {
fprintf(stderr, "Invalid key file format\n");
exit(1); /* not a match */
}
p+=n1; /* advance pointer */
if (sscanf((char *)p, "%d", &alg)!=1) {
fprintf(stderr, "Invalid key file format\n");
exit(1);
}
while (*p++!='\n'); /* skip to end of line */
n=strlen(p); /* get length of strings */
n1=strlen("Key: ");
if (n1 > n || strncmp(p, "Key: ", n1)) {
fprintf(stderr, "Invalid key file format\n");
exit(1); /* not a match */
}
p+=n1; /* advance pointer */
pp=p;
while (*pp++!='\n'); /* skip to end of line,
* terminate it */
*--pp='\0';
strcpy(key.name, keyname);
strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
#else
/* use the dst* routines to parse the key files
*
* This requires that both the .key and the .private
* files exist in your cwd, so the keyfile parmeter
* here is assumed to be a path in which the
* K*.{key,private} files exist.
*/
DST_KEY *dst_key;
char cwd[PATH_MAX+1];
if (getcwd(cwd, PATH_MAX)==NULL) {
perror("unable to get current directory");
exit(1);
}
if (chdir(keyfile)<0) {
fprintf(stderr,
"unable to chdir to %s: %s\n", keyfile,
strerror(errno));
exit(1);
}
dst_init();
dst_key = dst_read_key(keyname,
0 /* not used for priv keys */,
KEY_HMAC_MD5, DST_PRIVATE);
if (!dst_key) {
fprintf(stderr,
"dst_read_key: error reading key\n");
exit(1);
}
key.data=malloc(1024*sizeof(char));
dst_key_to_buffer(dst_key, key.data, 1024);
key.len=dst_key->dk_key_size;
/*
* Current env. (after this parse) is to become the
* new "working" environmnet. Used in conj. with sticky.
*/
if (envset) {
res_x = res;
envset = 0;
}
/*
* Current env. (after this parse) is to become the
* new default saved environmnet. Save in user specified
* file if exists else is SAVEENV (== "DiG.env").
*/
if (envsave) {
afile = (char *) getenv("LOCALDEF");
if ((afile &&
((fp = open(afile,
O_WRONLY|O_CREAT|O_TRUNC,
S_IREAD|S_IWRITE)) > 0))
||
((fp = open(SAVEENV,
O_WRONLY|O_CREAT|O_TRUNC,
S_IREAD|S_IWRITE)) > 0)) {
write(fp, (char *)&res, (sizeof res));
close(fp);
}
envsave = 0;
}
if (res.pfcode & RES_PRF_CMD)
printf("%s\n", cmd);
anyflag = 0;
/*
* Find address of server to query. If not dot-notation, then
* try to resolve domain-name (if so, save and turn off print
* options, this domain-query is not the one we want. Restore
* user options when done.
* Things get a bit wierd since we need to use resolver to be
* able to "put the resolver to work".
*/
if (srv != NULL) {
int nscount = 0;
union res_sockaddr_union u[MAXNS];
struct addrinfo *answer = NULL;
struct addrinfo *cur = NULL;
struct addrinfo hint;
memset(u, 0, sizeof(u));
res_t = res;
res_ninit(&res);
res.pfcode = 0;
res.options = RES_DEFAULT;
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_DGRAM;
if (!getaddrinfo(srv, NULL, &hint, &answer)) {
res = res_t;
cur = answer;
for (cur = answer;
cur != NULL;
cur = cur->ai_next) {
if (nscount == MAXNS)
break;
switch (cur->ai_addr->sa_family) {
case AF_INET6:
u[nscount].sin6 =
*(struct sockaddr_in6*)cur->ai_addr;
u[nscount++].sin6.sin6_port =
port;
break;
case AF_INET:
u[nscount].sin =
*(struct sockaddr_in*)cur->ai_addr;
u[nscount++].sin.sin_port =
port;
break;
}
}
if (nscount != 0)
res_setservers(&res, u, nscount);
freeaddrinfo(answer);
} else {
res = res_t;
fflush(stdout);
fprintf(stderr,
"; Bad server: %s -- using default server and timer opts\n",
srv);
fflush(stderr);
srv = NULL;
}
printf("; (%d server%s found)\n",
res.nscount, (res.nscount==1)?"":"s");
res.id += res.retry;
}
if (ns_t_xfr_p(xfr)) {
int i;
int nscount;
union res_sockaddr_union u[MAXNS];
nscount = res_getservers(&res, u, MAXNS);
for (i = 0; i < nscount; i++) {
int x;
if (keyfile)
x = printZone(xfr, domain,
&u[i].sin,
&key);
else
x = printZone(xfr, domain,
&u[i].sin,
NULL);
if (res.pfcode & RES_PRF_STATS) {
exectime = time(NULL);
printf(";; FROM: %s to SERVER: %s\n",
myhostname,
p_sockun(u[i], ubuf,
sizeof(ubuf)));
printf(";; WHEN: %s", ctime(&exectime));
}
if (!x)
break; /* success */
}
fflush(stdout);
continue;
}
if (*domain && !qtypeSet) {
queryType = T_A;
qtypeSet++;
}
fflush(stdout);
/*
* Argh ... not particularly elegant. Should put in *real* ping code.
* Would necessitate root priviledges for icmp port though!
*/
if (*pingstr && srv != NULL) {
sprintf(doping, pingfmt, pingstr, srv, DIG_TAIL);
system(doping);
}
putchar('\n');
/*
* Fairly crude method and low overhead method of keeping two
* batches started at different sites somewhat synchronized.
*/
gettimeofday(&tv2, NULL);
delay = (int)(tv2.tv_sec - tv1.tv_sec);
if (delay < wait) {
sleep(wait - delay);
}
tv1 = tv2;
}
return (eecode);
}
/* Private. */
static void
Usage() {
fputs("\
usage: dig [@server] [domain] [q-type] [q-class] {q-opt} {d-opt} [%comment]\n\
where: server,\n\
domain are names in the Domain Name System\n\
q-class is one of (in,any,...) [default: in]\n\
q-type is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default: a]\n\
", stderr);
fputs("\
q-opt is one of:\n\
-x dot-notation-address (shortcut to in-addr.arpa lookups)\n\
-f file (batch mode input file name)\n\
-T time (batch mode time delay, per query)\n\
-p port (nameserver is on this port) [53]\n\
-b addr[:port] (bind AXFR to this tcp address) [*]\n\
-P[ping-string] (see man page)\n\
-t query-type (synonym for q-type)\n\
-c query-class (synonym for q-class)\n\
-k keydir:keyname (sign the query with this TSIG key)\n\
-envsav,-envset (see man page)\n\
-[no]stick (see man page)\n\
", stderr);
fputs("\
d-opt is of the form ``+keyword=value'' where keyword is one of:\n\
[no]debug [no]d2 [no]recurse retry=# time=# [no]ko [no]vc\n\
[no]defname [no]search domain=NAME [no]ignore [no]primary\n\
[no]aaonly [no]cmd [no]stats [no]Header [no]header [no]trunc\n\
[no]ttlid [no]cl [no]qr [no]reply [no]ques [no]answer\n\
[no]author [no]addit [no]dnssec pfdef pfmin\n\
pfset=# pfand=# pfor=#\n\
", stderr);
fprintf(stderr, "\
notes: defname and search don't work; use fully-qualified names.\n\
this is DiG version %s (libbind %d)\n\
Id: dig8.c,v 1.4 2009/03/03 23:49:07 tbox Exp \n", VSTRING, __RES);
}
static int
setopt(const char *string) {
char option[NAME_LEN], *ptr;
int i;
i = pickString(string, option, sizeof option);
if (i == 0) {
fprintf(stderr, ";*** Invalid option: %s\n", string);
/* this is ugly, but fixing the caller to behave
properly with an error return value would require a major
cleanup. */
exit(9);
}
/*
* Force a reinitialization when the domain is changed.
*/
static void
res_re_init() {
static char localdomain[] = "LOCALDOMAIN";
u_long pfcode = res.pfcode, options = res.options;
unsigned ndots = res.ndots;
int retrans = res.retrans, retry = res.retry;
char *buf;
/*
* This is ugly but putenv() is more portable than setenv().
*/
buf = malloc((sizeof localdomain) + strlen(res.defdname) +10/*fuzz*/);
sprintf(buf, "%s=%s", localdomain, res.defdname);
putenv(buf); /* keeps the argument, so we won't free it */
res_ninit(&res);
res.pfcode = pfcode;
res.options = options;
res.ndots = ndots;
res.retrans = retrans;
res.retry = retry;
}
/*
* convert char string (decimal, octal, or hex) to integer
*/
static int
xstrtonum(char *p) {
int v = 0;
int i;
int b = 10;
int flag = 0;
while (*p != 0) {
if (!flag++)
if (*p == '0') {
b = 8; p++;
continue;
}
if (isupper((unsigned char)*p))
*p = tolower(*p);
if (*p == 'x') {
b = 16; p++;
continue;
}
if (isdigit((unsigned char)*p)) {
i = *p - '0';
} else if (isxdigit((unsigned char)*p)) {
i = *p - 'a' + 10;
} else {
fprintf(stderr,
"; *** Bad char in numeric string..ignored\n");
i = -1;
}
if (i >= b) {
fprintf(stderr,
"; *** Bad char in numeric string..ignored\n");
i = -1;
}
if (i >= 0)
v = v * b + i;
p++;
}
return (v);
}
typedef union {
HEADER qb1;
u_char qb2[PACKETSZ];
} querybuf;
querybuf buf;
int msglen, amtToRead, numRead, result, sockFD, len;
int count, type, rlen, done, n;
int numAnswers, numRecords, soacnt;
u_char *cp, tmp[NS_INT16SZ];
char dname[2][NS_MAXDNAME];
enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING }
error;
pid_t zpid = -1;
u_char *newmsg;
int newmsglen;
ns_tcp_tsig_state tsig_state;
int tsig_ret, tsig_required, tsig_present;
switch (xfr) {
case ns_t_axfr:
case ns_t_zxfr:
break;
default:
fprintf(stderr, ";; %s - transfer type not supported\n",
p_type(xfr));
return (ERROR);
}
/*
* Create a query packet for the requested zone name.
*/
msglen = res_nmkquery(&res, ns_o_query, zone,
queryClass, ns_t_axfr, NULL,
0, 0, buf.qb2, sizeof buf);
if (msglen < 0) {
if (res.options & RES_DEBUG)
fprintf(stderr, ";; res_nmkquery failed\n");
return (ERROR);
}
/*
* Sign the message if a key was sent
*/
if (key == NULL) {
newmsg = (u_char *)&buf;
newmsglen = msglen;
} else {
DST_KEY *dstkey;
int bufsize, siglen;
u_char sig[64];
int ret;
/* ns_sign() also calls dst_init(), but there is no harm
* doing it twice
*/
dst_init();
len = ns_get16(tmp);
if (len == 0)
break; /* nothing left to read */
/*
* The server sent too much data to fit the existing buffer --
* allocate a new one.
*/
if (len > answerLen) {
if (answerLen != 0)
free(answer);
answerLen = len;
answer = (u_char *)malloc(answerLen);
}
/*
* We are looking for info from answer resource records.
* If there aren't any, return with an error. We assume
* there aren't any question records.
*/
if (ns_msg_count(handle, ns_s_an) == 0)
return (NO_INFO);
#ifdef PROTOCOLDEBUG
printf(";;; (message of %d octets has %d answers)\n",
msglen, ns_msg_count(handle, ns_s_an));
#endif
for (;;) {
static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME];
const char *name;
char buf[2048]; /* XXX need to malloc/realloc. */
ns_rr rr;
/*
* Take arguments appearing in simple string (from file or command line)
* place in char**.
*/
static void
stackarg(char *l, char **y) {
int done = 0;
while (!done) {
switch (*l) {
case '\t':
case ' ':
l++;
break;
case '\0':
case '\n':
done++;
*y = NULL;
break;
default:
*y++ = l;
while (!isspace((unsigned char)*l))
l++;
if (*l == '\n')
done++;
*l++ = '\0';
*y = NULL;
}
}
}