<commit_before>/*
 * Copyright (C) 1999, 2000  Internet Software Consortium.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/* $Id: confip.c,v 1.26 2000/06/06 14:20:00 brister Exp $ */

#include <config.h>


#include <isc/mem.h>
#include <isc/string.h>
#include <isc/util.h>

#include <dns/confip.h>
#include <dns/log.h>

/*
 * Flag for dns_c_ipmatch_element.
 */
#define DNS_C_IPMATCH_NEGATE	0x01	/* match means deny access */


static isc_result_t checkmask(isc_sockaddr_t *address, isc_uint32_t bits);
static isc_result_t bits2v6mask(struct in6_addr *addr, isc_uint32_t bits);

isc_result_t
dns_c_ipmatchelement_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime ;

	REQUIRE(result != NULL);

	*result = NULL;

	ime = isc_mem_get(mem, sizeof *ime);
	if (ime == NULL) {
		return (ISC_R_NOMEMORY);
	}

	ime->magic = DNS_C_IPMELEM_MAGIC;
	ime->type = dns_c_ipmatch_none;
	ime->flags = 0;
	memset(&ime->u, 0x0, sizeof ime->u);

	ISC_LINK_INIT(ime, next);

	*result = ime;

	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchelement_isneg(dns_c_ipmatchelement_t *elem) {

	REQUIRE(DNS_C_IPMELEM_VALID(elem));
	
	return (ISC_TF((elem->flags & DNS_C_IPMATCH_NEGATE) ==
			DNS_C_IPMATCH_NEGATE));
}

isc_result_t
dns_c_ipmatchelement_delete(isc_mem_t *mem, dns_c_ipmatchelement_t **ipme) {
	dns_c_ipmatchelement_t *elem;
	
	REQUIRE(mem != NULL);
	REQUIRE(ipme != NULL);
	REQUIRE(*ipme != NULL);
	
	elem = *ipme;

	REQUIRE(DNS_C_IPMELEM_VALID(elem));
	
	switch (elem->type) {
	case dns_c_ipmatch_localhost:
	case dns_c_ipmatch_localnets:
	case dns_c_ipmatch_pattern:
		/* nothing */
		break;

	case dns_c_ipmatch_indirect:
		INSIST(elem->u.indirect.list != NULL);

		if (elem->u.indirect.list != NULL)
			dns_c_ipmatchlist_detach(&elem->u.indirect.list);

		if (elem->u.indirect.refname.base != NULL) {
			isc_mem_put(mem, elem->u.indirect.refname.base,
				    elem->u.indirect.refname.length);
		}
		break;

	case dns_c_ipmatch_key:
		isc_mem_free(mem, elem->u.key);
		break;

	case dns_c_ipmatch_acl:
		isc_mem_free(mem, elem->u.aclname);
		break;

	case dns_c_ipmatch_any:
		/* nothing */
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "dns_ipmath_none element type");
		return (ISC_R_FAILURE);
	}

	elem->magic = 0;
	isc_mem_put(mem, elem, sizeof *elem);

	*ipme = NULL;
	
	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchelement_copy(isc_mem_t *mem,
			  dns_c_ipmatchelement_t **dest,
			  dns_c_ipmatchelement_t *src)
{
	isc_result_t result;
	dns_c_ipmatchelement_t *newel;

	REQUIRE(mem != NULL);
	REQUIRE(dest != NULL);
	REQUIRE(DNS_C_IPMELEM_VALID(src));
		
	result = dns_c_ipmatchelement_new(mem, &newel);
	if (result != ISC_R_SUCCESS) {
		return (result);
	}

	newel->type = src->type;
	newel->flags = src->flags;
	
	switch(src->type) {
	case dns_c_ipmatch_pattern:
		newel->u.direct.address = src->u.direct.address;
		newel->u.direct.mask = src->u.direct.mask;
		break;

	case dns_c_ipmatch_indirect:
		result = dns_c_ipmatchlist_copy(mem,
						&newel->u.indirect.list,
						src->u.indirect.list);
		break;	

	case dns_c_ipmatch_localhost:
		break;

	case dns_c_ipmatch_localnets:
		break;

	case dns_c_ipmatch_key:
		newel->u.key = isc_mem_strdup(mem, src->u.key);
		break;

	case dns_c_ipmatch_acl:
		newel->u.aclname = isc_mem_strdup(mem, src->u.aclname);
		break;
		
	case dns_c_ipmatch_any:
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "ipmatch 'none' element type");
		return (ISC_R_FAILURE);
	}

	*dest = newel;
	
	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchelement_equal(dns_c_ipmatchelement_t *e1,
			   dns_c_ipmatchelement_t *e2)
{
	REQUIRE(DNS_C_IPMELEM_VALID(e1));
	REQUIRE(DNS_C_IPMELEM_VALID(e2));

	if ((e1->type != e2->type) || (e1->flags != e2->flags))
		return (ISC_FALSE);

	switch (e1->type) {
	case dns_c_ipmatch_pattern:
		if (e1->u.direct.mask != e2->u.direct.mask)
			return (ISC_FALSE);
		return (isc_sockaddr_equal(&e1->u.direct.address,
					   &e2->u.direct.address));

	case dns_c_ipmatch_indirect:
		return (dns_c_ipmatchlist_equal(e1->u.indirect.list,
						e2->u.indirect.list));

	case dns_c_ipmatch_localhost:
		break;

	case dns_c_ipmatch_localnets:
		break;

	case dns_c_ipmatch_key:
		return (ISC_TF(strcmp(e1->u.key, e2->u.key) == 0));

	case dns_c_ipmatch_acl:
		return (ISC_TF(strcmp(e1->u.aclname, e2->u.aclname) == 0));

	case dns_c_ipmatch_any:
		break;

	case dns_c_ipmatch_none:
		break;
	}
	return (ISC_TRUE);
}

isc_result_t
dns_c_ipmatchlocalhost_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_localhost;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchlocalnets_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_localnets;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchany_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_any;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchindirect_new(isc_mem_t *mem,
			  dns_c_ipmatchelement_t **result,
			  dns_c_ipmatchlist_t *iml,
			  const char *name)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchlist_t *iml_copy;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);
	REQUIRE(DNS_C_IPMLIST_VALID(iml));

	*result = NULL;

	res = dns_c_ipmatchlist_copy(mem, &iml_copy, iml);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_indirect;
		ime->u.indirect.list = iml_copy;
		if (name != NULL) {
			ime->u.indirect.refname.length = strlen(name) + 1;
			ime->u.indirect.refname.base =
				isc_mem_get(mem,
					    ime->u.indirect.refname.length);
			RUNTIME_CHECK(ime->u.indirect.refname.base != NULL);
			strcpy(ime->u.indirect.refname.base, name);
		}
	} else {
		dns_c_ipmatchlist_detach(&iml_copy);
	}
	
	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchpattern_new(isc_mem_t *mem,
			 dns_c_ipmatchelement_t **result,
			 isc_sockaddr_t address,
			 isc_uint32_t maskbits)
{
	dns_c_ipmatchelement_t *ime ;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);

	*result = NULL;

	res = checkmask(&address, maskbits);

	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ime->type = dns_c_ipmatch_pattern;
	ime->u.direct.address = address;
	ime->u.direct.mask = maskbits;

	*result = ime;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchkey_new(isc_mem_t *mem,
		     dns_c_ipmatchelement_t **result,
		     const char *key)
{
	dns_c_ipmatchelement_t *ipme;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);
	REQUIRE(key != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ipme);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ipme->type = dns_c_ipmatch_key;
	ipme->u.key = isc_mem_strdup(mem, key);

	*result = ipme;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatch_aclnew(isc_mem_t *mem,
		     dns_c_ipmatchelement_t **result,
		     const char *aclname)
{
	dns_c_ipmatchelement_t *ipme;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);
	REQUIRE(aclname != NULL);
	REQUIRE(*aclname != '\0');

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ipme);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ipme->type = dns_c_ipmatch_acl;
	ipme->u.aclname = isc_mem_strdup(mem, aclname);

	*result = ipme;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatch_negate(dns_c_ipmatchelement_t *ipe) {
	REQUIRE(DNS_C_IPMELEM_VALID(ipe));

	if ((ipe->flags & DNS_C_IPMATCH_NEGATE) == DNS_C_IPMATCH_NEGATE) {
		ipe->flags &= ~DNS_C_IPMATCH_NEGATE;
	} else {
		ipe->flags |= DNS_C_IPMATCH_NEGATE;
	}

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_new(isc_mem_t *mem, dns_c_ipmatchlist_t **ptr) {
	dns_c_ipmatchlist_t *newlist;

	REQUIRE(ptr != NULL);
	REQUIRE(mem != NULL);

	newlist = isc_mem_get(mem, sizeof *newlist);
	if (newlist == NULL) {
		return (ISC_R_NOMEMORY);
	}

	newlist->magic = DNS_C_IPMLIST_MAGIC;
	newlist->mem = mem;
	newlist->refcount = 1;
	
	ISC_LIST_INIT(newlist->elements);

	*ptr = newlist;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_detach(dns_c_ipmatchlist_t **ml) {
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *iptr;
	dns_c_ipmatchlist_t *iml;
	isc_mem_t *mem;

	REQUIRE(ml != NULL);
	REQUIRE(*ml != NULL);
	
	iml = *ml;
	*ml = NULL;

	REQUIRE(DNS_C_IPMLIST_VALID(iml));
	INSIST(iml->refcount > 0);

	iml->refcount--;
	if (iml->refcount > 0) {
		return (ISC_R_SUCCESS);
	}
	
	mem = iml->mem;
	INSIST(mem != NULL);

	ime = ISC_LIST_HEAD(iml->elements);
	while (ime != NULL) {
		iptr = ISC_LIST_NEXT(ime, next);
		dns_c_ipmatchelement_delete(mem, &ime);
		
		ime = iptr;
	}

	isc_mem_put(mem, iml, sizeof *iml);

	return (ISC_R_SUCCESS);
}

void
dns_c_ipmatchlist_attach(dns_c_ipmatchlist_t *source,
			 dns_c_ipmatchlist_t **target)
{

	REQUIRE(DNS_C_IPMLIST_VALID(source));
	
	INSIST(source->refcount > 0);

	source->refcount++;
	*target = source;
}

isc_result_t
dns_c_ipmatchlist_empty(dns_c_ipmatchlist_t *ipml) {
	dns_c_ipmatchelement_t *ime ;
	dns_c_ipmatchelement_t *imptmp;
	isc_result_t res = ISC_R_SUCCESS;

	REQUIRE(DNS_C_IPMLIST_VALID(ipml));
	
	ime = ISC_LIST_HEAD(ipml->elements);
	while (ime != NULL) {
		imptmp = ISC_LIST_NEXT(ime, next);
		res = dns_c_ipmatchelement_delete(ipml->mem, &ime);
		if (res != ISC_R_SUCCESS) {
			break;
		}
		ime = imptmp;
	}
	
	return (res);
}

isc_result_t
dns_c_ipmatchlist_copy(isc_mem_t *mem,
		       dns_c_ipmatchlist_t **dest, dns_c_ipmatchlist_t *src)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *ptr;
	dns_c_ipmatchlist_t *newlist;
	isc_result_t result;

	REQUIRE(mem != NULL);
	REQUIRE(dest != NULL);
	REQUIRE(DNS_C_IPMLIST_VALID(src));

	*dest = NULL;

	result = dns_c_ipmatchlist_new(mem, &newlist);
	if (result != ISC_R_SUCCESS) {
		return (result);
	}

	ime = ISC_LIST_HEAD(src->elements);
	while (ime != NULL) {
		result = dns_c_ipmatchelement_copy(mem, &ptr, ime);
		if (result != ISC_R_SUCCESS) {
			dns_c_ipmatchlist_detach(&newlist);
			return (result);
		}
		
		ISC_LIST_APPEND(newlist->elements, ptr, next);

		ime = ISC_LIST_NEXT(ime, next);
	}

	*dest = newlist;

	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchlist_equal(dns_c_ipmatchlist_t *l1, dns_c_ipmatchlist_t *l2) {
	dns_c_ipmatchelement_t *e1, *e2;

	REQUIRE(l1 == NULL || DNS_C_IPMLIST_VALID(l1));
	REQUIRE(l2 == NULL || DNS_C_IPMLIST_VALID(l2));
	
	if (l1 == NULL && l2 == NULL)
		return (ISC_TRUE);
	if (l1 != NULL || l2 != NULL)
		return (ISC_FALSE);

	e1 = ISC_LIST_HEAD(l1->elements);
	e2 = ISC_LIST_HEAD(l2->elements);
	while (e1 != NULL && e2 != NULL) {
		if (!dns_c_ipmatchelement_equal(e1, e2))
			return (ISC_FALSE);
		e1 = ISC_LIST_NEXT(e1, next);
		e2 = ISC_LIST_NEXT(e2, next);
	}

	if (l1 != NULL || l2 != NULL)
		return (ISC_FALSE);
	return (ISC_TRUE);
}

isc_result_t
dns_c_ipmatchlist_append(dns_c_ipmatchlist_t *dest,
			 dns_c_ipmatchlist_t *src,
			 isc_boolean_t negate)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *ime_copy;
	isc_result_t result = ISC_R_SUCCESS;

	REQUIRE(DNS_C_IPMLIST_VALID(dest));
	REQUIRE(DNS_C_IPMLIST_VALID(src));

	ime = ISC_LIST_HEAD(src->elements);
	while (ime != NULL) {
		result = dns_c_ipmatchelement_copy(dest->mem,
						   &ime_copy,
						   ime);
		if (result != ISC_R_SUCCESS) {
			break;
		}

		if (negate) {
			dns_c_ipmatch_negate(ime_copy);
		}
		
		ISC_LIST_APPEND(dest->elements, ime_copy, next);

		ime = ISC_LIST_NEXT(ime, next);
	}

	return (result);
}

isc_result_t
dns_c_ipmatchelement_print(FILE *fp, int indent, dns_c_ipmatchelement_t *ipme)
{
	int bits;

	REQUIRE(fp != NULL);
	REQUIRE(DNS_C_IPMELEM_VALID(ipme));

	if (dns_c_ipmatchelement_isneg(ipme) &&
	    ipme->type != dns_c_ipmatch_any) {
		/* a '!any' element gets printed as 'none' */
		fputc('!', fp);
	} else {
		fputc(' ', fp);
	}

	switch (ipme->type) {
	case dns_c_ipmatch_pattern:
		dns_c_print_ipaddr(fp, &ipme->u.direct.address);
		
		bits = ipme->u.direct.mask;
		if (bits > 0) {
			isc_uint32_t fam =
				ipme->u.direct.address.type.sa.sa_family;
			if ((fam == AF_INET && bits < 32) ||
			    (fam == AF_INET6 && bits < 128)) {
				fprintf(fp, "/%d", bits);
			}
		}
		break;

	case dns_c_ipmatch_indirect:
		if (ipme->u.indirect.refname.base != NULL) {
			fprintf(fp, "%s", ipme->u.indirect.refname.base);
		} else {
			dns_c_ipmatchlist_print(fp, indent,
						ipme->u.indirect.list);
		}
		break;

	case dns_c_ipmatch_key:
		fprintf(fp, "key %s", ipme->u.key);
		break;

	case dns_c_ipmatch_localhost:
		fprintf(fp, "localhost");
		break;

	case dns_c_ipmatch_localnets:
		fprintf(fp, "localnets");
		break;

	case dns_c_ipmatch_any:
		if (dns_c_ipmatchelement_isneg(ipme)) {
			fprintf(fp, "none");
		} else {
			fprintf(fp, "any");
		}
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "dns_ipmatch_none element type");
		return (ISC_R_FAILURE);

	case dns_c_ipmatch_acl:
		fprintf(fp, "%s", ipme->u.aclname);
		break;
	}

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_print(FILE *fp, int indent, dns_c_ipmatchlist_t *ml) {
	dns_c_ipmatchelement_t *ipme ;

	REQUIRE(DNS_C_IPMLIST_VALID(ml));
	REQUIRE(fp != NULL);

	/* no indent on first line. */
	fprintf(fp, "{\n");
	ipme = ISC_LIST_HEAD(ml->elements);
	if (ipme == NULL) {
		dns_c_printtabs(fp, indent);
		fprintf(fp,
			"/* this list intentionally left blank */\n");
	} else {
		while (ipme != NULL) {
			dns_c_printtabs(fp, indent);
			dns_c_ipmatchelement_print(fp, indent + 1, ipme);
			fprintf(fp, ";\n");
			
			ipme = ISC_LIST_NEXT(ipme, next);
		}
	}
	
	dns_c_printtabs(fp, indent - 1);
	fprintf(fp, "}");

	return (ISC_R_SUCCESS);
}


isc_boolean_t
dns_c_ipmatchlist_walk(dns_c_ipmatchlist_t *list, dns_c_ipmlwalker func)
{
	isc_boolean_t retval = ISC_TRUE;
	dns_c_ipmatchelement_t *ipme ;

	REQUIRE(DNS_C_IPMLIST_VALID(list));

	ipme = ISC_LIST_HEAD(list->elements);
	while (retval == ISC_TRUE && ipme != NULL) {
		switch (ipme->type) {
		case dns_c_ipmatch_pattern:
		case dns_c_ipmatch_key:
		case dns_c_ipmatch_localhost:
		case dns_c_ipmatch_localnets:
		case dns_c_ipmatch_any:
		case dns_c_ipmatch_none:
			retval = retval && (*func)(ipme);
			break;

		case dns_c_ipmatch_indirect:
			retval = retval &&
				dns_c_ipmatchlist_walk(ipme->u.indirect.list,
						       func);
			break;<commit_msg>use ISC_TF to assign an isc_boolean_t<commit_after>/*
 * Copyright (C) 1999, 2000  Internet Software Consortium.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/* $Id: confip.c,v 1.27 2000/06/06 15:21:46 tale Exp $ */

#include <config.h>


#include <isc/mem.h>
#include <isc/string.h>
#include <isc/util.h>

#include <dns/confip.h>
#include <dns/log.h>

/*
 * Flag for dns_c_ipmatch_element.
 */
#define DNS_C_IPMATCH_NEGATE	0x01	/* match means deny access */


static isc_result_t checkmask(isc_sockaddr_t *address, isc_uint32_t bits);
static isc_result_t bits2v6mask(struct in6_addr *addr, isc_uint32_t bits);

isc_result_t
dns_c_ipmatchelement_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime ;

	REQUIRE(result != NULL);

	*result = NULL;

	ime = isc_mem_get(mem, sizeof *ime);
	if (ime == NULL) {
		return (ISC_R_NOMEMORY);
	}

	ime->magic = DNS_C_IPMELEM_MAGIC;
	ime->type = dns_c_ipmatch_none;
	ime->flags = 0;
	memset(&ime->u, 0x0, sizeof ime->u);

	ISC_LINK_INIT(ime, next);

	*result = ime;

	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchelement_isneg(dns_c_ipmatchelement_t *elem) {

	REQUIRE(DNS_C_IPMELEM_VALID(elem));
	
	return (ISC_TF((elem->flags & DNS_C_IPMATCH_NEGATE) ==
			DNS_C_IPMATCH_NEGATE));
}

isc_result_t
dns_c_ipmatchelement_delete(isc_mem_t *mem, dns_c_ipmatchelement_t **ipme) {
	dns_c_ipmatchelement_t *elem;
	
	REQUIRE(mem != NULL);
	REQUIRE(ipme != NULL);
	REQUIRE(*ipme != NULL);
	
	elem = *ipme;

	REQUIRE(DNS_C_IPMELEM_VALID(elem));
	
	switch (elem->type) {
	case dns_c_ipmatch_localhost:
	case dns_c_ipmatch_localnets:
	case dns_c_ipmatch_pattern:
		/* nothing */
		break;

	case dns_c_ipmatch_indirect:
		INSIST(elem->u.indirect.list != NULL);

		if (elem->u.indirect.list != NULL)
			dns_c_ipmatchlist_detach(&elem->u.indirect.list);

		if (elem->u.indirect.refname.base != NULL) {
			isc_mem_put(mem, elem->u.indirect.refname.base,
				    elem->u.indirect.refname.length);
		}
		break;

	case dns_c_ipmatch_key:
		isc_mem_free(mem, elem->u.key);
		break;

	case dns_c_ipmatch_acl:
		isc_mem_free(mem, elem->u.aclname);
		break;

	case dns_c_ipmatch_any:
		/* nothing */
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "dns_ipmath_none element type");
		return (ISC_R_FAILURE);
	}

	elem->magic = 0;
	isc_mem_put(mem, elem, sizeof *elem);

	*ipme = NULL;
	
	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchelement_copy(isc_mem_t *mem,
			  dns_c_ipmatchelement_t **dest,
			  dns_c_ipmatchelement_t *src)
{
	isc_result_t result;
	dns_c_ipmatchelement_t *newel;

	REQUIRE(mem != NULL);
	REQUIRE(dest != NULL);
	REQUIRE(DNS_C_IPMELEM_VALID(src));
		
	result = dns_c_ipmatchelement_new(mem, &newel);
	if (result != ISC_R_SUCCESS) {
		return (result);
	}

	newel->type = src->type;
	newel->flags = src->flags;
	
	switch(src->type) {
	case dns_c_ipmatch_pattern:
		newel->u.direct.address = src->u.direct.address;
		newel->u.direct.mask = src->u.direct.mask;
		break;

	case dns_c_ipmatch_indirect:
		result = dns_c_ipmatchlist_copy(mem,
						&newel->u.indirect.list,
						src->u.indirect.list);
		break;	

	case dns_c_ipmatch_localhost:
		break;

	case dns_c_ipmatch_localnets:
		break;

	case dns_c_ipmatch_key:
		newel->u.key = isc_mem_strdup(mem, src->u.key);
		break;

	case dns_c_ipmatch_acl:
		newel->u.aclname = isc_mem_strdup(mem, src->u.aclname);
		break;
		
	case dns_c_ipmatch_any:
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "ipmatch 'none' element type");
		return (ISC_R_FAILURE);
	}

	*dest = newel;
	
	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchelement_equal(dns_c_ipmatchelement_t *e1,
			   dns_c_ipmatchelement_t *e2)
{
	REQUIRE(DNS_C_IPMELEM_VALID(e1));
	REQUIRE(DNS_C_IPMELEM_VALID(e2));

	if ((e1->type != e2->type) || (e1->flags != e2->flags))
		return (ISC_FALSE);

	switch (e1->type) {
	case dns_c_ipmatch_pattern:
		if (e1->u.direct.mask != e2->u.direct.mask)
			return (ISC_FALSE);
		return (isc_sockaddr_equal(&e1->u.direct.address,
					   &e2->u.direct.address));

	case dns_c_ipmatch_indirect:
		return (dns_c_ipmatchlist_equal(e1->u.indirect.list,
						e2->u.indirect.list));

	case dns_c_ipmatch_localhost:
		break;

	case dns_c_ipmatch_localnets:
		break;

	case dns_c_ipmatch_key:
		return (ISC_TF(strcmp(e1->u.key, e2->u.key) == 0));

	case dns_c_ipmatch_acl:
		return (ISC_TF(strcmp(e1->u.aclname, e2->u.aclname) == 0));

	case dns_c_ipmatch_any:
		break;

	case dns_c_ipmatch_none:
		break;
	}
	return (ISC_TRUE);
}

isc_result_t
dns_c_ipmatchlocalhost_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_localhost;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchlocalnets_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_localnets;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchany_new(isc_mem_t *mem, dns_c_ipmatchelement_t **result) {
	dns_c_ipmatchelement_t *ime = NULL;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_any;
	}

	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchindirect_new(isc_mem_t *mem,
			  dns_c_ipmatchelement_t **result,
			  dns_c_ipmatchlist_t *iml,
			  const char *name)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchlist_t *iml_copy;
	isc_result_t res;

	REQUIRE(mem != NULL);
	REQUIRE(result != NULL);
	REQUIRE(DNS_C_IPMLIST_VALID(iml));

	*result = NULL;

	res = dns_c_ipmatchlist_copy(mem, &iml_copy, iml);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res == ISC_R_SUCCESS) {
		ime->type = dns_c_ipmatch_indirect;
		ime->u.indirect.list = iml_copy;
		if (name != NULL) {
			ime->u.indirect.refname.length = strlen(name) + 1;
			ime->u.indirect.refname.base =
				isc_mem_get(mem,
					    ime->u.indirect.refname.length);
			RUNTIME_CHECK(ime->u.indirect.refname.base != NULL);
			strcpy(ime->u.indirect.refname.base, name);
		}
	} else {
		dns_c_ipmatchlist_detach(&iml_copy);
	}
	
	*result = ime;

	return (res);
}

isc_result_t
dns_c_ipmatchpattern_new(isc_mem_t *mem,
			 dns_c_ipmatchelement_t **result,
			 isc_sockaddr_t address,
			 isc_uint32_t maskbits)
{
	dns_c_ipmatchelement_t *ime ;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);

	*result = NULL;

	res = checkmask(&address, maskbits);

	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	res = dns_c_ipmatchelement_new(mem, &ime);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ime->type = dns_c_ipmatch_pattern;
	ime->u.direct.address = address;
	ime->u.direct.mask = maskbits;

	*result = ime;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchkey_new(isc_mem_t *mem,
		     dns_c_ipmatchelement_t **result,
		     const char *key)
{
	dns_c_ipmatchelement_t *ipme;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);
	REQUIRE(key != NULL);

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ipme);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ipme->type = dns_c_ipmatch_key;
	ipme->u.key = isc_mem_strdup(mem, key);

	*result = ipme;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatch_aclnew(isc_mem_t *mem,
		     dns_c_ipmatchelement_t **result,
		     const char *aclname)
{
	dns_c_ipmatchelement_t *ipme;
	isc_result_t res;

	REQUIRE(result != NULL);
	REQUIRE(mem != NULL);
	REQUIRE(aclname != NULL);
	REQUIRE(*aclname != '\0');

	*result = NULL;

	res = dns_c_ipmatchelement_new(mem, &ipme);
	if (res != ISC_R_SUCCESS) {
		return (res);
	}

	ipme->type = dns_c_ipmatch_acl;
	ipme->u.aclname = isc_mem_strdup(mem, aclname);

	*result = ipme;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatch_negate(dns_c_ipmatchelement_t *ipe) {
	REQUIRE(DNS_C_IPMELEM_VALID(ipe));

	if ((ipe->flags & DNS_C_IPMATCH_NEGATE) == DNS_C_IPMATCH_NEGATE) {
		ipe->flags &= ~DNS_C_IPMATCH_NEGATE;
	} else {
		ipe->flags |= DNS_C_IPMATCH_NEGATE;
	}

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_new(isc_mem_t *mem, dns_c_ipmatchlist_t **ptr) {
	dns_c_ipmatchlist_t *newlist;

	REQUIRE(ptr != NULL);
	REQUIRE(mem != NULL);

	newlist = isc_mem_get(mem, sizeof *newlist);
	if (newlist == NULL) {
		return (ISC_R_NOMEMORY);
	}

	newlist->magic = DNS_C_IPMLIST_MAGIC;
	newlist->mem = mem;
	newlist->refcount = 1;
	
	ISC_LIST_INIT(newlist->elements);

	*ptr = newlist;

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_detach(dns_c_ipmatchlist_t **ml) {
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *iptr;
	dns_c_ipmatchlist_t *iml;
	isc_mem_t *mem;

	REQUIRE(ml != NULL);
	REQUIRE(*ml != NULL);
	
	iml = *ml;
	*ml = NULL;

	REQUIRE(DNS_C_IPMLIST_VALID(iml));
	INSIST(iml->refcount > 0);

	iml->refcount--;
	if (iml->refcount > 0) {
		return (ISC_R_SUCCESS);
	}
	
	mem = iml->mem;
	INSIST(mem != NULL);

	ime = ISC_LIST_HEAD(iml->elements);
	while (ime != NULL) {
		iptr = ISC_LIST_NEXT(ime, next);
		dns_c_ipmatchelement_delete(mem, &ime);
		
		ime = iptr;
	}

	isc_mem_put(mem, iml, sizeof *iml);

	return (ISC_R_SUCCESS);
}

void
dns_c_ipmatchlist_attach(dns_c_ipmatchlist_t *source,
			 dns_c_ipmatchlist_t **target)
{

	REQUIRE(DNS_C_IPMLIST_VALID(source));
	
	INSIST(source->refcount > 0);

	source->refcount++;
	*target = source;
}

isc_result_t
dns_c_ipmatchlist_empty(dns_c_ipmatchlist_t *ipml) {
	dns_c_ipmatchelement_t *ime ;
	dns_c_ipmatchelement_t *imptmp;
	isc_result_t res = ISC_R_SUCCESS;

	REQUIRE(DNS_C_IPMLIST_VALID(ipml));
	
	ime = ISC_LIST_HEAD(ipml->elements);
	while (ime != NULL) {
		imptmp = ISC_LIST_NEXT(ime, next);
		res = dns_c_ipmatchelement_delete(ipml->mem, &ime);
		if (res != ISC_R_SUCCESS) {
			break;
		}
		ime = imptmp;
	}
	
	return (res);
}

isc_result_t
dns_c_ipmatchlist_copy(isc_mem_t *mem,
		       dns_c_ipmatchlist_t **dest, dns_c_ipmatchlist_t *src)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *ptr;
	dns_c_ipmatchlist_t *newlist;
	isc_result_t result;

	REQUIRE(mem != NULL);
	REQUIRE(dest != NULL);
	REQUIRE(DNS_C_IPMLIST_VALID(src));

	*dest = NULL;

	result = dns_c_ipmatchlist_new(mem, &newlist);
	if (result != ISC_R_SUCCESS) {
		return (result);
	}

	ime = ISC_LIST_HEAD(src->elements);
	while (ime != NULL) {
		result = dns_c_ipmatchelement_copy(mem, &ptr, ime);
		if (result != ISC_R_SUCCESS) {
			dns_c_ipmatchlist_detach(&newlist);
			return (result);
		}
		
		ISC_LIST_APPEND(newlist->elements, ptr, next);

		ime = ISC_LIST_NEXT(ime, next);
	}

	*dest = newlist;

	return (ISC_R_SUCCESS);
}

isc_boolean_t
dns_c_ipmatchlist_equal(dns_c_ipmatchlist_t *l1, dns_c_ipmatchlist_t *l2) {
	dns_c_ipmatchelement_t *e1, *e2;

	REQUIRE(l1 == NULL || DNS_C_IPMLIST_VALID(l1));
	REQUIRE(l2 == NULL || DNS_C_IPMLIST_VALID(l2));
	
	if (l1 == NULL && l2 == NULL)
		return (ISC_TRUE);
	if (l1 != NULL || l2 != NULL)
		return (ISC_FALSE);

	e1 = ISC_LIST_HEAD(l1->elements);
	e2 = ISC_LIST_HEAD(l2->elements);
	while (e1 != NULL && e2 != NULL) {
		if (!dns_c_ipmatchelement_equal(e1, e2))
			return (ISC_FALSE);
		e1 = ISC_LIST_NEXT(e1, next);
		e2 = ISC_LIST_NEXT(e2, next);
	}

	if (l1 != NULL || l2 != NULL)
		return (ISC_FALSE);
	return (ISC_TRUE);
}

isc_result_t
dns_c_ipmatchlist_append(dns_c_ipmatchlist_t *dest,
			 dns_c_ipmatchlist_t *src,
			 isc_boolean_t negate)
{
	dns_c_ipmatchelement_t *ime;
	dns_c_ipmatchelement_t *ime_copy;
	isc_result_t result = ISC_R_SUCCESS;

	REQUIRE(DNS_C_IPMLIST_VALID(dest));
	REQUIRE(DNS_C_IPMLIST_VALID(src));

	ime = ISC_LIST_HEAD(src->elements);
	while (ime != NULL) {
		result = dns_c_ipmatchelement_copy(dest->mem,
						   &ime_copy,
						   ime);
		if (result != ISC_R_SUCCESS) {
			break;
		}

		if (negate) {
			dns_c_ipmatch_negate(ime_copy);
		}
		
		ISC_LIST_APPEND(dest->elements, ime_copy, next);

		ime = ISC_LIST_NEXT(ime, next);
	}

	return (result);
}

isc_result_t
dns_c_ipmatchelement_print(FILE *fp, int indent, dns_c_ipmatchelement_t *ipme)
{
	int bits;

	REQUIRE(fp != NULL);
	REQUIRE(DNS_C_IPMELEM_VALID(ipme));

	if (dns_c_ipmatchelement_isneg(ipme) &&
	    ipme->type != dns_c_ipmatch_any) {
		/* a '!any' element gets printed as 'none' */
		fputc('!', fp);
	} else {
		fputc(' ', fp);
	}

	switch (ipme->type) {
	case dns_c_ipmatch_pattern:
		dns_c_print_ipaddr(fp, &ipme->u.direct.address);
		
		bits = ipme->u.direct.mask;
		if (bits > 0) {
			isc_uint32_t fam =
				ipme->u.direct.address.type.sa.sa_family;
			if ((fam == AF_INET && bits < 32) ||
			    (fam == AF_INET6 && bits < 128)) {
				fprintf(fp, "/%d", bits);
			}
		}
		break;

	case dns_c_ipmatch_indirect:
		if (ipme->u.indirect.refname.base != NULL) {
			fprintf(fp, "%s", ipme->u.indirect.refname.base);
		} else {
			dns_c_ipmatchlist_print(fp, indent,
						ipme->u.indirect.list);
		}
		break;

	case dns_c_ipmatch_key:
		fprintf(fp, "key %s", ipme->u.key);
		break;

	case dns_c_ipmatch_localhost:
		fprintf(fp, "localhost");
		break;

	case dns_c_ipmatch_localnets:
		fprintf(fp, "localnets");
		break;

	case dns_c_ipmatch_any:
		if (dns_c_ipmatchelement_isneg(ipme)) {
			fprintf(fp, "none");
		} else {
			fprintf(fp, "any");
		}
		break;

	case dns_c_ipmatch_none:
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "dns_ipmatch_none element type");
		return (ISC_R_FAILURE);

	case dns_c_ipmatch_acl:
		fprintf(fp, "%s", ipme->u.aclname);
		break;
	}

	return (ISC_R_SUCCESS);
}

isc_result_t
dns_c_ipmatchlist_print(FILE *fp, int indent, dns_c_ipmatchlist_t *ml) {
	dns_c_ipmatchelement_t *ipme ;

	REQUIRE(DNS_C_IPMLIST_VALID(ml));
	REQUIRE(fp != NULL);

	/* no indent on first line. */
	fprintf(fp, "{\n");
	ipme = ISC_LIST_HEAD(ml->elements);
	if (ipme == NULL) {
		dns_c_printtabs(fp, indent);
		fprintf(fp,
			"/* this list intentionally left blank */\n");
	} else {
		while (ipme != NULL) {
			dns_c_printtabs(fp, indent);
			dns_c_ipmatchelement_print(fp, indent + 1, ipme);
			fprintf(fp, ";\n");
			
			ipme = ISC_LIST_NEXT(ipme, next);
		}
	}
	
	dns_c_printtabs(fp, indent - 1);
	fprintf(fp, "}");

	return (ISC_R_SUCCESS);
}


isc_boolean_t
dns_c_ipmatchlist_walk(dns_c_ipmatchlist_t *list, dns_c_ipmlwalker func)
{
	isc_boolean_t retval = ISC_TRUE;
	dns_c_ipmatchelement_t *ipme ;

	REQUIRE(DNS_C_IPMLIST_VALID(list));

	ipme = ISC_LIST_HEAD(list->elements);
	while (retval == ISC_TRUE && ipme != NULL) {
		switch (ipme->type) {
		case dns_c_ipmatch_pattern:
		case dns_c_ipmatch_key:
		case dns_c_ipmatch_localhost:
		case dns_c_ipmatch_localnets:
		case dns_c_ipmatch_any:
		case dns_c_ipmatch_none:
			retval = ISC_TF(retval && (*func)(ipme));
			break;

		case dns_c_ipmatch_indirect:
			retval = ISC_TF(retval &&
					dns_c_ipmatchlist_walk(ipme->
							       u.indirect.list,
							       func));
			break;
{'fraction_non_alphanumeric': 0.13063851055854056, 'fraction_numerical': 0.006560039985005623, 'mean_word_length': 4.492022645393721, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 223, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5250898', 'n_tokens_mistral': 15244, 'n_tokens_neox': 14121, 'n_words': 2377}