/* * 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 #include #include #include #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 void usage(FILE *fp, char *prog) { fprintf(fp, "\n\nUsage: %s [-hsv] [-ap NUM] [-o ORIGIN] []\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 add NUM artificial delegations (NS RRSets) to output.\n"); fprintf(fp, "\t-p 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, uint32_t 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", (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX, (unsigned) rand()%RAND_MAX); dsownerstr = ldns_rdf2str(dsowner); fprintf(stdout, "%s\t%u\tIN\tDS\t%d %d %d %s\n", dsownerstr, (unsigned) ttl, keytag, ALGO, DIGESTTYPE, digeststr); } return dsrand; } int main(int argc, char **argv) { char *filename, *rrstr, *ownerstr; const char *classtypestr1 = "IN NS ns1.example.com."; const char *classtypestr2 = "IN NS ns2.example.com."; const size_t 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 size_t punylen = strlen(punystr); size_t rrstrlen, ownerlen; FILE *fp; int c, nsrand; uint32_t ttl; 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; ldns_status status; 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 line %d\n", ldns_get_errorstr_by_id(s), line_nr); exit(EXIT_FAILURE); } if (!ldns_zone_soa(z)) { fprintf(stderr, "No zone data seen\n"); 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; rrstrlen *= 2; /* estimate */ rrstr = (char*)malloc(rrstrlen); if (!rrstr) { fprintf(stderr, "malloc() failed: Out of memory\n"); exit(EXIT_FAILURE); } (void)snprintf(rrstr, rrstrlen, "%s%d.%s %u %s", punystr, counta, ownerstr, (unsigned) ttl, classtypestr1); status = ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL); if (status == LDNS_STATUS_OK) { ldns_rr_print(stdout, cur_rr); ldns_rr_free(cur_rr); } else { fprintf(stderr, "ldns_rr_new_frm_str() failed\n"); exit(EXIT_FAILURE); } (void)snprintf(rrstr, rrstrlen, "%s%d.%s %u %s", punystr, counta, ownerstr, (unsigned) ttl, classtypestr2); status = ldns_rr_new_frm_str(&cur_rr, rrstr, 0, NULL, NULL); if (status == LDNS_STATUS_OK) { ldns_rr_print(stdout, cur_rr); } else { fprintf(stderr, "ldns_rr_new_frm_str() failed\n"); exit(EXIT_FAILURE); } 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)))) { owner = ldns_rr_list_owner(rrset_list); cur_rr_type = ldns_rr_list_type(rrset_list); /** * Print them... */ cur_rr = ldns_rr_list_pop_rr(rrset_list); while (cur_rr) { ttl = ldns_rr_ttl(cur_rr); fprintf(stdout, "%s", ldns_rr2str(cur_rr)); cur_rr = ldns_rr_list_pop_rr(rrset_list); } /* * 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++; countd += insert_ds(owner, ttl); } ldns_rr_list_free(rrset_list); ldns_rdf_free(owner); } } /** * And done... */ fclose(fp); 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", countd, dsperc, counta + countr, countr, counta, line_nr); exit(EXIT_SUCCESS); }