/*
 * Copyright (C) 1998-2001, 2003-2005, 2007, 2009, 2011, 2013, 2016, 2017  Internet Systems Consortium, Inc. ("ISC")
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/* $Id: t_master.c,v 1.41 2011/03/12 04:59:46 tbox Exp $ */

#include <config.h>

#include <ctype.h>
#include <stdlib.h>

#include <isc/buffer.h>
#include <isc/mem.h>
#include <isc/string.h>		/* Required for HP/UX (and others?) */
#include <isc/util.h>

#include <dns/callbacks.h>
#include <dns/master.h>
#include <dns/name.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/result.h>

#include <tests/t_api.h>

#define	BUFLEN		255
#define	BIGBUFLEN	(64 * 1024)

static isc_result_t
t1_add_callback(void *arg, dns_name_t *owner, dns_rdataset_t *dataset);

isc_mem_t	*T1_mctx;
char		*Tokens[T_MAXTOKS + 1];

static isc_result_t
t1_add_callback(void *arg, dns_name_t *owner, dns_rdataset_t *dataset) {
	char buf[BIGBUFLEN];
	isc_buffer_t target;
	isc_result_t result;

	UNUSED(arg);

	isc_buffer_init(&target, buf, BIGBUFLEN);
	result = dns_rdataset_totext(dataset, owner, ISC_FALSE, ISC_FALSE,
				     &target);
	if (result != ISC_R_SUCCESS)
		t_info("dns_rdataset_totext: %s\n", dns_result_totext(result));

	return(result);
}

static int
test_master(char *testfile, char *origin, char *db_class,
	    isc_result_t exp_result)
{
	int			result;
	int			len;
	isc_result_t		isc_result;
	isc_result_t		dns_result;
	dns_name_t		dns_origin;
	isc_buffer_t		source;
	isc_buffer_t		target;
	unsigned char		name_buf[BUFLEN];
	dns_rdatacallbacks_t	callbacks;
	dns_rdataclass_t	rdataclass;
	isc_textregion_t	textregion;

	if (T1_mctx == NULL)
		isc_result = isc_mem_create(0, 0, &T1_mctx);
	else
		isc_result = ISC_R_SUCCESS;
	if (isc_result != ISC_R_SUCCESS) {
		t_info("isc_mem_create failed %d\n", isc_result);
		return(T_UNRESOLVED);
	}

	len = strlen(origin);
	isc_buffer_init(&source, origin, len);
	isc_buffer_add(&source, len);
	isc_buffer_setactive(&source, len);
	isc_buffer_init(&target, name_buf, BUFLEN);
	dns_name_init(&dns_origin, NULL);
	dns_result = dns_name_fromtext(&dns_origin, &source, dns_rootname,
				       0, &target);
	if (dns_result != ISC_R_SUCCESS) {
		t_info("dns_name_fromtext failed %s\n",
				dns_result_totext(dns_result));
		return(T_UNRESOLVED);
	}

	dns_rdatacallbacks_init_stdio(&callbacks);
	callbacks.add = t1_add_callback;

	textregion.base = db_class;
	textregion.length = strlen(db_class);

	dns_result = dns_rdataclass_fromtext(&rdataclass, &textregion);
	if (dns_result != ISC_R_SUCCESS) {
		t_info("dns_rdataclass_fromtext failed %s\n",
				dns_result_totext(dns_result));
		return(T_UNRESOLVED);
	}

	dns_result = dns_master_loadfile(	testfile,
						&dns_origin,
						&dns_origin,
						rdataclass,
						ISC_TRUE,
						&callbacks,
						T1_mctx);

	if (dns_result == exp_result)
		result = T_PASS;
	else {
		t_info("dns_master_loadfile: got %s, expected %s\n",
			dns_result_totext(dns_result),
			dns_result_totext(exp_result));
		result = T_FAIL;
	}
	return(result);
}

static int
test_master_x(const char *filename) {
	FILE		*fp;
	char		*p;
	int		line;
	int		cnt;
	int		result;

	result = T_UNRESOLVED;

	fp = fopen(filename, "r");
	if (fp != NULL) {
		line = 0;
		while ((p = t_fgetbs(fp)) != NULL) {

			++line;

			/*
			 * Skip comment lines.
			 */
			if ((isspace(*p & 0xff)) || (*p == '#')) {
				(void)free(p);
				continue;
			}

			/*
			 * Name of data file, origin, zclass, expected result.
			 */
			cnt = t_bustline(p, Tokens);
			if (cnt == 4) {
				result = test_master(Tokens[0], Tokens[1],
					     Tokens[2],
					     t_dns_result_fromtext(Tokens[3]));
			} else {
				t_info("bad format in %s at line %d\n",
				       filename, line);
			}

			(void)free(p);
		}
		(void)fclose(fp);
	} else {
		t_info("Missing datafile %s\n", filename);
	}
	return(result);
}

static const char *a1 =	"dns_master_loadfile loads a valid master file and "
			"returns ISC_R_SUCCESS";
static void
t1(void) {
	int	result;
	t_assert("dns_master_loadfile", 1, T_REQUIRED, "%s", a1);
	result = test_master_x("dns_master_load_1_data");
	t_result(result);
}

static const char *a2 =
	"dns_master_loadfile returns ISC_R_UNEXPECTEDEND when the "
	"masterfile input ends unexpectedly";

static void
t2(void) {
	int	result;
	t_assert("dns_master_loadfile", 2, T_REQUIRED, "%s", a2);
	result = test_master_x("dns_master_load_2_data");
	t_result(result);
}

static const char *a3 =	"dns_master_loadfile returns DNS_R_NOOWNER when the "
			"an ownername is not specified";

static void
t3() {
	int	result;
	t_assert("dns_master_loadfile", 3, T_REQUIRED, "%s", a3);
	result = test_master_x("dns_master_load_3_data");
	t_result(result);
}

static const char *a4 =	"dns_master_loadfile accepts broken zone files "
			"where the first record has an undefined TTL, "
			"as long as it is a SOA";

static void
t4() {
	int	result;
	t_assert("dns_master_loadfile", 4, T_REQUIRED, "%s", a4);
	result = test_master_x("dns_master_load_4_data");
	t_result(result);
}

static const char *a5 =	"dns_master_loadfile returns DNS_R_BADCLASS when the "
			"the record class did not match the zone class";

static void
t5() {
	int	result;

	t_assert("dns_master_loadfile", 5, T_REQUIRED, "%s", a5);
	result = test_master_x("dns_master_load_5_data");

	t_result(result);
}

static const char *a6 =
	"dns_master_loadfile understands DNSKEY RR specifications "
	"containing key material";

static void
t6() {
	int	result;

	t_assert("dns_master_loadfile", 6, T_REQUIRED, "%s", a6);
	result = test_master_x("dns_master_load_6_data");

	t_result(result);
}

static const char *a7 =
	"dns_master_loadfile understands DNSKEY RR specifications "
	"containing no key material";

static void
t7() {
	int	result;

	t_assert("dns_master_loadfile", 7, T_REQUIRED, "%s", a7);
	result = test_master_x("dns_master_load_7_data");

	t_result(result);
}

static const char *a8 =
	"dns_master_loadfile understands $INCLUDE";

static void
t8() {
	int	result;

	t_assert("dns_master_loadfile", 8, T_REQUIRED, "%s", a8);
	result = test_master_x("dns_master_load_8_data");

	t_result(result);
}

static const char *a9 =
	"dns_master_loadfile understands $INCLUDE with failure";

static void
t9() {
	int	result;

	t_assert("dns_master_loadfile", 9, T_REQUIRED, "%s", a9);
	result = test_master_x("dns_master_load_9_data");

	t_result(result);
}

static const char *a10 =
	"dns_master_loadfile non-empty blank lines";

static void
t10() {
	int	result;

	t_assert("dns_master_loadfile", 10, T_REQUIRED, "%s", a10);
	result = test_master_x("dns_master_load_10_data");

	t_result(result);
}

static const char *a11 =
	"dns_master_loadfile allow leading zeros in SOA";

static void
t11() {
	int	result;

	t_assert("dns_master_loadfile", 11, T_REQUIRED, "%s", a11);
	result = test_master_x("dns_master_load_11_data");

	t_result(result);
}


testspec_t	T_testlist[] = {
	{	(PFV) t1,	"ISC_R_SUCCESS"			},
	{	(PFV) t2,	"ISC_R_UNEXPECTEDEND"		},
	{	(PFV) t3,	"DNS_NOOWNER"			},
	{	(PFV) t4,	"DNS_NOTTL"			},
	{	(PFV) t5,	"DNS_BADCLASS"			},
	{	(PFV) t6,	"DNSKEY RR 1"			},
	{	(PFV) t7,	"DNSKEY RR 2"			},
	{	(PFV) t8,	"$INCLUDE"			},
	{	(PFV) t9,	"$INCLUDE w/ DNS_BADCLASS"	},
	{	(PFV) t10,	"non empty blank lines"		},
	{	(PFV) t11,	"leading zeros in serial"	},
	{	(PFV) 0,	NULL				}
};

#ifdef WIN32
int
main(int argc, char **argv) {
	t_settests(T_testlist);
	return (t_main(argc, argv));
}
#endif
