freeswitch/libs/ldns/examples/ldns-zsplit.c

273 lines
6.5 KiB
C
Raw Normal View History

2011-03-25 16:23:32 +00:00
/*
* 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 unuseable 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));
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);
}