Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: ssu.c,v 1.10 2025/01/26 16:25:25 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 <stdbool.h>
     19 
     20 #include <isc/magic.h>
     21 #include <isc/mem.h>
     22 #include <isc/netaddr.h>
     23 #include <isc/refcount.h>
     24 #include <isc/result.h>
     25 #include <isc/string.h>
     26 #include <isc/util.h>
     27 
     28 #include <dns/dlz.h>
     29 #include <dns/fixedname.h>
     30 #include <dns/name.h>
     31 #include <dns/ssu.h>
     32 
     33 #include <dst/dst.h>
     34 #include <dst/gssapi.h>
     35 
     36 #define SSUTABLEMAGIC	      ISC_MAGIC('S', 'S', 'U', 'T')
     37 #define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
     38 
     39 #define SSURULEMAGIC	     ISC_MAGIC('S', 'S', 'U', 'R')
     40 #define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC)
     41 
     42 struct dns_ssurule {
     43 	unsigned int magic;
     44 	bool grant;		      /*%< is this a grant or a deny? */
     45 	dns_ssumatchtype_t matchtype; /*%< which type of pattern match? */
     46 	dns_name_t *identity;	      /*%< the identity to match */
     47 	dns_name_t *name;	      /*%< the name being updated */
     48 	unsigned int ntypes;	      /*%< number of data types covered */
     49 	dns_ssuruletype_t *types;     /*%< the data types.  Can include */
     50 				      /*   ANY. if NULL, defaults to all */
     51 				      /*   types except SIG, SOA, and NS */
     52 	ISC_LINK(dns_ssurule_t) link;
     53 };
     54 
     55 struct dns_ssutable {
     56 	unsigned int magic;
     57 	isc_mem_t *mctx;
     58 	isc_refcount_t references;
     59 	dns_dlzdb_t *dlzdatabase;
     60 	ISC_LIST(dns_ssurule_t) rules;
     61 };
     62 
     63 void
     64 dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) {
     65 	dns_ssutable_t *table;
     66 
     67 	REQUIRE(tablep != NULL && *tablep == NULL);
     68 	REQUIRE(mctx != NULL);
     69 
     70 	table = isc_mem_get(mctx, sizeof(*table));
     71 	isc_refcount_init(&table->references, 1);
     72 	table->mctx = NULL;
     73 	isc_mem_attach(mctx, &table->mctx);
     74 	ISC_LIST_INIT(table->rules);
     75 	table->magic = SSUTABLEMAGIC;
     76 	*tablep = table;
     77 }
     78 
     79 static void
     80 destroy(dns_ssutable_t *table) {
     81 	isc_mem_t *mctx;
     82 
     83 	REQUIRE(VALID_SSUTABLE(table));
     84 
     85 	mctx = table->mctx;
     86 	while (!ISC_LIST_EMPTY(table->rules)) {
     87 		dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
     88 		if (rule->identity != NULL) {
     89 			dns_name_free(rule->identity, mctx);
     90 			isc_mem_put(mctx, rule->identity,
     91 				    sizeof(*rule->identity));
     92 		}
     93 		if (rule->name != NULL) {
     94 			dns_name_free(rule->name, mctx);
     95 			isc_mem_put(mctx, rule->name, sizeof(*rule->name));
     96 		}
     97 		if (rule->types != NULL) {
     98 			isc_mem_cput(mctx, rule->types, rule->ntypes,
     99 				     sizeof(*rule->types));
    100 		}
    101 		ISC_LIST_UNLINK(table->rules, rule, link);
    102 		rule->magic = 0;
    103 		isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
    104 	}
    105 	isc_refcount_destroy(&table->references);
    106 	table->magic = 0;
    107 	isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t));
    108 }
    109 
    110 void
    111 dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
    112 	REQUIRE(VALID_SSUTABLE(source));
    113 	REQUIRE(targetp != NULL && *targetp == NULL);
    114 
    115 	isc_refcount_increment(&source->references);
    116 
    117 	*targetp = source;
    118 }
    119 
    120 void
    121 dns_ssutable_detach(dns_ssutable_t **tablep) {
    122 	dns_ssutable_t *table;
    123 
    124 	REQUIRE(tablep != NULL);
    125 	table = *tablep;
    126 	*tablep = NULL;
    127 	REQUIRE(VALID_SSUTABLE(table));
    128 
    129 	if (isc_refcount_decrement(&table->references) == 1) {
    130 		destroy(table);
    131 	}
    132 }
    133 
    134 void
    135 dns_ssutable_addrule(dns_ssutable_t *table, bool grant,
    136 		     const dns_name_t *identity, dns_ssumatchtype_t matchtype,
    137 		     const dns_name_t *name, unsigned int ntypes,
    138 		     dns_ssuruletype_t *types) {
    139 	dns_ssurule_t *rule;
    140 	isc_mem_t *mctx;
    141 
    142 	REQUIRE(VALID_SSUTABLE(table));
    143 	REQUIRE(dns_name_isabsolute(identity));
    144 	REQUIRE(dns_name_isabsolute(name));
    145 	REQUIRE(matchtype <= dns_ssumatchtype_max);
    146 	if (matchtype == dns_ssumatchtype_wildcard) {
    147 		REQUIRE(dns_name_iswildcard(name));
    148 	}
    149 	if (ntypes > 0) {
    150 		REQUIRE(types != NULL);
    151 	}
    152 
    153 	mctx = table->mctx;
    154 	rule = isc_mem_get(mctx, sizeof(*rule));
    155 
    156 	rule->identity = NULL;
    157 	rule->name = NULL;
    158 	rule->types = NULL;
    159 
    160 	rule->grant = grant;
    161 
    162 	rule->identity = isc_mem_get(mctx, sizeof(*rule->identity));
    163 	dns_name_init(rule->identity, NULL);
    164 	dns_name_dup(identity, mctx, rule->identity);
    165 
    166 	rule->name = isc_mem_get(mctx, sizeof(*rule->name));
    167 	dns_name_init(rule->name, NULL);
    168 	dns_name_dup(name, mctx, rule->name);
    169 
    170 	rule->matchtype = matchtype;
    171 
    172 	rule->ntypes = ntypes;
    173 	if (ntypes > 0) {
    174 		rule->types = isc_mem_cget(mctx, ntypes, sizeof(*rule->types));
    175 		memmove(rule->types, types, ntypes * sizeof(*rule->types));
    176 	} else {
    177 		rule->types = NULL;
    178 	}
    179 
    180 	rule->magic = SSURULEMAGIC;
    181 	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
    182 }
    183 
    184 static bool
    185 isusertype(dns_rdatatype_t type) {
    186 	return type != dns_rdatatype_ns && type != dns_rdatatype_soa &&
    187 	       type != dns_rdatatype_rrsig;
    188 }
    189 
    190 static void
    191 reverse_from_address(dns_name_t *tcpself, const isc_netaddr_t *tcpaddr) {
    192 	char buf[16 * 4 + sizeof("IP6.ARPA.")];
    193 	isc_result_t result;
    194 	const unsigned char *ap;
    195 	isc_buffer_t b;
    196 	unsigned long l;
    197 
    198 	switch (tcpaddr->family) {
    199 	case AF_INET:
    200 		l = ntohl(tcpaddr->type.in.s_addr);
    201 		result = snprintf(buf, sizeof(buf),
    202 				  "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
    203 				  (l >> 0) & 0xff, (l >> 8) & 0xff,
    204 				  (l >> 16) & 0xff, (l >> 24) & 0xff);
    205 		RUNTIME_CHECK(result < sizeof(buf));
    206 		break;
    207 	case AF_INET6:
    208 		ap = tcpaddr->type.in6.s6_addr;
    209 		result = snprintf(
    210 			buf, sizeof(buf),
    211 			"%x.%x.%x.%x.%x.%x.%x.%x."
    212 			"%x.%x.%x.%x.%x.%x.%x.%x."
    213 			"%x.%x.%x.%x.%x.%x.%x.%x."
    214 			"%x.%x.%x.%x.%x.%x.%x.%x."
    215 			"IP6.ARPA.",
    216 			ap[15] & 0x0f, (ap[15] >> 4) & 0x0f, ap[14] & 0x0f,
    217 			(ap[14] >> 4) & 0x0f, ap[13] & 0x0f,
    218 			(ap[13] >> 4) & 0x0f, ap[12] & 0x0f,
    219 			(ap[12] >> 4) & 0x0f, ap[11] & 0x0f,
    220 			(ap[11] >> 4) & 0x0f, ap[10] & 0x0f,
    221 			(ap[10] >> 4) & 0x0f, ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
    222 			ap[8] & 0x0f, (ap[8] >> 4) & 0x0f, ap[7] & 0x0f,
    223 			(ap[7] >> 4) & 0x0f, ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
    224 			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
    225 			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
    226 			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
    227 			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
    228 		RUNTIME_CHECK(result < sizeof(buf));
    229 		break;
    230 	default:
    231 		UNREACHABLE();
    232 	}
    233 	isc_buffer_init(&b, buf, strlen(buf));
    234 	isc_buffer_add(&b, strlen(buf));
    235 	result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
    236 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    237 }
    238 
    239 static void
    240 stf_from_address(dns_name_t *stfself, const isc_netaddr_t *tcpaddr) {
    241 	char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
    242 	isc_result_t result;
    243 	const unsigned char *ap;
    244 	isc_buffer_t b;
    245 	unsigned long l;
    246 
    247 	switch (tcpaddr->family) {
    248 	case AF_INET:
    249 		l = ntohl(tcpaddr->type.in.s_addr);
    250 		result = snprintf(
    251 			buf, sizeof(buf),
    252 			"%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx.2.0.0.2.IP6.ARPA.",
    253 			l & 0xf, (l >> 4) & 0xf, (l >> 8) & 0xf,
    254 			(l >> 12) & 0xf, (l >> 16) & 0xf, (l >> 20) & 0xf,
    255 			(l >> 24) & 0xf, (l >> 28) & 0xf);
    256 		RUNTIME_CHECK(result < sizeof(buf));
    257 		break;
    258 	case AF_INET6:
    259 		ap = tcpaddr->type.in6.s6_addr;
    260 		result = snprintf(
    261 			buf, sizeof(buf),
    262 			"%x.%x.%x.%x.%x.%x.%x.%x."
    263 			"%x.%x.%x.%x.IP6.ARPA.",
    264 			ap[5] & 0x0f, (ap[5] >> 4) & 0x0f, ap[4] & 0x0f,
    265 			(ap[4] >> 4) & 0x0f, ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
    266 			ap[2] & 0x0f, (ap[2] >> 4) & 0x0f, ap[1] & 0x0f,
    267 			(ap[1] >> 4) & 0x0f, ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
    268 		RUNTIME_CHECK(result < sizeof(buf));
    269 		break;
    270 	default:
    271 		UNREACHABLE();
    272 	}
    273 	isc_buffer_init(&b, buf, strlen(buf));
    274 	isc_buffer_add(&b, strlen(buf));
    275 	result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
    276 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    277 }
    278 
    279 bool
    280 dns_ssutable_checkrules(dns_ssutable_t *table, const dns_name_t *signer,
    281 			const dns_name_t *name, const isc_netaddr_t *addr,
    282 			bool tcp, dns_aclenv_t *env, dns_rdatatype_t type,
    283 			const dns_name_t *target, const dst_key_t *key,
    284 			const dns_ssurule_t **rulep) {
    285 	dns_fixedname_t fixed;
    286 	dns_name_t *stfself;
    287 	dns_name_t *tcpself;
    288 	dns_name_t *wildcard;
    289 	dns_ssurule_t *rule;
    290 	const dns_name_t *tname;
    291 	int match;
    292 	isc_result_t result;
    293 	unsigned int i;
    294 
    295 	REQUIRE(VALID_SSUTABLE(table));
    296 	REQUIRE(signer == NULL || dns_name_isabsolute(signer));
    297 	REQUIRE(dns_name_isabsolute(name));
    298 	REQUIRE(addr == NULL || env != NULL);
    299 
    300 	if (signer == NULL && addr == NULL) {
    301 		return false;
    302 	}
    303 
    304 	for (rule = ISC_LIST_HEAD(table->rules); rule != NULL;
    305 	     rule = ISC_LIST_NEXT(rule, link))
    306 	{
    307 		switch (rule->matchtype) {
    308 		case dns_ssumatchtype_local:
    309 		case dns_ssumatchtype_name:
    310 		case dns_ssumatchtype_self:
    311 		case dns_ssumatchtype_selfsub:
    312 		case dns_ssumatchtype_selfwild:
    313 		case dns_ssumatchtype_subdomain:
    314 		case dns_ssumatchtype_wildcard:
    315 			if (signer == NULL) {
    316 				continue;
    317 			}
    318 			if (dns_name_iswildcard(rule->identity)) {
    319 				if (!dns_name_matcheswildcard(signer,
    320 							      rule->identity))
    321 				{
    322 					continue;
    323 				}
    324 			} else {
    325 				if (!dns_name_equal(signer, rule->identity)) {
    326 					continue;
    327 				}
    328 			}
    329 			break;
    330 		case dns_ssumatchtype_selfkrb5:
    331 		case dns_ssumatchtype_selfms:
    332 		case dns_ssumatchtype_selfsubkrb5:
    333 		case dns_ssumatchtype_selfsubms:
    334 		case dns_ssumatchtype_subdomainkrb5:
    335 		case dns_ssumatchtype_subdomainms:
    336 		case dns_ssumatchtype_subdomainselfkrb5rhs:
    337 		case dns_ssumatchtype_subdomainselfmsrhs:
    338 			if (signer == NULL) {
    339 				continue;
    340 			}
    341 			break;
    342 		case dns_ssumatchtype_tcpself:
    343 		case dns_ssumatchtype_6to4self:
    344 			if (!tcp || addr == NULL) {
    345 				continue;
    346 			}
    347 			break;
    348 		case dns_ssumatchtype_external:
    349 		case dns_ssumatchtype_dlz:
    350 			break;
    351 		}
    352 
    353 		switch (rule->matchtype) {
    354 		case dns_ssumatchtype_name:
    355 			if (!dns_name_equal(name, rule->name)) {
    356 				continue;
    357 			}
    358 			break;
    359 		case dns_ssumatchtype_subdomain:
    360 			if (!dns_name_issubdomain(name, rule->name)) {
    361 				continue;
    362 			}
    363 			break;
    364 		case dns_ssumatchtype_local:
    365 			if (addr == NULL) {
    366 				continue;
    367 			}
    368 			if (!dns_name_issubdomain(name, rule->name)) {
    369 				continue;
    370 			}
    371 			rcu_read_lock();
    372 			dns_acl_t *localhost = rcu_dereference(env->localhost);
    373 			dns_acl_match(addr, NULL, localhost, NULL, &match,
    374 				      NULL);
    375 			rcu_read_unlock();
    376 			if (match == 0) {
    377 				if (signer != NULL) {
    378 					isc_log_write(dns_lctx,
    379 						      DNS_LOGCATEGORY_GENERAL,
    380 						      DNS_LOGMODULE_SSU,
    381 						      ISC_LOG_WARNING,
    382 						      "update-policy local: "
    383 						      "match on session "
    384 						      "key not from "
    385 						      "localhost");
    386 				}
    387 				continue;
    388 			}
    389 			break;
    390 		case dns_ssumatchtype_wildcard:
    391 			if (!dns_name_matcheswildcard(name, rule->name)) {
    392 				continue;
    393 			}
    394 			break;
    395 		case dns_ssumatchtype_self:
    396 			if (!dns_name_equal(signer, name)) {
    397 				continue;
    398 			}
    399 			break;
    400 		case dns_ssumatchtype_selfsub:
    401 			if (!dns_name_issubdomain(name, signer)) {
    402 				continue;
    403 			}
    404 			break;
    405 		case dns_ssumatchtype_selfwild:
    406 			wildcard = dns_fixedname_initname(&fixed);
    407 			result = dns_name_concatenate(dns_wildcardname, signer,
    408 						      wildcard, NULL);
    409 			if (result != ISC_R_SUCCESS) {
    410 				continue;
    411 			}
    412 			if (!dns_name_matcheswildcard(name, wildcard)) {
    413 				continue;
    414 			}
    415 			break;
    416 		case dns_ssumatchtype_selfkrb5:
    417 			if (dst_gssapi_identitymatchesrealmkrb5(
    418 				    signer, name, rule->identity, false))
    419 			{
    420 				break;
    421 			}
    422 			continue;
    423 		case dns_ssumatchtype_selfms:
    424 			if (dst_gssapi_identitymatchesrealmms(
    425 				    signer, name, rule->identity, false))
    426 			{
    427 				break;
    428 			}
    429 			continue;
    430 		case dns_ssumatchtype_selfsubkrb5:
    431 			if (dst_gssapi_identitymatchesrealmkrb5(
    432 				    signer, name, rule->identity, true))
    433 			{
    434 				break;
    435 			}
    436 			continue;
    437 		case dns_ssumatchtype_selfsubms:
    438 			if (dst_gssapi_identitymatchesrealmms(
    439 				    signer, name, rule->identity, true))
    440 			{
    441 				break;
    442 			}
    443 			continue;
    444 		case dns_ssumatchtype_subdomainkrb5:
    445 		case dns_ssumatchtype_subdomainselfkrb5rhs:
    446 			if (!dns_name_issubdomain(name, rule->name)) {
    447 				continue;
    448 			}
    449 			tname = NULL;
    450 			switch (rule->matchtype) {
    451 			case dns_ssumatchtype_subdomainselfkrb5rhs:
    452 				if (type == dns_rdatatype_ptr) {
    453 					tname = target;
    454 				}
    455 				if (type == dns_rdatatype_srv) {
    456 					tname = target;
    457 				}
    458 				break;
    459 			default:
    460 				break;
    461 			}
    462 			if (dst_gssapi_identitymatchesrealmkrb5(
    463 				    signer, tname, rule->identity, false))
    464 			{
    465 				break;
    466 			}
    467 			continue;
    468 		case dns_ssumatchtype_subdomainms:
    469 		case dns_ssumatchtype_subdomainselfmsrhs:
    470 			if (!dns_name_issubdomain(name, rule->name)) {
    471 				continue;
    472 			}
    473 			tname = NULL;
    474 			switch (rule->matchtype) {
    475 			case dns_ssumatchtype_subdomainselfmsrhs:
    476 				if (type == dns_rdatatype_ptr) {
    477 					tname = target;
    478 				}
    479 				if (type == dns_rdatatype_srv) {
    480 					tname = target;
    481 				}
    482 				break;
    483 			default:
    484 				break;
    485 			}
    486 			if (dst_gssapi_identitymatchesrealmms(
    487 				    signer, tname, rule->identity, false))
    488 			{
    489 				break;
    490 			}
    491 			continue;
    492 		case dns_ssumatchtype_tcpself:
    493 			tcpself = dns_fixedname_initname(&fixed);
    494 			reverse_from_address(tcpself, addr);
    495 			if (dns_name_iswildcard(rule->identity)) {
    496 				if (!dns_name_matcheswildcard(tcpself,
    497 							      rule->identity))
    498 				{
    499 					continue;
    500 				}
    501 			} else {
    502 				if (!dns_name_equal(tcpself, rule->identity)) {
    503 					continue;
    504 				}
    505 			}
    506 			if (!dns_name_equal(tcpself, name)) {
    507 				continue;
    508 			}
    509 			break;
    510 		case dns_ssumatchtype_6to4self:
    511 			stfself = dns_fixedname_initname(&fixed);
    512 			stf_from_address(stfself, addr);
    513 			if (dns_name_iswildcard(rule->identity)) {
    514 				if (!dns_name_matcheswildcard(stfself,
    515 							      rule->identity))
    516 				{
    517 					continue;
    518 				}
    519 			} else {
    520 				if (!dns_name_equal(stfself, rule->identity)) {
    521 					continue;
    522 				}
    523 			}
    524 			if (!dns_name_equal(stfself, name)) {
    525 				continue;
    526 			}
    527 			break;
    528 		case dns_ssumatchtype_external:
    529 			if (!dns_ssu_external_match(rule->identity, signer,
    530 						    name, addr, type, key,
    531 						    table->mctx))
    532 			{
    533 				continue;
    534 			}
    535 			break;
    536 		case dns_ssumatchtype_dlz:
    537 			if (!dns_dlz_ssumatch(table->dlzdatabase, signer, name,
    538 					      addr, type, key))
    539 			{
    540 				continue;
    541 			}
    542 			break;
    543 		}
    544 
    545 		if (rule->ntypes == 0) {
    546 			/*
    547 			 * If this is a DLZ rule, then the DLZ ssu
    548 			 * checks will have already checked the type.
    549 			 */
    550 			if (rule->matchtype != dns_ssumatchtype_dlz &&
    551 			    !isusertype(type))
    552 			{
    553 				continue;
    554 			}
    555 		} else {
    556 			for (i = 0; i < rule->ntypes; i++) {
    557 				if (rule->types[i].type == dns_rdatatype_any ||
    558 				    rule->types[i].type == type)
    559 				{
    560 					break;
    561 				}
    562 			}
    563 			if (i == rule->ntypes) {
    564 				continue;
    565 			}
    566 		}
    567 		if (rule->grant && rulep != NULL) {
    568 			*rulep = rule;
    569 		}
    570 		return rule->grant;
    571 	}
    572 
    573 	return false;
    574 }
    575 
    576 bool
    577 dns_ssurule_isgrant(const dns_ssurule_t *rule) {
    578 	REQUIRE(VALID_SSURULE(rule));
    579 	return rule->grant;
    580 }
    581 
    582 dns_name_t *
    583 dns_ssurule_identity(const dns_ssurule_t *rule) {
    584 	REQUIRE(VALID_SSURULE(rule));
    585 	return rule->identity;
    586 }
    587 
    588 unsigned int
    589 dns_ssurule_matchtype(const dns_ssurule_t *rule) {
    590 	REQUIRE(VALID_SSURULE(rule));
    591 	return rule->matchtype;
    592 }
    593 
    594 dns_name_t *
    595 dns_ssurule_name(const dns_ssurule_t *rule) {
    596 	REQUIRE(VALID_SSURULE(rule));
    597 	return rule->name;
    598 }
    599 
    600 unsigned int
    601 dns_ssurule_types(const dns_ssurule_t *rule, dns_ssuruletype_t **types) {
    602 	REQUIRE(VALID_SSURULE(rule));
    603 	REQUIRE(types != NULL && *types != NULL);
    604 	*types = rule->types;
    605 	return rule->ntypes;
    606 }
    607 
    608 unsigned int
    609 dns_ssurule_max(const dns_ssurule_t *rule, dns_rdatatype_t type) {
    610 	unsigned int i;
    611 	unsigned int max = 0;
    612 
    613 	REQUIRE(VALID_SSURULE(rule));
    614 
    615 	for (i = 0; i < rule->ntypes; i++) {
    616 		if (rule->types[i].type == dns_rdatatype_any) {
    617 			max = rule->types[i].max;
    618 		}
    619 		if (rule->types[i].type == type) {
    620 			return rule->types[i].max;
    621 		}
    622 	}
    623 	return max;
    624 }
    625 
    626 isc_result_t
    627 dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
    628 	REQUIRE(VALID_SSUTABLE(table));
    629 	REQUIRE(rule != NULL && *rule == NULL);
    630 	*rule = ISC_LIST_HEAD(table->rules);
    631 	return *rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE;
    632 }
    633 
    634 isc_result_t
    635 dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
    636 	REQUIRE(VALID_SSURULE(rule));
    637 	REQUIRE(nextrule != NULL && *nextrule == NULL);
    638 	*nextrule = ISC_LIST_NEXT(rule, link);
    639 	return *nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE;
    640 }
    641 
    642 /*
    643  * Create a specialised SSU table that points at an external DLZ database
    644  */
    645 void
    646 dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
    647 		       dns_dlzdb_t *dlzdatabase) {
    648 	dns_ssurule_t *rule;
    649 	dns_ssutable_t *table = NULL;
    650 
    651 	REQUIRE(tablep != NULL && *tablep == NULL);
    652 
    653 	dns_ssutable_create(mctx, &table);
    654 
    655 	table->dlzdatabase = dlzdatabase;
    656 
    657 	rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
    658 
    659 	rule->identity = NULL;
    660 	rule->name = NULL;
    661 	rule->grant = true;
    662 	rule->matchtype = dns_ssumatchtype_dlz;
    663 	rule->ntypes = 0;
    664 	rule->types = NULL;
    665 	rule->magic = SSURULEMAGIC;
    666 
    667 	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
    668 	*tablep = table;
    669 }
    670 
    671 isc_result_t
    672 dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) {
    673 	REQUIRE(str != NULL);
    674 	REQUIRE(mtype != NULL);
    675 
    676 	if (strcasecmp(str, "name") == 0) {
    677 		*mtype = dns_ssumatchtype_name;
    678 	} else if (strcasecmp(str, "subdomain") == 0) {
    679 		*mtype = dns_ssumatchtype_subdomain;
    680 	} else if (strcasecmp(str, "wildcard") == 0) {
    681 		*mtype = dns_ssumatchtype_wildcard;
    682 	} else if (strcasecmp(str, "self") == 0) {
    683 		*mtype = dns_ssumatchtype_self;
    684 	} else if (strcasecmp(str, "selfsub") == 0) {
    685 		*mtype = dns_ssumatchtype_selfsub;
    686 	} else if (strcasecmp(str, "selfwild") == 0) {
    687 		*mtype = dns_ssumatchtype_selfwild;
    688 	} else if (strcasecmp(str, "ms-self") == 0) {
    689 		*mtype = dns_ssumatchtype_selfms;
    690 	} else if (strcasecmp(str, "ms-selfsub") == 0) {
    691 		*mtype = dns_ssumatchtype_selfsubms;
    692 	} else if (strcasecmp(str, "krb5-self") == 0) {
    693 		*mtype = dns_ssumatchtype_selfkrb5;
    694 	} else if (strcasecmp(str, "krb5-selfsub") == 0) {
    695 		*mtype = dns_ssumatchtype_selfsubkrb5;
    696 	} else if (strcasecmp(str, "ms-subdomain") == 0) {
    697 		*mtype = dns_ssumatchtype_subdomainms;
    698 	} else if (strcasecmp(str, "ms-subdomain-self-rhs") == 0) {
    699 		*mtype = dns_ssumatchtype_subdomainselfmsrhs;
    700 	} else if (strcasecmp(str, "krb5-subdomain") == 0) {
    701 		*mtype = dns_ssumatchtype_subdomainkrb5;
    702 	} else if (strcasecmp(str, "krb5-subdomain-self-rhs") == 0) {
    703 		*mtype = dns_ssumatchtype_subdomainselfkrb5rhs;
    704 	} else if (strcasecmp(str, "tcp-self") == 0) {
    705 		*mtype = dns_ssumatchtype_tcpself;
    706 	} else if (strcasecmp(str, "6to4-self") == 0) {
    707 		*mtype = dns_ssumatchtype_6to4self;
    708 	} else if (strcasecmp(str, "zonesub") == 0) {
    709 		*mtype = dns_ssumatchtype_subdomain;
    710 	} else if (strcasecmp(str, "external") == 0) {
    711 		*mtype = dns_ssumatchtype_external;
    712 	} else {
    713 		return ISC_R_NOTFOUND;
    714 	}
    715 	return ISC_R_SUCCESS;
    716 }
    717