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