1 /*
2 * Reads a zone file from disk and prints it to stdout, one RR per line.
3 * Adds artificial DS records and RRs.
4 * For the purpose of generating a test zone file
5 *
6 * (c) SIDN 2010/2011 - Marco Davids/Miek Gieben
7 *
8 * See the LICENSE file for the license
9 */
10
11 #include "config.h"
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <ldns/ldns.h>
15 #include <errno.h>
16
17 #define NUM_DS 4 /* maximum of 4 DS records per delegation */
18 #define ALGO 8 /* Algorithm to use for fake DS records - RSASHA256 - RFC5702 */
19 #define DIGESTTYPE 2 /* Digest type to use for fake DS records - SHA-256 - RFC 4509 */
20
21
22 /**
23 * Usage function.
24 *
25 */
26 static void
usage(FILE * fp,char * prog)27 usage(FILE *fp, char *prog) {
28 fprintf(fp, "\n\nUsage: %s [-hsv] [-ap NUM] [-o ORIGIN] [<zonefile>]\n", prog);
29 fprintf(fp, "\tReads a zonefile and add some artificial NS RRsets and DS records.\n");
30 fprintf(fp, "\tIf no zonefile is given, the zone is read from stdin.\n");
31 fprintf(fp, "\t-a <NUM> add NUM artificial delegations (NS RRSets) to output.\n");
32 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);
33 fprintf(fp, "\t-o ORIGIN sets an $ORIGIN, which can be handy if the one in the zonefile is set to @.\n");
34 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");
35 fprintf(fp, "\t-h show this text.\n");
36 fprintf(fp, "\t-v shows the version and exits.\n");
37 fprintf(fp, "\nif no file is given standard input is read.\n\n");
38 }
39
40 /**
41 * Insert the DS records, return the amount added.
42 *
43 */
44 static int
insert_ds(ldns_rdf * dsowner,uint32_t ttl)45 insert_ds(ldns_rdf *dsowner, uint32_t ttl)
46 {
47 int d, dsrand;
48 int keytag = 0;
49 char *dsownerstr;
50 char digeststr[70];
51
52 /**
53 * Average the amount of DS records per delegation a little.
54 */
55 dsrand = 1+rand() % NUM_DS;
56 for(d = 0; d < dsrand; d++) {
57 keytag = 1+rand() % 65535;
58 /**
59 * Dynamic hashes method below is still too slow... 20% slower than a fixed string...
60 *
61 * We assume RAND_MAX is 32 bit, http://www.gnu.org/s/libc/manual/html_node/ISO-Random.html
62 * 2147483647 or 0x7FFFFFFF
63 */
64 snprintf(digeststr, 65,
65 "%08x%08x%08x%08x%08x%08x%08x%08x",
66 (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX,
67 (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX,
68 (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX);
69 dsownerstr = ldns_rdf2str(dsowner);
70 fprintf(stdout, "%s\t%u\tIN\tDS\t%d %d %d %s\n", dsownerstr, (unsigned) ttl, keytag, ALGO, DIGESTTYPE, digeststr);
71 }
72 return dsrand;
73 }
74
75 int
main(int argc,char ** argv)76 main(int argc, char **argv) {
77 char *filename, *rrstr, *ownerstr;
78 const char *classtypestr1 = "IN NS ns1.example.com.";
79 const char *classtypestr2 = "IN NS ns2.example.com.";
80 const size_t classtypelen = strlen(classtypestr1);
81 /* Simply because this was developed by SIDN and we don't use xn-- for .nl :-) */
82 const char *punystr = "xn--fake-rr";
83 const size_t punylen = strlen(punystr);
84 size_t rrstrlen, ownerlen;
85 FILE *fp;
86 int c, nsrand;
87 uint32_t ttl;
88 int counta,countd,countr;
89 ldns_zone *z;
90 ldns_rdf *origin = NULL;
91 int line_nr = 0;
92 int addrrs = 0;
93 int dsperc = 0;
94 bool canonicalize = true;
95 bool sort = true;
96 bool do_ds = false;
97 ldns_status s;
98 size_t i;
99 ldns_rr_list *rrset_list;
100 ldns_rdf *owner;
101 ldns_rr_type cur_rr_type;
102 ldns_rr *cur_rr;
103 ldns_status status;
104
105 counta = countd = countr = 0;
106
107 /**
108 * Set some random seed.
109 */
110 srand((unsigned int)time(NULL));
111
112 /**
113 * Commandline options.
114 */
115 while ((c = getopt(argc, argv, "a:p:shvo:")) != -1) {
116 switch (c) {
117 case 'a':
118 addrrs = atoi(optarg);
119 if (addrrs <= 0) {
120 fprintf(stderr, "error\n");
121 exit(EXIT_FAILURE);
122 }
123 break;
124 case 'o':
125 origin = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, optarg);
126 if (!origin) {
127 fprintf(stderr, "error: creating origin from -o %s failed.\n", optarg);
128 exit(EXIT_FAILURE);
129 }
130 break;
131 case 'p':
132 dsperc = atoi(optarg);
133 if (dsperc < 0 || dsperc > 100) {
134 fprintf(stderr, "error: percentage of signed delegations must be between [0-100].\n");
135 exit(EXIT_FAILURE);
136 }
137 do_ds = true;
138 break;
139 case 's':
140 sort = false;
141 canonicalize = false;
142 break;
143 case 'h':
144 usage(stdout, argv[0]);
145 exit(EXIT_SUCCESS);
146 case 'v':
147 fprintf(stdout, "ldns-gen-zone version %s (ldns version %s)\n", LDNS_VERSION, ldns_version());
148 exit(EXIT_SUCCESS);
149 default:
150 fprintf(stderr, "\nTry -h for more information.\n\n");
151 exit(EXIT_FAILURE);
152 }
153 }
154 argc -= optind;
155 argv += optind;
156
157 /**
158 * Read zone.
159 */
160 if (argc == 0) {
161 fp = stdin;
162 } else {
163 filename = argv[0];
164 fp = fopen(filename, "r");
165 if (!fp) {
166 fprintf(stderr, "Unable to open %s: %s\n", filename, strerror (errno));
167 exit(EXIT_FAILURE);
168 }
169 }
170 s = ldns_zone_new_frm_fp_l(&z, fp, origin, 0, LDNS_RR_CLASS_IN, &line_nr);
171 if (s != LDNS_STATUS_OK) {
172 fprintf(stderr, "%s at line %d\n", ldns_get_errorstr_by_id(s), line_nr);
173 exit(EXIT_FAILURE);
174 }
175 if (!ldns_zone_soa(z)) {
176 fprintf(stderr, "No zone data seen\n");
177 exit(EXIT_FAILURE);
178 }
179
180 ttl = ldns_rr_ttl(ldns_zone_soa(z));
181 if (!origin) {
182 origin = ldns_rr_owner(ldns_zone_soa(z));
183 // Check for root (.) origin here TODO(MG)
184 }
185 ownerstr = ldns_rdf2str(origin);
186 if (!ownerstr) {
187 fprintf(stderr, "ldns_rdf2str(origin) failed\n");
188 exit(EXIT_FAILURE);
189 }
190 ownerlen = strlen(ownerstr);
191
192 ldns_rr_print(stdout, ldns_zone_soa(z));
193
194 if (addrrs > 0) {
195 while (addrrs > counta) {
196 counta++;
197 rrstrlen = punylen + ownerlen + classtypelen + 4;
198 rrstrlen *= 2; /* estimate */
199 rrstr = (char*)malloc(rrstrlen);
200 if (!rrstr) {
201 fprintf(stderr, "malloc() failed: Out of memory\n");
202 exit(EXIT_FAILURE);
203 }
204 (void)snprintf(rrstr, rrstrlen, "%s%d.%s %u %s", punystr, counta,
205 ownerstr, (unsigned) ttl, classtypestr1);
206 status = ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL);
207 if (status == LDNS_STATUS_OK) {
208 ldns_rr_print(stdout, cur_rr);
209 ldns_rr_free(cur_rr);
210 } else {
211 fprintf(stderr, "ldns_rr_new_frm_str() failed\n");
212 exit(EXIT_FAILURE);
213 }
214
215 (void)snprintf(rrstr, rrstrlen, "%s%d.%s %u %s", punystr, counta,
216 ownerstr, (unsigned) ttl, classtypestr2);
217 status = ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL);
218 if (status == LDNS_STATUS_OK) {
219 ldns_rr_print(stdout, cur_rr);
220 } else {
221 fprintf(stderr, "ldns_rr_new_frm_str() failed\n");
222 exit(EXIT_FAILURE);
223 }
224
225 free(rrstr);
226
227 /* may we add a DS record as well? */
228 if (do_ds) {
229 /*
230 * Per definition this may not be the same as the origin, so no
231 * check required same for NS check - so the only thing left is some
232 * randomization.
233 */
234 nsrand = rand() % 100;
235 if (nsrand < dsperc) {
236 owner = ldns_rr_owner(cur_rr);
237 ttl = ldns_rr_ttl(cur_rr);
238 countd += insert_ds(owner, ttl);
239 }
240 }
241 ldns_rr_free(cur_rr);
242 }
243 }
244
245 if (!do_ds) {
246 ldns_rr_list_print(stdout, ldns_zone_rrs(z));
247 } else {
248 /*
249 * We use dns_rr_list_pop_rrset and that requires a sorted list weird things may happen
250 * if the -s option was used on unsorted, non-canonicalized input
251 */
252 if (canonicalize) {
253 ldns_rr2canonical(ldns_zone_soa(z));
254 for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z)); i++) {
255 ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z), i));
256 }
257 }
258
259 if (sort) {
260 ldns_zone_sort(z);
261 }
262
263 /* Work on a per RRset basis for DS records - weird things will happen if the -s option
264 * was used in combination with an unsorted zone file
265 */
266 while((rrset_list = ldns_rr_list_pop_rrset(ldns_zone_rrs(z)))) {
267 owner = ldns_rr_list_owner(rrset_list);
268 cur_rr_type = ldns_rr_list_type(rrset_list);
269 /**
270 * Print them...
271 */
272 cur_rr = ldns_rr_list_pop_rr(rrset_list);
273 while (cur_rr) {
274 ttl = ldns_rr_ttl(cur_rr);
275 fprintf(stdout, "%s", ldns_rr2str(cur_rr));
276 cur_rr = ldns_rr_list_pop_rr(rrset_list);
277 }
278 /*
279 * And all the way at the end a DS record if
280 * we are dealing with an NS rrset
281 */
282 nsrand = rand() % 100;
283 if (nsrand == 0) {
284 nsrand = 100;
285 }
286
287 if ((cur_rr_type == LDNS_RR_TYPE_NS) &&
288 (ldns_rdf_compare(owner, origin) != 0) && (nsrand < dsperc)) {
289 /**
290 * No DS records for the $ORIGIN, only for delegations, obey dsperc.
291 */
292 countr++;
293 countd += insert_ds(owner, ttl);
294 }
295 ldns_rr_list_free(rrset_list);
296 ldns_rdf_free(owner);
297 }
298 }
299
300 /**
301 * And done...
302 */
303 fclose(fp);
304 fprintf(stdout, ";; Added %d DS records (percentage was %d) to %d NS RRset's (from input-zone: %d, from added: %d)\n;; lines in original input-zone: %d\n",
305 countd, dsperc, counta + countr, countr, counta, line_nr);
306 exit(EXIT_SUCCESS);
307 }
308