summaryrefslogtreecommitdiff
path: root/examples/ldns-zsplit.c
blob: 618a57b03f7d7de08439e7c13d19145842d9e7d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
 * read a zone from disk and split it up:
 *
 * zone: SOA a b c d e f g h i j k l 
 * becomes:
 * zone1: SOA a b c d e f
 * zone2: SOA f g h i k l
 *
 * ldns-catzone removes the last name and put
 * the zone back together.
 *
 * This way you can incremental sign a zone
 *
 * See the file LICENSE for the license
 */

#include "config.h"
#include <errno.h>
#include <ldns/ldns.h>

#define DEFAULT_SPLIT 	1000
#define FILE_SIZE 	255
#define SPLIT_MAX 	999 
#define NO_SPLIT 	0
#define INTENT_TO_SPLIT 1
#define SPLIT_NOW	2

static void
usage(FILE *f, char *progname)
{
		fprintf(f, "Usage: %s [OPTIONS] <zonefile> [keys]\n", progname);
		fprintf(f, "  Cut a zone file into pieces, each part is put in a file\n");
		fprintf(f, "  named: '<zonefile>.NNN'. Where NNN is a integer ranging 000 to 999.\n");
		fprintf(f, "  If key files are given they are inserted in each part.\n");
		fprintf(f, "  The original SOA is also included in each part, making them correct DNS\n");
		fprintf(f, "  (mini) zones.\n");
		fprintf(f, "  This utility can be used to parallel sign a large zone.\n");
		fprintf(f, "  To make it work the original zone needs to be canonical ordered.\n");
		fprintf(f, "\nOPTIONS:\n");
		fprintf(f, "  -n NUMBER\tsplit after this many RRs\n");
		fprintf(f, "  -o ORIGIN\tuse this as initial origin, for zones starting with @\n");
		fprintf(f, "  -z\t\tsort the zone prior to splitting. The current ldns zone\n");
		fprintf(f, "  \t\timplementation makes this unusable for large zones.\n");
		fprintf(f, "  -v\t\tshow version number and exit\n");
}


/* key the keys from the cmd line */
static ldns_rr_list *
open_keyfiles(char **files, uint16_t filec) 
{
	uint16_t i;
	ldns_rr_list *pubkeys;
	ldns_rr *k;
	FILE *kfp;

 	pubkeys = ldns_rr_list_new();
	
	for (i = 0; i < filec; i++) {
		if (!(kfp = fopen(files[i], "r"))) {
			fprintf(stderr, "Error opening key file %s: %s\n", files[i], strerror(errno));
			return NULL;
		}
		if (ldns_rr_new_frm_fp(&k, kfp, NULL, NULL, NULL) != LDNS_STATUS_OK) {
			fprintf(stderr, "Error parsing the key file %s: %s\n", files[i], strerror(errno));
			ldns_rr_list_deep_free(pubkeys);
			return NULL;
		}
		fclose(kfp);
		ldns_rr_list_push_rr(pubkeys, k);
	}
	return pubkeys;
}

/* open a new zone file with the correct suffix */
static FILE *
open_newfile(char *basename, ldns_zone *z, size_t counter, ldns_rr_list *keys)
{
	char filename[FILE_SIZE];
	FILE *fp;

	if (counter > SPLIT_MAX)  {
		fprintf(stderr, "Maximum split count reached %u\n", (unsigned int) counter);
		return NULL;
	}

	snprintf(filename, FILE_SIZE, "%s.%03u", basename, (unsigned int) counter);

	if (!(fp = fopen(filename, "w"))) {
		fprintf(stderr, "Cannot open zone %s: %s\n", filename, strerror(errno));
		return NULL;
	} else {
		fprintf(stderr, "%s\n", filename);
	}
	ldns_rr_print(fp, ldns_zone_soa(z));
	if (keys) {
		ldns_rr_list_print(fp, keys);

	}
	return fp;
}

int
main(int argc, char **argv)
{
	char *progname;
	FILE *fp;
	ldns_zone *z;
	ldns_rr_list *zrrs;
	ldns_rdf *lastname;
	int c; 
	int line_nr;
	size_t split;
	size_t i;
	int splitting;
	int compare;
	size_t file_counter;
	ldns_rdf *origin;
	ldns_rdf *current_rdf;
	ldns_rr *current_rr;
	ldns_rr_list *last_rrset;
	ldns_rr_list *pubkeys;
	bool sort;
	ldns_status s;

	progname = strdup(argv[0]);
	split = 0;
	splitting = NO_SPLIT; 
	file_counter = 0;
	lastname = NULL;
	origin = NULL;
	last_rrset = ldns_rr_list_new();
	sort = false;

	while ((c = getopt(argc, argv, "n:o:zv")) != -1) {
		switch(c) {
			case 'n':
				split = (size_t)atoi(optarg);
				if (split == 0) {
					fprintf(stderr, "-n want a integer\n");
					exit(EXIT_FAILURE);
				}
				break;
			case 'o':
				origin = ldns_dname_new_frm_str(strdup(optarg));
				if (!origin) {
					fprintf(stderr, "Cannot convert the origin %s to a domainname\n", optarg);
					exit(EXIT_FAILURE);
				}
				break;
			case 'v':
				printf("zone file splitter version %s (ldns version %s)\n", LDNS_VERSION, ldns_version());
				exit(EXIT_SUCCESS);
				break;
			case 'z':
				sort = true;
				break;
			default:
				fprintf(stderr, "Unrecognized option\n");
				usage(stdout, progname);
				exit(EXIT_FAILURE);
		}
	}
	if (split == 0) {
		split = DEFAULT_SPLIT;
	}
	
	argc -= optind;
	argv += optind;

	if (argc < 1) {
		usage(stdout, progname);
		exit(EXIT_FAILURE);
	}

	if (!(fp = fopen(argv[0], "r"))) {
		fprintf(stderr, "Unable to open %s: %s\n", argv[0], strerror(errno));
		exit(EXIT_FAILURE);
	}

	/* get the keys */
	pubkeys = open_keyfiles(argv + 1, (uint16_t) argc - 1);
	
	/* suck in the entire zone ... */
	if (!origin) {
		origin = ldns_dname_new_frm_str(".");
	}
	
	s = ldns_zone_new_frm_fp_l(&z, fp, origin, 0, LDNS_RR_CLASS_IN, &line_nr);
	fclose(fp);

	if (s != LDNS_STATUS_OK) {
		fprintf(stderr, "Zone file %s could not be parsed correctly: %s at line %d\n", 
				argv[0],
				ldns_get_errorstr_by_id(s),
				line_nr);
		exit(EXIT_FAILURE);
	}
	/* these kind of things can kill you... */
	if (sort) {
		ldns_zone_sort(z);
	}

	zrrs = ldns_zone_rrs(z);
	if (ldns_rr_list_rr_count(zrrs) / split > SPLIT_MAX) {
		fprintf(stderr, "The zone is too large for the used -n value: %u\n", (unsigned int) split);
		exit(EXIT_FAILURE);
	}
	
	
	/* Setup */
	if (!(fp = open_newfile(argv[0], z, file_counter, pubkeys))) {
			exit(EXIT_FAILURE);
	}

	for(i = 0; i < ldns_rr_list_rr_count(zrrs); i++) {
	
		current_rr = ldns_rr_list_rr(zrrs, i);
		current_rdf = ldns_rr_owner(current_rr);

		compare = ldns_dname_compare(current_rdf, lastname);

		if (compare == 0) {
			ldns_rr_list_push_rr(last_rrset, current_rr);
		} 

		if (i > 0 && (i % split) == 0) {
			splitting = INTENT_TO_SPLIT;
		}

		if (splitting == INTENT_TO_SPLIT) { 
			if (compare != 0) {
				splitting = SPLIT_NOW;
			} 
		}

		if (splitting == SPLIT_NOW) {
			fclose(fp);

			lastname = NULL;
			splitting = NO_SPLIT;
			file_counter++;
			if (!(fp = open_newfile(argv[0], z, file_counter, pubkeys))) {
				exit(EXIT_FAILURE);
			}

			/* insert the last RRset in the new file */
			ldns_rr_list_print(fp, last_rrset);

			/* print the current rr */
			ldns_rr_print(fp, current_rr); 

			/* remove them */
			ldns_rr_list_free(last_rrset); 
			last_rrset = ldns_rr_list_new();
			/* add the current RR */
			ldns_rr_list_push_rr(last_rrset, current_rr);
			continue;
		}
		if (splitting == NO_SPLIT || splitting == INTENT_TO_SPLIT) {
			ldns_rr_print(fp, current_rr); 
		}
		if (compare != 0) {
			/* remove them and then add the current one */
			ldns_rr_list_free(last_rrset); 
			last_rrset = ldns_rr_list_new();
			ldns_rr_list_push_rr(last_rrset, current_rr);
		}
		lastname = current_rdf;
	}
	fclose(fp); 
        exit(EXIT_SUCCESS);
}