summaryrefslogtreecommitdiffstats
path: root/src/utils/knsupdate/knsupdate_params.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/knsupdate/knsupdate_params.c')
-rw-r--r--src/utils/knsupdate/knsupdate_params.c160
1 files changed, 138 insertions, 22 deletions
diff --git a/src/utils/knsupdate/knsupdate_params.c b/src/utils/knsupdate/knsupdate_params.c
index f9fa41f..5aaf808 100644
--- a/src/utils/knsupdate/knsupdate_params.c
+++ b/src/utils/knsupdate/knsupdate_params.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2024 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
@@ -26,6 +26,7 @@
#include "utils/common/netio.h"
#include "libknot/libknot.h"
#include "libknot/tsig.h"
+#include "contrib/base64.h"
#include "contrib/mempattern.h"
#include "contrib/strtonum.h"
#include "contrib/ucw/mempool.h"
@@ -90,6 +91,8 @@ static int knsupdate_init(knsupdate_params_t *params)
init_list(&params->update_list);
init_list(&params->prereq_list);
+ tls_params_init(&params->tls_params);
+
/* Initialize memory context. */
mm_ctx_mempool(&params->mm, MM_DEFAULT_BLKSIZE);
@@ -142,6 +145,9 @@ void knsupdate_clean(knsupdate_params_t *params)
knot_pkt_free(params->answer);
knot_tsig_key_deinit(&params->tsig_key);
+ tls_params_clean(&params->tls_params);
+ quic_params_clean(&params->quic_params);
+
/* Clean up the structure. */
mp_delete(params->mm.ctx);
memset(params, 0, sizeof(*params));
@@ -172,9 +178,31 @@ void knsupdate_reset(knsupdate_params_t *params)
static void print_help(void)
{
- printf("Usage: %s [-d] [-v] [-k keyfile | -y [hmac:]name:key]\n"
- " [-p port] [-t timeout] [-r retries] [filename]\n",
- PROGRAM_NAME);
+ printf("Usage:\n"
+ " %s [-T] [options] [filename]\n"
+ " %s [-S | -Q] [tls_options] [options] [filename]\n"
+ "\n"
+ "Options:\n"
+ " -T, --tcp Use TCP protocol.\n"
+ " -S, --tls Use TLS protocol.\n"
+ " -Q, --quic Use QUIC protocol.\n"
+ " -p, --port <num> Remote port.\n"
+ " -r, --retry <num> Number of retries over UDP.\n"
+ " -t, --timeout <num> Update timeout.\n"
+ " -y, --tsig <str> TSIG key in the form [alg:]name:key.\n"
+ " -k, --tsigfile <path> Path to a TSIG key file.\n"
+ " -d, --debug Debug mode output.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n"
+ "\n"
+ "QUIC/TLS options:\n"
+ " -H, --hostname <str> Remote hostname validation.\n"
+ " -P, --pin <base64> Certificate key PIN.\n"
+ " -A, --ca [<path>] Path to a CA file.\n"
+ " -E, --certfile <path> Path to a client certificate file.\n"
+ " -K, --keyfile <path> Path to a client key file.\n"
+ " -s, --sni <str> Remote SNI.\n",
+ PROGRAM_NAME, PROGRAM_NAME);
}
int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
@@ -188,40 +216,74 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
return ret;
}
- // Long options.
+ const char *opts_str = "dhvTSQV::p:r:t:y:k:H:P:A::E:K:s:";
struct option opts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "debug", no_argument, NULL, 'd' },
+ { "help", no_argument, NULL, 'h' },
+ { "tcp", no_argument, NULL, 'T' },
+ { "tls", no_argument, NULL, 'S' },
+ { "quic", no_argument, NULL, 'Q' },
+ { "version", optional_argument, NULL, 'V' },
+ { "port", required_argument, NULL, 'p' },
+ { "retry", required_argument, NULL, 'r' },
+ { "timeout", required_argument, NULL, 't' },
+ { "tsig", required_argument, NULL, 'y' },
+ { "tsigfile", required_argument, NULL, 'k' },
+ { "hostname", required_argument, NULL, 'H' },
+ { "pin", required_argument, NULL, 'P' },
+ { "ca", optional_argument, NULL, 'A' },
+ { "certfile", required_argument, NULL, 'E' },
+ { "keyfile", required_argument, NULL, 'K' },
+ { "sni", required_argument, NULL, 's' },
{ NULL }
};
- /* Command line options processing. */
+ bool default_port = true;
+
int opt = 0;
- while ((opt = getopt_long(argc, argv, "dhDvVp:t:r:y:k:", opts, NULL))
- != -1) {
+ while ((opt = getopt_long(argc, argv, opts_str, opts, NULL)) != -1) {
switch (opt) {
case 'd':
- case 'D': /* Extra debugging. */
msg_enable_debug(1);
break;
case 'h':
print_help();
params->stop = true;
return KNOT_EOK;
- case 'v':
+ case 'v': // Compatibility with nsupdate.
+ case 'T':
params->protocol = PROTO_TCP;
break;
+ case 'S':
+ params->protocol = PROTO_TCP;
+
+ params->tls_params.enable = true;
+
+ if (default_port) {
+ free(params->server->service);
+ params->server->service = strdup(DEFAULT_DNS_TLS_PORT);
+ }
+ break;
+ case 'Q':
+ params->protocol = PROTO_UDP;
+
+ params->tls_params.enable = true;
+ params->quic_params.enable = true;
+
+ if (default_port) {
+ free(params->server->service);
+ params->server->service = strdup(DEFAULT_DNS_QUIC_PORT);
+ }
+ break;
case 'V':
- print_version(PROGRAM_NAME);
+ print_version(PROGRAM_NAME, optarg != NULL);
params->stop = true;
return KNOT_EOK;
case 'p':
+ assert(optarg);
+ default_port = false;
free(params->server->service);
params->server->service = strdup(optarg);
- if (!params->server->service) {
- ERR("failed to set default port '%s'", optarg);
- return KNOT_ENOMEM;
- }
break;
case 'r':
ret = str_to_u32(optarg, &params->retries);
@@ -241,7 +303,7 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
knot_tsig_key_deinit(&params->tsig_key);
ret = knot_tsig_key_init_str(&params->tsig_key, optarg);
if (ret != KNOT_EOK) {
- ERR("failed to parse key '%s'", optarg);
+ ERR("failed to parse TSIG key '%s'", optarg);
return ret;
}
break;
@@ -249,9 +311,63 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
knot_tsig_key_deinit(&params->tsig_key);
ret = knot_tsig_key_init_file(&params->tsig_key, optarg);
if (ret != KNOT_EOK) {
- ERR("failed to parse keyfile '%s'", optarg);
+ ERR("failed to parse TSIG keyfile '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 'H':
+ assert(optarg);
+ free(params->tls_params.hostname);
+ params->tls_params.hostname = strdup(optarg);
+ break;
+ case 'P':
+ assert(optarg);
+ uint8_t pin[64] = { 0 };
+ ret = knot_base64_decode((const uint8_t *)optarg, strlen(optarg), pin, sizeof(pin));
+ if (ret < 0) {
+ ERR("invalid certificate pin %s", optarg);
return ret;
+ } else if (ret != CERT_PIN_LEN) { // Check for 256-bit value.
+ ERR("invalid SHA256 hash length of certificate pin %s", optarg);
+ return KNOT_EINVAL;
}
+
+ uint8_t *item = malloc(1 + ret); // 1 ~ leading data length.
+ if (item == NULL) {
+ return KNOT_ENOMEM;
+ }
+ item[0] = ret;
+ memcpy(&item[1], pin, ret);
+
+ if (ptrlist_add(&params->tls_params.pins, item, NULL) == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ break;
+ case 'A':
+ if (optarg == NULL) {
+ params->tls_params.system_ca = true;
+ break;
+ }
+ if (ptrlist_add(&params->tls_params.ca_files, strdup(optarg), NULL) == NULL) {
+ ERR("failed to set CA file '%s'", optarg);
+ return KNOT_ENOMEM;
+ }
+ break;
+ case 'E':
+ assert(optarg);
+ free(params->tls_params.certfile);
+ params->tls_params.certfile = strdup(optarg);
+ break;
+ case 'K':
+ assert(optarg);
+ free(params->tls_params.keyfile);
+ params->tls_params.keyfile = strdup(optarg);
+ break;
+ case 's':
+ assert(optarg);
+ free(params->tls_params.sni);
+ params->tls_params.sni = strdup(optarg);
break;
default:
print_help();
@@ -259,8 +375,8 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
}
}
- /* No retries for TCP. */
- if (params->protocol == PROTO_TCP) {
+ /* Retries only for UDP. */
+ if (params->protocol == PROTO_TCP || params->quic_params.enable) {
params->retries = 0;
} else {
/* If wait/tries < 1 s, set 1 second for each try. */
@@ -277,7 +393,7 @@ int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
ptrlist_add(&params->qfiles, argv[optind], &params->mm);
}
- return ret;
+ return KNOT_EOK;
}
int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl)