diff options
Diffstat (limited to 'examples/ldns-verify-zone.c')
-rw-r--r-- | examples/ldns-verify-zone.c | 914 |
1 files changed, 914 insertions, 0 deletions
diff --git a/examples/ldns-verify-zone.c b/examples/ldns-verify-zone.c new file mode 100644 index 0000000..8a438ce --- /dev/null +++ b/examples/ldns-verify-zone.c @@ -0,0 +1,914 @@ +/* + * read a zone file from disk and prints it, one RR per line + * + * (c) NLnetLabs 2008 + * + * See the file LICENSE for the license + * + * Missing from the checks: empty non-terminals + */ + +#include "config.h" +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> + +#include <ldns/ldns.h> + +#include <errno.h> + +#ifdef HAVE_SSL +#include <openssl/err.h> + +static int verbosity = 3; +static time_t check_time = 0; +static int32_t inception_offset = 0; +static int32_t expiration_offset = 0; +static bool do_sigchase = false; +static bool no_nomatch_msg = false; + +static FILE* myout; +static FILE* myerr; + +static void +update_error(ldns_status* result, ldns_status status) +{ + if (status != LDNS_STATUS_OK) { + if (*result == LDNS_STATUS_OK || *result == LDNS_STATUS_ERR || + ( *result == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY + && status != LDNS_STATUS_ERR + )) { + *result = status; + } + } +} + +static void +print_type(FILE* stream, ldns_rr_type type) +{ + const ldns_rr_descriptor *descriptor = ldns_rr_descript(type); + + if (descriptor && descriptor->_name) { + fprintf(stream, "%s", descriptor->_name); + } else { + fprintf(stream, "TYPE%u", type); + } +} + +static ldns_status +read_key_file(const char *filename, ldns_rr_list *keys) +{ + ldns_status status = LDNS_STATUS_ERR; + ldns_rr *rr; + FILE *fp; + uint32_t my_ttl = 0; + ldns_rdf *my_origin = NULL; + ldns_rdf *my_prev = NULL; + int line_nr; + + if (!(fp = fopen(filename, "r"))) { + return LDNS_STATUS_FILE_ERR; + } + while (!feof(fp)) { + status = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, + &my_prev, &line_nr); + + if (status == LDNS_STATUS_OK) { + + if ( ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS + || ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY) + + ldns_rr_list_push_rr(keys, rr); + + } else if ( status == LDNS_STATUS_SYNTAX_EMPTY + || status == LDNS_STATUS_SYNTAX_TTL + || status == LDNS_STATUS_SYNTAX_ORIGIN + || status == LDNS_STATUS_SYNTAX_INCLUDE) + + status = LDNS_STATUS_OK; + else + break; + } + fclose(fp); + return status; +} + + + +static void +print_rr_error(FILE* stream, ldns_rr* rr, const char* msg) +{ + if (verbosity > 0) { + fprintf(stream, "Error: %s for ", msg); + ldns_rdf_print(stream, ldns_rr_owner(rr)); + fprintf(stream, "\t"); + print_type(stream, ldns_rr_get_type(rr)); + fprintf(stream, "\n"); + } +} + +static void +print_rr_status_error(FILE* stream, ldns_rr* rr, ldns_status status) +{ + if (status != LDNS_STATUS_OK) { + print_rr_error(stream, rr, ldns_get_errorstr_by_id(status)); + if (verbosity > 0 && status == LDNS_STATUS_SSL_ERR) { + ERR_load_crypto_strings(); + ERR_print_errors_fp(stream); + } + } +} + +static void +print_rrs_status_error(FILE* stream, ldns_rr_list* rrs, ldns_status status, + ldns_dnssec_rrs* cur_sig) +{ + if (status != LDNS_STATUS_OK) { + if (ldns_rr_list_rr_count(rrs) > 0) { + print_rr_status_error(stream, ldns_rr_list_rr(rrs, 0), + status); + } else if (verbosity > 0) { + fprintf(stream, "Error: %s for <unknown>\n", + ldns_get_errorstr_by_id(status)); + } + if (verbosity >= 4) { + fprintf(stream, "RRSet:\n"); + ldns_rr_list_print(stream, rrs); + fprintf(stream, "Signature:\n"); + ldns_rr_print(stream, cur_sig->rr); + fprintf(stream, "\n"); + } + } +} + +static ldns_status +rrsig_check_time_margins(ldns_rr* rrsig +#if 0 /* Passing those as arguments becomes sensible when + * rrsig_check_time_margins will be added to the library. + */ + ,time_t check_time, int32_t inception_offset, int32_t expiration_offset +#endif + ) +{ + int32_t inception, expiration; + + inception = ldns_rdf2native_int32(ldns_rr_rrsig_inception (rrsig)); + expiration = ldns_rdf2native_int32(ldns_rr_rrsig_expiration(rrsig)); + + if (((int32_t) (check_time - inception_offset)) - inception < 0) { + return LDNS_STATUS_CRYPTO_SIG_NOT_INCEPTED_WITHIN_MARGIN; + } + if (expiration - ((int32_t) (check_time + expiration_offset)) < 0) { + return LDNS_STATUS_CRYPTO_SIG_EXPIRED_WITHIN_MARGIN; + } + return LDNS_STATUS_OK; +} + +static ldns_status +verify_rrs(ldns_rr_list* rrset_rrs, ldns_dnssec_rrs* cur_sig, + ldns_rr_list* keys) +{ + ldns_rr_list* good_keys; + ldns_status status, result = LDNS_STATUS_OK; + + while (cur_sig) { + good_keys = ldns_rr_list_new(); + status = ldns_verify_rrsig_keylist_time(rrset_rrs, cur_sig->rr, + keys, check_time, good_keys); + status = status ? status + : rrsig_check_time_margins(cur_sig->rr); + if (status != LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY || + !no_nomatch_msg) { + + print_rrs_status_error(myerr, rrset_rrs, status, + cur_sig); + } + update_error(&result, status); + ldns_rr_list_free(good_keys); + cur_sig = cur_sig->next; + } + return result; +} + +static ldns_status +verify_dnssec_rrset(ldns_rdf *zone_name, ldns_rdf *name, + ldns_dnssec_rrsets *rrset, ldns_rr_list *keys) +{ + ldns_rr_list *rrset_rrs; + ldns_dnssec_rrs *cur_rr, *cur_sig; + ldns_status status; + + if (!rrset->rrs) return LDNS_STATUS_OK; + + rrset_rrs = ldns_rr_list_new(); + cur_rr = rrset->rrs; + while(cur_rr && cur_rr->rr) { + ldns_rr_list_push_rr(rrset_rrs, cur_rr->rr); + cur_rr = cur_rr->next; + } + cur_sig = rrset->signatures; + if (cur_sig) { + status = verify_rrs(rrset_rrs, cur_sig, keys); + + } else /* delegations may be unsigned (on opt out...) */ + if (rrset->type != LDNS_RR_TYPE_NS || + ldns_dname_compare(name, zone_name) == 0) { + + print_rr_error(myerr, rrset->rrs->rr, "no signatures"); + status = LDNS_STATUS_CRYPTO_NO_RRSIG; + } else { + status = LDNS_STATUS_OK; + } + ldns_rr_list_free(rrset_rrs); + + return status; +} + +static ldns_status +verify_single_rr(ldns_rr *rr, ldns_dnssec_rrs *signature_rrs, + ldns_rr_list *keys) +{ + ldns_rr_list *rrset_rrs; + ldns_status status; + + rrset_rrs = ldns_rr_list_new(); + ldns_rr_list_push_rr(rrset_rrs, rr); + + status = verify_rrs(rrset_rrs, signature_rrs, keys); + + ldns_rr_list_free(rrset_rrs); + + return status; +} + +static ldns_status +verify_next_hashed_name(ldns_dnssec_zone* zone, ldns_dnssec_name *name) +{ + ldns_rbnode_t *next_node; + ldns_dnssec_name *next_name; + int cmp; + char *next_owner_str; + ldns_rdf *next_owner_dname; + + assert(name->hashed_name != NULL); + + next_node = ldns_rbtree_search(zone->hashed_names, name->hashed_name); + assert(next_node != NULL); + do { + next_node = ldns_rbtree_next(next_node); + if (next_node == LDNS_RBTREE_NULL) { + next_node = ldns_rbtree_first(zone->hashed_names); + } + next_name = (ldns_dnssec_name *) next_node->data; + } while (! next_name->nsec); + + next_owner_str = ldns_rdf2str(ldns_nsec3_next_owner(name->nsec)); + next_owner_dname = ldns_dname_new_frm_str(next_owner_str); + cmp = ldns_dname_compare(next_owner_dname, next_name->hashed_name); + ldns_rdf_deep_free(next_owner_dname); + LDNS_FREE(next_owner_str); + if (cmp != 0) { + if (verbosity > 0) { + fprintf(myerr, "Error: The NSEC3 record for "); + ldns_rdf_print(stdout, name->name); + fprintf(myerr, " points to the wrong next hashed owner" + " name\n\tshould point to "); + ldns_rdf_print(myerr, next_name->name); + fprintf(myerr, ", whose hashed name is "); + ldns_rdf_print(myerr, next_name->hashed_name); + fprintf(myerr, "\n"); + } + return LDNS_STATUS_ERR; + } else { + return LDNS_STATUS_OK; + } +} + +static bool zone_is_nsec3_optout(ldns_dnssec_zone* zone) +{ + static int remember = -1; + + if (remember == -1) { + remember = ldns_dnssec_zone_is_nsec3_optout(zone) ? 1 : 0; + } + return remember == 1; +} + +static ldns_status +verify_nsec(ldns_dnssec_zone* zone, ldns_rbnode_t *cur_node, + ldns_rr_list *keys) +{ + ldns_rbnode_t *next_node; + ldns_dnssec_name *name, *next_name; + ldns_status status, result; + result = LDNS_STATUS_OK; + + name = (ldns_dnssec_name *) cur_node->data; + if (name->nsec) { + if (name->nsec_signatures) { + status = verify_single_rr(name->nsec, + name->nsec_signatures, keys); + + update_error(&result, status); + } else { + if (verbosity > 0) { + fprintf(myerr, + "Error: the NSEC(3) record of "); + ldns_rdf_print(myerr, name->name); + fprintf(myerr, " has no signatures\n"); + } + update_error(&result, LDNS_STATUS_ERR); + } + /* check whether the NSEC record points to the right name */ + switch (ldns_rr_get_type(name->nsec)) { + case LDNS_RR_TYPE_NSEC: + /* simply try next name */ + next_node = ldns_rbtree_next(cur_node); + if (next_node == LDNS_RBTREE_NULL) { + next_node = ldns_rbtree_first( + zone->names); + } + next_node = ldns_dnssec_name_node_next_nonglue( + next_node); + if (!next_node) { + next_node = + ldns_dnssec_name_node_next_nonglue( + ldns_rbtree_first(zone->names)); + } + next_name = (ldns_dnssec_name*)next_node->data; + if (ldns_dname_compare(next_name->name, + ldns_rr_rdf(name->nsec, + 0)) != 0) { + if (verbosity > 0) { + fprintf(myerr, "Error: the " + "NSEC record for "); + ldns_rdf_print(myerr, + name->name); + fprintf(myerr, " points to " + "the wrong " + "next owner name\n"); + } + if (verbosity >= 4) { + fprintf(myerr, "\t: "); + ldns_rdf_print(myerr, + ldns_rr_rdf( + name->nsec, + 0)); + fprintf(myerr, " i.s.o. "); + ldns_rdf_print(myerr, + next_name->name); + fprintf(myerr, ".\n"); + } + update_error(&result, + LDNS_STATUS_ERR); + } + break; + case LDNS_RR_TYPE_NSEC3: + /* find the hashed next name in the tree */ + /* this is expensive, do we need to add + * support for this in the structs? + * (ie. pointer to next hashed name?) + */ + status = verify_next_hashed_name(zone, name); + update_error(&result, status); + break; + default: + break; + } + } else { + if (zone_is_nsec3_optout(zone) && + (ldns_dnssec_name_is_glue(name) || + ( ldns_dnssec_rrsets_contains_type(name->rrsets, + LDNS_RR_TYPE_NS) + && !ldns_dnssec_rrsets_contains_type(name->rrsets, + LDNS_RR_TYPE_DS)))) { + /* ok, no problem, but we need to remember to check + * whether the chain does not actually point to this + * name later */ + } else { + if (verbosity > 0) { + fprintf(myerr, + "Error: there is no NSEC(3) for "); + ldns_rdf_print(myerr, name->name); + fprintf(myerr, "\n"); + } + update_error(&result, LDNS_STATUS_ERR); + } + } + return result; +} + +static ldns_status +verify_dnssec_name(ldns_rdf *zone_name, ldns_dnssec_zone* zone, + ldns_rbnode_t *cur_node, ldns_rr_list *keys) +{ + ldns_status result = LDNS_STATUS_OK; + ldns_status status; + ldns_dnssec_rrsets *cur_rrset; + ldns_dnssec_name *name; + int on_delegation_point; + /* for NSEC chain checks */ + + name = (ldns_dnssec_name *) cur_node->data; + if (verbosity >= 5) { + fprintf(myout, "Checking: "); + ldns_rdf_print(myout, name->name); + fprintf(myout, "\n"); + } + + if (ldns_dnssec_name_is_glue(name)) { + /* glue */ + cur_rrset = name->rrsets; + while (cur_rrset) { + if (cur_rrset->signatures) { + if (verbosity > 0) { + fprintf(myerr, "Error: "); + ldns_rdf_print(myerr, name->name); + fprintf(myerr, "\t"); + print_type(myerr, cur_rrset->type); + fprintf(myerr, " has signature(s)," + " but is glue\n"); + } + result = LDNS_STATUS_ERR; + } + cur_rrset = cur_rrset->next; + } + if (name->nsec) { + if (verbosity > 0) { + fprintf(myerr, "Error: "); + ldns_rdf_print(myerr, name->name); + fprintf(myerr, " has an NSEC(3)," + " but is glue\n"); + } + result = LDNS_STATUS_ERR; + } + } else { + /* not glue, do real verify */ + + on_delegation_point = + ldns_dnssec_rrsets_contains_type(name->rrsets, + LDNS_RR_TYPE_NS) + && !ldns_dnssec_rrsets_contains_type(name->rrsets, + LDNS_RR_TYPE_SOA); + cur_rrset = name->rrsets; + while(cur_rrset) { + + /* Do not check occluded rrsets + * on the delegation point + */ + if ((on_delegation_point && + (cur_rrset->type == LDNS_RR_TYPE_NS || + cur_rrset->type == LDNS_RR_TYPE_DS)) || + (!on_delegation_point && + cur_rrset->type != LDNS_RR_TYPE_RRSIG && + cur_rrset->type != LDNS_RR_TYPE_NSEC)) { + + status = verify_dnssec_rrset(zone_name, + name->name, cur_rrset, keys); + update_error(&result, status); + } + cur_rrset = cur_rrset->next; + } + status = verify_nsec(zone, cur_node, keys); + update_error(&result, status); + } + return result; +} + +static void +add_keys_with_matching_ds(ldns_dnssec_rrsets* from_keys, ldns_rr_list *dss, + ldns_rr_list *to_keys) +{ + size_t i; + ldns_rr* ds_rr; + ldns_dnssec_rrs *cur_key; + + for (i = 0; i < ldns_rr_list_rr_count(dss); i++) { + + if (ldns_rr_get_type(ds_rr = ldns_rr_list_rr(dss, i)) + == LDNS_RR_TYPE_DS) { + + for (cur_key = from_keys->rrs; cur_key; + cur_key = cur_key->next ) { + + if (ldns_rr_compare_ds(cur_key->rr, ds_rr)) { + ldns_rr_list_push_rr(to_keys, + cur_key->rr); + break; + } + } + } + } +} + +static ldns_status +sigchase(ldns_resolver* res, ldns_rdf *zone_name, ldns_dnssec_rrsets *zonekeys, + ldns_rr_list *keys) +{ + ldns_dnssec_rrs* cur_key; + ldns_status status; + bool free_resolver = false; + ldns_rdf* parent_name; + ldns_rr_list* parent_keys; + ldns_rr_list* ds_keys; + + add_keys_with_matching_ds(zonekeys, keys, keys); + + /* First try to authenticate the keys offline. + * When do_sigchase is given validation may continue lookup up + * keys online. Reporting the failure of the offline validation + * should then be suppressed. + */ + no_nomatch_msg = do_sigchase; + status = verify_dnssec_rrset(zone_name, zone_name, zonekeys, keys); + no_nomatch_msg = false; + + /* Continue online on validation failure when the -S option was given. + */ + if (do_sigchase && + status == LDNS_STATUS_CRYPTO_NO_MATCHING_KEYTAG_DNSKEY && + ldns_dname_label_count(zone_name) > 0 ) { + + if (!res) { + if ((status = ldns_resolver_new_frm_file(&res, NULL))){ + ldns_resolver_free(res); + if (verbosity > 0) { + fprintf(myerr, + "Could not create resolver: " + "%s\n", + ldns_get_errorstr_by_id(status) + ); + } + return status; + } + free_resolver = true; + ldns_resolver_set_dnssec(res,1); + ldns_resolver_set_dnssec_cd(res, 1); + } + if ((parent_name = ldns_dname_left_chop(zone_name))) { + /* + * Use the (authenticated) keys of the parent zone ... + */ + parent_keys = ldns_fetch_valid_domain_keys(res, + parent_name, keys, &status); + ldns_rdf_deep_free(parent_name); + + /* + * ... to validate the DS for the zone ... + */ + ds_keys = ldns_validate_domain_ds(res, zone_name, + parent_keys); + ldns_rr_list_free(parent_keys); + + /* + * ... to use it to add the KSK to the trusted keys ... + */ + add_keys_with_matching_ds(zonekeys, ds_keys, keys); + ldns_rr_list_free(ds_keys); + + /* + * ... to validate all zonekeys ... + */ + status = verify_dnssec_rrset(zone_name, zone_name, + zonekeys, keys); + } else { + status = LDNS_STATUS_MEM_ERR; + } + if (free_resolver) { + ldns_resolver_deep_free(res); + } + + } + /* + * ... so they can all be added to our list of trusted keys. + */ + if (status == LDNS_STATUS_OK) + for (cur_key = zonekeys->rrs; cur_key; cur_key = cur_key->next) + ldns_rr_list_push_rr(keys, cur_key->rr); + return status; +} + +static ldns_status +verify_dnssec_zone(ldns_dnssec_zone *dnssec_zone, ldns_rdf *zone_name, + ldns_rr_list *keys, bool apexonly, int percentage) +{ + ldns_rbnode_t *cur_node; + ldns_dnssec_rrsets *cur_key_rrset; + ldns_dnssec_rrs *cur_key; + ldns_status status; + ldns_status result = LDNS_STATUS_OK; + + cur_key_rrset = ldns_dnssec_zone_find_rrset(dnssec_zone, zone_name, + LDNS_RR_TYPE_DNSKEY); + if (!cur_key_rrset || !cur_key_rrset->rrs) { + if (verbosity > 0) { + fprintf(myerr, + "Error: No DNSKEY records at zone apex\n"); + } + result = LDNS_STATUS_ERR; + } else { + /* are keys given with -k to use for validation? */ + if (ldns_rr_list_rr_count(keys) > 0) { + if ((result = sigchase(NULL, zone_name, cur_key_rrset, + keys))) + goto error; + } else + for (cur_key = cur_key_rrset->rrs; cur_key; + cur_key = cur_key->next) + ldns_rr_list_push_rr(keys, cur_key->rr); + + cur_node = ldns_rbtree_first(dnssec_zone->names); + if (cur_node == LDNS_RBTREE_NULL) { + if (verbosity > 0) { + fprintf(myerr, "Error: Empty zone?\n"); + } + result = LDNS_STATUS_ERR; + } + if (apexonly) { + /* + * In this case, only the first node in the treewalk + * below should be checked. + */ + assert( cur_node->data == dnssec_zone->soa ); + /* + * Allthough the percentage option doesn't make sense + * here, we set it to 100 to force the first node to + * be checked. + */ + percentage = 100; + } + while (cur_node != LDNS_RBTREE_NULL) { + /* should we check this one? saves calls to random. */ + if (percentage == 100 + || ((random() % 100) >= 100 - percentage)) { + status = verify_dnssec_name(zone_name, + dnssec_zone, cur_node, keys); + update_error(&result, status); + if (apexonly) + break; + } + cur_node = ldns_rbtree_next(cur_node); + } + } +error: + ldns_rr_list_free(keys); + return result; +} + +static void print_usage(FILE *out, const char *progname) +{ + fprintf(out, "Usage: %s [OPTIONS] <zonefile>\n", progname); + fprintf(out, "\tReads the zonefile and checks for DNSSEC errors.\n"); + fprintf(out, "\nIt checks whether NSEC(3)s are present, " + "and verifies all signatures\n"); + fprintf(out, "It also checks the NSEC(3) chain, but it " + "will error on opted-out delegations\n"); + fprintf(out, "\nOPTIONS:\n"); + fprintf(out, "\t-h\t\tshow this text\n"); + fprintf(out, "\t-a\t\tapex only, check only the zone apex\n"); + fprintf(out, "\t-e <period>\tsignatures may not expire " + "within this period.\n\t\t\t" + "(default no period is used)\n"); + fprintf(out, "\t-i <period>\tsignatures must have been " + "valid at least this long.\n\t\t\t" + "(default signatures should just be valid now)\n"); + fprintf(out, "\t-k <file>\tspecify a file that contains a " + "trusted DNSKEY or DS rr.\n\t\t\t" + "This option may be given more than once.\n" + "\t\t\tDefault is %s\n", LDNS_TRUST_ANCHOR_FILE); + fprintf(out, "\t-p [0-100]\tonly checks this percentage of " + "the zone.\n\t\t\tDefaults to 100\n"); + fprintf(out, "\t-S\t\tchase signature(s) to a known key. " + "The network may be\n\t\t\taccessed to " + "validate the zone's DNSKEYs. (implies -k)\n"); + fprintf(out, "\t-t YYYYMMDDhhmmss | [+|-]offset\n\t\t\t" + "set the validation time either by an " + "absolute time\n\t\t\tvalue or as an " + "offset in seconds from <now>.\n\t\t\t" + "For data that came from the network (while " + "chasing),\n\t\t\tsystem time will be used " + "for validating it regardless.\n"); + fprintf(out, "\t-v\t\tshows the version and exits\n"); + fprintf(out, "\t-V [0-5]\tset verbosity level (default 3)\n"); + fprintf(out, "\n<period>s are given in ISO 8601 duration format: " + "P[n]Y[n]M[n]DT[n]H[n]M[n]S\n"); + fprintf(out, "\nif no file is given standard input is read\n"); +} + +int +main(int argc, char **argv) +{ + char *filename; + FILE *fp; + int line_nr = 0; + int c; + ldns_status s; + ldns_dnssec_zone *dnssec_zone = NULL; + ldns_status result = LDNS_STATUS_ERR; + bool apexonly = false; + int percentage = 100; + struct tm tm; + ldns_duration_type *duration; + ldns_rr_list *keys = ldns_rr_list_new(); + size_t nkeys = 0; + const char *progname = argv[0]; + + check_time = ldns_time(NULL); + myout = stdout; + myerr = stderr; + + while ((c = getopt(argc, argv, "ae:hi:k:vV:p:St:")) != -1) { + switch(c) { + case 'a': + apexonly = true; + break; + case 'h': + print_usage(stdout, progname); + exit(EXIT_SUCCESS); + break; + case 'e': + case 'i': + duration = ldns_duration_create_from_string(optarg); + if (!duration) { + if (verbosity > 0) { + fprintf(myerr, + "<period> should be in ISO " + "8601 duration format: " + "P[n]Y[n]M[n]DT[n]H[n]M[n]S\n" + ); + } + exit(EXIT_FAILURE); + } + if (c == 'e') + expiration_offset = + ldns_duration2time(duration); + else + inception_offset = + ldns_duration2time(duration); + break; + case 'k': + s = read_key_file(optarg, keys); + if (s == LDNS_STATUS_FILE_ERR) { + if (verbosity > 0) { + fprintf(myerr, + "Error opening %s: %s\n", + optarg, strerror(errno)); + } + } + if (s != LDNS_STATUS_OK) { + if (verbosity > 0) { + fprintf(myerr, + "Could not parse key file " + "%s: %s\n",optarg, + ldns_get_errorstr_by_id(s)); + } + exit(EXIT_FAILURE); + } + if (ldns_rr_list_rr_count(keys) == nkeys) { + if (verbosity > 0) { + fprintf(myerr, + "No keys found in file %s\n", + optarg); + } + exit(EXIT_FAILURE); + } + nkeys = ldns_rr_list_rr_count(keys); + break; + case 'p': + percentage = atoi(optarg); + if (percentage < 0 || percentage > 100) { + if (verbosity > 0) { + fprintf(myerr, + "percentage needs to fall " + "between 0..100\n"); + } + exit(EXIT_FAILURE); + } + srandom(time(NULL) ^ getpid()); + break; + case 'S': + do_sigchase = true; + /* may chase */ + break; + case 't': + if (strlen(optarg) == 14 && + sscanf(optarg, "%4d%2d%2d%2d%2d%2d", + &tm.tm_year, &tm.tm_mon, + &tm.tm_mday, &tm.tm_hour, + &tm.tm_min , &tm.tm_sec ) == 6) { + + tm.tm_year -= 1900; + tm.tm_mon--; + check_time = ldns_mktime_from_utc(&tm); + } + else { + check_time += atoi(optarg); + } + break; + case 'v': + printf("verify-zone version %s (ldns version %s)\n", + LDNS_VERSION, ldns_version()); + exit(EXIT_SUCCESS); + break; + case 'V': + verbosity = atoi(optarg); + break; + } + } + if (do_sigchase && nkeys == 0) { + (void) read_key_file(LDNS_TRUST_ANCHOR_FILE, keys); + nkeys = ldns_rr_list_rr_count(keys); + + if (nkeys == 0) { + if (verbosity > 0) { + fprintf(myerr, "Unable to chase " + "signature without keys.\n"); + } + exit(EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) { + fp = stdin; + } else if (argc == 1) { + filename = argv[0]; + + fp = fopen(filename, "r"); + if (!fp) { + if (verbosity > 0) { + fprintf(myerr, "Unable to open %s: %s\n", + filename, strerror(errno)); + } + exit(EXIT_FAILURE); + } + } else { + print_usage(stderr, progname); + exit(EXIT_FAILURE); + } + + s = ldns_dnssec_zone_new_frm_fp_l(&dnssec_zone, fp, NULL, 0, + LDNS_RR_CLASS_IN, &line_nr); + if (s == LDNS_STATUS_OK) { + if (!dnssec_zone->soa) { + if (verbosity > 0) { + fprintf(myerr, + "; Error: no SOA in the zone\n"); + } + exit(EXIT_FAILURE); + } + + result = ldns_dnssec_zone_mark_glue(dnssec_zone); + if (result != LDNS_STATUS_OK) { + if (verbosity > 0) { + fprintf(myerr, + "There were errors identifying the " + "glue in the zone\n"); + } + } + if (verbosity >= 5) { + ldns_dnssec_zone_print(myout, dnssec_zone); + } + + result = verify_dnssec_zone(dnssec_zone, + dnssec_zone->soa->name, keys, apexonly, + percentage); + + if (result == LDNS_STATUS_OK) { + if (verbosity >= 3) { + fprintf(myout, + "Zone is verified and complete\n"); + } + } else { + if (verbosity > 0) { + fprintf(myerr, + "There were errors in the zone\n"); + } + } + + ldns_dnssec_zone_deep_free(dnssec_zone); + } else { + if (verbosity > 0) { + fprintf(myerr, "%s at %d\n", + ldns_get_errorstr_by_id(s), line_nr); + } + exit(EXIT_FAILURE); + } + fclose(fp); + + exit(result); +} + +#else + +int +main(int argc, char **argv) +{ + fprintf(stderr, "ldns-verify-zone needs OpenSSL support, " + "which has not been compiled in\n"); + return 1; +} +#endif /* HAVE_SSL */ + |