Home | History | Annotate | Line # | Download | only in named
      1 /*	$NetBSD: config.c,v 1.20 2026/01/29 18:36:27 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <bind.keys.h>
     19 #include <inttypes.h>
     20 #include <stdlib.h>
     21 
     22 #include <isc/buffer.h>
     23 #include <isc/log.h>
     24 #include <isc/mem.h>
     25 #include <isc/netmgr.h>
     26 #include <isc/parseint.h>
     27 #include <isc/region.h>
     28 #include <isc/result.h>
     29 #include <isc/sockaddr.h>
     30 #include <isc/string.h>
     31 #include <isc/util.h>
     32 
     33 #include <dns/fixedname.h>
     34 #include <dns/kasp.h>
     35 #include <dns/name.h>
     36 #include <dns/rdataclass.h>
     37 #include <dns/rdatatype.h>
     38 #include <dns/tsig.h>
     39 #include <dns/zone.h>
     40 
     41 #include <dst/dst.h>
     42 
     43 #include <isccfg/grammar.h>
     44 #include <isccfg/namedconf.h>
     45 
     46 #include <named/config.h>
     47 #include <named/globals.h>
     48 
     49 /*% default configuration */
     50 static char defaultconf[] = "\
     51 options {\n\
     52 	answer-cookie true;\n\
     53 	automatic-interface-scan yes;\n\
     54 #	blackhole {none;};\n\
     55 	cookie-algorithm siphash24;\n\
     56 #	directory <none>\n\
     57 	dnssec-policy \"none\";\n\
     58 	dump-file \"named_dump.db\";\n\
     59 	edns-udp-size 1232;\n"
     60 #if defined(HAVE_GEOIP2)
     61 			    "\
     62 	geoip-directory \"" MAXMINDDB_PREFIX "/share/GeoIP\";\n"
     63 #elif defined(HAVE_GEOIP2)
     64 			    "\
     65 	geoip-directory \".\";\n"
     66 #endif /* if defined(HAVE_GEOIP2) */
     67 			    "\
     68 	heartbeat-interval 60;\n\
     69 	interface-interval 60m;\n					\
     70 	listen-on {any;};\n\
     71 	listen-on-v6 {any;};\n\
     72 	match-mapped-addresses no;\n\
     73 	max-ixfr-ratio 100%;\n\
     74 	max-rsa-exponent-size 0; /* no limit */\n\
     75 	max-udp-size 1232;\n\
     76 	memstatistics-file \"named.memstats\";\n\
     77 	nocookie-udp-size 4096;\n\
     78 	notify-rate 20;\n\
     79 	nta-lifetime 3600;\n\
     80 	nta-recheck 300;\n\
     81 #	pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\
     82 	port 53;\n"
     83 #if HAVE_SO_REUSEPORT_LB
     84 			    "\
     85 	reuseport yes;\n"
     86 #else
     87 			    "\
     88 	reuseport no;\n"
     89 #endif
     90 			    "\
     91 	tls-port 853;\n"
     92 #if HAVE_LIBNGHTTP2
     93 			    "\
     94 	http-port 80;\n\
     95 	https-port 443;\n\
     96 	http-listener-clients 300;\n\
     97 	http-streams-per-connection 100;\n"
     98 #endif
     99 			    "\
    100 	prefetch 2 9;\n\
    101 #	querylog <boolean>;\n\
    102 	recursing-file \"named.recursing\";\n\
    103 	recursive-clients 1000;\n\
    104 	request-nsid false;\n\
    105 	resolver-query-timeout 10;\n\
    106 #	responselog <boolean>;\n\
    107 	rrset-order { order random; };\n\
    108 	secroots-file \"named.secroots\";\n\
    109 	send-cookie true;\n\
    110 	serial-query-rate 20;\n\
    111 	server-id none;\n\
    112 	session-keyalg hmac-sha256;\n\
    113 #	session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
    114 	session-keyname local-ddns;\n\
    115 	startup-notify-rate 20;\n\
    116 	sig0checks-quota 1;\n\
    117 	sig0key-checks-limit 16;\n\
    118 	sig0message-checks-limit 2;\n\
    119 	statistics-file \"named.stats\";\n\
    120 	tcp-advertised-timeout 300;\n\
    121 	tcp-clients 150;\n\
    122 	tcp-idle-timeout 300;\n\
    123 	tcp-initial-timeout 300;\n\
    124 	tcp-keepalive-timeout 300;\n\
    125 	tcp-listen-queue 10;\n\
    126 	tcp-receive-buffer 0;\n\
    127 	tcp-send-buffer 0;\n\
    128 #	tkey-gssapi-credential <none>\n\
    129 	transfer-message-size 20480;\n\
    130 	transfers-in 10;\n\
    131 	transfers-out 10;\n\
    132 	transfers-per-ns 2;\n\
    133 	trust-anchor-telemetry yes;\n\
    134 	udp-receive-buffer 0;\n\
    135 	udp-send-buffer 0;\n\
    136 	update-quota 100;\n\
    137 \n\
    138 	/* view */\n\
    139 	allow-new-zones no;\n\
    140 	allow-notify {none;};\n\
    141 	allow-proxy {none;};\n\
    142 	allow-proxy-on {any;};\n\
    143 	allow-query-cache { localnets; localhost; };\n\
    144 	allow-query-cache-on { any; };\n\
    145 	allow-recursion { localnets; localhost; };\n\
    146 	allow-recursion-on { any; };\n\
    147 	allow-update-forwarding {none;};\n\
    148 	auth-nxdomain false;\n\
    149 	check-dup-records warn;\n\
    150 	check-mx warn;\n\
    151 	check-names primary fail;\n\
    152 	check-names response ignore;\n\
    153 	check-names secondary warn;\n\
    154 	check-spf warn;\n\
    155 	check-svcb yes;\n\
    156 	clients-per-query 10;\n\
    157 	dnssec-accept-expired no;\n\
    158 	dnssec-validation " VALIDATION_DEFAULT "; \n"
    159 #ifdef USE_DNSRPS
    160 			    "	dnsrps-library \"" DNSRPS_LIBRPZ_PATH "\";\n"
    161 #endif /* ifdef USE_DNSRPS */
    162 #ifdef HAVE_DNSTAP
    163 			    "	dnstap-identity hostname;\n"
    164 #endif /* ifdef HAVE_DNSTAP */
    165 			    "\
    166 	fetch-quota-params 100 0.1 0.3 0.7;\n\
    167 	fetches-per-server 0;\n\
    168 	fetches-per-zone 0;\n\
    169 	lame-ttl 0;\n"
    170 #ifdef HAVE_LMDB
    171 			    "	lmdb-mapsize 32M;\n"
    172 #endif /* ifdef HAVE_LMDB */
    173 			    "	max-cache-size default;\n\
    174 	max-cache-ttl 604800; /* 1 week */\n\
    175 	max-clients-per-query 100;\n\
    176 	max-ncache-ttl 10800; /* 3 hours */\n\
    177 	max-recursion-depth 7;\n\
    178 	max-recursion-queries 50;\n\
    179 	max-query-count 200;\n\
    180 	max-query-restarts 11;\n\
    181 	max-stale-ttl 86400; /* 1 day */\n\
    182 	message-compression yes;\n\
    183 	min-ncache-ttl 0; /* 0 hours */\n\
    184 	min-cache-ttl 0; /* 0 seconds */\n\
    185 	minimal-any false;\n\
    186 	minimal-responses no-auth-recursive;\n\
    187 	notify-source *;\n\
    188 	notify-source-v6 *;\n\
    189 	nsec3-test-zone no;\n\
    190 	parental-source *;\n\
    191 	parental-source-v6 *;\n\
    192 	provide-ixfr true;\n\
    193 	response-padding { none; } block-size 0;\n\
    194 	qname-minimization relaxed;\n\
    195 	query-source address *;\n\
    196 	query-source-v6 address *;\n\
    197 	recursion true;\n\
    198 	request-expire true;\n\
    199 	request-ixfr true;\n\
    200 	require-server-cookie no;\n\
    201 	root-key-sentinel yes;\n\
    202 	servfail-ttl 1;\n\
    203 #	sortlist <none>\n\
    204 	stale-answer-client-timeout off;\n\
    205 	stale-answer-enable false;\n\
    206 	stale-answer-ttl 30; /* 30 seconds */\n\
    207 	stale-cache-enable false;\n\
    208 	stale-refresh-time 30; /* 30 seconds */\n\
    209 	synth-from-dnssec yes;\n\
    210 #	topology <none>\n\
    211 	transfer-format many-answers;\n\
    212 	resolver-use-dns64 false;\n\
    213 	v6-bias 50;\n\
    214 	zero-no-soa-ttl-cache no;\n\
    215 \n\
    216 	/* zone */\n\
    217 	allow-query {any;};\n\
    218 	allow-query-on {any;};\n\
    219 	allow-transfer {none;};\n\
    220 #	also-notify <none>\n\
    221 	check-integrity yes;\n\
    222 	check-mx-cname warn;\n\
    223 	check-sibling yes;\n\
    224 	check-srv-cname warn;\n\
    225 	check-wildcard yes;\n\
    226 	dialup no;\n\
    227 	dnssec-loadkeys-interval 60;\n\
    228 #	forward <none>\n\
    229 #	forwarders <none>\n\
    230 #	inline-signing no;\n\
    231 	ixfr-from-differences false;\n\
    232 	max-journal-size default;\n\
    233 	max-records 0;\n\
    234 	max-records-per-type 100;\n\
    235 	max-refresh-time 2419200; /* 4 weeks */\n\
    236 	max-retry-time 1209600; /* 2 weeks */\n\
    237 	max-types-per-name 100;\n\
    238 	max-transfer-idle-in 60;\n\
    239 	max-transfer-idle-out 60;\n\
    240 	max-transfer-time-in 120;\n\
    241 	max-transfer-time-out 120;\n\
    242 	min-refresh-time 300;\n\
    243 	min-retry-time 500;\n\
    244 	min-transfer-rate-in 10240 5;\n\
    245 	multi-master no;\n\
    246 	notify yes;\n\
    247 	notify-defer 0;\n\
    248 	notify-delay 5;\n\
    249 	notify-to-soa no;\n\
    250 	serial-update-method increment;\n\
    251 	sig-signing-nodes 100;\n\
    252 	sig-signing-signatures 10;\n\
    253 	sig-signing-type 65534;\n\
    254 	transfer-source *;\n\
    255 	transfer-source-v6 *;\n\
    256 	try-tcp-refresh yes; /* BIND 8 compat */\n\
    257 	zero-no-soa-ttl yes;\n\
    258 	zone-statistics terse;\n\
    259 };\n\
    260 "
    261 
    262 			    "#\n\
    263 #  Zones in the \"_bind\" view are NOT counted in the count of zones.\n\
    264 #\n\
    265 view \"_bind\" chaos {\n\
    266 	recursion no;\n\
    267 	notify no;\n\
    268 	allow-new-zones no;\n\
    269 	max-cache-size 2M;\n\
    270 \n\
    271 	# Prevent use of this zone in DNS amplified reflection DoS attacks\n\
    272 	rate-limit {\n\
    273 		responses-per-second 3;\n\
    274 		slip 0;\n\
    275 		min-table-size 10;\n\
    276 	};\n\
    277 \n\
    278 	zone \"version.bind\" chaos {\n\
    279 		type primary;\n\
    280 		database \"_builtin version\";\n\
    281 	};\n\
    282 \n\
    283 	zone \"hostname.bind\" chaos {\n\
    284 		type primary;\n\
    285 		database \"_builtin hostname\";\n\
    286 	};\n\
    287 \n\
    288 	zone \"authors.bind\" chaos {\n\
    289 		type primary;\n\
    290 		database \"_builtin authors\";\n\
    291 	};\n\
    292 \n\
    293 	zone \"id.server\" chaos {\n\
    294 		type primary;\n\
    295 		database \"_builtin id\";\n\
    296 	};\n\
    297 };\n\
    298 "
    299 			    "#\n\
    300 #  Built-in DNSSEC key and signing policies.\n\
    301 #\n\
    302 dnssec-policy \"default\" {\n\
    303 	keys {\n\
    304 		csk key-directory lifetime unlimited algorithm 13;\n\
    305 	};\n\
    306 \n\
    307 	cdnskey yes;\n\
    308 	cds-digest-types { 2; };\n\
    309 	dnskey-ttl " DNS_KASP_KEY_TTL ";\n\
    310 	inline-signing yes;\n\
    311 	manual-mode no;\n\
    312 	offline-ksk no;\n\
    313 	publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\
    314 	retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\
    315 	purge-keys " DNS_KASP_PURGE_KEYS "; \n\
    316 	signatures-jitter " DNS_KASP_SIG_JITTER "; \n\
    317 	signatures-refresh " DNS_KASP_SIG_REFRESH "; \n\
    318 	signatures-validity " DNS_KASP_SIG_VALIDITY "; \n\
    319 	signatures-validity-dnskey " DNS_KASP_SIG_VALIDITY_DNSKEY "; \n\
    320 	max-zone-ttl " DNS_KASP_ZONE_MAXTTL "; \n\
    321 	zone-propagation-delay " DNS_KASP_ZONE_PROPDELAY "; \n\
    322 	parent-ds-ttl " DNS_KASP_DS_TTL "; \n\
    323 	parent-propagation-delay " DNS_KASP_PARENT_PROPDELAY "; \n\
    324 };\n\
    325 \n\
    326 dnssec-policy \"insecure\" {\n\
    327 	max-zone-ttl 0; \n\
    328 	keys { };\n\
    329 	inline-signing yes;\n\
    330 	manual-mode no;\n\
    331 };\n\
    332 \n\
    333 "
    334 			    "#\n\
    335 #  Default trusted key(s), used if \n\
    336 # \"dnssec-validation auto;\" is set and\n\
    337 #  " NAMED_SYSCONFDIR "/bind.keys doesn't exist).\n\
    338 #\n\
    339 # BEGIN TRUST ANCHORS\n"
    340 
    341 	/* Imported from bind.keys.h: */
    342 	TRUST_ANCHORS
    343 
    344 			    "# END TRUST ANCHORS\n\
    345 \n\
    346 remote-servers " DEFAULT_IANA_ROOT_ZONE_PRIMARIES " {\n\
    347 	2801:1b8:10::b;		# b.root-servers.net\n\
    348 	2001:500:2::c;		# c.root-servers.net\n\
    349 	2001:500:2f::f;		# f.root-servers.net\n\
    350 	2001:500:12::d0d;	# g.root-servers.net\n\
    351 	2001:7fd::1;		# k.root-servers.net\n\
    352 	2620:0:2830:202::132;	# xfr.cjr.dns.icann.org\n\
    353 	2620:0:2d0:202::132;	# xfr.lax.dns.icann.org\n\
    354 	170.247.170.2;		# b.root-servers.net\n\
    355 	192.33.4.12;		# c.root-servers.net\n\
    356 	192.5.5.241;		# f.root-servers.net\n\
    357 	192.112.36.4;		# g.root-servers.net\n\
    358 	193.0.14.129;		# k.root-servers.net\n\
    359 	192.0.47.132;		# xfr.cjr.dns.icann.org\n\
    360 	192.0.32.132;		# xfr.lax.dns.icann.org\n\
    361 };\n\
    362 ";
    363 
    364 isc_result_t
    365 named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) {
    366 	isc_buffer_t b;
    367 
    368 	isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1);
    369 	isc_buffer_add(&b, sizeof(defaultconf) - 1);
    370 	return cfg_parse_buffer(parser, &b, __FILE__, 0, &cfg_type_namedconf,
    371 				CFG_PCTX_NODEPRECATED | CFG_PCTX_NOOBSOLETE |
    372 					CFG_PCTX_NOEXPERIMENTAL,
    373 				conf);
    374 }
    375 
    376 const char *
    377 named_config_getdefault(void) {
    378 	return defaultconf;
    379 }
    380 
    381 isc_result_t
    382 named_config_get(cfg_obj_t const *const *maps, const char *name,
    383 		 const cfg_obj_t **obj) {
    384 	int i;
    385 
    386 	for (i = 0; maps[i] != NULL; i++) {
    387 		if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
    388 			return ISC_R_SUCCESS;
    389 		}
    390 	}
    391 	return ISC_R_NOTFOUND;
    392 }
    393 
    394 isc_result_t
    395 named_checknames_get(const cfg_obj_t **maps, const char *const names[],
    396 		     const cfg_obj_t **obj) {
    397 	const cfg_listelt_t *element;
    398 	const cfg_obj_t *checknames;
    399 	const cfg_obj_t *type;
    400 	const cfg_obj_t *value;
    401 	int i;
    402 
    403 	REQUIRE(maps != NULL);
    404 	REQUIRE(names != NULL);
    405 	REQUIRE(obj != NULL && *obj == NULL);
    406 
    407 	for (i = 0; maps[i] != NULL; i++) {
    408 		checknames = NULL;
    409 		if (cfg_map_get(maps[i], "check-names", &checknames) ==
    410 		    ISC_R_SUCCESS)
    411 		{
    412 			/*
    413 			 * Zone map entry is not a list.
    414 			 */
    415 			if (checknames != NULL && !cfg_obj_islist(checknames)) {
    416 				*obj = checknames;
    417 				return ISC_R_SUCCESS;
    418 			}
    419 			for (element = cfg_list_first(checknames);
    420 			     element != NULL; element = cfg_list_next(element))
    421 			{
    422 				value = cfg_listelt_value(element);
    423 				type = cfg_tuple_get(value, "type");
    424 
    425 				for (size_t j = 0; names[j] != NULL; j++) {
    426 					if (strcasecmp(cfg_obj_asstring(type),
    427 						       names[j]) == 0)
    428 					{
    429 						*obj = cfg_tuple_get(value,
    430 								     "mode");
    431 						return ISC_R_SUCCESS;
    432 					}
    433 				}
    434 			}
    435 		}
    436 	}
    437 	return ISC_R_NOTFOUND;
    438 }
    439 
    440 int
    441 named_config_listcount(const cfg_obj_t *list) {
    442 	const cfg_listelt_t *e;
    443 	int i = 0;
    444 
    445 	for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e)) {
    446 		i++;
    447 	}
    448 
    449 	return i;
    450 }
    451 
    452 isc_result_t
    453 named_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
    454 		      dns_rdataclass_t *classp) {
    455 	isc_textregion_t r;
    456 	isc_result_t result;
    457 
    458 	if (!cfg_obj_isstring(classobj)) {
    459 		*classp = defclass;
    460 		return ISC_R_SUCCESS;
    461 	}
    462 	r.base = UNCONST(cfg_obj_asstring(classobj));
    463 	r.length = strlen(r.base);
    464 	result = dns_rdataclass_fromtext(classp, &r);
    465 	if (result != ISC_R_SUCCESS) {
    466 		cfg_obj_log(classobj, named_g_lctx, ISC_LOG_ERROR,
    467 			    "unknown class '%s'", r.base);
    468 	}
    469 	return result;
    470 }
    471 
    472 isc_result_t
    473 named_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
    474 		     dns_rdatatype_t *typep) {
    475 	isc_textregion_t r;
    476 	isc_result_t result;
    477 
    478 	if (!cfg_obj_isstring(typeobj)) {
    479 		*typep = deftype;
    480 		return ISC_R_SUCCESS;
    481 	}
    482 	r.base = UNCONST(cfg_obj_asstring(typeobj));
    483 	r.length = strlen(r.base);
    484 	result = dns_rdatatype_fromtext(typep, &r);
    485 	if (result != ISC_R_SUCCESS) {
    486 		cfg_obj_log(typeobj, named_g_lctx, ISC_LOG_ERROR,
    487 			    "unknown type '%s'", r.base);
    488 	}
    489 	return result;
    490 }
    491 
    492 dns_zonetype_t
    493 named_config_getzonetype(const cfg_obj_t *zonetypeobj) {
    494 	dns_zonetype_t ztype = dns_zone_none;
    495 	const char *str;
    496 
    497 	str = cfg_obj_asstring(zonetypeobj);
    498 	if (strcasecmp(str, "primary") == 0 || strcasecmp(str, "master") == 0) {
    499 		ztype = dns_zone_primary;
    500 	} else if (strcasecmp(str, "secondary") == 0 ||
    501 		   strcasecmp(str, "slave") == 0)
    502 	{
    503 		ztype = dns_zone_secondary;
    504 	} else if (strcasecmp(str, "mirror") == 0) {
    505 		ztype = dns_zone_mirror;
    506 	} else if (strcasecmp(str, "stub") == 0) {
    507 		ztype = dns_zone_stub;
    508 	} else if (strcasecmp(str, "static-stub") == 0) {
    509 		ztype = dns_zone_staticstub;
    510 	} else if (strcasecmp(str, "redirect") == 0) {
    511 		ztype = dns_zone_redirect;
    512 	} else {
    513 		UNREACHABLE();
    514 	}
    515 	return ztype;
    516 }
    517 
    518 isc_result_t
    519 named_config_getremotesdef(const cfg_obj_t *cctx, const char *list,
    520 			   const char *name, const cfg_obj_t **ret) {
    521 	isc_result_t result;
    522 	const cfg_obj_t *obj = NULL;
    523 	const cfg_listelt_t *elt;
    524 
    525 	REQUIRE(cctx != NULL);
    526 	REQUIRE(name != NULL);
    527 	REQUIRE(ret != NULL && *ret == NULL);
    528 
    529 	result = cfg_map_get(cctx, list, &obj);
    530 	if (result != ISC_R_SUCCESS) {
    531 		return result;
    532 	}
    533 	elt = cfg_list_first(obj);
    534 	while (elt != NULL) {
    535 		obj = cfg_listelt_value(elt);
    536 		if (strcasecmp(cfg_obj_asstring(cfg_tuple_get(obj, "name")),
    537 			       name) == 0)
    538 		{
    539 			*ret = obj;
    540 			return ISC_R_SUCCESS;
    541 		}
    542 		elt = cfg_list_next(elt);
    543 	}
    544 	return ISC_R_NOTFOUND;
    545 }
    546 
    547 static isc_result_t
    548 named_config_getname(isc_mem_t *mctx, const cfg_obj_t *obj,
    549 		     dns_name_t **namep) {
    550 	REQUIRE(namep != NULL && *namep == NULL);
    551 
    552 	const char *objstr;
    553 	isc_result_t result;
    554 	isc_buffer_t b;
    555 	dns_fixedname_t fname;
    556 
    557 	if (!cfg_obj_isstring(obj)) {
    558 		*namep = NULL;
    559 		return ISC_R_SUCCESS;
    560 	}
    561 
    562 	*namep = isc_mem_get(mctx, sizeof(**namep));
    563 	dns_name_init(*namep, NULL);
    564 
    565 	objstr = cfg_obj_asstring(obj);
    566 	isc_buffer_constinit(&b, objstr, strlen(objstr));
    567 	isc_buffer_add(&b, strlen(objstr));
    568 	dns_fixedname_init(&fname);
    569 	result = dns_name_fromtext(dns_fixedname_name(&fname), &b, dns_rootname,
    570 				   0, NULL);
    571 	if (result != ISC_R_SUCCESS) {
    572 		isc_mem_put(mctx, *namep, sizeof(**namep));
    573 		*namep = NULL;
    574 		return result;
    575 	}
    576 	dns_name_dup(dns_fixedname_name(&fname), mctx, *namep);
    577 
    578 	return ISC_R_SUCCESS;
    579 }
    580 
    581 #define grow_array(mctx, array, newlen, oldlen)                          \
    582 	if (newlen >= oldlen) {                                          \
    583 		array = isc_mem_creget(mctx, array, oldlen, newlen + 16, \
    584 				       sizeof(array[0]));                \
    585 		oldlen = newlen + 16;                                    \
    586 	}
    587 
    588 #define shrink_array(mctx, array, newlen, oldlen)                   \
    589 	if (newlen < oldlen) {                                      \
    590 		array = isc_mem_creget(mctx, array, oldlen, newlen, \
    591 				       sizeof(array[0]));           \
    592 		oldlen = newlen;                                    \
    593 	}
    594 
    595 static const char *remotesnames[4] = { "remote-servers", "parental-agents",
    596 				       "primaries", "masters" };
    597 
    598 typedef struct {
    599 	isc_sockaddr_t *addrs;
    600 	size_t addrsallocated;
    601 
    602 	isc_sockaddr_t *sources;
    603 	size_t sourcesallocated;
    604 
    605 	dns_name_t **keys;
    606 	size_t keysallocated;
    607 
    608 	dns_name_t **tlss;
    609 	size_t tlssallocated;
    610 
    611 	size_t count; /* common to addrs, sources, keys and tlss */
    612 
    613 	const char **seen;
    614 	size_t seencount;
    615 	size_t seenallocated;
    616 } getipandkeylist_state_t;
    617 
    618 static isc_result_t
    619 getipandkeylist(in_port_t defport, in_port_t deftlsport,
    620 		const cfg_obj_t *config, const cfg_obj_t *list,
    621 		in_port_t listport, const cfg_obj_t *listkey,
    622 		const cfg_obj_t *listtls, isc_mem_t *mctx,
    623 		getipandkeylist_state_t *s) {
    624 	const cfg_obj_t *addrlist = cfg_tuple_get(list, "addresses");
    625 	const cfg_obj_t *portobj = cfg_tuple_get(list, "port");
    626 	const cfg_obj_t *src4obj = cfg_tuple_get(list, "source");
    627 	const cfg_obj_t *src6obj = cfg_tuple_get(list, "source-v6");
    628 	in_port_t port = (in_port_t)0;
    629 	isc_sockaddr_t src4;
    630 	isc_sockaddr_t src6;
    631 	isc_result_t result = ISC_R_SUCCESS;
    632 
    633 	if (cfg_obj_isuint32(portobj)) {
    634 		uint32_t val = cfg_obj_asuint32(portobj);
    635 		if (val > UINT16_MAX) {
    636 			cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
    637 				    "port '%u' out of range", val);
    638 			return ISC_R_RANGE;
    639 		}
    640 		port = (in_port_t)val;
    641 	} else if (listport > 0) {
    642 		/*
    643 		 * No port in the current list, but it is a list named elsewhere
    644 		 * where the port is defined, i.e:
    645 		 *
    646 		 * remote-servers bar { 10.53.0.4; };
    647 		 * remote-servers foo port 5555 { bar; 10.54.0.3; };
    648 		 *                                ^^^
    649 		 *
    650 		 * The current list is the list `bar`, and the server
    651 		 * `10.53.0.4` has the port `5555` defined.
    652 		 */
    653 		port = listport;
    654 	}
    655 
    656 	if (src4obj != NULL && cfg_obj_issockaddr(src4obj)) {
    657 		src4 = *cfg_obj_assockaddr(src4obj);
    658 	} else {
    659 		isc_sockaddr_any(&src4);
    660 	}
    661 
    662 	if (src6obj != NULL && cfg_obj_issockaddr(src6obj)) {
    663 		src6 = *cfg_obj_assockaddr(src6obj);
    664 	} else {
    665 		isc_sockaddr_any6(&src6);
    666 	}
    667 
    668 	for (const cfg_listelt_t *element = cfg_list_first(addrlist);
    669 	     element != NULL; element = cfg_list_next(element))
    670 	{
    671 		const cfg_obj_t *addr;
    672 		const cfg_obj_t *key;
    673 		const cfg_obj_t *tls;
    674 
    675 	skiplist:
    676 		addr = cfg_tuple_get(cfg_listelt_value(element),
    677 				     "remoteselement");
    678 		key = cfg_tuple_get(cfg_listelt_value(element), "key");
    679 		tls = cfg_tuple_get(cfg_listelt_value(element), "tls");
    680 
    681 		/*
    682 		 * If this is not an address, this is the name of a nested list,
    683 		 * i.e.
    684 		 *
    685 		 * remote-servers nestedlist { 10.53.0.4; };
    686 		 * remote-servers list { nestedlist key foo; 10.54.0.6; };
    687 		 *                       ^^^^^^^^^^^^^^^^^^
    688 		 *
    689 		 * We are currently in the list `list`, and `addr` is the name
    690 		 * `nestedlist`, so we'll immediately recurse to process
    691 		 * `nestedlist` before processing the next element of `list`.
    692 		 */
    693 		if (!cfg_obj_issockaddr(addr)) {
    694 			const char *listname = cfg_obj_asstring(addr);
    695 			const cfg_obj_t *nestedlist = NULL;
    696 			isc_result_t tresult;
    697 
    698 			for (size_t i = 0; i < s->seencount; i++) {
    699 				if (strcasecmp(s->seen[i], listname) == 0) {
    700 					element = cfg_list_next(element);
    701 					goto skiplist;
    702 				}
    703 			}
    704 
    705 			grow_array(mctx, s->seen, s->seencount,
    706 				   s->seenallocated);
    707 			s->seen[s->seencount] = listname;
    708 
    709 			for (size_t i = 0; i < ARRAY_SIZE(remotesnames); i++) {
    710 				tresult = named_config_getremotesdef(
    711 					config, remotesnames[i], listname,
    712 					&nestedlist);
    713 				if (tresult == ISC_R_SUCCESS) {
    714 					break;
    715 				}
    716 			}
    717 
    718 			if (tresult != ISC_R_SUCCESS) {
    719 				cfg_obj_log(addr, named_g_lctx, ISC_LOG_ERROR,
    720 					    "remote-servers \"%s\" not found",
    721 					    listname);
    722 				return tresult;
    723 			}
    724 
    725 			result = getipandkeylist(defport, deftlsport, config,
    726 						 nestedlist, port, key, tls,
    727 						 mctx, s);
    728 			if (result != ISC_R_SUCCESS) {
    729 				goto out;
    730 			}
    731 			continue;
    732 		}
    733 
    734 		grow_array(mctx, s->addrs, s->count, s->addrsallocated);
    735 		grow_array(mctx, s->keys, s->count, s->keysallocated);
    736 		grow_array(mctx, s->tlss, s->count, s->tlssallocated);
    737 		grow_array(mctx, s->sources, s->count, s->sourcesallocated);
    738 
    739 		s->addrs[s->count] = *cfg_obj_assockaddr(addr);
    740 
    741 		result = named_config_getname(mctx, key, &s->keys[s->count]);
    742 		if (result != ISC_R_SUCCESS) {
    743 			goto out;
    744 		}
    745 
    746 		/*
    747 		 * The `key` is not provided for this address, so, if we're
    748 		 * inside a named list, get the `key` provided at the point the
    749 		 * list is used.
    750 		 */
    751 		if (s->keys[s->count] == NULL && listkey != NULL) {
    752 			result = named_config_getname(mctx, listkey,
    753 						      &s->keys[s->count]);
    754 			if (result != ISC_R_SUCCESS) {
    755 				goto out;
    756 			}
    757 		}
    758 
    759 		result = named_config_getname(mctx, tls, &s->tlss[s->count]);
    760 		if (result != ISC_R_SUCCESS) {
    761 			goto out;
    762 		}
    763 
    764 		/*
    765 		 * The `tls` is not provided for this address, so, if we're
    766 		 * inside a named list, get the `tls` provided at the point the
    767 		 * named list is used.
    768 		 */
    769 		if (s->tlss[s->count] == NULL && listtls != NULL) {
    770 			result = named_config_getname(mctx, listtls,
    771 						      &s->tlss[s->count]);
    772 		}
    773 
    774 		/* If the port is unset, take it from one of the upper levels */
    775 		if (isc_sockaddr_getport(&s->addrs[s->count]) == 0) {
    776 			in_port_t addr_port = port;
    777 
    778 			/* If unset, use the default port or tls-port */
    779 			if (addr_port == 0) {
    780 				if (s->tlss[s->count] != NULL) {
    781 					addr_port = deftlsport;
    782 				} else {
    783 					addr_port = defport;
    784 				}
    785 			}
    786 
    787 			isc_sockaddr_setport(&s->addrs[s->count], addr_port);
    788 		}
    789 
    790 		switch (isc_sockaddr_pf(&s->addrs[s->count])) {
    791 		case PF_INET:
    792 			s->sources[s->count] = src4;
    793 			break;
    794 		case PF_INET6:
    795 			s->sources[s->count] = src6;
    796 			break;
    797 		default:
    798 			result = ISC_R_NOTIMPLEMENTED;
    799 			goto out;
    800 		}
    801 
    802 		s->count++;
    803 	}
    804 
    805 out:
    806 	if (result != ISC_R_SUCCESS) {
    807 		/*
    808 		 * Reaching this point without success means we were in the
    809 		 * middle of adding a new entry, so it needs to be counted for
    810 		 * correctly free `s.keys` and `s.tlss` (as they potentially
    811 		 * added a new element right before something fails)
    812 		 */
    813 		s->count++;
    814 	}
    815 	return result;
    816 }
    817 
    818 isc_result_t
    819 named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
    820 			     isc_mem_t *mctx, dns_ipkeylist_t *ipkl) {
    821 	isc_result_t result;
    822 	in_port_t def_port;
    823 	in_port_t def_tlsport;
    824 	getipandkeylist_state_t s = {};
    825 
    826 	REQUIRE(ipkl != NULL);
    827 	REQUIRE(ipkl->count == 0);
    828 	REQUIRE(ipkl->addrs == NULL);
    829 	REQUIRE(ipkl->keys == NULL);
    830 	REQUIRE(ipkl->tlss == NULL);
    831 	REQUIRE(ipkl->labels == NULL);
    832 	REQUIRE(ipkl->allocated == 0);
    833 
    834 	/*
    835 	 * Get system defaults.
    836 	 */
    837 	result = named_config_getport(config, "port", &def_port);
    838 	if (result != ISC_R_SUCCESS) {
    839 		goto cleanup;
    840 	}
    841 
    842 	result = named_config_getport(config, "tls-port", &def_tlsport);
    843 	if (result != ISC_R_SUCCESS) {
    844 		goto cleanup;
    845 	}
    846 
    847 	/*
    848 	 * Process the (nested) list(s).
    849 	 */
    850 	result = getipandkeylist(def_port, def_tlsport, config, list,
    851 				 (in_port_t)0, NULL, NULL, mctx, &s);
    852 	if (result != ISC_R_SUCCESS) {
    853 		goto cleanup;
    854 	}
    855 
    856 	shrink_array(mctx, s.addrs, s.count, s.addrsallocated);
    857 	shrink_array(mctx, s.keys, s.count, s.keysallocated);
    858 	shrink_array(mctx, s.tlss, s.count, s.tlssallocated);
    859 	shrink_array(mctx, s.sources, s.count, s.sourcesallocated);
    860 
    861 	ipkl->addrs = s.addrs;
    862 	ipkl->keys = s.keys;
    863 	ipkl->tlss = s.tlss;
    864 	ipkl->sources = s.sources;
    865 	ipkl->count = s.count;
    866 
    867 	INSIST(s.addrsallocated == s.keysallocated);
    868 	INSIST(s.addrsallocated == s.tlssallocated);
    869 	INSIST(s.addrsallocated == s.sourcesallocated);
    870 	ipkl->allocated = s.addrsallocated;
    871 
    872 	if (s.seen != NULL) {
    873 		/*
    874 		 * `s.seen` is not shrinked (no point, as it's deleted right
    875 		 * away anyway), so we need to use `s.seenallocated` to
    876 		 * correctly free the array.
    877 		 */
    878 		isc_mem_cput(mctx, s.seen, s.seenallocated, sizeof(s.seen[0]));
    879 	}
    880 
    881 	return ISC_R_SUCCESS;
    882 
    883 cleanup:
    884 	/*
    885 	 * Because we didn't shrinked the array back in this path, we need to
    886 	 * use `s.*allocated` to correctly free the allocated arrays.
    887 	 */
    888 	if (s.addrs != NULL) {
    889 		isc_mem_cput(mctx, s.addrs, s.count, sizeof(s.addrs[0]));
    890 	}
    891 	if (s.keys != NULL) {
    892 		for (size_t i = 0; i < s.count; i++) {
    893 			if (s.keys[i] == NULL) {
    894 				continue;
    895 			}
    896 			if (dns_name_dynamic(s.keys[i])) {
    897 				dns_name_free(s.keys[i], mctx);
    898 			}
    899 			isc_mem_put(mctx, s.keys[i], sizeof(*s.keys[i]));
    900 		}
    901 		isc_mem_cput(mctx, s.keys, s.keysallocated, sizeof(s.keys[0]));
    902 	}
    903 	if (s.tlss != NULL) {
    904 		for (size_t i = 0; i < s.count; i++) {
    905 			if (s.tlss[i] == NULL) {
    906 				continue;
    907 			}
    908 			if (dns_name_dynamic(s.tlss[i])) {
    909 				dns_name_free(s.tlss[i], mctx);
    910 			}
    911 			isc_mem_put(mctx, s.tlss[i], sizeof(*s.tlss[i]));
    912 		}
    913 		isc_mem_cput(mctx, s.tlss, s.tlssallocated, sizeof(s.tlss[0]));
    914 	}
    915 	if (s.sources != NULL) {
    916 		isc_mem_cput(mctx, s.sources, s.sourcesallocated,
    917 			     sizeof(s.sources[0]));
    918 	}
    919 	if (s.seen != NULL) {
    920 		isc_mem_cput(mctx, s.seen, s.seenallocated, sizeof(s.seen[0]));
    921 	}
    922 
    923 	return result;
    924 }
    925 
    926 isc_result_t
    927 named_config_getport(const cfg_obj_t *config, const char *type,
    928 		     in_port_t *portp) {
    929 	const cfg_obj_t *maps[3];
    930 	const cfg_obj_t *options = NULL;
    931 	const cfg_obj_t *portobj = NULL;
    932 	isc_result_t result;
    933 	int i;
    934 
    935 	(void)cfg_map_get(config, "options", &options);
    936 	i = 0;
    937 	if (options != NULL) {
    938 		maps[i++] = options;
    939 	}
    940 	maps[i++] = named_g_defaults;
    941 	maps[i] = NULL;
    942 
    943 	result = named_config_get(maps, type, &portobj);
    944 	INSIST(result == ISC_R_SUCCESS);
    945 	if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
    946 		cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
    947 			    "port '%u' out of range",
    948 			    cfg_obj_asuint32(portobj));
    949 		return ISC_R_RANGE;
    950 	}
    951 	*portp = (in_port_t)cfg_obj_asuint32(portobj);
    952 	return ISC_R_SUCCESS;
    953 }
    954 
    955 struct keyalgorithms {
    956 	const char *str;
    957 	enum {
    958 		hmacnone,
    959 		hmacmd5,
    960 		hmacsha1,
    961 		hmacsha224,
    962 		hmacsha256,
    963 		hmacsha384,
    964 		hmacsha512
    965 	} hmac;
    966 	unsigned int type;
    967 	uint16_t size;
    968 } algorithms[] = { { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 },
    969 		   { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 },
    970 		   { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 },
    971 		   { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 },
    972 		   { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 },
    973 		   { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 },
    974 		   { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 },
    975 		   { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 },
    976 		   { NULL, hmacnone, DST_ALG_UNKNOWN, 0 } };
    977 
    978 isc_result_t
    979 named_config_getkeyalgorithm(const char *str, unsigned int *typep,
    980 			     uint16_t *digestbits) {
    981 	int i;
    982 	size_t len = 0;
    983 	uint16_t bits;
    984 	isc_result_t result;
    985 
    986 	for (i = 0; algorithms[i].str != NULL; i++) {
    987 		len = strlen(algorithms[i].str);
    988 		if (strncasecmp(algorithms[i].str, str, len) == 0 &&
    989 		    (str[len] == '\0' ||
    990 		     (algorithms[i].size != 0 && str[len] == '-')))
    991 		{
    992 			break;
    993 		}
    994 	}
    995 	if (algorithms[i].str == NULL) {
    996 		return ISC_R_NOTFOUND;
    997 	}
    998 	if (str[len] == '-') {
    999 		result = isc_parse_uint16(&bits, str + len + 1, 10);
   1000 		if (result != ISC_R_SUCCESS) {
   1001 			return result;
   1002 		}
   1003 		if (bits > algorithms[i].size) {
   1004 			return ISC_R_RANGE;
   1005 		}
   1006 	} else if (algorithms[i].size == 0) {
   1007 		bits = 128;
   1008 	} else {
   1009 		bits = algorithms[i].size;
   1010 	}
   1011 	SET_IF_NOT_NULL(typep, algorithms[i].type);
   1012 	SET_IF_NOT_NULL(digestbits, bits);
   1013 	return ISC_R_SUCCESS;
   1014 }
   1015