Developing against libbind

Paul Vixie Paul_Vixie at isc.org
Sun Jul 6 20:39:46 UTC 2008


Andy Shellam <andy.shellam-lists at mailnetwork.co.uk> writes:

> I apologise if this is the wrong place for this type of question, but I'm
> developing an application that needs to do DNS lookups, and I cannot find
> any documentation about how to use libbind.  I have build bind 9.5.0 with
> --enable-libbind and cannot find anything helpful (except the header
> files) in the build.

no apologies nec'y.  libbind has been the fair haired stepchild around here.

> Can anyone point me in the direction of any documentation available
> showing how to use the Bind library to do DNS lookups from a C
> application?

well, i use it all the time.  here's a small demo application called "dnsget"
which is sort of like a stripped down "dig" but it's specifically meant to be
called by shell scripts to do dns lookups.  it shows the libbind basics.

[nsa:amd64] ./dnsget
usage error: missing domain argument
usage: dnsget [-a] [-d] [-l] [-s server[,...] name type [field[,field...]]
        soa     mname rname serial refresh retry expire minimum
        ns      nsdname
        a       address
        aaaa    address
        mx      preference exchange
        cname   cname
        ptr     ptrdname

[nsa:amd64] ./dnsget mailnetwork.co.uk mx exchange
NOERROR 1 0 0
mx.fusemail.net

[nsa:amd64] ./dnsget mailnetworx.co.uk mx exchange
NXDOMAIN

ref:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	dnsget.c
#	soacheck.sh
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#BINDROOT= /usr/local/bind
XCDEBUG= -g -Wall
XCFLAGS= $(CDEBUG) -I${BINDROOT}/include
XLDFLAGS= -L${BINDROOT}/lib -L/usr/local/lib
XLIBS= -lbind
X
XALL= dnsget
X
Xall: $(ALL)
X
Xclean: FRC
X	rm -f $(ALL)
X	rm -f *.o
X
Xdistclean: clean
X	rm -f *.CKP *.BAK *~
X
XFRC:
X
XDNSGET= dnsget.o
Xdnsget: $(DNSGET)
X	$(CC) $(LDFLAGS) -o dnsget $(DNSGET) $(LIBS)
END-of-Makefile
echo x - dnsget.c
sed 's/^X//' >dnsget.c << 'END-of-dnsget.c'
X#ifndef lint
Xstatic const char rcsid[] = "$Id: dnsget.c,v 1.10 2008/07/06 20:35:20 vixie Exp $";
X#endif
X
X/* Import. */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/nameser.h>
X#include <arpa/inet.h>
X
X#include <assert.h>
X#include <errno.h>
X#include <netdb.h>
X#include <resolv.h>
X#include <stdarg.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <unistd.h>
X
X/* Macros. */
X
X#define CHK(x,e) if ((x) < 0) { \
X	fprintf(stderr, "%s: %s\n", e, strerror(errno)); \
X	exit(1); \
X}
X
X#define ISSET(x, in) (((in) & (1 << (x))) != 0)
X
X/* Types. */
X
Xstruct rr {
X	const char *name;
X	ns_type type;
X	const char **fields;
X	int (*get)(ns_msg *, ns_rr *, int);
X};
X
X/* Forward. */
X
Xstatic void usage(const char *msg, ...);
Xstatic const char *setservers(res_state res, const char *servers);
Xstatic int dnsget(const char *, const struct rr *, res_state, int, int);
Xstatic int rrget(ns_msg *, ns_rr *);
X
Xstatic int soa_get(ns_msg *, ns_rr *, int);
Xstatic const char *soa_fields[] = {
X	"mname", "rname", "serial", "refresh", "retry",
X	"expire", "minimum", NULL
X};
Xenum {
X	soa_mname, soa_rname, soa_serial, soa_refresh, soa_retry,
X	soa_expire, soa_minimum
X};
X
Xstatic int ns_get(ns_msg *, ns_rr *, int);
Xstatic const char *ns_fields[] = { "nsdname", NULL };
Xenum { ns_nsdname };
X
Xstatic int a_get(ns_msg *, ns_rr *, int);
Xstatic const char *a_fields[] = { "address", NULL };
Xenum { a_address };
X
Xstatic int aaaa_get(ns_msg *, ns_rr *, int);
Xstatic const char *aaaa_fields[] = { "address", NULL };
Xenum { aaaa_address };
X
Xstatic int mx_get(ns_msg *, ns_rr *, int);
Xstatic const char *mx_fields[] = { "preference", "exchange", NULL };
Xenum { mx_preference, mx_exchange };
X
X#define cname_get ns_get
Xstatic const char *cname_fields[] = { "cname", NULL };
Xenum { cname_cname = ns_nsdname };
X
X#define ptr_get ns_get
Xstatic const char *ptr_fields[] = { "ptrdname", NULL };
Xenum { ptr_ptrdname = ns_nsdname };
X
X/* Data. */
X
Xstatic const char *progname = "amnesia";
Xstatic struct rr rrs[] = {
X	{ "soa",	ns_t_soa,	soa_fields,	soa_get },
X	{ "ns",		ns_t_ns,	ns_fields,	ns_get },
X	{ "a",		ns_t_a,		a_fields,	a_get },
X	{ "aaaa",	ns_t_aaaa,	aaaa_fields,	aaaa_get },
X	{ "mx",		ns_t_mx,	mx_fields,	mx_get },
X	{ "cname",	ns_t_cname,	cname_fields,	cname_get },
X	{ "ptr",	ns_t_ptr,	ptr_fields,	ptr_get },
X	{ NULL }
X};
X
X/* Public. */
X
Xint
Xmain(int argc, char **argv) {
X	const char *domain, *cp, *typename;
X	const struct rr *rr;
X	struct __res_state res;
X	int ch, all = 0, fields;
X	char *dot;
X
X	if ((progname = strrchr(*argv, '/')) == NULL)
X		progname = *argv;
X	else
X		progname++;
X	memset(&res, 0, sizeof res);
X	CHK(res_ninit(&res), "res_ninit");
X	while ((ch = getopt(argc, argv, "adrls:")) != -1) {
X		switch (ch) {
X		case 'a':
X			all++;
X			break;
X		case 'd':
X			res.options |= RES_DEBUG;
X			break;
X		case 'r':
X			res.options &= ~RES_RECURSE;
X			break;
X		case 'l':
X#ifdef RES_DONTROUTE
X			res._flags |= RES_DONTROUTE;
X#endif
X			break;
X		case 's':
X			if ((cp = setservers(&res, optarg)) != NULL)
X				usage(cp);
X			break;
X		default:
X			usage("unrecognized argument -%c", ch);
X		}
X	}
X	argv += optind, argc -= optind;
X	if (argc < 1)
X		usage("missing domain argument");
X	domain = *argv++; argc--;
X	dot = strrchr(domain, '.');
X	if (dot != NULL && dot[1] == '\0')
X		*dot = '\0';
X	if (argc < 1)
X		usage("missing rrtype argument");
X	typename = *argv++; argc--;
X	for (rr = rrs; rr->name != NULL; rr++)
X		if (strcasecmp(rr->name, typename) == 0)
X			break;
X	if (rr->name == NULL)
X		usage("unrecognized rr type %s", typename);
X	fields = -1;	/* all ones */
X	if (argc > 0) {
X		cp = *argv++; argc--;
X		fields = 0;
X		while (*cp != '\0') {
X			const char *x = strchr(cp, ',');
X			int l = (x != NULL) ? x - cp : strlen(cp);
X			const char **s;
X
X			for (s = rr->fields; *s != NULL; s++)
X				if (strlen(*s) == l &&
X				    strncasecmp(*s, cp, l) == 0)
X					break;
X			if (*s == NULL)
X				usage("unrecognized field name");
X			fields |= (1 << (s - rr->fields));
X			cp += l;
X			if (*cp == ',')
X				cp++;
X		}
X		assert(fields != 0);
X	}
X	if (argc > 1)
X		usage("too many arguments");
X	exit(dnsget(domain, rr, &res, fields, all));
X}
X
X/* Private. */
X
Xstatic void
Xusage(const char *msg, ...) {
X	va_list ap;
X	struct rr *rr;
X	const char **s;
X
X	va_start(ap, msg);
X	fputs("usage error: ", stderr);
X	vfprintf(stderr, msg, ap);
X	fputc('\n', stderr);
X	va_end(ap);
X	fprintf(stderr, "usage: %s [-a] [-d] [-l] [-s server[,...] "
X			"name type [field[,field...]]\n",
X		progname);
X	for (rr = rrs; rr->name != NULL; rr++) {
X		fprintf(stderr, "\t%s\t", rr->name);
X		for (s = rr->fields; *s != NULL; s++)
X			fprintf(stderr, "%s ", *s);
X		fputc('\n', stderr);
X	}
X
X	exit(1);
X}
X
Xstatic const char *
Xsetservers(res_state res, const char *servers) {
X	union res_sockaddr_union serverset[MAXNS];
X	int nscount = 0;
X
X	while (*servers != '\0') {
X		const char *x = strchr(servers, ',');
X		int l = (x != NULL) ? x - servers : strlen(servers);
X		char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
X		union res_sockaddr_union *s = &serverset[nscount];
X
X		if (nscount > MAXNS)
X			return ("too many servers specified");
X		if (l + 1 > sizeof t)
X			return ("server literal is too long");
X		memset(t, 0, sizeof t);
X		strncpy(t, servers, l);
X		memset(s, 0, sizeof *s);
X		if (inet_pton(AF_INET6, t, &s->sin6.sin6_addr)) {
X			s->sin6.sin6_family = AF_INET6;
X			s->sin6.sin6_port = htons(NS_DEFAULTPORT);
X		} else if (inet_pton(AF_INET, t, &s->sin.sin_addr)) {
X			s->sin.sin_family = AF_INET;
X			s->sin.sin_port = htons(NS_DEFAULTPORT);
X		} else {
X			return ("server must be valid INET6 or INET literal");
X		}
X		nscount++;
X		servers += l;
X		while (*servers == ',')
X			servers++;
X	}
X	res_setservers(res, serverset, nscount);
X	return (NULL);
X}
X
Xstatic int
Xdnsget(const char *domain, const struct rr *rr, res_state res,
X       int fields, int all)
X{
X	char qname[NS_MAXDNAME];
X	u_char buf[NS_MAXMSG];
X	ns_rcode rcode;
X	ns_msg msg;
X	int n, sn;
X
X	n = res_nquery(res, domain, ns_c_in, rr->type, buf, sizeof buf);
X	if (n <= 0) {
X		switch (res->res_h_errno) {
X		case HOST_NOT_FOUND:
X			fputs("NXDOMAIN", stdout);
X			break;
X		case TRY_AGAIN:
X			fputs("TEMPFAIL", stdout);
X			break;
X		case NO_RECOVERY:
X			fputs("PERMFAIL", stdout);
X			break;
X		case NO_DATA:
X			fputs("NODATA", stdout);
X			break;
X		default:
X			printf("?%d?", res->res_h_errno);
X			break;
X		}
X		fputc('\n', stdout);
X		return (1);
X	}
X
X	CHK(ns_initparse(buf, n, &msg), "ns_initparse");
X
X	rcode = ns_msg_getflag(msg, ns_f_rcode);
X	if (rcode == ns_r_noerror &&
X	    (res->options & RES_RECURSE) == 0 &&
X	    ns_msg_getflag(msg, ns_f_aa) == 0)
X		rcode = ns_r_notauth;
X	printf("%s %d %d %d\n", p_rcode(rcode),
X	       ns_msg_count(msg, ns_s_an),
X	       ns_msg_count(msg, ns_s_ns),
X	       ns_msg_count(msg, ns_s_ar));
X	if (rcode != ns_r_noerror)
X		return (1);
X
X	strncpy(qname, domain, sizeof qname);
X	qname[sizeof qname - 1] = '\0';
X	for (sn = ns_s_an; sn <= (all ? ns_s_ar : ns_s_an); sn++) {
X		int rrn;
X
X		for (rrn = 0; rrn < ns_msg_count(msg, sn); rrn++) {
X			int need_newline = 1;
X			ns_rr nsrr;
X
X			CHK(ns_parserr(&msg, sn, rrn, &nsrr), "ns_parserr");
X			if (all)
X				printf("%s %s %s ",
X				       p_section(sn, ns_o_query),
X				       ns_rr_name(nsrr),
X				       p_type(ns_rr_type(nsrr)));
X			if (sn == ns_s_an) {
X				if (strcasecmp(ns_rr_name(nsrr), qname) != 0) {
X					printf("? %s != %s",
X					       ns_rr_name(nsrr), qname);
X					n = 1;
X				} else if (ns_rr_type(nsrr) == ns_t_cname) {
X					n = dn_expand(ns_msg_base(msg),
X						      ns_msg_end(msg),
X						      ns_rr_rdata(nsrr),
X						      qname, sizeof qname);
X					CHK(n, "dn_expand");
X					if (all)
X						n = cname_get(&msg, &nsrr, -1);
X					else
X						need_newline = n = 0;
X				} else if (ns_rr_type(nsrr) != rr->type) {
X					printf("? %d", ns_rr_type(nsrr));
X					n = 1;
X				} else {
X					n = rr->get(&msg, &nsrr, fields);
X				}
X			} else {
X				n = rrget(&msg, &nsrr);
X			}
X			if (need_newline)
X				putchar('\n');
X			if (n != 0)
X				return (n);
X		}
X	}
X	return (0);
X}
X
Xstatic int
Xrrget(ns_msg *msg, ns_rr *nsrr) {
X	struct rr *rr;
X
X	for (rr = rrs; rr->name != NULL; rr++)
X		if (rr->type == ns_rr_type(*nsrr))
X			return (rr->get(msg, nsrr, -1));	/* all ones */
X	putchar('?');
X	return (0);
X}
X
X/* Private - SOA. */
X
Xstatic int
Xsoa_get(ns_msg *msg, ns_rr *rr, int fields) {
X	const u_char *base = ns_msg_base(*msg);
X	const u_char *end = ns_msg_end(*msg);
X	const u_char *rdata = ns_rr_rdata(*rr);
X	char domain[NS_MAXDNAME];
X	int n, printed = 0;
X	u_long l;
X
X	/* Do SOA MNAME. */
X	n = dn_expand(base, end, rdata, domain, sizeof domain);
X	CHK(n, "dn_expand");
X	rdata += n;
X	if (ISSET(soa_mname, fields)) {
X		fputs(domain, stdout);
X		printed = 1;
X	}
X
X	/* Do SOA RNAME. */
X	n = dn_expand(base, end, rdata, domain, sizeof domain);
X	CHK(n, "dn_expand");
X	rdata += n;
X	if (ISSET(soa_rname, fields)) {
X		if (printed)
X			fputc(' ', stdout);
X		fputs(domain, stdout);
X		printed = 1;
X	}
X
X	/* Check length all at once. */
X	if (end - rdata < 5 * NS_INT32SZ) {
X		fprintf(stderr, "soa rdlength too short\n");
X		return (1);
X	}
X
X	/* Do SOA SERIAL. */
X	l = ns_get32(rdata); rdata += NS_INT32SZ;
X	if (ISSET(soa_serial, fields)) {
X		fprintf(stdout, "%s%lu", printed ? " " : "", l);
X		printed = 1;
X	}
X
X	/* Do SOA REFRESH. */
X	l = ns_get32(rdata); rdata += NS_INT32SZ;
X	if (ISSET(soa_refresh, fields)) {
X		fprintf(stdout, "%s%lu", printed ? " " : "", l);
X		printed = 1;
X	}
X
X	/* Do SOA RETRY. */
X	l = ns_get32(rdata); rdata += NS_INT32SZ;
X	if (ISSET(soa_retry, fields)) {
X		fprintf(stdout, "%s%lu", printed ? " " : "", l);
X		printed = 1;
X	}
X
X	/* Do SOA EXPIRE. */
X	l = ns_get32(rdata); rdata += NS_INT32SZ;
X	if (ISSET(soa_expire, fields)) {
X		fprintf(stdout, "%s%lu", printed ? " " : "", l);
X		printed = 1;
X	}
X
X	/* Do SOA MINIMUM. */
X	l = ns_get32(rdata); rdata += NS_INT32SZ;
X	if (ISSET(soa_minimum, fields)) {
X		fprintf(stdout, "%s%lu", printed ? " " : "", l);
X		printed = 1;
X	}
X
X	return (0);
X}
X
X/* Private - NS. */
X
Xstatic int
Xns_get(ns_msg *msg, ns_rr *rr, int fields) {
X	const u_char *base = ns_msg_base(*msg);
X	const u_char *end = ns_msg_end(*msg);
X	const u_char *rdata = ns_rr_rdata(*rr);
X	char domain[NS_MAXDNAME];
X	int n;
X
X	/* Do NS NSDNAME. */
X	n = dn_expand(base, end, rdata, domain, sizeof domain);
X	CHK(n, "dn_expand");
X	rdata += n;
X	if (ISSET(ns_nsdname, fields))
X		fputs(domain, stdout);
X
X	return (0);
X}
X
X/* Private - A. */
X
Xstatic int
Xa_get(ns_msg *msg, ns_rr *rr, int fields) {
X	const u_char *end = ns_msg_end(*msg);
X	const u_char *rdata = ns_rr_rdata(*rr);
X	struct in_addr addr;
X
X	/* Do A ADDRESS. */
X	if (end - rdata < NS_INT32SZ) {
X		fprintf(stderr, "a rdlength too short\n");
X		return (1);
X	}
X	addr.s_addr = htonl(ns_get32(rdata)); rdata += NS_INT32SZ;
X	if (ISSET(a_address, fields))
X		printf("%s", inet_ntoa(addr));
X
X	return (0);
X}
X
X/* Private - AAAA. */
X
Xstatic int
Xaaaa_get(ns_msg *msg, ns_rr *rr, int fields) {
X	const u_char *end = ns_msg_end(*msg);
X	const u_char *rdata = ns_rr_rdata(*rr);
X	const void *addr;
X
X	/* Do AAAA ADDRESS. */
X	if (end - rdata < 16) {
X		fprintf(stderr, "a rdlength too short\n");
X		return (1);
X	}
X	addr = rdata;  rdata += 16;
X	if (ISSET(a_address, fields)) {
X		char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
X		(void) inet_ntop(AF_INET6, addr, t, sizeof t);
X		printf("%s", t);
X	}
X
X	return (0);
X}
X
X/* Private - MX. */
X
Xstatic int
Xmx_get(ns_msg *msg, ns_rr *rr, int fields) {
X	const u_char *base = ns_msg_base(*msg);
X	const u_char *end = ns_msg_end(*msg);
X	const u_char *rdata = ns_rr_rdata(*rr);
X	char domain[NS_MAXDNAME];
X	int n, printed = 0;
X	u_long l;
X
X	/* Do MX PREFERENCE. */
X	if (end - rdata < NS_INT16SZ) {
X		fprintf(stderr, "mx rdlength too short\n");
X		return (1);
X	}
X	l = ns_get16(rdata); rdata += NS_INT16SZ;
X	if (ISSET(mx_preference, fields))
X		printf("%s%lu", printed++ ? " " : "", l);
X
X	/* Do MX EXCHANGE. */
X	n = dn_expand(base, end, rdata, domain, sizeof domain);
X	CHK(n, "dn_expand");
X	rdata += n;
X	if (ISSET(mx_exchange, fields))
X		printf("%s%s", printed++ ? " " : "", domain);
X
X	return (0);
X}
END-of-dnsget.c
echo x - soacheck.sh
sed 's/^X//' >soacheck.sh << 'END-of-soacheck.sh'
X#!/bin/sh
X
Xzone=$1
Xmaster=$2
X
Xdnsget -a -s $master $zone ns | {
X	read result
X	case "$result" in
X	NOERROR*)
X		glue=`awk '
X			/^ANSWER/	{ ns[$4] = 1 }
X			/^ADDITIONAL/	{ if (ns[$2]) {
X						if (a[$2]) a[$2] = a[$2] ",";
X						a[$2] = a[$2] $4
X					} }
X			END		{ for (x in a) print a[x] }
X		'`
X		;;
X	*)
X		echo $0: cannot get initial NS RRset - $result
X		exit 1
X	esac
X	for ns in $glue; do
X		echo "$ns: " `dnsget -s $ns $zone soa serial`
X	done
X}
X
Xexit 0
END-of-soacheck.sh
exit
-- 
Paul Vixie

-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.



More information about the bind-users mailing list