]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
new example tool
authorMatthijs Mekking <matje@NLnetLabs.nl>
Thu, 7 Apr 2011 10:37:30 +0000 (10:37 +0000)
committerMatthijs Mekking <matje@NLnetLabs.nl>
Thu, 7 Apr 2011 10:37:30 +0000 (10:37 +0000)
examples/Makefile.in
examples/ldns-gen-zone.1 [new file with mode: 0644]
examples/ldns-gen-zone.c [new file with mode: 0644]

index 659efc0c33cc2fa7ec685a144d05170b5ae6b95c..8fe4fbbb4bed8888b65e333f193e0a5e1cb94407 100644 (file)
@@ -62,6 +62,7 @@ MAIN_SOURCES = ldns-read-zone.c \
                  ldns-notify.c \
                  ldns-testns.c \
                  ldns-compare-zones.c \
+                 ldns-gen-zone.c \
                  ldnsd.c
 
 MAIN_SSL_SOURCES = ldns-signzone.c \
diff --git a/examples/ldns-gen-zone.1 b/examples/ldns-gen-zone.1
new file mode 100644 (file)
index 0000000..6e682f5
--- /dev/null
@@ -0,0 +1,93 @@
+.TH ldns-gen-zone 1 "10 June 2010"
+.SH NAME
+ldns-gen-zone \- read a zonefile and print it while adding DS records and extra RR's
+.SH SYNOPSIS
+.B ldns-gen-zone 
+.IR ZONEFILE 
+
+.SH DESCRIPTION
+
+\fBldns-gen-zone\fR reads a DNS zone file and prints it. 
+
+It is build for speed, not for a nice formatting. The output
+has one resource record per line and no pretty-printing makeup.
+
+DNSSEC data (NSEC, NSEC3, RRSIG or DNSKEY) is not stripped. You may want to
+use \fBldns-read-zone\fR for that. Existing DS records are also not stripped.
+
+The idea is to use this tool for quickly generating a representative
+artificial zonefile from a real zonefile, to use it for testing purposes.
+
+.SH OPTIONS
+.TP
+\fB-a NUM\fR
+Adds NUM extra artificial NS RRSets to the output. 
+The RRSets owner names start
+with 'xn--' in an attempt to ensure uniqueness (nl.-zone does not support
+IDN's - and this tool was written with that knowledge in mind).
+
+An artificial NS RRSet has two NS records; ns1.example.com and
+ns2.example.com.
+
+.TP
+\fB-p NUM\fR
+Add NUM% of DS RRSets to the NS RRSets (anywhere between
+1-4 DS records per RRSet).
+
+.TP
+\fB-o ORIGIN\fR
+Sets an $ORIGIN, which can be handy if the one in the zonefile
+is set to '@' for example. If there is an $ORIGIN in the zonefile,
+this option will silently be ignored.
+
+.TP
+\fB-s\fR
+This is the recommended way of processing large zones that
+are already sorted and canonicalized (ie lowercase). It skips the
+sorting and canonicalization step that is required for properly
+grouping RRSets together (before adding any DS records to them. Skipping
+this step will speed things up.
+
+It is not recommended to use this option if you want to add DS records
+to unsorted, non-canonicalized zones.
+
+.TP
+\fB-h\fR
+Show usage and exit.
+
+.TP
+\fB-v\fR
+Show version and exit.
+
+.SH EXAMPLES
+
+.TP
+\fBldns-gen-zone -a 100000 -p 10 -s ./zonefile.txt\fR
+Read a zonefile, add 100.000 artificial NS RRSets and 10% of DS records,
+print it to standard output. Don't sort (will only work well if the input
+zonefile is already sorted and canonicalized).
+
+.SH AUTHOR
+Initially written by Marco Davids, several modifications added by Miek
+Gieben, both from SIDN.
+
+.SH REPORTING BUGS
+Report bugs to <ldns-team@nlnetlabs.nl>.
+
+.SH BUGS
+Only undiscovered ones.
+
+.SH CAVEATS
+May require a machine with a considerable amount of memory for large zone files.
+
+Fake DS records hashes are generated as digest type SHA-256 (RFC4509). Be aware not to change
+the DIGESTTYPE #define in the source code in anything else but 2 if you want
+to keep things realistic.
+
+Despite a number of efforts, this program is still not the fastest in the
+world.
+
+.SH COPYRIGHT
+Copyright (C) 2010 SIDN. This is free software. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.
diff --git a/examples/ldns-gen-zone.c b/examples/ldns-gen-zone.c
new file mode 100644 (file)
index 0000000..8b0e69b
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Reads a zone file from disk and prints it to stdout, one RR per line.
+ * Adds artificial DS records and RRs.
+ * For the purpose of generating a test zone file
+ *
+ * (c) SIDN 2010/2011 - Marco Davids/Miek Gieben
+ *
+ * See the LICENSE file for the license
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <ldns/ldns.h>
+#include <errno.h>
+
+#define NUM_DS 4                /* maximum of 4 DS records per delegation */
+#define ALGO 8                 /* Algorithm to use for fake DS records - RSASHA256 - RFC5702  */
+#define DIGESTTYPE 2           /* Digest type to use for fake DS records - SHA-256 - RFC 4509 */
+
+
+/**
+ * Usage function.
+ *
+ */
+static int
+usage(FILE *fp, char *prog) {
+        fprintf(fp, "\n\nUsage: %s [-cshnv] [-ap NUM] [-o ORIGIN] [<zonefile>]\n", prog);
+        fprintf(fp, "\tReads a zonefile and add some artificial NS RRsets and DS records.\n");
+        fprintf(fp, "\tIf no zonefile is given, the zone is read from stdin.\n");
+        fprintf(fp, "\t-a <NUM> add NUM artifical delegations (NS RRSets) to output.\n");
+        fprintf(fp, "\t-p <NUM> add NUM percent of DS RRset's to the NS RRsets (1-%d RR's per DS RRset).\n", NUM_DS);
+        fprintf(fp, "\t-o ORIGIN sets an $ORIGIN, which can be handy if the one in the zonefile is set to @.\n");
+        fprintf(fp, "\t-s if input zone file is already sorted and canonicalized (ie all lowercase),\n\t   use this option to speed things up while inserting DS records.\n");
+        fprintf(fp, "\t-h show this text.\n");
+        fprintf(fp, "\t-v shows the version and exits.\n");
+        fprintf(fp, "\nif no file is given standard input is read.\n\n");
+}
+
+/**
+ * Insert the DS records, return the amount added.
+ *
+ */
+static int
+insert_ds(ldns_rdf *dsowner, int ttl)
+{
+        int d, dsrand;
+        int keytag = 0;
+        char *dsownerstr;
+        char digeststr[70];
+
+        /**
+         * Average the amount of DS records per delegation a little.
+         */
+        dsrand = 1+rand() % NUM_DS;
+        for(d = 0; d < dsrand; d++) {
+                keytag = 1+rand() % 65535;
+                /**
+                 * Dynamic hashes method below is still too slow... 20% slower than a fixed string...
+                 *
+                 * We assume RAND_MAX is 32 bit, http://www.gnu.org/s/libc/manual/html_node/ISO-Random.html
+                 * 2147483647 or 0x7FFFFFFF
+                 */
+                snprintf(digeststr, 65,
+                        "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
+                        rand()%RAND_MAX, rand()%RAND_MAX, rand()%RAND_MAX,
+                        rand()%RAND_MAX, rand()%RAND_MAX, rand()%RAND_MAX,
+                        rand()%RAND_MAX, rand()%RAND_MAX, rand()%RAND_MAX,
+                        rand()%RAND_MAX, rand()%RAND_MAX, rand()%RAND_MAX,
+                        rand()%RAND_MAX, rand()%RAND_MAX, rand()%RAND_MAX,
+                        rand()%RAND_MAX);
+                dsownerstr = ldns_rdf2str(dsowner);
+                fprintf(stdout, "%s\t%d\tIN\tDS\t%d %d %d %s\n", dsownerstr, ttl, keytag, ALGO, DIGESTTYPE, digeststr);
+        }
+        return dsrand;
+}
+
+int
+main(int argc, char **argv) {
+        char *filename, *rrstr, *ownerstr;
+        char *classtypestr1 = "IN NS ns1.example.com.";
+        char *classtypestr2 = "IN NS ns2.example.com.";
+        const int classtypelen = strlen(classtypestr1);
+        /* Simply because this was developed by SIDN and we don't use xn-- for .nl :-) */
+        const char *punystr = "xn--fake-rr";
+        const int punylen = strlen(punystr);
+        int rrstrlen, ownerlen;
+        FILE *fp;
+        int c, ttl, nsrand;
+        int counta,countd,countr;
+        ldns_zone *z;
+        ldns_rdf *origin = NULL;
+        int line_nr = 0;
+        int addrrs = 0;
+        int dsperc = 0;
+        bool canonicalize = true;
+        bool sort = true;
+        bool do_ds = false;
+        ldns_status s;
+        size_t i;
+        ldns_rr_list *rrset_list;
+        ldns_rdf *owner;
+        ldns_rr_type cur_rr_type;
+        ldns_rr *cur_rr;
+
+        counta = countd = countr = 0;
+
+        /**
+         * Set some random seed.
+         */
+        srand((unsigned int)time(NULL));
+
+        /**
+         * Commandline options.
+         */
+        while ((c = getopt(argc, argv, "a:p:shvo:")) != -1) {
+                switch (c) {
+                   case 'a':
+                        addrrs = atoi(optarg);
+                        if (addrrs <= 0) {
+                                fprintf(stderr, "error\n");
+                                exit(EXIT_FAILURE);
+                        }
+                        break;
+                case 'o':
+                        origin = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, optarg);
+                        if (!origin) {
+                                fprintf(stderr, "error: creating origin from -o %s failed.\n", optarg);
+                                exit(EXIT_FAILURE);
+                        }
+                        break;
+                case 'p':
+                        dsperc = atoi(optarg);
+                        if (dsperc <= 0 || dsperc > 100) {
+                                fprintf(stderr, "error: percentage of signed delegations must be between [0-100].\n");
+                                exit(EXIT_FAILURE);
+                        }
+                        do_ds = true;
+                        break;
+                case 's':
+                        sort = false;
+                        canonicalize = false;
+                        break;
+                case 'h':
+                        usage(stdout, argv[0]);
+                        exit(EXIT_SUCCESS);
+                case 'v':
+                        fprintf(stdout, "ldns-gen-zone version %s (ldns version %s)\n", LDNS_VERSION, ldns_version());
+                        exit(EXIT_SUCCESS);
+                default:
+                        fprintf(stderr, "\nTry -h for more information.\n\n");
+                        exit(EXIT_FAILURE);
+               }
+        }
+        argc -= optind;
+        argv += optind;
+
+        /**
+         * Read zone.
+         */
+        if (argc == 0) {
+                fp = stdin;
+        } else {
+                filename = argv[0];
+                fp = fopen(filename, "r");
+                if (!fp) {
+                       fprintf(stderr, "Unable to open %s: %s\n", filename, strerror (errno));
+                       exit(EXIT_FAILURE);
+               }
+        }
+        s = ldns_zone_new_frm_fp_l(&z, fp, origin, 0, LDNS_RR_CLASS_IN, &line_nr);
+        if (s != LDNS_STATUS_OK) {
+                fprintf(stderr, "%s at %d\n", ldns_get_errorstr_by_id(s), line_nr);
+                exit(EXIT_FAILURE);
+        }
+        ttl = ldns_rr_ttl(ldns_zone_soa(z));
+        if (!origin) {
+                origin = ldns_rr_owner(ldns_zone_soa(z));
+                // Check for root (.) origin here TODO(MG)
+        }
+        ownerstr = ldns_rdf2str(origin);
+        if (!ownerstr) {
+                fprintf(stderr, "ldns_rdf2str(origin) failed\n");
+                exit(EXIT_FAILURE);
+        }
+        ownerlen = strlen(ownerstr);
+
+        ldns_rr_print(stdout, ldns_zone_soa(z));
+
+        if (addrrs > 0) {
+                while (addrrs > counta) {
+                        counta++;
+                        rrstrlen = punylen + ownerlen + classtypelen + 4;
+                        rrstr = (char*)malloc(rrstrlen);
+                        if (!rrstr) {
+                                fprintf(stderr, "malloc() failed: Out of memory\n");
+                                exit(EXIT_FAILURE);
+                        }
+                        snprintf(rrstr, rrstrlen, "%s%d.%s %d %s", punystr, counta,
+                                ownerstr, ttl, classtypestr1);
+                        ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL);
+                           ldns_rr_print(stdout, cur_rr);
+                        ldns_rr_free(cur_rr);
+
+                        snprintf(rrstr, rrstrlen, "%s%d.%s %d %s", punystr, counta,
+                            ownerstr, ttl, classtypestr2);
+                        ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL);
+                           ldns_rr_print(stdout, cur_rr);
+
+                        free(rrstr);
+
+                        /* may we add a DS record as well? */
+                        if (do_ds) {
+                                /*
+                                 * Per definition this may not be the same as the origin, so no
+                                 * check required same for NS check - so the only thing left is some
+                                 * randomization.
+                                 */
+                                nsrand = rand() % 100;
+                                if (nsrand < dsperc) {
+                                        owner = ldns_rr_owner(cur_rr);
+                                        ttl = ldns_rr_ttl(cur_rr);
+                                        countd += insert_ds(owner, ttl);
+                                }
+                        }
+                        ldns_rr_free(cur_rr);
+                }
+        }
+
+        if (!do_ds) {
+                ldns_rr_list_print(stdout, ldns_zone_rrs(z));
+        } else {
+                /*
+                * We use dns_rr_list_pop_rrset and that requires a sorted list weird things may happen
+                * if the -s option was used on unsorted, non-canonicalized input
+                */
+                if (canonicalize) {
+                        ldns_rr2canonical(ldns_zone_soa(z));
+                        for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z)); i++) {
+                                ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z), i));
+                        }
+                }
+
+                if (sort) {
+                        ldns_zone_sort(z);
+                }
+
+                /* Work on a per RRset basis for DS records - weird things will happen if the -s option
+                 * was used in combination with an unsorted zone file
+                 */
+                while((rrset_list = ldns_rr_list_pop_rrset(ldns_zone_rrs(z)))) {
+                        /**
+                         * SOA record is not counted as a RRset apparantly - hence not printed, unless we do it explicitly (below).
+                         */
+                        owner = ldns_rr_list_owner(rrset_list);
+                        cur_rr_type = ldns_rr_list_type(rrset_list);
+                        /**
+                         * Print them...
+                         */
+                        while (cur_rr = ldns_rr_list_pop_rr(rrset_list)) {
+                                ttl = ldns_rr_ttl(cur_rr);
+                                fprintf(stdout, "%s", ldns_rr2str(cur_rr));
+                        }
+                        /*
+                         * And all the way at the end a DS record if
+                         * we are dealing with an NS rrset
+                         */
+                        nsrand = rand() % 100;
+                        if (nsrand == 0) {
+                                nsrand = 100;
+                        }
+
+                        if ((cur_rr_type == LDNS_RR_TYPE_NS) &&
+                                (ldns_rdf_compare(owner, origin) != 0) && (nsrand < dsperc)) {
+                                /**
+                                 * No DS records for the $ORIGIN, only for delegations, obey dsperc.
+                                 */
+                                countr++;
+                                insert_ds(owner, ttl);
+                        }
+                        ldns_rr_list_free(rrset_list);
+                        ldns_rdf_free(owner);
+                }
+        }
+
+        /**
+         * And done...
+         */
+        fclose(fp);
+        fprintf(stdout, ";; Added %d DS records to %d NS RRset's (from input-zone: %d, from added: %d)\n;; lines in original input-zone: %d\n",
+                countd, counta + countr, countr, counta, line_nr, dsperc);
+        exit(EXIT_SUCCESS);
+}