/*
 * Copyright (c) 2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-greyctl-0.c,v 1.6 2006/12/01 16:54:53 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/types.h"
#include "sm/net.h"
#include "sm/greyctl.h"
#include "sm/greycnf.h"
#include "sm/io.h"
#include "sm/test.h"
#include "greyctl.h"

static int Verbose = 0;

static sm_ret_T
grey_display1(DB *db, DB *sdb, sm_file_T *fp)
{
	sm_ret_T ret;
	DBC *dbcp;
	DBT db_data, db_key;
	greyentry_T ge;
	bool first;

	ret = db->cursor(sdb, NULL, &dbcp, 0);
	if (ret != 0)
		return ret;
	first = true;
	do
	{
		sm_memzero(&db_key, sizeof(db_key));
		sm_memzero(&db_data, sizeof(db_data));
		db_data.flags = DB_DBT_USERMEM;
		db_data.data = ≥
		db_data.ulen = sizeof(ge);

		ret = dbcp->c_get(dbcp, &db_key, &db_data, first ? DB_FIRST : DB_NEXT);
		if (0 == ret)
		{
			sm_io_fprintf(fp,
				"addr=%16A, time=%7ld, expire=%7ld, type=%s\n",
				(ipv4_T) ge.ge_key,
				(long) ge.ge_time,
				(long) ge.ge_expire,
				ge.ge_type == GREY_TYPE_GREY ? "grey" :
				ge.ge_type == GREY_TYPE_WHITE ? "white"
				: "oops");
		}
		first = false;
	} while (0 == ret);
	ret = dbcp->c_close(dbcp);
	return ret;
}

static sm_ret_T
grey_display(greyctx_P greyctx, sm_file_T *fp)
{
	sm_ret_T ret;

	ret = grey_display1(greyctx->greyc_grey_db, greyctx->greyc_grey_sdb, fp);
	sm_io_putc(fp, '\n');
	sm_io_flush(fp);
	return ret;
}

static sm_ret_T
grey_display0(DB *db, sm_file_T *fp)
{
	sm_ret_T ret;
	DBC *dbcp;
	DBT db_data, db_key;
	greyentry_T ge;

	ret = db->cursor(db, NULL, &dbcp, 0);
	if (ret != 0)
		return ret;
	do {
		sm_memzero(&db_key, sizeof(db_key));
		sm_memzero(&db_data, sizeof(db_data));
		db_data.flags = DB_DBT_USERMEM;
		db_data.data = ≥
		db_data.ulen = sizeof(ge);

		ret = dbcp->c_get(dbcp, &db_key, &db_data, DB_NEXT);
		if (0 == ret) {
			sm_io_fprintf(fp,
				"addr=%16A, time=%7ld, expire=%7ld, type=%s\n",
				(ipv4_T) ge.ge_key,
				(long) ge.ge_time,
				(long) ge.ge_expire,
				ge.ge_type == GREY_TYPE_GREY ? "grey" :
				ge.ge_type == GREY_TYPE_WHITE ? "white"
				: "oops");
		}
	} while (0 == ret);
	ret = dbcp->c_close(dbcp);
	return ret;
}

static sm_ret_T
grey_display_m(greyctx_P greyctx, sm_file_T *fp)
{
	sm_ret_T ret;

	ret = grey_display0(greyctx->greyc_grey_db, fp);
	sm_io_putc(fp, '\n');
	sm_io_flush(fp);
	return ret;
}


static void
testg(bool display, bool display_m)
{
	sm_ret_T ret;
	uint ht_size, ht_limit, ge_limit, u, off;
	ipv4_T addr, grey_ip1, grey_ip2;
	time_t t;
	greyctx_P greyctx;

#define T_GREY_MIN_WAIT	60
#define T_GREY_MAX_WAIT	600
#define T_WHITE_EXP	6000
#define T_WHITE_TMO	12000
#define T_GREY_IP1	1
#define T_GREY_IP2	2

	ht_size = 31;
	ht_limit = 16;
	ge_limit = 8;

	ret = sm_greyctl_crt(&greyctx);
	SM_TEST(SM_SUCCESS == ret);
	if (ret != SM_SUCCESS)
		return;

	greyctx->greyc_cnf.greycnf_limit = ht_limit;
	greyctx->greyc_cnf.greycnf_min_grey_wait = T_GREY_MIN_WAIT;
	greyctx->greyc_cnf.greycnf_max_grey_wait = T_GREY_MAX_WAIT;
	greyctx->greyc_cnf.greycnf_white_expire = T_WHITE_EXP;
	greyctx->greyc_cnf.greycnf_white_reconfirm = T_WHITE_TMO;

	grey_ip1 = T_GREY_IP1;
	grey_ip2 = T_GREY_IP2;

	ret = sm_greyctl_open(greyctx, NULL);
	SM_TEST(SM_SUCCESS == ret);
	if (ret != SM_SUCCESS)
		return;

	if (display) {
		grey_display(greyctx, smioout);
		goto done;
	}
	if (display_m) {
		grey_display_m(greyctx, smioout);
		goto done;
	}

	t = 0;
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip1, sizeof(ipv4_T), t + 1);
	SM_TEST(SM_GREY_FIRST == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + 2);
	SM_TEST(SM_GREY_FIRST == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip1, sizeof(ipv4_T), t + 10);
	SM_TEST(SM_GREY_WAIT == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip1, sizeof(ipv4_T), t + T_GREY_MIN_WAIT - 1);
	SM_TEST(SM_GREY_WAIT == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip1, sizeof(ipv4_T), t + T_GREY_MIN_WAIT);
	SM_TEST(SM_GREY_WAIT == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip1, sizeof(ipv4_T), t + T_GREY_MIN_WAIT + 1);
	SM_TEST(SM_GREY_OK == ret);

	off =  T_GREY_MIN_WAIT + 2;
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + off);
	SM_TEST(SM_GREY_OK == ret);
	off += 10;
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + off);
	SM_TEST(SM_GREY_OK == ret);
	off += 30;
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + off);
	SM_TEST(SM_GREY_OK == ret);
	off += 90;
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + off);
	SM_TEST(SM_GREY_OK == ret);
	ret = sm_greyctl(greyctx, (uchar *)&grey_ip2, sizeof(ipv4_T), t + off + T_WHITE_TMO + 3);
	SM_TEST(SM_GREY_AGAIN == ret);

	/* test overflow of hash table */
	t = T_WHITE_TMO + T_WHITE_EXP + 10;
	for (u = 0; u < ht_limit; u++) {
		addr = u + 4;
		ret = sm_greyctl(greyctx, (uchar *)&addr, sizeof(addr), t + u);
		SM_TEST(SM_GREY_FIRST == ret);
		if (ret != SM_GREY_FIRST)
			sm_io_fprintf(smioerr, "u=%d, ret=%r\n", u, ret);
	}
/*
	addr = u + 4;
	ret = sm_greyctl(greyctx, (uchar *)&addr, sizeof(addr), t + u);
	SM_TEST(sm_is_err(ret));
*/

	/* timeout: can add new entry */
	u = ht_limit + 1;
	t += T_GREY_MAX_WAIT + 1 + u;
	addr = 4 + ht_limit + 1;
	ret = sm_greyctl(greyctx, (uchar *)&addr, sizeof(addr), t);
	SM_TEST(SM_GREY_FIRST == ret);
	t += T_GREY_MIN_WAIT;
	ret = sm_greyctl(greyctx, (uchar *)&addr, sizeof(addr), t);
	SM_TEST(SM_GREY_OK == ret);

	/* more tests??? */

	if (Verbose > 0)
		grey_display(greyctx, smioout);

  done:
	ret = sm_greyctl_free(greyctx);
	SM_TEST(SM_SUCCESS == ret);
}

int
main(int argc, char *argv[])
{
	int c;
	bool display, display_m;

	display = false;
	display_m = false;
	while ((c = getopt(argc, argv, "dmV")) != -1)
	{
		switch (c)
		{
		  case 'd':
			display = true;
			break;
		  case 'm':
			display_m = true;
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			return(1);
		}
	}
	if (!(display || display_m)) {
		(void) unlink("grey_grey_m.db");
		(void) unlink("grey_grey_s.db");
	}
	sm_test_begin(argc, argv, "test greylist");
	testg(display, display_m);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1