diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2012-07-04 14:24:26 +0000 | 
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2012-07-04 14:24:26 +0000 | 
| commit | afb79913ce00d885b8b43f7478e1e054edadb567 (patch) | |
| tree | b9037afac70edd3c6342318cedbbadc648b799ca /testcode/harvest.c | |
Diffstat (limited to 'testcode/harvest.c')
| -rw-r--r-- | testcode/harvest.c | 857 | 
1 files changed, 857 insertions, 0 deletions
diff --git a/testcode/harvest.c b/testcode/harvest.c new file mode 100644 index 000000000000..1952dc2a221f --- /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; +}  | 
