/* Copyright (C) 2022 CZ.NIC, z.s.p.o. 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 . */ #include #include #include #include #include #include #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(¶ms->qfiles); init_list(¶ms->update_list); init_list(¶ms->prereq_list); /* Initialize memory context. */ mm_ctx_mempool(¶ms->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(¶ms->parser, ".", params->class_num, 3600) != 0 || zs_set_processing(¶ms->parser, NULL, NULL, NULL) != 0) { zs_deinit(¶ms->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, ¶ms->mm); params->answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, ¶ms->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(¶ms->qfiles, ¶ms->mm); srv_info_free(params->server); srv_info_free(params->srcif); free(params->zone); zs_deinit(¶ms->parser); knot_pkt_free(params->query); knot_pkt_free(params->answer); knot_tsig_key_deinit(¶ms->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(¶ms->update_list, ¶ms->mm); /* Free PREREQ RRSets. */ rr_list_free(¶ms->prereq_list, ¶ms->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, ¶ms->retries); if (ret != KNOT_EOK) { ERR("invalid retries '%s'", optarg); return ret; } break; case 't': ret = params_parse_wait(optarg, ¶ms->wait); if (ret != KNOT_EOK) { ERR("invalid timeout '%s'", optarg); return ret; } break; case 'y': knot_tsig_key_deinit(¶ms->tsig_key); ret = knot_tsig_key_init_str(¶ms->tsig_key, optarg); if (ret != KNOT_EOK) { ERR("failed to parse key '%s'", optarg); return ret; } break; case 'k': knot_tsig_key_deinit(¶ms->tsig_key); ret = knot_tsig_key_init_file(¶ms->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(¶ms->qfiles, argv[optind], ¶ms->mm); } return ret; } int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl) { int ret = parser_set_default(¶ms->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(¶ms->parser, "$ORIGIN %s\n", fqdn); free(fqdn); if (ret != KNOT_EOK) { ERR("failed to set default origin, %s", zs_strerror(ret)); } return ret; }