From: Miek Gieben Date: Wed, 31 Aug 2005 08:29:34 +0000 (+0000) Subject: added drill-trunk to ldns - rm-ed the old .svn dir btw X-Git-Tag: release-1.0.0~226 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc98c1b84c771300a672201c1e5f130d9dc50a47;p=thirdparty%2Fldns.git added drill-trunk to ldns - rm-ed the old .svn dir btw --- diff --git a/drill/ChangeLog b/drill/ChangeLog new file mode 100644 index 00000000..fef63ffe --- /dev/null +++ b/drill/ChangeLog @@ -0,0 +1,99 @@ +1.0-pre3: to be released: drill-team + * Secure tracing works + * Added section about DNSSEC in the manual page + * Allow the class information to be given to do_chase() + * Lint fixes for the code + * Bugzilla was setup for drill + * Bug #97 (drill); -S crash was fixed + * Add -Q (quiet) flag was added. This supresses output from drill. + +1.0-pre2: 20 Jun 2005: drill-team + * Second prerelease + * Bugs where fix in the chasing functionality + +1.0-pre1: 1 Jun 2005: drill-team + * First drill release based on ldns + * drill's core code is not much more simple, as + all the difficult stuff is moved to ldns. + * Much saner argument parsing + +---------- Above Newer drill based on ldns -------------- +---------- Below Older drill with it's own DNS handling -------------- + +0.9.2: Feb 3 2005: drill-team + * Added two more options (borrowed from dig) + --rd, don't set the RD bit in queries + --fail, don't query the next nameserver on SERVFAIL + * Fixed handling of obscure data types + * Handle classes other the 'IN' when making a query + + * For people using FreeBSD: drill is now in the ports + (Thanks to Jaap Akkerhuis) + +0.9.1: Jan 5 2005: drill-team + * Makefile tweaks + * drill ns . works + * re-check the root in when tracing + * added handling for some lesser known types (including WKS) + +0.9: Dec 6 2004: drill-team + * big configure.ac and Makefile.in updates (made more general) + * escapes in names argument and txt and dname data + * gcc 2(.95) support + * packet wire data is now checked for dangerous elements (like + looping compression etc) + * (Multiple) Octal char representation + * Responses can be saved to file + * 'Answers' can be read from file instead of server + * Lots and lots of bugfixes and improvements + +0.8.1: Oct 27 2004: Miek + * configure.ac updates + * secure resolving updates (still doesn't work) + * printing additions + - CERT RR supported + - LOC RR support + * All non supported RRs are handled as unknown + * If no namservers found in /etc/resolv.conf + default to 127.0.0.1 + * Various bugs fixed + - Close sockets after using them + - Some memory leaks were plugged + +0.8: Oct 26 2004: Miek + * Lots of features added. Drill is almost feature complete + * Unknown RR's are supported + * Numerous smaller updates in documentation + * Numerous code cleanups + * Dig is no longer needed to build drill + +0.7: Oct 21 2004: Miek + * reworked interal code + * DNSSEC is working, except the secure resolving + * build updates + * more sane options parsing + * more sane argument handling + +0.6-alpha: Oct 2004: Jelte + * No log + +0.5-alpha: Sept 22 2004: Miek + * most of the DNS stuff is working + * moved to configure + * tested on Linux/FreeBSD + * fully IPV6 capable + * new DNSSEC types supported + * DNSSEC somewhat working + * gcc => 3 is needed for building + +0.4-alpha: Sept 9 2004: Miek + * moved to autoconf for building + * lots of various updates + * really a workable program now + +0.3-alpha: Sept 6 2004: Miek + * IPv6 support + * automatic secure resolving + * --trace updates + * --chase updates + * more checks diff --git a/drill/Makefile.in b/drill/Makefile.in new file mode 100644 index 00000000..157bd4cf --- /dev/null +++ b/drill/Makefile.in @@ -0,0 +1,104 @@ +# Standard installation pathnames +# See the file LICENSE for the license +SHELL = @SHELL@ +VERSION = @PACKAGE_VERSION@ +basesrcdir = $(shell basename `pwd`) +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ +includedir = @INCLUDEDIR@ + +LDNSDIR = @LDNSDIR@ +CC = @CC@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +INSTALL = $(srcdir)/install-sh -c +INSTALL_PROGRAM = $(INSTALL) + +COMPILE = $(CC) -Wall $(CPPFLAGS) $(CFLAGS) +LINK = $(CC) $(CFLAGS) $(LDFLAGS) + +LINT = splint +LINTFLAGS = +quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned -Du_char=uint8_t + +LIBOBJ=drill.o drill_util.o error.o root.o work.o chasetrace.o dnssec.o +LIBSRC=$(LIBOBJ:.o=.c) + +HEADER=drill.h drill_util.h + +.PHONY: all clean allclean docclean doc release tags install + +all: drill + +release: clean docclean tarclean + (rm -rf ../drill-$(VERSION)/) + (cd .. ; cp -r $(basesrcdir)/ drill-$(VERSION)/) + (cd .. ; tar --verbose --exclude ".svn" --create --gzip --file drill-$(VERSION).tar.bz2 drill-$(VERSION)/) + (rm -rf ../drill-$(VERSION)/) + +tags: + ctags *.[ch] + +drill: $(LIBOBJ) + $(LINK) -o $@ $(LIBOBJ) $(LIBS) + +## implicit rule +%.o: %.c $(HEADER) + $(COMPILE) -c $< + +clean: + rm -f *.o + rm -f drill + rm -f *core + rm -f config.h.in~ + rm -f config.log + rm -f config.guess + rm -f config.status + +docclean: + rm -rf doxydoc + +allclean: clean docclean + rm -f tags + rm -f config.log + rm -f config.status + rm -rf autom4te.cache + rm -f config.h + rm -f config.h.in + rm -f drill.h + rm -f configure + rm -f Makefile + rm -f aclocal.m4 + +# exclude configure here +tarclean: + rm -f tags + rm -f config.log + rm -f config.status + rm -rf autom4te.cache + rm -f config.h + rm -f drill.h + rm -f Makefile + +doc: + doxygen drill.doxygen + +install: all + $(INSTALL) -d $(DESTDIR)$(bindir) + $(INSTALL) drill $(DESTDIR)$(bindir)/drill + $(INSTALL) -m 644 drill.1 $(DESTDIR)$(mandir)/man1/drill.1 + +uninstall: + @echo + rm -f -- $(bindir)/drill + rm -f -- $(mandir)/man1/drill.1 + @echo + +lint: + @for i in $(LIBSRC) ; do \ + $(LINT) $(LINTFLAGS) -I$(LDNSDIR) -I$(srcdir) $(srcdir)/$$i ; \ + if [ $$? -ne 0 ] ; then exit 1 ; fi ; \ + done diff --git a/drill/README b/drill/README new file mode 100644 index 00000000..28ce1000 --- /dev/null +++ b/drill/README @@ -0,0 +1,11 @@ +QUICK INSTALL GUIDE + +* First install ldns + http://www.nlnetlabs.nl/ldns + + and follow the instructions + +* Now configure drill and compile it: + autoreconf && ./configure --with-ldns && make + + or --with-ldns=, also see ./configure --help diff --git a/drill/REGRESSIONS b/drill/REGRESSIONS new file mode 100644 index 00000000..b8f6be9c --- /dev/null +++ b/drill/REGRESSIONS @@ -0,0 +1,25 @@ +REGRESSIONS + +This version of drill is based on ldns and as such some things +are slightly changed. This file documents the changes. + +o When tracing (-T option) we use the local resolver (as specified + in /etc/resolv.conf) to lookup names. This increases the speed + dramatically, but you obviously need to be able to reach a recursive + server/cache. + Previously drill would try to resolve the names by itself. + +o Printing of DSs after DNSKEY records. Because we don't parse our + own packets anymore, we cannot print the DS directly after the DNSKEY + record. The DSs are now printed AFTER the packet. + +o The long options are removed. + +o The chase function has a different output, and will be subject to change + in the near future. + +o The useless (for jokes only) -I option was dropped. + +FIXED: +o the argument parsing is much smarter, the order doesn't matter (much) + anymore diff --git a/drill/chasetrace.c b/drill/chasetrace.c new file mode 100644 index 00000000..41e8a4ee --- /dev/null +++ b/drill/chasetrace.c @@ -0,0 +1,419 @@ +/* + * chasetrace.c + * Where all the hard work concerning chasing + * and tracing is done + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/** + * trace down from the root to name + */ + +/* same naive method as in drill0.9 + * We resolver _ALL_ the names, which is ofcourse not needed + * We _do_ use the local resolver to do that, so it still is + * fast, but it can be made to run much faster + */ +ldns_pkt * +do_trace(ldns_resolver *local_res, ldns_rdf *name, ldns_rr_type t, + ldns_rr_class c) +{ + ldns_resolver *res; + ldns_pkt *p; + ldns_rr_list *new_nss_a; + ldns_rr_list *new_nss_aaaa; + ldns_rr_list *final_answer; + ldns_rr_list *new_nss; + ldns_rr_list *hostnames; + ldns_rr_list *ns_addr; + uint16_t loop_count; + ldns_rdf *pop; + ldns_status status; + size_t i; + + res = ldns_resolver_new(); + /* transfer some properties of local_res to res, + * because they were given on the commandline */ + ldns_resolver_set_ip6(res, + ldns_resolver_ip6(local_res)); + ldns_resolver_set_port(res, + ldns_resolver_port(local_res)); + ldns_resolver_set_debug(res, + ldns_resolver_debug(local_res)); + ldns_resolver_set_dnssec(res, + ldns_resolver_dnssec(local_res)); + ldns_resolver_set_fail(res, + ldns_resolver_fail(local_res)); + ldns_resolver_set_usevc(res, + ldns_resolver_usevc(local_res)); + ldns_resolver_set_random(res, + ldns_resolver_random(local_res)); + + loop_count = 0; + new_nss_a = NULL; + new_nss_aaaa = NULL; + new_nss = NULL; + ns_addr = NULL; + final_answer = NULL; + p = ldns_pkt_new(); + + /* setup the root nameserver in the new resolver */ + if (ldns_resolver_push_nameserver_rr_list(res, global_dns_root) != LDNS_STATUS_OK) { + return NULL; + } + + /* this must be a real query to local_res */ + status = ldns_resolver_send(&p, local_res, ldns_dname_new_frm_str("."), LDNS_RR_TYPE_NS, c, 0); + + if (status == LDNS_STATUS_OK) { + drill_pkt_print(stdout, local_res, p); + } else { + printf("cannot use local resolver\n"); + return NULL; + } + + status = ldns_resolver_send(&p, res, name, t, c, 0); + + while(status == LDNS_STATUS_OK && + ldns_pkt_reply_type(p) == LDNS_PACKET_REFERRAL) { + + if (!p) { + /* some error occurred, bail out */ + return NULL; + } + + new_nss_a = ldns_pkt_rr_list_by_type(p, + LDNS_RR_TYPE_A, LDNS_SECTION_ADDITIONAL); + new_nss_aaaa = ldns_pkt_rr_list_by_type(p, + LDNS_RR_TYPE_AAAA, LDNS_SECTION_ADDITIONAL); + new_nss = ldns_pkt_rr_list_by_type(p, + LDNS_RR_TYPE_NS, LDNS_SECTION_AUTHORITY); + + if (qdebug != -1) { + ldns_rr_list_print(stdout, new_nss); + } + /* checks itself for qdebug */ + drill_pkt_print_footer(stdout, local_res, p); + + /* remove the old nameserver from the resolver */ + while((pop = ldns_resolver_pop_nameserver(res))) { /* do it */ } + + if (!new_nss_aaaa && !new_nss_a) { + /* + * no nameserver found!!! + * try to resolve the names we do got + */ + for(i = 0; i < ldns_rr_list_rr_count(new_nss); i++) { + /* get the name of the nameserver */ + pop = ldns_rr_rdf(ldns_rr_list_rr(new_nss, i), 0); + /* retrieve it's addresses */ + ns_addr = ldns_rr_list_cat_clone(ns_addr, + ldns_get_rr_list_addr_by_name(local_res, pop, c, 0)); + } + if (ns_addr) { + if (ldns_resolver_push_nameserver_rr_list(res, ns_addr) != + LDNS_STATUS_OK) { + error("Error adding new nameservers"); + ldns_pkt_free(p); + return NULL; + } + } else { + error("%s", "Could not find the ip addr; abort"); + ldns_pkt_free(p); + return NULL; + } + } + + /* add the new ones */ + if (new_nss_aaaa) { + if (ldns_resolver_push_nameserver_rr_list(res, new_nss_aaaa) != + LDNS_STATUS_OK) { + error("%s", "adding new nameservers"); + ldns_pkt_free(p); + return NULL; + } + } + if (new_nss_a) { + if (ldns_resolver_push_nameserver_rr_list(res, new_nss_a) != + LDNS_STATUS_OK) { + error("adding new nameservers"); + ldns_pkt_free(p); + return NULL; + } + } + + if (loop_count++ > 20) { + /* unlikely that we are doing something usefull */ + error("Looks like we are looping"); + ldns_pkt_free(p); + return NULL; + } + + status = ldns_resolver_send(&p, res, name, t, c, 0); + new_nss_aaaa = NULL; + new_nss_a = NULL; + ns_addr = NULL; + } + /* mesg("Came out of recursion"); */ + + status = ldns_resolver_send(&p, res, name, t, c, 0); + + if (!p) { + return NULL; + } + + hostnames = ldns_get_rr_list_name_by_addr(local_res, + ldns_pkt_answerfrom(p), 0, 0); + + new_nss = ldns_pkt_authority(p); + final_answer = ldns_pkt_answer(p); + + if (qdebug != -1) { + ldns_rr_list_print(stdout, final_answer); + ldns_rr_list_print(stdout, new_nss); + + } + drill_pkt_print_footer(stdout, local_res, p); + ldns_pkt_free(p); + return NULL; +} + + +/* do a secure trace from the root down to the local leaf node + * requested. + * + * this alg is complicated. We try to do it all in one go, resolving + * and keeping track of the security information + * + * update this some more. Miek; TODO + */ +ldns_status +do_secure_trace(ldns_resolver *local_res, ldns_rdf *name, ldns_rr_type t, + ldns_rr_class c, ldns_rr_list *trusted_keys) +{ + ldns_resolver *res; + ldns_pkt *p; + + ldns_rdf *cur_zone; + uint16_t loop_count; + ldns_rr_list *keys; + ldns_status status; + bool secure; + + /* always as for dnskey records, but when secure is true also + * ask for DS + */ + + secure = true; + cur_zone = ldns_dname_new_frm_data(1, "."); + res = ldns_resolver_new(); + + /* transfer some properties of local_res to res, + * because they were given on the commandline */ + ldns_resolver_set_ip6(res, + ldns_resolver_ip6(local_res)); + ldns_resolver_set_port(res, + ldns_resolver_port(local_res)); + ldns_resolver_set_debug(res, + ldns_resolver_debug(local_res)); + ldns_resolver_set_dnssec(res, + ldns_resolver_dnssec(local_res)); + ldns_resolver_set_fail(res, + ldns_resolver_fail(local_res)); + ldns_resolver_set_usevc(res, + ldns_resolver_usevc(local_res)); + ldns_resolver_set_random(res, + ldns_resolver_random(local_res)); + + loop_count = 0; + p = ldns_pkt_new(); + + /* setup the root nameserver in the new resolver */ + if (ldns_resolver_push_nameserver_rr_list(res, global_dns_root) != LDNS_STATUS_OK) { + return LDNS_STATUS_ERR; + } + + cur_zone = ldns_dname_new_frm_str("."); + + /* this must be a real query to local_res */ + status = ldns_resolver_send(&p, local_res, cur_zone, LDNS_RR_TYPE_NS, c, 0); + + if (status == LDNS_STATUS_OK) { + drill_pkt_print(stdout, local_res, p); + } else { + printf("cannot use local resolver\n"); + return LDNS_STATUS_ERR; + } + /* next ask the for keys */ + keys = get_rr(res, cur_zone, LDNS_RR_TYPE_DNSKEY, c); + if (keys) { + ldns_rr_list_print(stdout, keys); + } else { + secure = false; + } + if (secure) { + /* if true get the DS for the child zone */ + } + + /* we can now start our decent to the zone we're looking + * and query for ds/dnskey along the way + */ + + + return LDNS_STATUS_ERR; /* need to finish this function */ +} + +/** + * Chase the given rr to a known key + * + * Based on drill 0.9 + * pkt optional? + * TODO: lots + keys + status codes + helper functions (for instance for rrset owner retrieval + rest ;) + if this is moved to the library, status codes should be added and prints removed + */ +ldns_status +do_chase(ldns_resolver *res, ldns_rdf *name, ldns_rr_type type, ldns_rr_class c, + ldns_rr_list *trusted_keys, ldns_pkt *pkt_o, uint16_t qflags) +{ + ldns_rr_list *rrset = NULL; + ldns_status result; + + ldns_rr_list *sigs; + ldns_rr *cur_sig; + uint16_t sig_i; + ldns_rr_list *keys; + uint16_t key_i; + uint16_t tkey_i; + ldns_pkt *pkt; + + pkt = ldns_pkt_clone(pkt_o); + + if (!name) { + mesg("no name to chase\n"); + ldns_pkt_free(pkt); + return LDNS_STATUS_EMPTY_LABEL; + } + + if (pkt) { + rrset = ldns_pkt_rr_list_by_name_and_type(pkt, + name, + type, + LDNS_SECTION_ANSWER + ); + } else { + /* no packet? */ + return LDNS_STATUS_MEM_ERR; + } + + if (!rrset) { + /* not found in original packet, try again */ + ldns_pkt_free(pkt); + pkt = NULL; + pkt = ldns_resolver_query(res, name, type, c, qflags); + + if (!pkt) { + return LDNS_STATUS_NETWORK_ERR; + } + rrset = ldns_pkt_rr_list_by_name_and_type(pkt, + name, + type, + LDNS_SECTION_ANSWER + ); + } + + sigs = ldns_pkt_rr_list_by_name_and_type(pkt, + name, + LDNS_RR_TYPE_RRSIG, + LDNS_SECTION_ANY_NOQUESTION + ); + + for (sig_i = 0; sig_i < ldns_rr_list_rr_count(sigs); sig_i++) { + cur_sig = ldns_rr_clone(ldns_rr_list_rr(sigs, sig_i)); + + keys = ldns_pkt_rr_list_by_name_and_type(pkt, + ldns_rr_rdf(cur_sig, 7), + LDNS_RR_TYPE_DNSKEY, + LDNS_SECTION_ANY_NOQUESTION + ); + + if (qdebug != -1) { + printf(";; Signed by: "); + ldns_rdf_print(stdout, ldns_rr_rdf(cur_sig, 7)); + printf("\n"); + } + + if (!keys) { + ldns_pkt_free(pkt); + pkt = NULL; + pkt = ldns_resolver_query(res, + ldns_rr_rdf(cur_sig, 7), + LDNS_RR_TYPE_DNSKEY, c, qflags); + if (!pkt) { + ldns_rr_list_deep_free(rrset); + ldns_rr_list_deep_free(sigs); + return LDNS_STATUS_NETWORK_ERR; + } + + keys = ldns_pkt_rr_list_by_name_and_type(pkt, + ldns_rr_rdf(cur_sig, 7), + LDNS_RR_TYPE_DNSKEY, + LDNS_SECTION_ANY_NOQUESTION + ); + } + if(!keys) { + mesg("No key for data found in that zone!\n"); + ldns_rr_list_deep_free(rrset); + ldns_rr_list_deep_free(sigs); + ldns_pkt_free(pkt); + ldns_rr_free(cur_sig); + return LDNS_STATUS_CRYPTO_NO_DNSKEY; + } else { + for (key_i = 0; key_i < ldns_rr_list_rr_count(keys); key_i++) { + if (ldns_verify_rrsig(rrset, cur_sig, ldns_rr_list_rr(keys, key_i))) { + for (tkey_i = 0; tkey_i < ldns_rr_list_rr_count(trusted_keys); tkey_i++) { + if (ldns_rr_compare_ds(ldns_rr_list_rr(keys, key_i), + ldns_rr_list_rr(trusted_keys, tkey_i) + )) { + mesg("Key is trusted\n"); + ldns_rr_list_deep_free(rrset); + ldns_rr_list_deep_free(sigs); + ldns_rr_list_deep_free(keys); + ldns_pkt_free(pkt); + ldns_rr_free(cur_sig); + return LDNS_STATUS_OK; + } + } + result = do_chase(res, ldns_rr_rdf(cur_sig, 7), LDNS_RR_TYPE_DS, c, trusted_keys, pkt, qflags); + ldns_rr_list_deep_free(rrset); + ldns_rr_list_deep_free(sigs); + ldns_rr_list_deep_free(keys); + ldns_pkt_free(pkt); + ldns_rr_free(cur_sig); + return result; + } else { + mesg("Bad signature of wrong key\n"); + } + } + ldns_rr_list_deep_free(keys); + } + ldns_rr_free(cur_sig); + } + ldns_rr_list_deep_free(rrset); + ldns_pkt_free(pkt); + if (ldns_rr_list_rr_count(sigs) > 0) { + ldns_rr_list_deep_free(sigs); + return LDNS_STATUS_CRYPTO_NO_TRUSTED_DNSKEY; + } else { + return LDNS_STATUS_CRYPTO_NO_RRSIG; + } +} diff --git a/drill/configure.ac b/drill/configure.ac new file mode 100644 index 00000000..dc6e95f7 --- /dev/null +++ b/drill/configure.ac @@ -0,0 +1,89 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) +AC_INIT(drill, 1.0-pre3, drill@nlnetlabs.nl, drill1.0-pre) +AC_CONFIG_SRCDIR([drill_util.h]) + +AC_AIX +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET + +# Checks for libraries. +# Checks for header files. +#AC_HEADER_STDC +#AC_HEADER_SYS_WAIT +# do the very minimum - we can always extend this +AC_CHECK_HEADERS([getopt.h stdlib.h stdio.h assert.h netinet/in.hctype.h]) +AC_CHECK_HEADERS(sys/param.h sys/mount.h,,, +[ + [ + #if HAVE_SYS_PARAM_H + # include + #endif + ] +]) + +# ripped from http://autoconf-archive.cryp.to/check_ssl.html +# check for ldns +AC_ARG_WITH(ldns, AC_HELP_STRING([--with-ldns=PATHNAME],[])) +AC_MSG_CHECKING(for ldns/dns.h) +#echo "[" $withval "]" +for dir in $withval /usr/local/ldns /usr/lib/ldns /usr/ldns /usr/pkg /usr/local /usr; do +ldnsdir="$dir" +#echo "$dir/include/ldns/dns.h" +#echo "$dir/ldns/dns.h" +if test -f "$dir/include/ldns/dns.h"; then + found_ldns="yes"; + LDNSDIR="$ldnsdir/include/" + CFLAGS="$CFLAGS -I$LDNSDIR -DHAVE_LDNS"; + CXXFLAGS="$CXXFLAGS -I$ldnsdir/include/ -DHAVE_LDNS"; + break; +fi +if test -f "$dir/ldns/dns.h"; then + found_ldns="yes"; + LDNSDIR="$ldnsdir/" + CFLAGS="$CFLAGS -I$LDNSDIR -DHAVE_LDNS"; + CXXFLAGS="$CXXFLAGS -I$ldnsdir/ -DHAVE_LDNS"; + break +fi +done +if test x_$found_ldns != x_yes; then + AC_MSG_RESULT(no) + AC_MSG_ERROR(Cannot find ldns libraries) +else +# printf "ldns found in $ldnsdir\n"; + LIBS="$LIBS -lldns"; + LDFLAGS="$LDFLAGS -L$ldnsdir/lib"; + LDFLAGS="$LDFLAGS -L$ldnsdir/.libs"; # hack for dev. + HAVE_LDNS=yes + AC_MSG_RESULT(yes) +fi +AC_SUBST(HAVE_LDNS) +AC_SUBST(LDNSDIR, [$LDNSDIR]) + +# I don't use these +# Checks for typedefs, structures, and compiler characteristics. +#AC_TYPE_UID_T +#AC_TYPE_MODE_T +#AC_TYPE_OFF_T +#AC_TYPE_SIZE_T +#AC_STRUCT_TM + +# Checks for library functions. +# check for ldns +#AC_FUNC_CHOWN +#AC_FUNC_FORK +#AC_FUNC_MALLOC +#AC_FUNC_MKTIME +#AC_FUNC_STAT +#AC_CHECK_FUNCS([mkdir rmdir strchr strrchr strstr]) + +#AC_DEFINE_UNQUOTED(SYSCONFDIR, "$sysconfdir") + +AC_CONFIG_FILES([Makefile + drill.h + ]) +AC_CONFIG_HEADER([config.h]) +AC_OUTPUT diff --git a/drill/dnssec.c b/drill/dnssec.c new file mode 100644 index 00000000..ee615cab --- /dev/null +++ b/drill/dnssec.c @@ -0,0 +1,91 @@ +/* + * dnssec.c + * Some DNSSEC helper function are defined here + * and tracing is done + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/* get rr_type from a server from a server */ +ldns_rr_list * +get_rr(ldns_resolver *res, ldns_rdf *zname, ldns_rr_type t, ldns_rr_class c) +{ + /* query, retrieve, extract and return */ + ldns_pkt *p; + ldns_rr_list *found; + + p = ldns_pkt_new(); + found = NULL; + + if (ldns_resolver_send(&p, res, zname, t, c, 0) != LDNS_STATUS_OK) { + /* oops */ + return NULL; + } else { + found = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANY_NOQUESTION); + } + return found; +} + +void +drill_pkt_print(FILE *fd, ldns_resolver *r, ldns_pkt *p) +{ + ldns_rr_list *new_nss; + ldns_rr_list *hostnames; + + if (qdebug == -1) { + return; + } + + hostnames = ldns_get_rr_list_name_by_addr(r, ldns_pkt_answerfrom(p), 0, 0); + + new_nss = ldns_pkt_rr_list_by_type(p, + LDNS_RR_TYPE_NS, LDNS_SECTION_ANSWER); + ldns_rr_list_print(fd, new_nss); + + /* new_nss can be empty.... */ + + fprintf(fd, ";; Received %d bytes from %s#%d(", + (int) ldns_pkt_size(p), + ldns_rdf2str(ldns_pkt_answerfrom(p)), + (int) ldns_resolver_port(r)); + /* if we can resolve this print it, other print the ip again */ + if (hostnames) { + ldns_rdf_print(fd, + ldns_rr_rdf(ldns_rr_list_rr(hostnames, 0), 0)); + ldns_rr_list_deep_free(hostnames); + } else { + fprintf(fd, "%s", ldns_rdf2str(ldns_pkt_answerfrom(p))); + } + fprintf(fd, ") in %u ms\n\n", (unsigned int)ldns_pkt_querytime(p)); +} + +void +drill_pkt_print_footer(FILE *fd, ldns_resolver *r, ldns_pkt *p) +{ + ldns_rr_list *hostnames; + + if (qdebug == -1) { + return; + } + + hostnames = ldns_get_rr_list_name_by_addr(r, ldns_pkt_answerfrom(p), 0, 0); + + fprintf(fd, ";; Received %d bytes from %s#%d(", + (int) ldns_pkt_size(p), + ldns_rdf2str(ldns_pkt_answerfrom(p)), + (int) ldns_resolver_port(r)); + /* if we can resolve this print it, other print the ip again */ + if (hostnames) { + ldns_rdf_print(fd, + ldns_rr_rdf(ldns_rr_list_rr(hostnames, 0), 0)); + ldns_rr_list_deep_free(hostnames); + } else { + fprintf(fd, "%s", ldns_rdf2str(ldns_pkt_answerfrom(p))); + } + fprintf(fd, ") in %u ms\n\n", (unsigned int)ldns_pkt_querytime(p)); +} diff --git a/drill/drill.1 b/drill/drill.1 new file mode 100644 index 00000000..d8d17992 --- /dev/null +++ b/drill/drill.1 @@ -0,0 +1,181 @@ +.\" @(#)drill.1 1.7.0 14-Jul-2004 OF; +.TH drill 1 "28 Apr 2005" +.SH NAME +drill \- get (debug) information out of DNS(SEC) +.SH SYNOPSIS +.B drill +[ +.IR OPTION +] +.IR name +[ +.IR @server +] +[ +.IR type +] +[ +.IR class +] + +.SH DESCRIPTION +\fBdrill\fR is a tool to designed to get all sorts of information out of the +DNS. It is specificly designed to be used with DNSSEC. +.PP +The name \fBdrill\fR is a pun on \fBdig\fR. With \fBdrill\fR you should be able +get even more information than with \fBdig\fR. +.PP +The arguments to \fBdrill\fR may be placed in any order. If no arguments +are given class defaults to 'IN' and type to 'A'. The server(s) specified +in /etc/resolv.conf are used to query against. + +.PP +\fI@server\fR +Send to query to this server. If not specified use the nameservers from +\fI/etc/resolv.conf\fR. + +.PP +\fItype\fR +Ask for this RR type. If type is not given on the command line it defaults +to 'A'. Except when doing to reverse lookup there is defaults to 'PTR'. + +.PP +\fIname\fR +Ask for this name. + +.PP +\fIclass\fR +Use this class when querying. + +.SH SAMPLE USAGE +\fBdrill mx miek.nl\fR +Show the MX records of the domain miek.nl + +.TP +\fBdrill -S jelte.nlnetlabs.nl\fR +Chase any signatures a the jelte.nlnetlab.nl domain. + +.TP +\fBdrill -TD www.example.com\fR +Do a DNSSEC (-D) trace (-T) from the rootservers down to www.example.com. + +.TP +\fBdrill -s dnskey jelte.nlnetlabs.nl\fR +Show the DNSKEY record(s) for jelte.nlnetlabs.nl. For each found DNSKEY +record also print the DS record. + +.SH OPTIONS +.TP +\fB\-D +Enable DNSSEC in the query. When querying for DNSSEC types (DNSKEY, RRSIG, +DS and NSEC) this is automaticly enabled. + +.TP +\fB\-S +Chase the signature(s) of 'name' to a known key or as high up in +the tree as possible. + +.TP +\fB\-T +Trace \fIname\fR from the root down. When using this option the @server and +the type arguments are not used. + +.TP +\fB\-V +Be more verbose. Enable once for more messages on the screen. Enable twice +for a hexdump of the packets sent. + +.TP +\fB\-4 +Stay on ip4. Only send queries to ip4 enabled nameservers. + +.TP +\fB\-6 +Stay on ip6. Only send queries to ip6 enabled nameservers. + +.TP +\fB\-a +Don't try the next nameserver on SERVFAIL. The default is to do this. + +.TP +\fB\-b \fIsize\fR + + +.TP +\fB\-c +Use TCP/IP when querying a server. + + +.TP +\fB\-f \fIfile\fR +Read the query from a file. The query must be dumped with -w. + +.TP +\fB\-i \fIfile\fR +read the answer from the file instead from the network. This aids +in debugging and can be used to check if a query on disk is valid. +If the file contains binary data it is assumed to be a query in +network order. + +.TP +\fB\-k \fIkeyfile\fR +Use this file to read a (trusted) key from. When this options is +given \fBdrill\fR tries to validate the current answer with this +key. No chasing is done. + +.TP +\fB\-p \fIport\fR +Use this port instead of the DNS default of 53. + +.TP +\fB\-r +Don't set the RD bit in the query - the default is yes. + +.TP +\fB\-s +When encountering a DNSKEY print the DS also. + +.TP +\fB\-u +Use UDP when querying a server. This is the default. + +.TP +\fB\-v + +.TP +\fB\-w \fIfile\fR +write the answer to a file. The file will contain a hexadecimal dump +of the query. This can be used in conjunction with -f. + +.TP +\fB\-x +Do a reverse loopup. The type argument is not used, it is preset to PTR. + +.SH DNSSEC +When calling \fBdrill\fR with \fI-S\fR it chases down signatures (RRSIG) to +a known key. This uses a bottom-up approach. +[Jelte please fill in the blanks here] +.PP +With \fI-TD\fR (trace + DNSSEC) \fBdrill\fR will securely trace from the +root down. If the optional \fI-k\fR argument is given a genuine chain of +trust can be established. +[bla bla, Miek please add more] + +.SH AUTHOR +Jelte Jansen and Miek Gieben. Both of NLnet Labs. + +.SH REPORTING BUGS +Report bugs to . + +.SH BUGS + +.SH LIMITATIONS +None - you can do \fIeverything\fR with it, including washing your car. + +.SH COPYRIGHT +Copyright (c) 2004 NLnet Labs. +Licensed under the GPL 2. There is NO warranty; not even for MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. + +.SH SEE ALSO +\fBdig\fR(1), \fIRFC403{3,4,5}\fR. diff --git a/drill/drill.c b/drill/drill.c new file mode 100644 index 00000000..fd53e462 --- /dev/null +++ b/drill/drill.c @@ -0,0 +1,657 @@ +/* + * drill.c + * the main file of drill + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/* query debug, 2 hex dumps */ +int8_t qdebug; /* -1, be quiet, 1 show question, 2 show hex */ + +static void +usage(FILE *stream, const char *progname) +{ + fprintf(stream, " Usage: %s name [@server] [type] [class]\n", progname); + fprintf(stream, "\t can be a domain name or an IP address (-x lookups)\n"); + fprintf(stream, "\t defaults to A\n"); + fprintf(stream, "\t defaults to IN\n"); + fprintf(stream, "\n\targuments may be placed in random order\n"); + fprintf(stream, "\n Options:\n"); + fprintf(stream, "\t-D\t\tenable DNSSEC (DO bit)\n"); + fprintf(stream, "\t-T\t\ttrace from the root down to \n"); + fprintf(stream, "\t-S\t\tchase signature(s) from to a know key [*]\n"); + fprintf(stream, "\t-V\t\tverbose mode (once shows question, twice for hexdumps)\n"); + fprintf(stream, "\t-Q\t\tquiet mode (overrules -V)\n"); + fprintf(stream, "\n"); + fprintf(stream, "\t-f file\t\tread packet from file and send it\n"); + fprintf(stream, "\t-i file\t\tread packet from file and print it\n"); + fprintf(stream, "\t-w file\t\twrite answer packet to file\n"); + fprintf(stream, "\t-q file\t\twrite query packet to file\n"); + fprintf(stream, "\t-h\t\tshow this help\n"); + fprintf(stream, "\t-v\t\tshow version\n"); + fprintf(stream, "\n Query options:\n"); + fprintf(stream, "\t-4\t\tstay on ip4\n"); + fprintf(stream, "\t-6\t\tstay on ip6\n"); + fprintf(stream, "\t-a\t\tonly query the first nameserver (default is to try all)\n"); + fprintf(stream, "\t-b \tuse as the buffer size (defaults to 512 b)\n"); + fprintf(stream, "\t-c\t\tsend the query with tcp (connected)\n"); + fprintf(stream, "\t-k \tspecify a file that contains a trusted DNSSEC key [**]\n"); + fprintf(stream, "\t\t\tused to verify any signatures in the current answer\n"); + fprintf(stream, "\t-p \tuse as remote port number\n"); + fprintf(stream, "\t-r\t\tdon't set the RD bit in queries (default is on)\n"); + fprintf(stream, "\t-s\t\tshow the DS RR for each key in a packet\n"); + fprintf(stream, "\t-u\t\tsend the query with udp (the default)\n"); + fprintf(stream, "\t-x\t\tdo a reverse (PTR) lookup\n"); + fprintf(stream, "\t-y \tspecify named base64 tsig key, and optional an\n\t\t\talgorithm (defaults to hmac-md5.sig-alg.reg.int)\n"); + fprintf(stream, "\t-z\t\tdon't randomize the nameservers before use\n"); + fprintf(stream, "\n [*] = enables/implies DNSSEC\n"); + fprintf(stream, " [**] = can be given more than once\n"); + fprintf(stream, "\n drill@nlnetlabs.nl | www.nlnetlabs.nl/dnssec/drill.html\n"); +} + +/** + * Prints the drill version to stderr + */ +static void +version(FILE *stream, const char *progname) +{ + fprintf(stream, "%s version %s\n", progname, DRILL_VERSION); + fprintf(stream, "Written by NLnet Labs.\n"); + fprintf(stream, "\nCopyright (c) 2004, 2005 NLnet Labs.\n"); + fprintf(stream, "Licensed under the GPL 2.\n"); + fprintf(stream, "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"); + fprintf(stream, "FOR A PARTICULAR PURPOSE.\n"); +} + + +/** + * Main function of drill + * parse the arguments and prepare a query + */ +int +main(int argc, char *argv[]) +{ + ldns_resolver *res = NULL; + ldns_resolver *cmdline_res = NULL; /* only used to resolv @name names */ + ldns_rr_list *cmdline_rr_list = NULL; + ldns_rdf *cmdline_dname = NULL; + ldns_rdf *qname, *qname_tmp; + ldns_pkt *pkt; + ldns_pkt *qpkt; + char *serv; + char *name; + char *progname; + char *query_file = NULL; + char *answer_file = NULL; + ldns_rdf *serv_rdf; + ldns_rr_type type; + ldns_rr_class clas; + int i, c; + int int_type; + int int_clas; + int PURPOSE; + char *tsig_name = NULL; + char *tsig_data = NULL; + char *tsig_algorithm = NULL; + ldns_rr *dnssec_key = NULL; + size_t tsig_separator; + size_t tsig_separator2; + ldns_rr *axfr_rr; + + /* list of keys used in dnssec operations */ + ldns_rr_list *key_list = ldns_rr_list_new(); + /* what key verify the current answer */ + ldns_rr_list *key_verified; + + /* resolver options */ + uint16_t qflags; + uint16_t qbuf; + uint16_t qport; + uint8_t qfamily; + bool qdnssec; + bool qfail; + bool qds; + bool qusevc; + bool qrandom; + + int result = 0; + + int_type = -1; serv = NULL; type = 0; + int_clas = -1; name = NULL; clas = 0; + qname = NULL; + progname = strdup(argv[0]); + + PURPOSE = DRILL_QUERY; + qflags = LDNS_RD; + qport = LDNS_PORT; + qdebug = 0; + qdnssec = false; + qfamily = LDNS_RESOLV_INETANY; + qfail = false; + qds = false; + qbuf = 0; + qusevc = false; + qrandom = true; + key_verified = NULL; + + if (argc == 0) { + usage(stdout, progname); + result = EXIT_FAILURE; + goto exit; + } + + /* string from orig drill: "i:w:I46Sk:TNp:b:DsvhVcuaq:f:xr" */ + /* global first, query opt next, option with parm's last + * and sorted */ + while ((c = getopt(argc, argv, "46DITSVQf:i:w:q:achruvxzy:sp:b:k:")) != -1) { + switch(c) { + /* global options */ + case '4': + qfamily = LDNS_RESOLV_INET; + break; + case '6': + qfamily = LDNS_RESOLV_INET6; + break; + case 'D': + qdnssec = true; + break; + case 'I': + /* reserved for backward compatibility */ + break; + case 'T': + PURPOSE = DRILL_TRACE; + break; + case 'S': + PURPOSE = DRILL_CHASE; + break; + case 'V': + if (qdebug != -1) { + qdebug++; + } + break; + case 'Q': + qdebug = -1; + case 'f': + query_file = optarg; + break; + case 'i': + answer_file = optarg; + PURPOSE = DRILL_AFROMFILE; + break; + case 'w': + answer_file = optarg; + break; + case 'q': + query_file = optarg; + PURPOSE = DRILL_QTOFILE; + /* query options */ + case 'a': + qfail = true; + break; + case 'b': + qbuf = (uint16_t)atoi(optarg); + if (qbuf == 0) { + error("%s", " could not be converted"); + result = EXIT_FAILURE; + goto exit; + } + break; + case 'c': + qusevc = true; + break; + case 'k': + dnssec_key = read_key_file(optarg); + if (!dnssec_key) { + result = EXIT_FAILURE; + goto exit; + } + ldns_rr_list_push_rr(key_list, dnssec_key); + qdnssec = true; /* enable that too */ + break; + case 'p': + qport = (uint16_t)atoi(optarg); + if (qport == 0) { + error("%s", " could not be converted"); + result = EXIT_FAILURE; + goto exit; + } + break; + case 's': + qds = true; + break; + case 'r': + qflags = qflags & ~LDNS_RD; + break; + case 'u': + qusevc = false; + break; + case 'v': + version(stdout, progname); + result = EXIT_SUCCESS; + goto exit; + case 'x': + type = LDNS_RR_TYPE_PTR; + int_type = 1; /* set this so the type does not change */ + PURPOSE = DRILL_REVERSE; + break; + case 'y': + if (strchr(optarg, ':')) { + tsig_separator = (size_t) (strchr(optarg, ':') - optarg); + if (strchr(optarg + tsig_separator + 1, ':')) { + tsig_separator2 = (size_t) (strchr(optarg + tsig_separator + 1, ':') - optarg); + tsig_algorithm = xmalloc(strlen(optarg) - tsig_separator2); + strncpy(tsig_algorithm, optarg + tsig_separator2 + 1, strlen(optarg) - tsig_separator2); + tsig_algorithm[strlen(optarg) - tsig_separator2 - 1] = '\0'; + } else { + tsig_separator2 = strlen(optarg); + tsig_algorithm = xmalloc(26); + strncpy(tsig_algorithm, "hmac-md5.sig-alg.reg.int.", 25); + tsig_algorithm[25] = '\0'; + } + tsig_name = xmalloc(tsig_separator + 1); + tsig_data = xmalloc(tsig_separator2 - tsig_separator); + strncpy(tsig_name, optarg, tsig_separator); + strncpy(tsig_data, optarg + tsig_separator + 1, tsig_separator2 - tsig_separator - 1); + /* strncpy does not append \0 if source is longer than n */ + tsig_name[tsig_separator] = '\0'; + tsig_data[ tsig_separator2 - tsig_separator - 1] = '\0'; + } + break; + case 'z': + qrandom = false; + break; + case 'h': + default: + usage(stdout, progname); + result = EXIT_SUCCESS; + goto exit; + } + } + argc -= optind; + argv += optind; + + /* do a secure trace when requested */ + if (PURPOSE == DRILL_TRACE && qdnssec) { + if (ldns_rr_list_rr_count(key_list) == 0) { + warning("%s", "No keys were given. Will not be able to verify authenticity!"); + } + PURPOSE = DRILL_SECTRACE; + } + + /* parse the arguments, with multiple arguments, the last argument + * found is used */ + for(i = 0; i < argc; i++) { + + /* if ^@ then it's a server */ + if (argv[i][0] == '@') { + serv = argv[i] + 1; + continue; + } + /* if has a dot, it's a name */ + if (strchr(argv[i], '.')) { + name = argv[i]; + continue; + } + /* if it matches a type, it's a type */ + if (int_type == -1) { + type = ldns_get_rr_type_by_name(argv[i]); + if (type != 0) { + int_type = 0; + continue; + } + } + /* if it matches a class, it's a class */ + if (int_clas == -1) { + clas = ldns_get_rr_class_by_name(argv[i]); + if (clas != 0) { + int_clas = 0; + continue; + } + } + /* it all fails assume it's a name */ + name = argv[i]; + } + /* defaults if not given */ + if (int_clas == -1) { + clas = LDNS_RR_CLASS_IN; + } + if (int_type == -1) { + type = LDNS_RR_TYPE_A; + } + + /* if we're asking for DNSSEC record, act as if -D with given */ + if (type == LDNS_RR_TYPE_DNSKEY || + type == LDNS_RR_TYPE_RRSIG || + type == LDNS_RR_TYPE_NSEC) { + qdnssec = true; + } + + /* set the nameserver to use */ + if (!serv) { + /* no server given make a resolver from /etc/resolv.conf */ + res = ldns_resolver_new_frm_file(NULL); + if (!res) { + result = EXIT_FAILURE; + goto exit; + } + } else { + res = ldns_resolver_new(); + if (!res) { + result = EXIT_FAILURE; + goto exit; + } + /* add the nameserver */ + serv_rdf = ldns_rdf_new_addr_frm_str(serv); + if (!serv_rdf) { + /* try to resolv the name if possible */ + cmdline_res = ldns_resolver_new_frm_file(NULL); + + if (!cmdline_res) { + error("%s", "@server ip could not be converted"); + result = EXIT_FAILURE; + goto exit; + } + ldns_resolver_set_dnssec(cmdline_res, qdnssec); + ldns_resolver_set_ip6(cmdline_res, qfamily); + ldns_resolver_set_fail(cmdline_res, qfail); + ldns_resolver_set_usevc(cmdline_res, qusevc); + + cmdline_dname = ldns_dname_new_frm_str(serv); + cmdline_rr_list = ldns_get_rr_list_addr_by_name( + cmdline_res, + cmdline_dname, + clas, + qflags); + ldns_rdf_deep_free(cmdline_dname); + if (!cmdline_rr_list) { + error("%s", "could not find any address for the name"); + result = EXIT_FAILURE; + goto exit; + } else { + if (ldns_resolver_push_nameserver_rr_list( + res, + cmdline_rr_list + ) != LDNS_STATUS_OK) { + error("%s", "pushing nameserver"); + result = EXIT_FAILURE; + goto exit; + } + } + } else { + if (ldns_resolver_push_nameserver(res, serv_rdf) != LDNS_STATUS_OK) { + error("%s", "pushing nameserver"); + result = EXIT_FAILURE; + goto exit; + } else { + ldns_rdf_deep_free(serv_rdf); + } + } + } + /* set the resolver options */ + ldns_resolver_set_port(res, qport); + if (qdebug > 0 && qdebug != -1) { + ldns_resolver_set_debug(res, true); + } else { + ldns_resolver_set_debug(res, false); + } + ldns_resolver_set_dnssec(res, qdnssec); + ldns_resolver_set_dnssec_cd(res, qdnssec); + ldns_resolver_set_ip6(res, qfamily); + ldns_resolver_set_fail(res, qfail); + ldns_resolver_set_usevc(res, qusevc); + ldns_resolver_set_random(res, qrandom); + if (qbuf != 0) { + ldns_resolver_set_edns_udp_size(res, qbuf); + } + + if (!name && + PURPOSE != DRILL_AFROMFILE && + !query_file + ) { + usage(stdout, progname); + result = EXIT_FAILURE; + goto exit; + } + + if (tsig_name && tsig_data) { + ldns_resolver_set_tsig_keyname(res, tsig_name); + ldns_resolver_set_tsig_keydata(res, tsig_data); + ldns_resolver_set_tsig_algorithm(res, tsig_algorithm); + } + + /* main switching part of drill */ + switch(PURPOSE) { + case DRILL_TRACE: + /* do a trace from the root down */ + init_root(); + qname = ldns_dname_new_frm_str(name); + /* don't care about return packet */ + (void)do_trace(res, qname, type, clas); + break; + case DRILL_SECTRACE: + /* do a secure trace from the root down */ + init_root(); + qname = ldns_dname_new_frm_str(name); + /* don't care about return packet */ + result = do_secure_trace(res, qname, type, clas, key_list); + break; + case DRILL_CHASE: + qname = ldns_dname_new_frm_str(name); + + ldns_resolver_set_dnssec(res, true); + ldns_resolver_set_dnssec_cd(res, true); + /* set dnssec implies udp_size of 4096 */ + ldns_resolver_set_edns_udp_size(res, 4096); + if (!qname) { + error("%s", "making qname"); + result = EXIT_FAILURE; + goto exit; + } + pkt = ldns_resolver_query(res, qname, type, clas, qflags); + + if (!pkt) { + error("%s", "error pkt sending"); + result = EXIT_FAILURE; + } else { + if (qdebug != -1) { + ldns_pkt_print(stdout, pkt); + } + + if (!ldns_pkt_answer(pkt)) { + mesg("%s", "No answer in packet"); + } else { + result = do_chase(res, qname, type, + clas, key_list, + pkt, qflags); + if (result == LDNS_STATUS_OK) { + if (qdebug != -1) { + mesg("%s", "Chase successful"); + } + result = 0; + } else { + if (qdebug != -1) { + mesg("Chase failed: %s", ldns_get_errorstr_by_id(result)); + } + /*result = EXIT_FAILURE;*/ + } + } + ldns_pkt_free(pkt); + } + break; + case DRILL_AFROMFILE: + pkt = read_hex_pkt(answer_file); + if (pkt) { + if (qdebug != -1) { + ldns_pkt_print(stdout, pkt); + } + ldns_pkt_free(pkt); + } + + break; + case DRILL_QTOFILE: + qname = ldns_dname_new_frm_str(name); + if (!qname) { + error("%s", "making qname\n"); + result = EXIT_FAILURE; + goto exit; + } + + qpkt = ldns_pkt_query_new(qname, type, clas, qflags); + + dump_hex(qpkt, query_file); + + ldns_pkt_free(qpkt); + break; + case DRILL_NSEC: + break; + case DRILL_REVERSE: + /* name should be an ip addr */ + qname = ldns_rdf_new_addr_frm_str(name); + if (!qname) { + error("%s", "-x implies an ip address"); + result = EXIT_FAILURE; + goto exit; + } + qname_tmp = qname; + qname = ldns_rdf_address_reverse(qname); + ldns_rdf_deep_free(qname_tmp); + + /* create a packet and set the RD flag on it */ + pkt = ldns_resolver_query(res, qname, type, clas, qflags); + if (!pkt) { + error("%s", "pkt sending\n"); + result = EXIT_FAILURE; + } else { + if (qdebug != -1) { + ldns_pkt_print(stdout, pkt); + } + ldns_pkt_free(pkt); + } + break; + case DRILL_QUERY: + default: + if (query_file) { + qpkt = read_hex_pkt(query_file); + if (qpkt) { + (void) ldns_resolver_send_pkt(&pkt, res, qpkt); + } + } else { + qname = ldns_dname_new_frm_str(name); + if (!qname) { + error("%s", "error in making qname\n"); + result = EXIT_FAILURE; + goto exit; + } + + if (type == LDNS_RR_TYPE_AXFR) { + (void) ldns_axfr_start(res, qname, clas); + + axfr_rr = ldns_axfr_next(res); + while (axfr_rr) { + if (qdebug != -1) { + ldns_rr_print(stdout, axfr_rr); + printf("\n"); + } + ldns_rr_free(axfr_rr); + axfr_rr = ldns_axfr_next(res); + } + + goto exit; + } else { + /* create a packet and set the RD flag on it */ + pkt = ldns_resolver_query(res, qname, type, clas, qflags); + } + } + + if (!pkt) { + error("%s", "no packet received\n"); + result = EXIT_FAILURE; + } else { + if (qdebug != -1) { + ldns_pkt_print(stdout, pkt); + } + if (qds) { + if (qdebug != -1) { + print_ds_of_keys(pkt); + printf("\n"); + } + } + + if (ldns_rr_list_rr_count(key_list) > 0) { + /* -k's were given on the cmd line */ + ldns_rr_list *rrset_verified; + uint16_t key_count; + + rrset_verified = ldns_pkt_rr_list_by_name_and_type( + pkt, qname, type, + LDNS_SECTION_ANY_NOQUESTION); + + if (type == LDNS_RR_TYPE_ANY) { + /* don't verify this */ + break; + } + + if (qdebug != -1) { + printf("; "); + ldns_rr_list_print(stdout, rrset_verified); + } + + /* verify */ + key_verified = ldns_pkt_verify(pkt, type, qname, key_list, NULL); + + if (key_verified) { + for(key_count = 0; key_count < ldns_rr_list_rr_count(key_verified); + key_count++) { + if (qdebug != -1) { + mesg("VALIDATED by id = %d, owner = ", + (int)ldns_calc_keytag( + ldns_rr_list_rr(key_verified, key_count))); + ldns_rdf_print(stdout, ldns_rr_owner( + ldns_rr_list_rr(key_list, key_count))); + printf("\n"); + } + } + } else { + for(key_count = 0; key_count < ldns_rr_list_rr_count(key_list); + key_count++) { + if (qdebug != -1) { + mesg("BOGUS by id = %d, owner = ", + (int)ldns_calc_keytag( + ldns_rr_list_rr(key_list, key_count))); + ldns_rdf_print(stdout, ldns_rr_owner( + ldns_rr_list_rr(key_list, key_count))); + printf("\n"); + } + } + + } + + } + if (answer_file) { + dump_hex(pkt, answer_file); + } + ldns_pkt_free(pkt); + } + + break; + } + + exit: + ldns_rdf_deep_free(qname); + ldns_resolver_deep_free(res); + ldns_resolver_deep_free(cmdline_res); + ldns_rr_list_deep_free(key_list); + ldns_rr_list_deep_free(cmdline_rr_list); + xfree(progname); +/* + xfree(tsig_name); +*/ + xfree(tsig_data); + xfree(tsig_algorithm); + return result; +} diff --git a/drill/drill.h.in b/drill/drill.h.in new file mode 100644 index 00000000..a1898352 --- /dev/null +++ b/drill/drill.h.in @@ -0,0 +1,61 @@ +/* + * drill.h + * the main header file of drill + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ +#ifndef _DRILL_H_ +#define _DRILL_H_ +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif /* HAVE_STDINT_H */ + +#include "drill_util.h" + +#define DRILL_VERSION "@PACKAGE_VERSION@" + +/* what kind of stuff do we allow */ +#define DRILL_QUERY 0 +#define DRILL_TRACE 1 +#define DRILL_CHASE 2 +#define DRILL_AFROMFILE 3 +#define DRILL_QTOFILE 4 +#define DRILL_NSEC 5 +#define DRILL_REVERSE 6 +#define DRILL_SECTRACE 7 + +extern ldns_rr_list *global_dns_root; +extern bool qds; + +extern int8_t qdebug; + +ldns_pkt *do_trace(ldns_resolver *res, ldns_rdf *name, ldns_rr_type type, + ldns_rr_class c); +ldns_status do_chase(ldns_resolver *res, ldns_rdf *name, ldns_rr_type type, + ldns_rr_class c, ldns_rr_list *trusted_keys, + ldns_pkt *pkt_o, uint16_t qflags); +ldns_status do_secure_trace(ldns_resolver *res, ldns_rdf *name, ldns_rr_type type, + ldns_rr_class c, ldns_rr_list *trusted_keys); +/* dnssec.c */ +ldns_rr_list *get_rr(ldns_resolver *res, ldns_rdf *zname, ldns_rr_type t, ldns_rr_class c); +void drill_pkt_print(FILE *fd, ldns_resolver *r, ldns_pkt *p); +void drill_pkt_print_footer(FILE *fd, ldns_resolver *r, ldns_pkt *p); + +ldns_rr *read_key_file(const char *filename); +ldns_pkt *read_hex_pkt(char *filename); +void init_root(void); +void dump_hex(const ldns_pkt *pkt, const char *file); +void warning(const char *fmt, ...); +void error(const char *fmt, ...); +void mesg(const char *fmt, ...); +#endif /* _DRILL_H_ */ diff --git a/drill/drill_util.c b/drill/drill_util.c new file mode 100644 index 00000000..122b3bfd --- /dev/null +++ b/drill/drill_util.c @@ -0,0 +1,122 @@ +/* + * util.c + * some handy function needed in drill and not implemented + * in ldns + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/* lnds_rr_new_frm_fp?? */ +ldns_rr * +read_key_file(const char *filename) +{ + FILE *fp; + char line[LDNS_MAX_PACKETLEN]; + char c; + size_t i = 0; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Unable to open %s: ", filename); + perror(""); + return NULL; + } + + while ((c = fgetc(fp)) && i < LDNS_MAX_PACKETLEN && c != EOF) { + line[i] = c; + i++; + } + line[i] = '\0'; + + fclose(fp); + + if (i <= 0) { + return NULL; + } else { + return ldns_rr_new_frm_str(line, 0, NULL); + } +} + +ldns_rdf * +ldns_rdf_new_addr_frm_str(char *str) +{ + ldns_rdf *a; + + a = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str); + if (!a) { + /* maybe ip6 */ + a = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str); + if (!a) { + return NULL; + } + } + return a; +} + +/* + * For all keys in a packet print the DS + */ +void +print_ds_of_keys(ldns_pkt *p) +{ + ldns_rr_list *keys; + uint16_t i; + ldns_rr *ds; + + /* TODO fix the section stuff, here or in ldns */ + keys = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_DNSKEY, + LDNS_SECTION_ANSWER); + + /* this also returns the question section rr, which does not + * have any data.... and this inturn crashes everything */ + + if (keys) { + for (i = 0; i < ldns_rr_list_rr_count(keys); i++) { + ds = ldns_key_rr2ds(ldns_rr_list_rr(keys, i)); + if (ds) { + printf("; "); + ldns_rr_print(stdout, ds); + printf("\n"); + } + } + } +} + +void * +xmalloc(size_t s) +{ + void *p; + + p = malloc(s); + if (!p) { + printf("Mem failure\n"); + exit(EXIT_FAILURE); + } + return p; +} + +void * +xrealloc(void *p, size_t size) +{ + void *q; + + q = realloc(p, size); + if (!q) { + printf("Mem failure\n"); + exit(EXIT_FAILURE); + } + return q; +} + +void +xfree(void *p) +{ + if (p) { + free(p); + } +} diff --git a/drill/drill_util.h b/drill/drill_util.h new file mode 100644 index 00000000..eebfe459 --- /dev/null +++ b/drill/drill_util.h @@ -0,0 +1,40 @@ +/* + * util.h + * util.c header file + * in ldns + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#ifndef _DRILL_UTIL_H_ +#define _DRILL_UTIL_H_ +#include + +/** + * return a address rdf, either A or AAAA + * NULL if anything goes wrong + */ +ldns_rdf * ldns_rdf_new_addr_frm_str(char *); + +/** + * print all the ds of the keys in the packet + */ +void print_ds_of_keys(ldns_pkt *p); + +/** + * Alloc some memory, with error checking + */ +void *xmalloc(size_t s); + +/** + * Realloc some memory, with error checking + */ +void *xrealloc(void *p, size_t s); + +/** + * Free the data + */ +void xfree(void *q); +#endif /* _DRILL_UTIL_H_ */ diff --git a/drill/error.c b/drill/error.c new file mode 100644 index 00000000..1981df07 --- /dev/null +++ b/drill/error.c @@ -0,0 +1,115 @@ +/** + * error.c + * + * error reporting routines + * basicly wrappers around printf + * + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +static void +warning_va_list(const char *fmt, va_list args) +{ + fprintf(stderr, "Warning: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void +warning(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + warning_va_list(fmt, args); + va_end(args); +} + +static void +error_va_list(const char *fmt, va_list args) +{ + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void +error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + error_va_list(fmt, args); + va_end(args); + exit(EXIT_FAILURE); +} + +static void +verbose_va_list(const char *fmt, va_list args) +{ + vfprintf(stdout, fmt, args); + fprintf(stdout, "\n"); +} + +/* print stuff */ +void +mesg(const char *fmt, ...) +{ + va_list args; + if (qdebug == -1) { + return; + } + fprintf(stdout, ";; "); + va_start(args, fmt); + verbose_va_list(fmt, args); + va_end(args); +} + +/* print stuff when in verbose mode (1) */ +void +verbose(const char *fmt, ...) +{ + va_list args; + if (qdebug < 1) { + return; + } + + va_start(args, fmt); + verbose_va_list(fmt, args); + va_end(args); +} + +/* print stuff when in vverbose mode (2) */ +void +vverbose(const char *fmt, ...) +{ + va_list args; + if (qdebug < 2) { + return; + } + + va_start(args, fmt); + verbose_va_list(fmt, args); + va_end(args); +} + +static void +debug_va_list(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void +debug(const char *fmt, ...) +{ + va_list args; + fprintf(stderr, "[DEBUG] "); + va_start(args, fmt); + debug_va_list(fmt, args); + va_end(args); +} diff --git a/drill/install-sh b/drill/install-sh new file mode 100755 index 00000000..6ce63b9f --- /dev/null +++ b/drill/install-sh @@ -0,0 +1,294 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd=$cpprog + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "$0: no input file specified" >&2 + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d "$dst" ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "$0: $src does not exist" >&2 + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "$0: no destination specified" >&2 + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d "$dst" ] + then + dst=$dst/`basename "$src"` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-$defaultIFS}" + +oIFS=$IFS +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS=$oIFS + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp=$pathcomp$1 + shift + + if [ ! -d "$pathcomp" ] ; + then + $mkdirprog "$pathcomp" + else + : + fi + + pathcomp=$pathcomp/ +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd "$dst" && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename "$dst"` + else + dstfile=`basename "$dst" $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename "$dst"` + else + : + fi + +# Make a couple of temp file names in the proper directory. + + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + +# Trap to clean up temp files at exit. + + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + +# Move or copy the file name to the temp name + + $doit $instcmd "$src" "$dsttmp" && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi && + +# Now remove or move aside any old file at destination location. We try this +# two ways since rm can't unlink itself on some systems and the destination +# file might be busy for other reasons. In this case, the final cleanup +# might fail but the new file should still install successfully. + +{ + if [ -f "$dstdir/$dstfile" ] + then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null || + $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null || + { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi +} && + +# Now rename the file to the real destination. + + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + +fi && + +# The final little trick to "correctly" pass the exit status to the exit trap. + +{ + (exit 0); exit +} diff --git a/drill/root.c b/drill/root.c new file mode 100644 index 00000000..db54f42e --- /dev/null +++ b/drill/root.c @@ -0,0 +1,64 @@ +/* + * root.c + * Function to handle to the rootservers + * and to update and prime them + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/* a global list of the root-servers */ +ldns_rr_list *global_dns_root; + +/* put a hardcoded list in the root and + * init the root rrlist structure */ +void +init_root(void) +{ + ldns_rr *r; + + global_dns_root = ldns_rr_list_new(); + + r = ldns_rr_new_frm_str("a.root-servers.net 3600 IN A 198.41.0.4", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("b.root-servers.net 3600 IN A 192.228.79.201", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("c.root-servers.net 3600 IN A 192.33.4.12", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("d.root-servers.net 3600 IN A 128.8.10.90", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("e.root-servers.net 3600 IN A 192.203.230.10", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("f.root-servers.net 3600 IN A 192.5.5.241", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("g.root-servers.net 3600 IN A 192.112.36.4", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("h.root-servers.net 3600 IN A 128.63.2.53", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("i.root-servers.net 3600 IN A 192.36.148.17", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("j.root-servers.net 3600 IN A 192.58.128.30", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("k.root-servers.net 3600 IN A 193.0.14.129", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("l.root-servers.net 3600 IN A 198.32.64.12", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); + + r = ldns_rr_new_frm_str("m.root-servers.net 3600 IN A 202.12.27.33", 0, NULL); + ldns_rr_list_push_rr(global_dns_root, r); +} diff --git a/drill/work.c b/drill/work.c new file mode 100644 index 00000000..ca14ca11 --- /dev/null +++ b/drill/work.c @@ -0,0 +1,243 @@ +/* + * work.c + * Where all the hard work is done + * (c) 2005 NLnet Labs + * + * See the file LICENSE for the license + * + */ + +#include "drill.h" +#include + +/** + * Converts a hex string to binary data + * len is the length of the string + * buf is the buffer to store the result in + * offset is the starting position in the result buffer + * + * This function returns the length of the result + */ +size_t +hexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len) +{ + char c; + int i; + uint8_t int8 = 0; + int sec = 0; + size_t bufpos = 0; + + if (len % 2 != 0) { + return 0; + } + + for (i=0; i= '0' && c <= '9') { + int8 += c & 0x0f; + } else if (c >= 'a' && c <= 'z') { + int8 += (c & 0x0f) + 9; + } else if (c >= 'A' && c <= 'Z') { + int8 += (c & 0x0f) + 9; + } else { + /* + warning("Error in reading hex data: \n"); + warning("%s ('%c' at %d, should read %d bytes)\n", hexstr, c, i, len); + */ + return 0; + } + + if (sec == 0) { + int8 = int8 << 4; + sec = 1; + } else { + if (bufpos + offset + 1 <= buf_len) { + buf[bufpos+offset] = int8; + int8 = 0; + sec = 0; + bufpos++; + } else { + fprintf(stderr, "Buffer too small in hexstr2bin\n"); + exit(1); + } + } + } + } + return bufpos; +} + +size_t +packetbuffromfile(char *filename, uint8_t *wire) +{ + FILE *fp = NULL; + char c; + + /* stat hack + * 0 = normal + * 1 = comment (skip to end of line) + * 2 = unprintable character found, read binary data directly + */ + int state = 0; + uint8_t *hexbuf = xmalloc(LDNS_MAX_PACKETLEN); + int hexbufpos = 0; + size_t wirelen; + + if (strncmp(filename, "-", 2) == 0) { + fp = stdin; + } else { + fp = fopen(filename, "r"); + } + if (fp == NULL) { + perror("Unable to open file for reading"); + xfree(hexbuf); + return 0; + } + + /*verbose("Opened %s\n", filename);*/ + + c = fgetc(fp); + while (c != EOF && hexbufpos < LDNS_MAX_PACKETLEN) { + if (state < 2 && !isascii(c)) { + /*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/ + state = 2; + } + switch (state) { + case 0: + if ( (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F') ) + { + hexbuf[hexbufpos] = (uint8_t) c; + hexbufpos++; + } else if (c == ';') { + state = 1; + } else if (c == ' ' || c == '\t' || c == '\n') { + /* skip whitespace */ + } + break; + case 1: + if (c == '\n' || c == EOF) { + state = 0; + } + break; + case 2: + hexbuf[hexbufpos] = (uint8_t) c; + hexbufpos++; + break; + default: + fprintf(stderr, "unknown state while reading %s\n", filename); + xfree(hexbuf); + return 0; + break; + } + c = fgetc(fp); + } + + if (c == EOF) { + /* + if (have_drill_opt && drill_opt->verbose) { + verbose("END OF FILE REACHED\n"); + if (state < 2) { + verbose("read:\n"); + verbose("%s\n", hexbuf); + } else { + verbose("Not printing wire because it contains non ascii data\n"); + } + } + */ + } + if (hexbufpos >= LDNS_MAX_PACKETLEN) { + /*verbose("packet size reached\n");*/ + } + + /* lenient mode: length must be multiple of 2 */ + if (hexbufpos % 2 != 0) { + hexbuf[hexbufpos] = (uint8_t) '0'; + hexbufpos++; + } + + if (state < 2) { + wirelen = hexstr2bin((char *) hexbuf, hexbufpos, wire, 0, LDNS_MAX_PACKETLEN); + } else { + memcpy(wire, hexbuf, (size_t) hexbufpos); + wirelen = (size_t) hexbufpos; + } + xfree(hexbuf); + return wirelen; +} + +ldns_pkt * +read_hex_pkt(char *filename) +{ + uint8_t *wire; + size_t wiresize; + + ldns_pkt *pkt = NULL; + + ldns_status status; + FILE *fp; + + fp = fopen(filename, "r"); + + if (fp == NULL) { + fprintf(stderr, "Unable to open %s\n", filename); + perror(""); + return NULL; + } + + wire = xmalloc(LDNS_MAX_PACKETLEN); + + wiresize = packetbuffromfile(filename, wire); + + if (wiresize > 0) { + status = ldns_wire2pkt(&pkt, wire, wiresize); + } + + xfree(wire); + + return pkt; +} + +void +dump_hex(const ldns_pkt *pkt, const char *filename) +{ + uint8_t *wire;// = xmalloc((packet->udppacketsize)*21); + size_t size, i; + FILE *fp; + ldns_status status; + + fp = fopen(filename, "w"); + + if (fp == NULL) { + fprintf(stderr, "Unable to open %s for writing", filename); + return; + } + + status = ldns_pkt2wire(&wire, pkt, &size); + + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "Unable to convert packet: error code %u\n", status); + return; + } + + fprintf(fp, "; 0"); + for (i = 1; i < 20; i++) { + fprintf(fp, " %2u", (unsigned int) i); + } + fprintf(fp, "\n"); + fprintf(fp, ";--"); + for (i = 1; i < 20; i++) { + fprintf(fp, " --"); + } + fprintf(fp, "\n"); + for (i = 0; i < size; i++) { + if (i % 20 == 0 && i > 0) { + fprintf(fp, "\t;\t%4u-%4u\n", (unsigned int) i-19, (unsigned int) i); + } + fprintf(fp, " %02x", (unsigned int)wire[i]); + } + fclose(fp); +}