summaryrefslogtreecommitdiff
path: root/testcode/harvest.c
diff options
context:
space:
mode:
Diffstat (limited to 'testcode/harvest.c')
-rw-r--r--testcode/harvest.c857
1 files changed, 857 insertions, 0 deletions
diff --git a/testcode/harvest.c b/testcode/harvest.c
new file mode 100644
index 0000000000000..1952dc2a221fe
--- /dev/null
+++ b/testcode/harvest.c
@@ -0,0 +1,857 @@
+/*
+ * testcode/harvest.c - debug program to get relevant data to a set of queries.
+ *
+ * Copyright (c) 2008, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This program downloads relevant DNS data to a set of queries.
+ * This means that the queries are asked to root, TLD, SLD servers and
+ * the results stored per zone.
+ * The following data is pertinent:
+ *
+ * At each label:
+ * SOA
+ * NS
+ * DNSKEY
+ * DS
+ * For the whole query:
+ * the result.
+ * For NS-records:
+ * their label data
+ * and the A and AAAA records for it.
+ * (as if the name, with A and AAAA query type is in the list,
+ * referred to as recursion depth+1)
+ * Any NSEC, NSEC3, SOA records or additional data found in answers.
+ *
+ * All of this is data that would be encountered during an iterative lookup
+ * for the queries in the list. It is saved to enable a replay of iterative
+ * lookups for performance testing.
+ *
+ * A number of assumptions are made.
+ * 1) configuration is correct.
+ * The parent has the same NS records as the child.
+ * All nameservers carry the same data.
+ * 2) EDNS/nonEDNS responses and other behaviour is ignored.
+ * Only the data is saved.
+ * This creates a snapshot that represents the data as this resolver saw it.
+ */
+
+#include "config.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <ldns/ldns.h>
+#include <signal.h>
+#include "libunbound/unbound.h"
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef UNBOUND_ALLOC_LITE
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef strdup
+#define unbound_lite_wrapstr(s) s
+#endif
+struct todo_item;
+struct labdata;
+
+/** this represents the data that has been collected
+ * as well as a todo list and some settings */
+struct harvest_data {
+ /** the unbound context */
+ struct ub_ctx* ctx;
+
+ /** a tree per label; thus this first one is one root entry,
+ * that has a tree of TLD labels. Those have trees of SLD labels. */
+ struct labdata* root;
+ /** the original query list */
+ struct todo_item* orig_list;
+ /** the query list todo */
+ struct todo_item* todo_list;
+ /** last item in todo list */
+ struct todo_item* todo_last;
+ /** number of todo items */
+ int numtodo;
+
+ /** where to store the results */
+ char* resultdir;
+ /** maximum recursion depth */
+ int maxdepth;
+ /** current recursion depth */
+ int curdepth;
+
+ /** max depth of labels */
+ int maxlabels;
+ /** number of RRs stored */
+ int num_rrs;
+ /** number of zones written */
+ int num_zones;
+};
+
+/**
+ * Todo item
+ */
+struct todo_item {
+ /** the next item */
+ struct todo_item* next;
+
+ /** query as rdf */
+ ldns_rdf* qname;
+ /** the query type */
+ int qtype;
+ /** query class */
+ int qclass;
+
+ /** recursion depth of todo item (orig list is 0) */
+ int depth;
+ /** the label associated with the query */
+ struct labdata* lab;
+};
+
+/**
+ * Every label has a sest of sublabels, that have sets of sublabels ...
+ * Per label is stored also a set of data items, and todo information
+ */
+struct labdata {
+ /** node in ldns rbtree */
+ ldns_rbnode_t node;
+ /** the name of this label */
+ ldns_rdf* label;
+ /** full name of point in domain tree */
+ ldns_rdf* name;
+
+ /** parent in label tree (NULL for root) */
+ struct labdata* parent;
+ /** tree of sublabels (if any) */
+ ldns_rbtree_t* sublabels;
+
+ /** list of RRs for this label */
+ ldns_rr_list* rrlist;
+ /** have queries for this label been queued */
+ int done;
+};
+
+/** usage information for harvest */
+static void usage(char* nm)
+{
+ printf("usage: %s [options]\n", nm);
+ printf("-f fnm query list to read from file\n");
+ printf(" every line has format: qname qclass qtype\n");
+ printf("-v verbose (-v -v even more)\n");
+ printf("-C cfg config file with resolver options\n");
+ exit(1);
+}
+
+/** verbosity for harvest */
+static int hverb = 0;
+
+/** exit with error */
+static void error_exit(const char* str)
+{
+ printf("error: %s\n", str);
+ exit(1);
+}
+
+/** read a query file */
+static void
+qlist_read_file(struct harvest_data* data, char* fname)
+{
+ char buf[1024];
+ char nm[1024], cl[1024], tp[1024];
+ int r;
+ int num = 0;
+ FILE* in = fopen(fname, "r");
+ struct todo_item* t;
+ if(!in) {
+ perror(fname);
+ error_exit("could not open file");
+ }
+ while(fgets(buf, (int)sizeof(buf), in)) {
+ if(buf[0] == 0) continue;
+ if(buf[0] == '\n') continue;
+ /* allow some comments */
+ if(buf[0] == ';') continue;
+ if(buf[0] == '#') continue;
+ nm[0] = 0; cl[0] = 0; tp[0] = 0;
+ r = sscanf(buf, " %1023s %1023s %1023s", nm, cl, tp);
+ if(r == 0) continue;
+ t = (struct todo_item*)calloc(1, sizeof(*t));
+ if(!t) error_exit("out of memory");
+ t->qname = ldns_dname_new_frm_str(nm);
+ if(!t->qname) {
+ printf("parse error: %s\n", nm);
+ error_exit("bad qname");
+ }
+ t->depth = 0;
+ t->qtype = LDNS_RR_TYPE_A;
+ t->qclass = LDNS_RR_CLASS_IN;
+ if(r >= 2) {
+ if(strcmp(cl, "IN") == 0 || strcmp(cl, "CH") == 0)
+ t->qclass = ldns_get_rr_class_by_name(cl);
+ else t->qtype = ldns_get_rr_type_by_name(cl);
+ }
+ if(r >= 3) {
+ if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0)
+ t->qclass = ldns_get_rr_class_by_name(tp);
+ else t->qtype = ldns_get_rr_type_by_name(tp);
+ }
+ num++;
+
+ t->next = data->orig_list;
+ data->orig_list = t;
+ }
+ printf("read %s: %d queries\n", fname, num);
+ fclose(in);
+}
+
+/** compare two labels */
+static int
+lab_cmp(const void *x, const void *y)
+{
+ return ldns_dname_compare((const ldns_rdf*)x, (const ldns_rdf*)y);
+}
+
+/** create label entry */
+static struct labdata*
+lab_create(const char* name)
+{
+ struct labdata* lab = (struct labdata*)calloc(1, sizeof(*lab));
+ if(!lab) error_exit("out of memory");
+ lab->label = ldns_dname_new_frm_str(name);
+ if(!lab->label) error_exit("out of memory");
+ lab->name = ldns_dname_new_frm_str(name);
+ if(!lab->name) error_exit("out of memory");
+ lab->node.key = lab->label;
+ lab->node.data = lab;
+ lab->sublabels = ldns_rbtree_create(lab_cmp);
+ if(!lab->sublabels) error_exit("out of memory");
+ lab->rrlist = ldns_rr_list_new();
+ if(!lab->rrlist) error_exit("out of memory");
+
+ return lab;
+}
+
+/** for this name, lookup the label, create if does not exist */
+static struct labdata*
+find_create_lab(struct harvest_data* data, ldns_rdf* name)
+{
+ struct labdata* lab = data->root;
+ struct labdata* nextlab;
+ ldns_rdf* next;
+ uint8_t numlab = ldns_dname_label_count(name);
+ if((int)numlab > data->maxlabels)
+ data->maxlabels = (int)numlab;
+ while(numlab--) {
+ next = ldns_dname_label(name, numlab);
+ if(!next) error_exit("ldns_dname_label");
+
+ nextlab = (struct labdata*)
+ ldns_rbtree_search(lab->sublabels, next);
+ if(!nextlab) {
+ /* create it */
+ nextlab = (struct labdata*)calloc(1, sizeof(*lab));
+ if(!nextlab) error_exit("out of memory");
+ nextlab->label = ldns_rdf_clone(next);
+ if(!nextlab->label) error_exit("out of memory");
+ nextlab->node.key = nextlab->label;
+ nextlab->node.data = nextlab;
+ nextlab->sublabels = ldns_rbtree_create(lab_cmp);
+ if(!nextlab->sublabels) error_exit("out of memory");
+ nextlab->parent = lab;
+ nextlab->name = ldns_rdf_clone(next);
+ if(!nextlab->name) error_exit("out of memory");
+ if(ldns_dname_cat(nextlab->name, lab->name)
+ != LDNS_STATUS_OK) error_exit("outofmem");
+ nextlab->rrlist = ldns_rr_list_new();
+ if(!nextlab->rrlist) error_exit("out of memory");
+ (void)ldns_rbtree_insert(lab->sublabels,
+ &nextlab->node);
+ if(hverb) {
+ printf("new label: ");
+ ldns_rdf_print(stdout, nextlab->name);
+ printf("\n");
+ }
+ }
+ lab = nextlab;
+ ldns_rdf_deep_free(next);
+ }
+ return lab;
+}
+
+/** for given query, create todo items, and labels if needed */
+static void
+new_todo_item(struct harvest_data* data, ldns_rdf* qname, int qtype,
+ int qclass, int depth)
+{
+ struct labdata* lab = find_create_lab(data, qname);
+ struct todo_item* it;
+ if(!lab) error_exit("out of memory creating new label");
+ it = (struct todo_item*)calloc(1, sizeof(*it));
+ it->qname = ldns_rdf_clone(qname);
+ it->qtype = qtype;
+ it->qclass = qclass;
+ it->depth = depth;
+ it->lab = lab;
+ it->next = NULL;
+ if(data->todo_last)
+ data->todo_last->next = it;
+ else data->todo_list = it;
+ data->todo_last = it;
+ data->numtodo ++;
+ if(hverb >= 2) {
+ printf("new todo: ");
+ ldns_rdf_print(stdout, it->qname);
+ if(ldns_rr_descript((uint16_t)it->qtype) &&
+ ldns_rr_descript((uint16_t)it->qtype)->_name)
+ printf(" %s", ldns_rr_descript((uint16_t)
+ it->qtype)->_name);
+ if(ldns_lookup_by_id(ldns_rr_classes, it->qclass) &&
+ ldns_lookup_by_id(ldns_rr_classes, it->qclass)->name)
+ printf(" %s", ldns_lookup_by_id(ldns_rr_classes,
+ it->qclass)->name);
+ printf("\n");
+ }
+}
+
+/** add infra todo items for this query */
+static void
+new_todo_infra(struct harvest_data* data, struct labdata* startlab, int depth)
+{
+ struct labdata* lab;
+ for(lab = startlab; lab; lab = lab->parent) {
+ if(lab->done)
+ return;
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_NS,
+ LDNS_RR_CLASS_IN, depth);
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_SOA,
+ LDNS_RR_CLASS_IN, depth);
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_DNSKEY,
+ LDNS_RR_CLASS_IN, depth);
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_DS,
+ LDNS_RR_CLASS_IN, depth);
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_A,
+ LDNS_RR_CLASS_IN, depth);
+ new_todo_item(data, lab->name, LDNS_RR_TYPE_AAAA,
+ LDNS_RR_CLASS_IN, depth);
+ lab->done = 1;
+ }
+}
+
+/** make todo items for initial data */
+static void
+make_todo(struct harvest_data* data)
+{
+ struct todo_item* it;
+ for(it=data->orig_list; it; it = it->next) {
+ /* create todo item for this query itself */
+ new_todo_item(data, it->qname, it->qtype, it->qclass, 0);
+ /* create todo items for infra queries to support it */
+ new_todo_infra(data, data->todo_list->lab,
+ data->todo_list->depth);
+ }
+}
+
+/** store RR and make new work items for it if needed */
+static void
+process_rr(struct harvest_data* data, ldns_rr* rr, int depth)
+{
+ /* must free or store rr */
+ struct labdata* lab = find_create_lab(data, ldns_rr_owner(rr));
+ if(!lab) error_exit("cannot find/create label");
+ /* generate extra queries */
+ if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
+ new_todo_infra(data, find_create_lab(data,
+ ldns_rr_ns_nsdname(rr)), depth+1);
+ } else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_MX) {
+ new_todo_infra(data, find_create_lab(data,
+ ldns_rr_mx_exchange(rr)), depth+1);
+ } else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
+ new_todo_infra(data, find_create_lab(data,
+ ldns_rr_rdf(rr, 0)), depth+1);
+ } else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_CNAME) {
+ int t = ldns_rr_get_type(rr);
+ if(t!=LDNS_RR_TYPE_A && t!=LDNS_RR_TYPE_AAAA &&
+ t!=LDNS_RR_TYPE_SOA && t!=LDNS_RR_TYPE_NS &&
+ t!=LDNS_RR_TYPE_DS && t!=LDNS_RR_TYPE_DNSKEY)
+ new_todo_item(data, ldns_rr_rdf(rr, 0), t,
+ ldns_rr_get_class(rr), depth+1);
+ /* can get caught in CNAME loop, but depth will
+ * catch that; unbound cache helps too(servfails on
+ * a cname loop) */
+ new_todo_infra(data, find_create_lab(data,
+ ldns_rr_rdf(rr, 0)), depth+1);
+ }
+ /* store it */
+ if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NSEC) {
+ /* find correct zone to store NSEC in (for delegation zones) */
+ if(ldns_dname_compare(ldns_rr_rdf(rr, 0), ldns_rr_owner(rr))
+ == 0) {
+ /* store at the single name = apex */
+ } else if(!ldns_dname_is_subdomain(ldns_rr_rdf(rr, 0),
+ ldns_rr_owner(rr)) && lab->parent) {
+ /* if owner NSEC subdomain-of-owner then
+ * store at owner (owner is apex or empty nonterminal).
+ * Otherwise at owner parent. */
+ lab = lab->parent;
+ }
+ } else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS) {
+ /* store DSes in parent zone */
+ if(lab->parent)
+ lab = lab->parent;
+ } else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NSEC3) {
+ /* store NSEC3s one label up at zone apex */
+ if(lab->parent)
+ lab = lab->parent;
+ }
+ /* we assume NS set is equal across parent-child border. */
+
+ if(!ldns_rr_list_contains_rr(lab->rrlist, rr)) {
+ if(hverb >= 2) {
+ printf("store RR ");
+ ldns_rr_print(stdout, rr);
+ printf("\n");
+ }
+ if(!ldns_rr_list_push_rr(lab->rrlist, rr))
+ error_exit("outofmem ldns_rr_list_push_rr");
+ data->num_rrs++;
+ } else {
+ if(hverb >= 2) {
+ printf("duplicate RR ");
+ ldns_rr_print(stdout, rr);
+ printf("\n");
+ }
+ ldns_rr_free(rr);
+ }
+}
+
+/** store RRs and make new work items if needed */
+static void
+process_pkt(struct harvest_data* data, ldns_pkt* pkt, int depth)
+{
+ size_t i;
+ ldns_rr_list* list;
+ list = ldns_pkt_get_section_clone(pkt, LDNS_SECTION_ANY_NOQUESTION);
+ if(!list) error_exit("outofmemory");
+ for(i=0; i<ldns_rr_list_rr_count(list); i++) {
+ process_rr(data, ldns_rr_list_rr(list, i), depth);
+ }
+ ldns_rr_list_free(list);
+}
+
+/** process a todo item */
+static void
+process(struct harvest_data* data, struct todo_item* it)
+{
+ int r;
+ char* nm;
+ struct ub_result* result = NULL;
+ ldns_pkt* pkt = NULL;
+ ldns_status s;
+ if(hverb) {
+ printf("process: ");
+ ldns_rdf_print(stdout, it->qname);
+ if(ldns_rr_descript((uint16_t)it->qtype) &&
+ ldns_rr_descript((uint16_t)it->qtype)->_name)
+ printf(" %s", ldns_rr_descript((uint16_t)
+ it->qtype)->_name);
+ if(ldns_lookup_by_id(ldns_rr_classes, it->qclass) &&
+ ldns_lookup_by_id(ldns_rr_classes, it->qclass)->name)
+ printf(" %s", ldns_lookup_by_id(ldns_rr_classes,
+ it->qclass)->name);
+ printf("\n");
+ }
+ /* do lookup */
+ nm = ldns_rdf2str(it->qname);
+ if(!nm) error_exit("ldns_rdf2str");
+ r = ub_resolve(data->ctx, nm, it->qtype, it->qclass, &result);
+ if(r != 0) {
+ printf("ub_resolve(%s, %d, %d): %s\n", nm, it->qtype,
+ it->qclass, ub_strerror(r));
+ free(nm);
+ return;
+ }
+ if(result->rcode == LDNS_RCODE_SERVFAIL) {
+ free(nm);
+ return;
+ }
+ /* even if result is a negative, try to store resulting SOA/NSEC */
+
+ /* create ldns pkt */
+ s = ldns_wire2pkt(&pkt, result->answer_packet,
+ (size_t)result->answer_len);
+ if(s != LDNS_STATUS_OK) {
+ printf("ldns_wire2pkt failed! %s %d %d %s %d\n", nm,
+ it->qtype, it->qclass, ldns_get_errorstr_by_id(s),
+ result->answer_len);
+ free(nm);
+ return;
+ }
+ if(hverb >= 2) {
+ printf("answer: ");
+ ldns_pkt_print(stdout, pkt);
+ printf("\n");
+ }
+ /* process results */
+ process_pkt(data, pkt, it->depth);
+
+ ldns_pkt_free(pkt);
+ free(nm);
+ ub_resolve_free(result);
+}
+
+/** perform main harvesting */
+static void
+harvest_main(struct harvest_data* data)
+{
+ struct todo_item* it;
+ int numdone = 0;
+ /* register todo queries for all original queries */
+ make_todo(data);
+ printf("depth 0: done %d todo %d\n", 0, data->numtodo);
+ /* pick up a todo item and process it */
+ while(data->todo_list) {
+ numdone++;
+ it = data->todo_list;
+ data->todo_list = it->next;
+ if(!data->todo_list) data->todo_last = NULL;
+ if(numdone%1000==0 || it->depth > data->curdepth) {
+ data->curdepth = it->depth;
+ printf("depth %d: done %d todo %d, %d rrs\n",
+ it->depth, numdone, data->numtodo,
+ data->num_rrs);
+ }
+ if(it->depth >= data->maxdepth) {
+ printf("obtained %d rrs to a max of %d labels.\n",
+ data->num_rrs, data->maxlabels);
+ return;
+ }
+ data->numtodo--;
+ process(data, it);
+ usleep(1000000/100);
+ }
+}
+
+/** create directory if it does not exist */
+static void
+hv_mkdir(char* dir)
+{
+#ifdef MKDIR_HAS_ONE_ARG
+ if(mkdir(dir) == -1) {
+#else
+ if(mkdir(dir, 0755) == -1) {
+#endif
+ if(errno == EEXIST)
+ return;
+ perror(dir);
+ error_exit("mkdir failed");
+ }
+}
+
+
+/** see if rrlist contains a SOA record */
+static ldns_rr*
+has_SOA(ldns_rr_list* list)
+{
+ size_t i;
+ for(i=0; i<ldns_rr_list_rr_count(list); i++) {
+ if(ldns_rr_get_type(ldns_rr_list_rr(list, i))
+ == LDNS_RR_TYPE_SOA)
+ return ldns_rr_list_rr(list, i);
+ }
+ return NULL;
+}
+
+/** write moredata for a zone*/
+static void
+write_moredata(struct harvest_data* data, struct labdata* zone,
+ FILE *f, struct labdata* thislab, ldns_rr* nslist)
+{
+ struct labdata* lab;
+ size_t i;
+ ldns_rr* ns;
+ LDNS_RBTREE_FOR(lab, struct labdata*, thislab->sublabels) {
+ if(has_SOA(lab->rrlist)) {
+ /* copy only NS glue */
+ for(i=0; i<ldns_rr_list_rr_count(lab->rrlist); i++) {
+ ns = ldns_rr_list_rr(lab->rrlist, i);
+ if(ldns_rr_get_type(ns) == LDNS_RR_TYPE_NS) {
+ ldns_rr_print(f, ns);
+ if(ldns_dname_is_subdomain(
+ ldns_rr_ns_nsdname(ns),
+ lab->name)) {
+ ldns_rr_push_rdf(nslist,
+ ldns_rdf_clone(
+ ldns_rr_ns_nsdname(ns)));
+ }
+ }
+ }
+ } else {
+ /* copy all, recurse */
+ for(i=0; i<ldns_rr_list_rr_count(lab->rrlist); i++) {
+ ldns_rr_print(f,
+ ldns_rr_list_rr(lab->rrlist, i));
+ }
+ write_moredata(data, zone, f, lab, nslist);
+ }
+ }
+}
+
+/** find and write glue into zone file */
+static void
+write_glue(struct harvest_data* data, struct labdata* thislab, FILE* f,
+ ldns_rdf* name, int dep)
+{
+ size_t i;
+ struct labdata* lab;
+ ldns_rr* rr;
+ if(ldns_dname_compare(name, thislab->name) == 0) {
+ /* this is it! Did we go outside the zone? */
+ if(dep == 0)
+ return;
+ /* find A and AAAA */
+ for(i=0; i<ldns_rr_list_rr_count(thislab->rrlist); i++) {
+ rr = ldns_rr_list_rr(thislab->rrlist, i);
+ if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A ||
+ ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
+ ldns_rr_print(f, rr);
+ }
+ }
+ return;
+ }
+ /* recurse deeper */
+ LDNS_RBTREE_FOR(lab, struct labdata*, thislab->sublabels) {
+ if(has_SOA(lab->rrlist)) {
+ write_glue(data, lab, f, name, dep+1);
+ } else {
+ write_glue(data, lab, f, name, dep);
+ }
+ }
+}
+
+/** write zonefile for zone at this apex */
+static void
+write_zonefile(struct harvest_data* data, int dep, FILE* zlist,
+ struct labdata* apex, ldns_rr* soa)
+{
+ FILE *f;
+ char fname[1024];
+ char* zname = ldns_rdf2str(apex->name);
+ time_t tm = time(NULL);
+ size_t i;
+ ldns_rr* nslist;
+ if(!zname) error_exit("out of mem ldns_rdf2str");
+ if(strcmp(zname, ".") == 0)
+ snprintf(fname, sizeof(fname), "l%d/root.zone", dep);
+ else snprintf(fname, sizeof(fname), "l%d/%szone", dep, zname);
+
+ fprintf(zlist, "zone: name: \"%s\" %s%szonefile: \"%s\"\n",
+ zname,
+ strlen(zname)/8<1?"\t":"",
+ strlen(zname)/8<2?"\t":"",
+ fname);
+
+ if(hverb) printf("writing %s\n", fname);
+ f = fopen(fname, "w");
+ if(!f) {
+ perror(fname);
+ error_exit("cannot open zone file");
+ }
+ fprintf(f, "; %s - generated by harvest program.\n", fname);
+ fprintf(f, "; zone name %s - this is a partial snapshot of "
+ "data relevant to the query list.\n", zname);
+ fprintf(f, "; created %u - date %s\n", (unsigned)tm, ctime(&tm));
+ ldns_rr_print(f, soa);
+ fprintf(f, "\n");
+ for(i=0; i<ldns_rr_list_rr_count(apex->rrlist); i++) {
+ if(ldns_rr_get_type(ldns_rr_list_rr(apex->rrlist, i))
+ == LDNS_RR_TYPE_SOA) continue;
+ ldns_rr_print(f, ldns_rr_list_rr(apex->rrlist, i));
+ }
+ /* search for more data - subdomains inside the zone, NS glue */
+ nslist = ldns_rr_new();
+ if(!nslist) error_exit("out of memory");
+ fprintf(f, "; end of apex, more data follows\n");
+ write_moredata(data, apex, f, apex, nslist);
+
+ /* add NS from apex that need glue too */
+ for(i=0; i<ldns_rr_list_rr_count(apex->rrlist); i++) {
+ if(ldns_rr_get_type(ldns_rr_list_rr(apex->rrlist, i)) !=
+ LDNS_RR_TYPE_NS)
+ continue;
+ /* these are only added again if in a subzone */
+ if(ldns_dname_is_subdomain(ldns_rr_ns_nsdname(
+ ldns_rr_list_rr(apex->rrlist, i)), apex->name)) {
+ ldns_rr_push_rdf(nslist, ldns_rdf_clone(
+ ldns_rr_ns_nsdname(ldns_rr_list_rr(
+ apex->rrlist, i))));
+ }
+ }
+
+ fprintf(f, "; glue data follows\n");
+ /* lookup and add glue (if not already in zone) */
+ for(i=0; i<ldns_rr_rd_count(nslist); i++) {
+ write_glue(data, apex, f, ldns_rr_rdf(nslist, i), 0);
+ }
+
+ fclose(f);
+ ldns_rr_free(nslist);
+ free(zname);
+}
+
+/** create zones at depth d in label tree */
+static void
+create_zones(struct harvest_data* data, int dep, FILE* zlist,
+ struct labdata* labnow, int depnow)
+{
+ struct labdata* s;
+ ldns_rr* soa;
+ if(depnow == dep) {
+ /* see if this is a zone start - a SOA */
+ if((soa=has_SOA(labnow->rrlist))) {
+ write_zonefile(data, dep, zlist, labnow, soa);
+ data->num_zones++;
+ }
+ return;
+ }
+ /* recurse */
+ LDNS_RBTREE_FOR(s, struct labdata*, labnow->sublabels) {
+ create_zones(data, dep, zlist, s, depnow+1);
+ }
+}
+
+/** sort rrlists */
+static void
+harvest_sort(struct labdata* lab)
+{
+ struct labdata* s;
+ /* prettier output if sorted here */
+ ldns_rr_list_sort(lab->rrlist);
+ /* and recurse */
+ LDNS_RBTREE_FOR(s, struct labdata*, lab->sublabels) {
+ harvest_sort(s);
+ }
+}
+
+/** output harvested results */
+static void
+harvest_output(struct harvest_data* data)
+{
+ int d;
+ char buf[20];
+ FILE* zlist;
+ int lastzones;
+ hv_mkdir(data->resultdir);
+ if(chdir(data->resultdir) == -1) {
+ perror(data->resultdir);
+ error_exit("cannot chdir");
+ }
+ harvest_sort(data->root);
+ /* create zones */
+ for(d = 0; d<data->maxlabels; d++) {
+ lastzones = data->num_zones;
+ printf("creating zones %d\n", d);
+ snprintf(buf, sizeof(buf), "l%d", d);
+ hv_mkdir(buf);
+ snprintf(buf, sizeof(buf), "l%d.zones", d);
+ zlist = fopen(buf, "w");
+ if(!zlist) {
+ perror(buf);
+ error_exit("cannot write zonelist file");
+ }
+ fprintf(zlist, "# partial zones at depth %d\n", d);
+ create_zones(data, d, zlist, data->root, 0);
+ fclose(zlist);
+ printf("creating zones %d - %d zones written\n", d,
+ data->num_zones - lastzones);
+ }
+}
+
+/** getopt global, in case header files fail to declare it. */
+extern int optind;
+/** getopt global, in case header files fail to declare it. */
+extern char* optarg;
+
+/** main program for harvest */
+int main(int argc, char* argv[])
+{
+ struct harvest_data data;
+ char* nm = argv[0];
+ int c;
+
+ /* defaults */
+ memset(&data, 0, sizeof(data));
+ data.ctx = ub_ctx_create();
+ data.resultdir = strdup("harvested_zones");
+ if(!data.resultdir) error_exit("out of memory");
+ data.maxdepth = 2;
+
+ /* parse the options */
+ while( (c=getopt(argc, argv, "hf:vC:")) != -1) {
+ switch(c) {
+ case 'C':
+ if(ub_ctx_config(data.ctx, optarg) != 0)
+ error_exit("config read failed");
+ break;
+ case 'f':
+ qlist_read_file(&data, optarg);
+ break;
+ case 'v':
+ hverb++;
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage(nm);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if(argc != 0)
+ usage(nm);
+ if(data.orig_list == NULL)
+ error_exit("No queries to make, use -f (help with -h).");
+ data.root = lab_create(".");
+ if(!data.root) error_exit("out of memory");
+
+ /* harvest the data */
+ harvest_main(&data);
+ harvest_output(&data);
+
+ /* no cleanup except the context (to close open sockets) */
+ ub_ctx_delete(data.ctx);
+ return 0;
+}