/*
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-greyctl-2.c,v 1.2 2007/02/14 03:34:24 ca Exp $")

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

static int Verbose = 0;
#define KEYLEN	GL_KEYLEN
static DBC *DBcursor;

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;
	ipv4_T addr;
	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 (ret == 0) {
			SM_TEST_E(ge.ge_len > 4);
			sm_memcpy(&addr, ge.ge_key, sizeof(addr));
			sm_io_fprintf(fp,
				"addr=%16A, mail/rcpt=%*s time=%7ld, expire=%7ld, type=%s\n",
				addr, (int) ge.ge_len - 4, ge.ge_key + 4,
				(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 (ret == 0);
 error:
	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
sm_grey_findprefix(DB *db, void *key, uint len, greyentry_P ge)
{
	sm_ret_T ret;
	DBT db_data, db_key;
	uchar mykey[KEYLEN];

	sm_memzero(&db_key, sizeof(db_key));
	sm_memzero(&db_data, sizeof(db_data));
	SM_REQUIRE(len < sizeof(mykey));
	sm_memcpy(mykey, key, len);
	db_key.data = mykey;
	db_key.size = len;
	db_key.ulen = sizeof(mykey);
	db_data.flags = DB_DBT_USERMEM;
	db_data.data = ge;
	db_data.ulen = sizeof(*ge);
/*
	ret = db->get(db, NULL, &db_key, &db_data, DB_SET_RANGE);
*/
	ret = DBcursor->c_get(DBcursor,&db_key, &db_data, DB_SET_RANGE);
	SM_TEST(0 == ret);
sm_io_fprintf(smioerr, "get_prefix=%d\n", ret);
	return ret;
}


static void
testg(uint entries, uint ips, bool display)
{
	sm_ret_T ret;
	uint ht_size, ht_limit, ge_limit, u;
	int r;
	uchar **keys, *key;
	uint *keylens;
	time_t t;
	greyctx_P greyctx;
	time_t  start, elapsed;
	uint greyr[2];
	greyentry_T ges;
	greyentry_P ge;

#define T_GREY_MIN_WAIT	60
#define T_GREY_MAX_WAIT	600
#define T_WHITE_EXP	6000
#define T_WHITE_TMO	12000

	ht_size = 40000;
	ht_limit = 40000;
	ge_limit = 20000;
	keys = (uchar **) sm_malloc(ips * sizeof(*keys));
	SM_TEST_E(keys != NULL);
	keylens = (uint *) sm_malloc(ips * sizeof(*keylens));
	SM_TEST_E(keylens != NULL);

	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;

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

	srand(1);
	for (u = 0; u < ips; u++) {
		key = (uchar *) sm_malloc(KEYLEN);
		SM_TEST_E(key != NULL);
		r = sm_snprintf((char *)key, KEYLEN,
				"%c%c%c%c<sender%d@domain-%d.tld><rcpt+%d@other-domain-%d.invalid>"
				, (char) rand(), (char) rand(), (char) rand(), (char) rand()
				, rand(), rand(), rand(), rand());
		SM_TEST_E(r < KEYLEN);
		keys[u] = key;
		keylens[u] = r;

if (Verbose > 2) {
	sm_io_fprintf(smioerr, "key[%d], len=%u, %s\n", u, r, key + 4);
}
	}


	greyr[0] = 0;
	greyr[1] = 0;
	start = time((time_t *) 0);
	t = 1;
	for (u = 0; u < entries; u++)
	{
		key = keys[u % ips];
		ret = sm_greyctl(greyctx, key, keylens[u % ips], t + u);
if (Verbose > 1) {
	sm_io_fprintf(smioerr, "key[%d] %s, ret=%#x\n", u % ips, key + 4, ret);
}
		switch (ret)
		{
		  case SM_GREY_AGAIN:
		  case SM_GREY_FIRST:
		  case SM_GREY_WAIT:
			++greyr[1];
			break;
		  case SM_GREY_OK:
			++greyr[0];
			break;
		  default:
			goto done;
		}
	}
	elapsed = time((time_t *) 0) - start;
	sm_io_fprintf(smioout, "elapsed time: %ld\n", (long) elapsed);
	if (elapsed > 0)
		sm_io_fprintf(smioout, "updates/s: %ld\n", (long) entries / elapsed);
	sm_io_fprintf(smioout, "wait=%u\nok=  %u\n", greyr[1], greyr[0]);
	if (display)
		grey_display(greyctx, smioout);
	sm_io_flush(smioout);

	ge = &ges;
	key = keys[0];
	ret = greyctx->greyc_grey_db->cursor(greyctx->greyc_grey_db, NULL, &DBcursor, 0);
	SM_TEST_E(0 == ret);
	ret = sm_grey_findprefix(greyctx->greyc_grey_db, key, 4, ge);
	SM_TEST_E(0 == ret);

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

  error:
	if (greyctx != NULL)
		sm_greyctl_free(greyctx);
    return;
}

int
main(int argc, char *argv[])
{
	int c;
	bool rm, display;
	uint entries, ips;

	entries = 1000;
	ips = 100;
	rm = true;
	display = false;
	while ((c = getopt(argc, argv, "de:i:k:V")) != -1)
	{
		switch (c)
		{
		  case 'd':
			display = true;
			break;
		  case 'e':
			entries = strtoul(optarg, NULL, 0);
			break;
		  case 'i':
			ips = strtoul(optarg, NULL, 0);
			break;
		  case 'k':
			rm = false;
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			return(1);
		}
	}
	if (rm)
	{
		(void) unlink("grey_grey_m.db");
		(void) unlink("grey_grey_s.db");
	}
	sm_test_begin(argc, argv, "test greylist");
	testg(entries, ips, display);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1