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.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/utils/knsupdate/knsupdate_params.c b/src/utils/knsupdate/knsupdate_params.c
new file mode 100644
index 0000000..f9fa41f
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_params.c
@@ -0,0 +1,304 @@
+/* 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 <assert.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/knsupdate/knsupdate_params.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "libknot/libknot.h"
+#include "libknot/tsig.h"
+#include "contrib/mempattern.h"
+#include "contrib/strtonum.h"
+#include "contrib/ucw/mempool.h"
+
+#define DEFAULT_RETRIES_NSUPDATE 3
+#define DEFAULT_TIMEOUT_NSUPDATE 12
+
+static const style_t DEFAULT_STYLE_NSUPDATE = {
+ .format = FORMAT_NSUPDATE,
+ .style = {
+ .wrap = false,
+ .show_class = true,
+ .show_ttl = true,
+ .verbose = false,
+ .original_ttl = false,
+ .empty_ttl = false,
+ .human_ttl = false,
+ .human_timestamp = true,
+ .generic = false,
+ .ascii_to_idn = NULL
+ },
+ .show_query = false,
+ .show_header = true,
+ .show_section = true,
+ .show_edns = false,
+ .show_question = true,
+ .show_answer = true,
+ .show_authority = true,
+ .show_additional = true,
+ .show_tsig = true,
+ .show_footer = false
+};
+
+static int parser_set_default(zs_scanner_t *s, const char *fmt, ...)
+{
+ /* Format string. */
+ char buf[512]; /* Must suffice for domain name and TTL. */
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || (size_t)n >= sizeof(buf)) {
+ return ZS_EINVAL;
+ }
+
+ /* Buffer must contain newline */
+ if (zs_set_input_string(s, buf, n) != 0 ||
+ zs_parse_all(s) != 0) {
+ return s->error.code;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knsupdate_init(knsupdate_params_t *params)
+{
+ memset(params, 0, sizeof(knsupdate_params_t));
+
+ /* Initialize lists. */
+ init_list(&params->qfiles);
+ init_list(&params->update_list);
+ init_list(&params->prereq_list);
+
+ /* Initialize memory context. */
+ mm_ctx_mempool(&params->mm, MM_DEFAULT_BLKSIZE);
+
+ /* Default server. */
+ params->server = srv_info_create(DEFAULT_IPV4_NAME, DEFAULT_DNS_PORT);
+ if (!params->server)
+ return KNOT_ENOMEM;
+
+ /* Default settings. */
+ params->ip = IP_ALL;
+ params->protocol = PROTO_ALL;
+ params->class_num = KNOT_CLASS_IN;
+ params->retries = DEFAULT_RETRIES_NSUPDATE;
+ params->wait = DEFAULT_TIMEOUT_NSUPDATE;
+
+ /* Initialize RR parser. */
+ if (zs_init(&params->parser, ".", params->class_num, 3600) != 0 ||
+ zs_set_processing(&params->parser, NULL, NULL, NULL) != 0) {
+ zs_deinit(&params->parser);
+ return KNOT_ENOMEM;
+ }
+
+ /* Default style. */
+ params->style = DEFAULT_STYLE_NSUPDATE;
+
+ /* Create query/answer packets. */
+ params->query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &params->mm);
+ params->answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &params->mm);
+
+ return KNOT_EOK;
+}
+
+void knsupdate_clean(knsupdate_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ /* Clear current query. */
+ knsupdate_reset(params);
+
+ /* Free qfiles. */
+ ptrlist_free(&params->qfiles, &params->mm);
+
+ srv_info_free(params->server);
+ srv_info_free(params->srcif);
+ free(params->zone);
+ zs_deinit(&params->parser);
+ knot_pkt_free(params->query);
+ knot_pkt_free(params->answer);
+ knot_tsig_key_deinit(&params->tsig_key);
+
+ /* Clean up the structure. */
+ mp_delete(params->mm.ctx);
+ memset(params, 0, sizeof(*params));
+}
+
+/*! \brief Free RRSet list. */
+static void rr_list_free(list_t *list, knot_mm_t *mm)
+{
+ assert(list != NULL);
+ assert(mm != NULL);
+
+ ptrnode_t *node;
+ WALK_LIST(node, *list) {
+ knot_rrset_t *rrset = (knot_rrset_t *)node->d;
+ knot_rrset_free(rrset, NULL);
+ }
+ ptrlist_free(list, mm);
+}
+
+void knsupdate_reset(knsupdate_params_t *params)
+{
+ /* Free ADD/REMOVE RRSets. */
+ rr_list_free(&params->update_list, &params->mm);
+
+ /* Free PREREQ RRSets. */
+ rr_list_free(&params->prereq_list, &params->mm);
+}
+
+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);
+}
+
+int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
+{
+ if (params == NULL || argv == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = knsupdate_init(params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Long options.
+ struct option opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ /* Command line options processing. */
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "dhDvVp:t:r:y:k:", 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':
+ params->protocol = PROTO_TCP;
+ break;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ params->stop = true;
+ return KNOT_EOK;
+ case 'p':
+ 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);
+ if (ret != KNOT_EOK) {
+ ERR("invalid retries '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 't':
+ ret = params_parse_wait(optarg, &params->wait);
+ if (ret != KNOT_EOK) {
+ ERR("invalid timeout '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 'y':
+ 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);
+ return ret;
+ }
+ break;
+ case 'k':
+ 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);
+ return ret;
+ }
+ break;
+ default:
+ print_help();
+ return KNOT_ENOTSUP;
+ }
+ }
+
+ /* No retries for TCP. */
+ if (params->protocol == PROTO_TCP) {
+ params->retries = 0;
+ } else {
+ /* If wait/tries < 1 s, set 1 second for each try. */
+ if (params->wait > 0 &&
+ (uint32_t)params->wait < ( 1 + params->retries)) {
+ params->wait = 1;
+ } else {
+ params->wait /= (1 + params->retries);
+ }
+ }
+
+ /* Process non-option parameters. */
+ for (; optind < argc; ++optind) {
+ ptrlist_add(&params->qfiles, argv[optind], &params->mm);
+ }
+
+ return ret;
+}
+
+int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl)
+{
+ int ret = parser_set_default(&params->parser, "$TTL %u\n", ttl);
+ if (ret != KNOT_EOK) {
+ ERR("failed to set default TTL, %s", zs_strerror(ret));
+ }
+ return ret;
+}
+
+int knsupdate_set_origin(knsupdate_params_t *params, const char *origin)
+{
+ char *fqdn = get_fqd_name(origin);
+
+ int ret = parser_set_default(&params->parser, "$ORIGIN %s\n", fqdn);
+
+ free(fqdn);
+
+ if (ret != KNOT_EOK) {
+ ERR("failed to set default origin, %s", zs_strerror(ret));
+ }
+ return ret;
+}