diff options
Diffstat (limited to '')
-rw-r--r-- | src/utils/knsec3hash/knsec3hash.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/utils/knsec3hash/knsec3hash.c b/src/utils/knsec3hash/knsec3hash.c new file mode 100644 index 0000000..a7bac97 --- /dev/null +++ b/src/utils/knsec3hash/knsec3hash.c @@ -0,0 +1,187 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <getopt.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "contrib/base32hex.h" +#include "contrib/string.h" +#include "contrib/strtonum.h" +#include "libdnssec/error.h" +#include "libdnssec/nsec.h" +#include "libknot/libknot.h" +#include "utils/common/msg.h" +#include "utils/common/params.h" + +#define PROGRAM_NAME "knsec3hash" + +/*! + * \brief Print program help (and example). + */ +static void print_help(void) +{ + printf("Usage: " PROGRAM_NAME " <salt> <algorithm> <iterations> <domain-name>\n"); + printf("Example: " PROGRAM_NAME " c01dcafe 1 10 knot-dns.cz\n"); + printf("Alternative usage: "PROGRAM_NAME " <algorithm> <flags> <iterations> <salt> <domain-name>\n"); + printf("Example: " PROGRAM_NAME " 1 0 10 c01dcafe knot-dns.cz\n"); +} + +/*! + * \brief Parse NSEC3 salt. + */ +static int str_to_salt(const char *str, dnssec_binary_t *salt) +{ + if (strcmp(str, "-") == 0) { + salt->size = 0; + return DNSSEC_EOK; + } else { + salt->data = hex_to_bin(str, &salt->size); + return (salt->data != NULL ? DNSSEC_EOK : DNSSEC_EINVAL); + } +} + +/*! + * \brief Parse NSEC3 parameters and fill structure with NSEC3 parameters. + */ +static bool parse_nsec3_params(dnssec_nsec3_params_t *params, const char *salt_str, + const char *algorithm_str, const char *iterations_str) +{ + uint8_t algorithm = 0; + int r = str_to_u8(algorithm_str, &algorithm); + if (r != KNOT_EOK) { + ERR2("invalid algorithm number"); + return false; + } + + uint16_t iterations = 0; + r = str_to_u16(iterations_str, &iterations); + if (r != KNOT_EOK) { + ERR2("invalid iteration count"); + return false; + } + + dnssec_binary_t salt = { 0 }; + r = str_to_salt(salt_str, &salt); + if (r != DNSSEC_EOK) { + ERR2("invalid salt (%s)", knot_strerror(r)); + return false; + } + + if (salt.size > UINT8_MAX) { + ERR2("invalid salt, maximum length is %d bytes", UINT8_MAX); + dnssec_binary_free(&salt); + return false; + } + + params->algorithm = algorithm; + params->iterations = iterations; + params->salt = salt; + params->flags = 0; + + return true; +} + +/*! + * \brief Entry point of 'knsec3hash'. + */ +int main(int argc, char *argv[]) +{ + struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + int opt = 0; + while ((opt = getopt_long(argc, argv, "hV", options, NULL)) != -1) { + switch(opt) { + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + default: + print_help(); + return EXIT_FAILURE; + } + } + + bool new_params = false; + if (argc == 6) { + // knsec3hash <algorithm> <flags> <iterations> <salt> <domain> + new_params = true; + } else if (argc != 5) { + // knsec3hash <salt> <algorithm> <iterations> <domain> + print_help(); + return EXIT_FAILURE; + } + + int exit_code = EXIT_FAILURE; + dnssec_nsec3_params_t nsec3_params = { 0 }; + + dnssec_binary_t dname = { 0 }; + dnssec_binary_t digest = { 0 }; + dnssec_binary_t digest_print = { 0 }; + + if (new_params) { + if (!parse_nsec3_params(&nsec3_params, argv[4], argv[1], argv[3])) { + goto fail; + } + } else { + if (!parse_nsec3_params(&nsec3_params, argv[1], argv[2], argv[3])) { + goto fail; + } + } + + dname.data = knot_dname_from_str_alloc(argv[new_params ? 5 : 4]); + if (dname.data == NULL) { + ERR2("cannot parse domain name"); + goto fail; + } + knot_dname_to_lower(dname.data); + dname.size = knot_dname_size(dname.data); + + int r = dnssec_nsec3_hash(&dname, &nsec3_params, &digest); + if (r != DNSSEC_EOK) { + ERR2("cannot compute NSEC3 hash (%s)", knot_strerror(r)); + goto fail; + } + + r = knot_base32hex_encode_alloc(digest.data, digest.size, &digest_print.data); + if (r < 0) { + ERR2("cannot encode computed hash (%s)", knot_strerror(r)); + goto fail; + } + digest_print.size = r; + + exit_code = EXIT_SUCCESS; + + printf("%.*s (salt=%s, hash=%d, iterations=%d)\n", (int)digest_print.size, + digest_print.data, argv[1], nsec3_params.algorithm, + nsec3_params.iterations); + +fail: + dnssec_nsec3_params_free(&nsec3_params); + dnssec_binary_free(&dname); + dnssec_binary_free(&digest); + dnssec_binary_free(&digest_print); + + return exit_code; +} |