]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
added zone walker
authorJelte Jansen <jeltejan@NLnetLabs.nl>
Mon, 21 Nov 2005 21:14:18 +0000 (21:14 +0000)
committerJelte Jansen <jeltejan@NLnetLabs.nl>
Mon, 21 Nov 2005 21:14:18 +0000 (21:14 +0000)
examples/Makefile.in
examples/ldns-walk.1 [new file with mode: 0644]
examples/ldns-walk.c [new file with mode: 0644]

index dc85f6311a16cfea308b8e0d1ad886c94ff7c622..2618c48388e0365f9e9edef61c2f2718a5e484d8 100644 (file)
@@ -29,7 +29,9 @@ SOURCES       = ldns-read-zone.c \
                  ldns-key2ds.c \
                  ldns-signzone.c \
                  ldns-version.c \
-                 ldns-rrsig.c
+                 ldns-rrsig.c \
+                 ldns-walk.c
+
 PROGRAMS=$(SOURCES:.c=)
 
 .PHONY:        all clean realclean
@@ -63,6 +65,9 @@ ldns-key2ds:  ldns-key2ds.o
 ldns-rrsig:    ldns-rrsig.o
                $(LINK) -o $@ $+
 
+ldns-walk:     ldns-walk.o
+               $(LINK) -o $@ $+
+
 ## implicit rule
 %.o:   $(srcdir)/%.c
        $(COMPILE) -c $(srcdir)/$<
diff --git a/examples/ldns-walk.1 b/examples/ldns-walk.1
new file mode 100644 (file)
index 0000000..d5e4a5f
--- /dev/null
@@ -0,0 +1,42 @@
+.TH ldns-walk 1 "21 Nov 2005"
+.SH NAME
+ldns-walk \- Retrieve the contents of a DNSSEC signed zone
+.SH SYNOPSIS
+.B ldns-walk
+[
+.IR OPTION
+]
+.IR ZONE 
+
+.SH DESCRIPTION
+
+\fBldns-walk\fR is used to retrieve the contents of a DNSSEC signed zone.
+It does this through NSEC-walking (following the chain of NSEC records) and
+'guessing' the next non-existent owner name for each NSEC.
+
+Note that it might get stuck on some wildcard records when used through a
+caching forwarder. This problem can be circumvented by querying the
+authoritative nameserver directly (with the @ argument).
+
+Of course the nameserver that is used must be DNSSEC-aware.
+
+.SH OPTIONS
+.TP
+\fB-s\f  \fIname\fR
+Start the walk with this owner name. Useful when continuing the walk for a
+large zone.
+
+.TP
+\fB@\f \fInameserver\fR
+Send the queries to this nameserver.
+
+.SH AUTHOR
+Written by Jelte Jansen as an example for ldns usage.
+
+.SH REPORTING BUGS
+Report bugs to <ldns-team@nlnetlabs.nl>. 
+
+.SH COPYRIGHT
+Copyright (C) 2005 NLnet Labs. This is free software. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.
diff --git a/examples/ldns-walk.c b/examples/ldns-walk.c
new file mode 100644 (file)
index 0000000..23ac224
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * ldns-walk uses educated guesses and NSEC data to retrieve the
+ * contents of a dnssec signed zone
+ * (c) NLnet Labs, 2005
+ * See the file LICENSE for the license
+ */
+
+#include "config.h"
+
+#include <ldns/dns.h>
+
+int
+usage(FILE *fp, char *prog) {
+       fprintf(fp, "%s [options] domain\n", prog);
+       fprintf(fp, "  print out the owner names for domain and the record types for those names\n");
+       fprintf(fp, "OPTIONS:\n");
+       fprintf(fp, "-s <name>\t\tStart from this name\n");
+       fprintf(fp, "@<nameserver>\t\tUse this nameserver\n");
+       return 0;
+}
+
+ldns_rdf *
+create_dname_plus_1(ldns_rdf *dname)
+{
+       uint8_t *wire;
+       size_t len;
+       ldns_rdf *newdname;
+       uint8_t labellen;
+       size_t pos;
+       ldns_status status;
+       
+       labellen = ldns_rdf_data(dname)[0];
+       if (labellen < 63) {
+               wire = malloc(ldns_rdf_size(dname) + 1);
+               if (!wire) {
+                       fprintf(stderr, "Malloc error: out of memory?\n");
+                       exit(127);
+               }
+               wire[0] = labellen + 1;
+               memcpy(&wire[1], ldns_rdf_data(dname) + 1, labellen);
+               memcpy(&wire[labellen+1], ldns_rdf_data(dname) + labellen, ldns_rdf_size(dname) - labellen);
+               wire[labellen+1] = '\000';
+               pos = 0;
+               status = ldns_wire2dname(&newdname, wire, ldns_rdf_size(dname) + 1, &pos);
+               free(wire);
+       } else {
+               fprintf(stderr, "maxlen not supported yet\n");
+               exit(9);
+       }
+
+       if (status != LDNS_STATUS_OK) {
+         printf("Error: %s\n", ldns_get_errorstr_by_id(status));
+         exit(10);
+        }
+       
+       return newdname;
+}
+
+ldns_rdf *
+create_plus_1_dname(ldns_rdf *dname)
+{
+       ldns_rdf *label;
+       ldns_status status;
+       
+       status = ldns_str2rdf_dname(&label, "\\000");
+       if (status != LDNS_STATUS_OK) {
+               printf("error creating \\000 dname: %s\n\n", ldns_get_errorstr_by_id(status));
+               exit(2);
+       }
+       status = ldns_dname_cat(label, dname);
+       if (status != LDNS_STATUS_OK) {
+               printf("error catting \000 dname: %s\n\n", ldns_get_errorstr_by_id(status));
+               exit(3);
+       }
+       return label;
+}
+
+int
+main(int argc, char *argv[])
+{
+       ldns_status status;
+
+       ldns_resolver *res;
+       ldns_rdf *domain = NULL;
+       ldns_pkt *p;
+       ldns_rr *soa;
+       ldns_rr_list *rrlist;
+       ldns_rr_list *rrlist2;
+       ldns_rr *rr;
+       ldns_rdf *soa_p1;
+       ldns_rdf *next_dname;
+       ldns_rdf *last_dname;
+       ldns_rdf *last_dname_p;
+       ldns_rdf *cur_dname;
+       ldns_rdf *cur_dname_p;
+       ldns_rdf *startpoint = NULL;
+       ldns_rdf *rrtypes;
+
+       char *serv = NULL;
+       ldns_rdf *serv_rdf;
+       ldns_resolver *cmdline_res;
+       ldns_rr_list *cmdline_rr_list;
+       ldns_rdf *cmdline_dname;
+
+       int result;
+       size_t i;
+
+       p = NULL;
+       rrlist = NULL;
+       rrlist2 = NULL;
+       soa = NULL;
+       domain = NULL;
+       res = NULL;
+       
+       if (argc < 2) {
+               usage(stdout, argv[0]);
+               exit(EXIT_FAILURE);
+       } else {
+               for (i = 1; i < argc; i++) {
+                       if (strncmp(argv[i], "-s", 3) == 0) {
+                               if (i + 1 < argc) {
+                                       if (!ldns_str2rdf_dname(&startpoint, argv[i + 1]) == LDNS_STATUS_OK) {
+                                               printf("Bad start point name: %s\n", argv[i + 1]);
+                                               exit(1);
+                                       }
+                               } else {
+                                       printf("Missing argument for -s\n");
+                                       exit(1);
+                               }
+                               i++;
+                       } else {
+                               if (argv[i][0] == '@') {
+                                       serv = argv[i] + 1;
+                               } else {
+                                       if (i < argc) {
+                                               if (!domain) {
+                                                       /* create a rdf from the command line arg */
+                                                       domain = ldns_dname_new_frm_str(argv[1]);
+                                                       if (!domain) {
+                                                               usage(stdout, argv[0]);
+                                                               exit(1);
+                                                       }
+                                               } else {
+                                                       printf("One domain at a time please\n");
+                                                       exit(1);
+                                               }
+                                       } else {
+                                               printf("No domain given to walk\n");
+                                               exit(1);
+                                       }
+                               }
+                       }
+               }
+       }
+       if (!domain) {
+               printf("Missing argument\n");
+               exit(1);
+       }
+
+
+       /* create a new resolver from /etc/resolv.conf */
+       if(!serv) {
+               res = ldns_resolver_new_frm_file(NULL);
+       } else {
+               res = ldns_resolver_new();
+               if (!res || strlen(serv) <= 0) {
+                       result = EXIT_FAILURE;
+                       goto exit;
+               }
+               /* add the nameserver */
+               serv_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, serv);
+               if (!serv_rdf) {
+                       /* maybe ip6 */
+                       serv_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, 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;
+                       }
+
+                       cmdline_dname = ldns_dname_new_frm_str(serv);
+                       cmdline_rr_list = ldns_get_rr_list_addr_by_name(
+                                               cmdline_res, 
+                                               cmdline_dname,
+                                               LDNS_RR_CLASS_IN,
+                                               0);
+                       ldns_rdf_deep_free(cmdline_dname);
+                       if (!cmdline_rr_list) {
+                               error("%s %s", "could not find any address for the name: ", serv);
+                               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);
+                       }
+               }
+
+       }
+
+       ldns_resolver_set_dnssec(res, true);
+       ldns_resolver_set_dnssec_cd(res, true);
+
+       if (!res) {
+               exit(2);
+       }
+
+       /* use the resolver to send it a query for the soa
+        * records of the domain given on the command line
+        */
+       p = ldns_resolver_query(res, domain, LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN, LDNS_RD);
+       soa = NULL;
+
+        if (!p)  {
+               exit(3);
+        } else {
+               /* retrieve the MX records from the answer section of that
+                * packet
+                */
+               rrlist = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_SOA, LDNS_SECTION_ANSWER);
+               if (!rrlist || ldns_rr_list_rr_count(rrlist) != 1) {
+                       if (rrlist) {
+                               printf(" *** > 1 SOA: %u\n", ldns_rr_list_rr_count(rrlist));
+                       } else {
+                               printf(" *** No rrlist...\b");
+                       }
+                       fprintf(stderr, 
+                                       " *** invalid answer name %s after SOA query for %s\n",
+                                       argv[1], argv[1]);
+                       ldns_pkt_print(stdout, p);
+                        ldns_pkt_free(p);
+                        ldns_resolver_deep_free(res);
+                       exit(4);
+               } else {
+                       soa = ldns_rr_clone(ldns_rr_list_rr(rrlist, 0));
+                       ldns_rr_list_deep_free(rrlist);
+               }
+        }
+
+       /* add \001 to soa */
+       status = ldns_str2rdf_dname(&soa_p1, "\001");
+       if (status != LDNS_STATUS_OK) {
+               printf("error. %s\n", ldns_get_errorstr_by_id(status));
+       }
+       if (!soa) {
+               printf("Error getting SOA\n");
+               exit(1);
+       }
+
+       if (startpoint) {
+               last_dname = startpoint;
+               last_dname_p = create_dname_plus_1(last_dname);
+       } else {
+               last_dname = ldns_rdf_clone(domain);
+               ldns_dname_cat(soa_p1, last_dname);
+               last_dname_p = ldns_rdf_clone(soa_p1);
+       }
+
+
+       ldns_rdf_print(stdout, ldns_rr_owner(soa));
+       printf("\t");
+
+       next_dname = NULL;
+       while (!next_dname || ldns_rdf_compare(next_dname, domain) != 0) {
+               if (p) {
+                       ldns_pkt_free(p);
+                       p = NULL;
+               }
+               p = ldns_resolver_query(res, last_dname_p, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
+
+               if (next_dname) {
+                       ldns_rdf_deep_free(next_dname);
+                       ldns_rdf_deep_free(rrtypes);
+                       next_dname = NULL;
+               }
+
+               if (!p)  {
+                 fprintf(stderr, "Error trying to resolve: ");
+                 ldns_rdf_print(stderr, last_dname_p);
+                 fprintf(stderr, "\n");
+                 while (!p) {
+                   p = ldns_resolver_query(res, last_dname_p, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
+                   if (!p)  {
+                     fprintf(stderr, "Error trying to resolve: ");
+                     ldns_rdf_print(stderr, last_dname_p);
+                     fprintf(stderr, "\n");
+                   }
+                 }
+               }
+
+               /* if the current name is an empty non-terminal, bind returns
+                * SERVFAIL on the plus1-query...
+                * so requery with only the last dname
+                */
+               if (ldns_pkt_rcode(p) == 2) {
+                       ldns_pkt_free(p);
+                       p = NULL;
+                       p = ldns_resolver_query(res, last_dname, LDNS_RR_TYPE_ANY, LDNS_RR_CLASS_IN, LDNS_RD);
+                       if (!p) {
+                               exit(51);
+                       }
+                       rrlist = ldns_pkt_rr_list_by_name_and_type(p, last_dname, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
+                       rrlist2 = ldns_pkt_rr_list_by_name_and_type(p, last_dname_p, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
+               } else {
+                       rrlist = ldns_pkt_rr_list_by_name_and_type(p, last_dname, LDNS_RR_TYPE_NSEC, LDNS_SECTION_AUTHORITY);
+                       rrlist2 = ldns_pkt_rr_list_by_name_and_type(p, last_dname_p, LDNS_RR_TYPE_NSEC, LDNS_SECTION_ANSWER);
+               }
+       if (rrlist && rrlist2) {
+               ldns_rr_list_cat(rrlist, rrlist2);
+       } else if (rrlist2) {
+               rrlist = rrlist2;
+       }
+
+       if (!rrlist || ldns_rr_list_rr_count(rrlist) != 1) {
+       } else {
+               next_dname = ldns_rdf_clone(ldns_rr_rdf(ldns_rr_list_rr(rrlist, 0), 0));
+               rrtypes = ldns_rdf_clone(ldns_rr_rdf(ldns_rr_list_rr(rrlist, 0), 1));
+               ldns_rr_list_deep_free(rrlist);
+       }
+
+       if (!next_dname) {
+               /* apparently the zone also has prepended data (i.e. a.example and www.a.example, 
+                * The www comes after the a but befpre a\\000, so we need to make another name (\\000.a)
+                */
+               if (last_dname_p) {
+                       ldns_rdf_deep_free(last_dname_p);
+               }
+               last_dname_p = create_plus_1_dname(last_dname);
+       } else {
+
+               if (last_dname) {
+                       if (ldns_rdf_compare(last_dname, next_dname) == 0) {
+                               printf("Next dname is the same as current, this would loop forever. This is a problem that usually occurs when walking through a caching forwarder. Try using the authoritative nameserver to walk.\n");
+                               exit(2);
+                       }
+                       ldns_rdf_deep_free(last_dname);
+               }
+               last_dname = ldns_rdf_clone(next_dname);
+               if (last_dname_p) {
+                       ldns_rdf_deep_free(last_dname_p);
+               }
+               last_dname_p = create_dname_plus_1(last_dname);
+               ldns_rdf_print(stdout, rrtypes);
+               printf("\n");
+               ldns_rdf_print(stdout, next_dname);
+               printf("\t");
+       }
+}
+
+       ldns_rdf_deep_free(domain);
+       ldns_rdf_deep_free(soa_p1);
+       ldns_rdf_deep_free(last_dname_p);
+       ldns_rdf_deep_free(last_dname);
+       ldns_rdf_deep_free(next_dname);
+       ldns_rdf_deep_free(rrtypes);
+
+        ldns_pkt_free(p);
+
+       ldns_rr_free(soa);
+       
+
+       printf("\n\n");
+        ldns_resolver_deep_free(res);
+
+        exit:
+        return result;
+}