273 lines
6.5 KiB
C
273 lines
6.5 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|