;/* ==========================================================================
 * ldig - Simple DNS Query Interface
 * --------------------------------------------------------------------------
 * Copyright (c) 2004, 2005, 2006  Barracuda Networks, Inc.
 * Copyright (c) 2006  William Ahern <william@25thandClement.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * --------------------------------------------------------------------------
 * History
 *
 * 2006-04-23 (william@25thandClement.com)
 * 	Make work with refactored interface.
 *
 * 2006-02-14 (william@25thandClement.com)
 * 	Published by Barracuda Networks, originally authored by
 * 	employee William Ahern (wahern@barracudanetworks.com).
 * --------------------------------------------------------------------------
 * Description
 *
 * Clone of the dig(1) utility.
 *
 * ==========================================================================
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <sched.h>

#include <netdb.h>		/* NI_MAXHOST */

#include <string.h>

#include <getopt.h>

#include <event.h>

#include <err.h>

#include <assert.h>

#include "lookup.h"


#define USAGE									\
	"Usage: ldig [OPTIONS] [QUERY NAME]\n"					\
	"  -t, --type=TYPE   Query type: PTR, A, AAAA, MX, MX+A+AAAA, etc.\n"	\
	"  -f, --file=PATH   Path to query input: <qtype> <qname>\n"		\
	"  -o, --option=OPT  Set flag: PREFER_A, FALLBACK\n"			\
	"  -l, --list        List supported query types.\n"			\
	"  -v, --verbose     Enable verbose output.\n"				\
	"  -h, --help        Print this usage information.\n\n"			\


static int verbose;
static int pending;


static void split(int *argc, char *argv[], int len, char *line, char *sep) {
	char **ap;
	char *nxt	= line;

	if (!sep)
		sep	= " \t";

	for (*argc = 0, ap = argv; ap < &argv[len - 1];) {
		if ((*ap = strsep(&nxt, sep)) == 0)
			break;

		if (**ap != '\0')
			ap++, (*argc)++;
	}

	*ap	= 0;

	return /* void */;
} /* split() */


void catch_rr(int nanswers, struct lookup_rr *answer, int nadditional, struct lookup_rr *additional, enum lookup_errno status, void *query) {
	char name[128];

	pending--;

	(void)printf("Query %s:",(char *)query);
	
	if (!LOOKUP_SUCCESS(status))
		printf(" FAILED (%s)\n", lookup_errlist[status]);
	else
		printf(" SUCCEEDED\n");

	if (nanswers)
		puts("\t--- ANSWERS ---");
print:
	for (; answer; answer = answer->next) {
		switch (answer->type) {
		case LOOKUP_IN_PTR:
			printf("\t%s",answer->rr.ptr.host);
			break;
		case LOOKUP_IN_TXT:
			printf("\t%s",answer->rr.txt.data);
			break;
		case LOOKUP_IN_SOA:
			printf("\t%s %s %u %d %d %d %u",answer->rr.soa.host,answer->rr.soa.mbox,answer->rr.soa.serial,answer->rr.soa.refresh,answer->rr.soa.retry,answer->rr.soa.expire,answer->rr.soa.minimum);
			break;
		case LOOKUP_IN_NS:
			printf("\t%s",answer->rr.ns.host);
			break;
		case LOOKUP_IN_CNAME:
			printf("\t%s",answer->rr.cname.host);
			break;
		case LOOKUP_IN_MX:
			printf("\t%hu %s",answer->rr.mx.preference,answer->rr.mx.host);
			break;
		case LOOKUP_IN_SRV:
			printf("\t%hu %hu %hu %s",answer->rr.srv.priority,answer->rr.srv.weight,answer->rr.srv.port,answer->rr.srv.host);
			break;
		case LOOKUP_IN_A:
			/* FALL THROUGH */
		case LOOKUP_IN_AAAA:
			if (0 != getnameinfo(&answer->rr.ip.sa.sa,answer->rr.ip.salen,name,sizeof name,NULL,0,NI_NUMERICHOST|NI_NUMERICSERV))
				err(1,"getnameinfo");

			printf("\t%s",name);
			break;
		default:
			assert(0);
		}

		switch (answer->section) {
		case LOOKUP_SECTION_LOCAL:
			puts(" (section: local)");
			break;
		case LOOKUP_SECTION_ANSWERS:
			puts(" (section: answers)");
			break;
		case LOOKUP_SECTION_AUTHORITY:
			puts(" (section: authority)");
			break;
		case LOOKUP_SECTION_ADDITIONAL:
			puts(" (section: additional)");
			break;
		default:
			assert(0);
		}
	}

	if (nadditional) {
		puts("\t--- ADDITIONAL ---");

		answer		= additional;
		nanswers	= nadditional;
		nadditional	= 0;
		
		goto print;
	}

	free(query);

	return /* void */;
} /* catch_rr() */


char *strquery(char *qname, int qtype, int flags) {
	struct {
		const char *name;
		int type;
	} *nxt, map[] = {
		{ "PTR",	LOOKUP_IN_PTR },
		{ "TXT",	LOOKUP_IN_TXT },
		{ "SOA",	LOOKUP_IN_SOA },
		{ "NS",		LOOKUP_IN_NS },
		{ "CNAME",	LOOKUP_IN_CNAME },
		{ "MX",		LOOKUP_IN_MX },
		{ "SRV",	LOOKUP_IN_SRV },
		{ "A",		LOOKUP_IN_A },
		{ "AAAA",	LOOKUP_IN_AAAA },
		{ NULL,		0}
	};
	char type[64];
	unsigned len;
	char *q;

	*type	= '\0';
	len	= 0;

	for (nxt = map; nxt->name; nxt++) {
		if (nxt->type & qtype) {
			len	= strlen(strncat(type, nxt->name, sizeof type - 1));
			len	= strlen(strncat(type, "+", sizeof type - len - 1));
		}
	}

	if (len && type[len - 1] == '+')
		type[--len]	= '\0';

	if (0 >= asprintf(&q,"%s IN %s",qname,type))
		err(EXIT_FAILURE,"asprintf");

	return q;
} /* strquery() */


int doquery(char *qname, int qtype, int flags) {
	struct timeval tv = { 5 , 0 };
	char buf[NI_MAXHOST];
	char ptr[64];
	char *octets[5];
	int noctets;
	char *q;

	if (qtype == LOOKUP_IN_PTR && !strstr(qname,".in-addr.arpa")) {
		strncpy(buf, qname, sizeof buf);
		buf[sizeof buf - 1]	= '\0';

		split(&noctets,octets,sizeof octets / sizeof *octets,buf,".");

		if (noctets != 4)
			return -1;

		(void)snprintf(ptr,sizeof ptr,"%s.%s.%s.%s.in-addr.arpa",octets[3],octets[2],octets[1],octets[0]);
		qname	= ptr;
	}

	q	= strquery(qname,qtype,flags);

	if (verbose)
		fprintf(stderr,"Issuing %s\n",q);

	pending++;

	lookup(qname, strlen(qname), qtype, flags, catch_rr, q, &tv);

	return 0;
} /* doquery() */


int settype(int *type,const char *name) {
	if (0 == strcasecmp("PTR",name))
		*type	|= LOOKUP_IN_PTR;
	else if (0 == strcasecmp("A",name))
		*type	|= LOOKUP_IN_A;
	else if (0 == strcasecmp("AAAA",name))
		*type	|= LOOKUP_IN_AAAA;
	else if (0 == strcasecmp("MX",name))
		*type	|= LOOKUP_IN_MX;
	else if (0 == strcasecmp("SRV",name))
		*type	|= LOOKUP_IN_SRV;
	else if (0 == strcasecmp("TXT",name))
		*type	|= LOOKUP_IN_TXT;
	else if (0 == strcasecmp("SOA",name))
		*type	|= LOOKUP_IN_SOA;
	else if (0 == strcasecmp("NS",name))
		*type	|= LOOKUP_IN_NS;
	else if (0 == strcasecmp("CNAME",name))
		*type	|= LOOKUP_IN_CNAME;
	else
		return 0;

	return 1;
} /* settype() */


int setflag(int *type,const char *name) {
	if (0 == strcasecmp("FALLBACK",name))
		*type	|= LOOKUP_FALLBACK;
	else if (0 == strcasecmp("PREFER_A",name))
		*type	|= LOOKUP_PREFER_A;
	else
		return 0;

	return 1;
} /* setflag() */


int main(int argc, char *argv[]) {
	extern char *optarg;
	extern int optind;
	int c;
	struct option list[] = {
		{"type", 	1, 	NULL,	't'},
		{"file",	1,	NULL,	'f'},
		{"option",	1,	NULL,	'o'},
		{"list",	0,	NULL,	'l'},
		{"verbose",	0,	NULL,	'v'},
		{"help",	0,	NULL,	'h'},
		{NULL,		0,	NULL,	0}
	};
	const char *file	= NULL;
	int type		= 0;
	int flags 		= 0;
	char *types[8];
	int ntypes;
	char *opts[8];
	int nopts;
	FILE *fp	= NULL;
	char *ln;
	int lineno	= 0;
	char linebuf[1024];
	int last;

	event_init();
	lookup_init(0, 0, 0);
	setlinebuf(stdout);

	while (-1 != (c = getopt_long(argc,argv,"t:f:o:lvh",list,NULL))) {
		switch (c) {
		case 't':
			split(&ntypes,types,sizeof types / sizeof *types,optarg,"+");

			while (ntypes--) if (!settype(&type,types[ntypes]))
				warnx("Invalid type: %s",types[ntypes]);

			break;
		case 'f':
			file	= optarg;
			break;
		case 'o':
			split(&nopts,opts,sizeof opts / sizeof *opts,optarg,"+");

			while (nopts--) if (!setflag(&flags,opts[nopts]))
				warnx("Invalid flag: %s",opts[nopts]);

			break;
		case 'l':
			fprintf(stderr,"Query types:\n");
			fprintf(stderr,"	PTR\n");
			fprintf(stderr,"	A\n");
			fprintf(stderr,"	AAAA\n");
			fprintf(stderr,"	MX\n");
			fprintf(stderr,"	MX+A\n");
			fprintf(stderr,"	MX+AAAA\n");
			fprintf(stderr,"	MX+A+AAAA\n");
			fprintf(stderr,"	SRV\n");
			fprintf(stderr,"	SRV+A\n");
			fprintf(stderr,"	SRV+AAAA\n");
			fprintf(stderr,"	SRV+A+AAAA\n");
			fprintf(stderr,"	TXT\n");
			fprintf(stderr,"	SOA\n");
			fprintf(stderr,"	NS\n");
			fprintf(stderr,"	CNAME\n");

			return EXIT_SUCCESS;
		case 'v':
			verbose	= 1;
			break;
/*help:*/
		case 'h':
		default:
			fprintf(stderr,USAGE);
			return (c == 'h')? EXIT_SUCCESS : EXIT_FAILURE;
		}	
	} /* getopt_long() */

	event_init();
	lookup_init(0, 0, 0);
	setlinebuf(stdout);

	if (!type)
		type	|= LOOKUP_IN_A | LOOKUP_IN_AAAA;

	if (optind < argc) {
		while (optind < argc) {
			if (0 != doquery(argv[optind],type,flags))
				warnx("Invalid query: %s",strquery(argv[optind],type,flags));
			optind++;
		}
	} else fp = stdin;
	
	if (file) {
		fp	= fopen(file,"r");

		if (!fp)
			err(EXIT_FAILURE,"Could not open %s",file);
	}

	if (fp) while ((ln = fgets(linebuf, sizeof linebuf, fp))) {
		char *args[3];
		int nargs;

		assert(ln = strdup(ln));

		lineno++;

		split(&nargs, args, sizeof args / sizeof *args, linebuf, " \t");

		type	= LOOKUP_IN_A | LOOKUP_IN_AAAA;

		switch (nargs) {
		case 2:
			type	= 0;

			split(&ntypes, types, sizeof types / sizeof *types, args[0], "+");

			while (ntypes--) if (!settype(&type, types[ntypes]))
				warnx("Invalid type at line %d in %s: %s", lineno, ln, types[ntypes]);

			args[0] = args[1];
		case 1:
			if (0 == doquery(args[0], type, flags))
				break;

			/* FALL THROUGH */
		default:
			warnx("Invalid query: %s", ln);
		}

		if (verbose)
			fprintf(stderr,"%d queries pending\n", pending);

		free(ln);
	}

	for (last = 0;;) {

		if (verbose) {
			if (last != pending) {
				last	= pending;
				fprintf(stderr,"%d queries pending\n",pending);
			}
		}

		event_loop(EVLOOP_NONBLOCK);

		event_loop(EVLOOP_ONCE);

		if (!pending)
			break;
	}

	return 0;
} /* main() */



syntax highlighted by Code2HTML, v. 0.9.1