/*
 * Copyright (c) 2003, 2004, 2006 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"
#if 0
SM_RCSID("@(#)$Id: t-connctl-0.c,v 1.13 2006/05/02 17:13:37 ca Exp $")
#endif /* 0 */

/* For Solaris */
#define _STRPTIME_DONTZERO 1

#include <stdio.h>
#include "sm/types.h"
#include "sm/ctype.h"
#include "sm/string.h"
#include "sm/time.h"
#include "sm/error.h"
#include "sm/net.h"
#include "sm/connctl.h"
#include "sm/test.h"

#define MAXCPM	100
#define MAXLINE	128

static int CollTime = 60;
static int Verbose = 0;

#ifndef CPMHSIZE
# define CPMHSIZE	256
#endif /* CPMHSIZE */
#ifndef MAX_CT_STEPS
# define MAX_CT_STEPS	5
#endif /* MAX_CT_STEPS */

/* Hack... to get a "statistics" version */
#define SM_CONNCTL_PERF	1
#include "../libmta/connctl.c"

static int size = CPMHSIZE;
static int steps = MAX_CT_STEPS;

#if TEST_BHTABLE

#include "sm/bhtable.h"
#define T_BHT_SIZE	4095
#define MAX_OPEN_CONN	100
static ipv4_T open_conns[MAX_OPEN_CONN];	/* used as fifo */
static int oc_first = 0, oc_last = 0;

void
stats(bht_P tn)
{
#if SM_BHT_PERF
	char buf[1024];

	if (Verbose > 0)
	{
		bht_stats(tn, buf, sizeof(buf));
		fprintf(stderr, "%s\n", buf);
	}
#endif /* SM_BHT_PERF */
}
#endif /* TEST_BHTABLE */

/*
**  READCONN -- read connection data from file and call connctl
**
**	Parameters:
**		fp -- file from which to read data
**		rdonly -- don't call connctl
**		writeit -- write pre-processed data to stdout
**		preproc -- input is pre-processed
**
**	Returns:
**		usual sm_error code
*/

int
readconn(FILE *fp, int rdonly, int writeit, int preproc)
{
	sm_ret_T ret;
	int r;
	char *s, *h;
	struct tm tm;
	struct in_addr addr;
	time_t t, prev;
	ulong colls, unused, lc, total, maxcoll;
	char line[MAXLINE];
#if TEST_BHTABLE
	bht_P tn;
	bht_entry_P bi;
#else /* TEST_BHTABLE */
	connctx_P cctx;
#endif /* TEST_BHTABLE */

#if TEST_BHTABLE
	tn = bht_new(size, size * 10);
	SM_TEST(tn != NULL);
	if (tn == NULL)
		return -1;
#else /* TEST_BHTABLE */
	ret = sm_connctl_new(size, steps, &cctx);
	SM_TEST(ret == SM_SUCCESS);
#endif /* TEST_BHTABLE */

	lc = 0;
	prev = 0;
	colls = unused = lc = total = maxcoll = 0;
	while (fgets(line, sizeof(line), fp) != NULL)
	{
		++lc;
		if (preproc)
			t = strtol(line, &s, 10);
		else
		{
			bzero(&tm, sizeof(tm));
			tm.tm_year = 103;
			tm.tm_wday = 0;
			tm.tm_yday = 0;
			s = strptime(line, "%b %e %T", &tm);
			if (s == NULL)
			{
				fprintf(stderr, "%ld: can't read date: '%s'\n",
					lc, line);
				continue;
			}
#if HAVE_TIMEGM
			t = timegm(&tm);
#else /* HAVE_TIMEGM */
			t = mktime(&tm);
#endif /* HAVE_TIMEGM */
		}
		if (t == -1)
		{
			fprintf(stderr, "%ld: can't convert time: '%s' (%d)\n",
				lc, line, errno);
			continue;
		}
		if (t < prev)
		{
			fprintf(stderr, "%ld: time went backwards: %ld -> %ld\n",
				lc, (long) prev, (long) t);
			prev = t;
			continue;
		}
		prev = t;
		while (*s != '\0' && *s != '\n' &&
		       !(isascii(*s) && isdigit(*s)))
			++s;
		h = s;
		while (*h != '\0' && *h != '\n')
			++h;
		if (*h == '\n')
			*h = '\0';
#if HAVE_INET_PTON
		r = inet_pton(AF_INET, s, &addr);
#else /* HAVE_INET_PTON */
# if HAVE_INET_ATON
		r = inet_aton(s, &addr);
# else /* HAVE_INET_ATON */
		/* yeah, right */
		SM_TEST(0 == 1);
		return -1;
# endif /* HAVE_INET_ATON */
#endif /* HAVE_INET_PTON */
		if (r != 1)
		{
			fprintf(stderr, "%ld: can't read IP addr: '%s', (%d)\n",
				lc, s, errno);
			continue;
		}
		if (!rdonly)
		{
#if TEST_BHTABLE
			open_conns[oc_first++] = (ipv4_T) addr.s_addr;
			if (oc_first >= MAX_OPEN_CONN)
				oc_first = 0;
			if (bht_find(tn, (char *) &addr, sizeof(addr)) == NULL)
			{
				ret = bht_add(tn, (char *) &addr, sizeof(addr),
						"value", &bi);
				SM_TEST(ret == SM_SUCCESS);
				if (ret != SM_SUCCESS)
					goto done;
				SM_TEST(bi != NULL);
			}
/*
printf("first=%d, last=%d\n", oc_first, oc_last);
*/
			if ((oc_first > oc_last &&
			     oc_first - oc_last >= MAX_OPEN_CONN - 2) ||
			    (oc_first < oc_last &&
			     oc_last - oc_first <= 2))
			{
/*
printf("remove first=%d, last=%d, addr=%x\n", oc_first, oc_last, open_conns[oc_last]);
*/
				bht_rm(tn, (char *)&(open_conns[oc_last]),
					sizeof(open_conns[oc_last]),
					NULL, NULL);
				++oc_last;
				if (oc_last >= MAX_OPEN_CONN)
					oc_last = 0;
			}
#else /* TEST_BHTABLE */
			ret = sm_connctl(cctx, addr, t);
#endif /* TEST_BHTABLE */
		}
		if (writeit)
			printf("%ld %s\n", (long) t, s);
	}

#if TEST_BHTABLE
  done:
	stats(tn);
	bht_destroy(tn, NULL, NULL);
#else /* TEST_BHTABLE */
#if SM_CONNCTL_PERF
	ret = sm_connctl_stats(cctx, &colls, &maxcoll, &unused, &total);
#endif /* SM_CONNCTL_PERF */
	printf("total:    %7ld\n", total);
	printf("colls:    %7ld\n", colls);
	printf("maxcolls: %7ld\n", maxcoll);
	printf("unused:   %7ld\n", unused);
	sm_connctl_free(cctx);
#endif /* TEST_BHTABLE */
	return 0;
}

int
main(int argc, char *argv[])
{
	int i, c, rdonly, writeit, preproc;
	FILE *fp;

	rdonly = writeit = preproc = 0;
	while ((c = getopt(argc, argv, "c:h:prs:Vw")) != -1)
	{
		switch (c)
		{
		  case 'c':
			CollTime = strtol(optarg, NULL, 0);
			if (CollTime <= 1)
				return 1;
			break;
		  case 'h':
			size = strtol(optarg, NULL, 0);
			if (size <= 4)
				return 1;
			break;
		  case 'p':
			++preproc;
			break;
		  case 'r':
			++rdonly;
			break;
		  case 's':
			steps = strtol(optarg, NULL, 0);
			if (steps <= 2 || steps >= size)
				return 1;
			break;
		  case 'V':
			++Verbose;
			break;
		  case 'w':
			++writeit;
			break;
		  default:
			return 1;
		}
	}
	sm_test_begin(argc, argv, "test connctl 0");
	argc -= optind;
	if (argc > 0)
	{
		for (i = 0; i < argc; i++)
		{
			if ((fp = fopen(argv[i + optind], "r")) != (FILE *)0)
			{
				readconn(fp, rdonly, writeit, preproc);
				fclose(fp);
			}
			else
			{
				fprintf(stderr, "can't read: '%s'\n", argv[i]);
			}
		}
	}
	else
		readconn(stdin, rdonly, writeit, preproc);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1