database.c #7

  • //
  • guest/
  • bryan_costales/
  • database.c
  • View
  • Commits
  • Open Download .zip Download (8 KB)
/************************************************************
** $Id: database.c,v 0.8 2003/12/29 16:45:38 bcx Exp bcx $
**
** Copyright Bryan Costales and Perforce Software, Inc. 2003
**
** This code is "open source" as defined by version 1.9 of
** the Open Source Definition from:
**
**	http://www.opensource.org/docs/definition.php.
**
** Entry points defined in this file:
**
** open_database():
** 	Open a database, creating it if asked to.
** close_database():
** 	Close a database.
** put_event_database():
** 	Add an ip/date item to the secondary database.
** get_event_database():
** 	Read an ip/date item from the secondary database.
** dump_events():
** 	Print the database contents to a file pointer.
** event_to_str():
** 	Format the event for printing or logging.
*************************************************************/

# define EXTERN extern
# include "slow.h"

DB *
open_database(char *fname, int force)
{
	static DB *dp;
	u_int32_t flags = DB_THREAD|DB_DIRTY_READ;
	int ret;

	ret = db_create(&dp, (DB_ENV *)NULL, 0);
	if (ret != 0)
	{
		milterr(Prog, __FILE__, __LINE__, FALSE,
			errno, fname, "db_create database");
	}
	ret = dp->set_flags(dp, DB_DUP);
	if (ret != 0)
	{
		milterr(Prog, __FILE__, __LINE__, FALSE,
			errno, fname, "db_set_flags database");
	}
	if (force == TRUE)
		flags |= DB_CREATE;
	if (force == DBREADONLY)
		flags |= DB_RDONLY;
	ret = dp->open(dp, NULL, fname, NULL, DB_HASH, flags, 0644);
	if (ret != 0)
	{
		milterr(Prog, __FILE__, __LINE__, FALSE,
			errno, fname, "db_open database");
	}
	return dp;
}

void
close_database(DB *dp)
{
	(void)dp->close(dp, 0);
}


int
get_db_version(DB *dp, int threading)
{
	SECONDARY_DATUM *rp;
	int ret;

	rp = get_event_database(dp, NULL, FALSE, &ret);
	if (rp == NULL)
		return 0;
	(void) free(rp);
	if (ret != 0)
		return 0;
	return DATABASE_VERSION;
}


int
put_event_database(DB *dp, PRIMARY_KEY ip, SECONDARY_DATUM *event)
{
	DBT key, data;
	int ret;
	extern int errno;

	if (dp == NULL)
		return EFAULT;

	(void) memset(&data, 0, sizeof data);
	(void) memset(&key, 0, sizeof key);

	key.data = &ip;
	key.size = sizeof(PRIMARY_KEY);
	data.data = event;
	data.size = sizeof(SECONDARY_DATUM);

	ret = dp->put(dp, NULL, &key, &data, 0);
	if (ret != 0)
	{
		return ret;
	}
	(void)dp->sync(dp, 0);

	return 0;
}

int
del_event_database(DB *dp, PRIMARY_KEY *ip, time_t t)
{
	DBT key, data;
	int ret;
	DBC *curs;
	extern int errno;

	if (dp == NULL)
		return EFAULT;

	(void) memset(&data, 0, sizeof data);
	(void) memset(&key, 0, sizeof key);

	key.data = ip;
	key.size = sizeof(PRIMARY_KEY);

	if (t == 0)
	{
		ret = dp->del(dp, NULL, &key, 0);
		if (ret != 0)
			return ret;
		(void)dp->sync(dp, 0);
		return 0;
	}

	(void) memset(&data, 0, sizeof data);
	data.flags = DB_DBT_REALLOC;

	ret = dp->get(dp, NULL, &key, &data, 0);
	if (ret != 0)
		return ret;

	ret = Dp->cursor(Dp, NULL, &curs, 0);
	if (ret != 0)
		return ret;

	(void) memset(&data, 0, sizeof data);
	(void) memset(&key, 0, sizeof key);
	key.flags = DB_DBT_REALLOC;
	data.flags = DB_DBT_REALLOC;
	for(;;)
	{
		ret = curs->c_get(curs, &key, &data, DB_NEXT);
		if (ret == DB_NOTFOUND)
			break;
		if (ret != 0)
			return ret;

		if (*((PRIMARY_KEY *)key.data) != *ip)
			continue;
		if (((SECONDARY_DATUM *)data.data)->date != t)
			continue;
		ret = curs->c_del(curs, 0);
		if (ret != 0)
			return ret;
	}
	(void) curs->c_close(curs);
	(void)dp->sync(dp, 0);
	return 0;
}

SECONDARY_DATUM *
get_event_database(DB *dp, PRIMARY_KEY *ip, bool verbose, int *ret)
{
	DBT key, data;
	extern int errno;
	PRIMARY_KEY p;

	if (dp == NULL)
		return NULL;

	(void) memset(&key, 0, sizeof key);
	(void) memset(&data, 0, sizeof data);

	/*
	** If the datum to look up is NULL, look up the version.
	*/
	if (ip == NULL)
	{
		p = DATABASE_VERSION;
		key.data = &p;
	}
	else
		key.data = ip;

	key.size = sizeof(PRIMARY_KEY);
	data.data = NULL;
	data.size = 0;
	data.ulen = 0;
	data.flags = DB_DBT_MALLOC;

	*ret = 0;
	*ret = dp->get(dp, NULL, &key, &data, 0);
	if (*ret != 0)
	{
		if (verbose)
		{
			dp->err(dp, *ret, "%s", DatabaseLoc);
		}
		return NULL;
	}

	return (SECONDARY_DATUM *)(data.data);
}

/*
** Walk the databases, producing a report of its contents.
** The fp pointer is where to print the report, and is usually
** the standard output.
**
** This routine is intended for use from the command-line, and is
** not thread safe.
*/
int
dump_events(DB *recdp, FILE *fp, int clean, PRIMARY_KEY ip)
{
	DBT key, data;
	int ret;
	DBC *curs;
	SECONDARY_DATUM *event;
	PRIMARY_KEY *p;
	struct in_addr in;
	char ipbuf[MAXHOSTNAMELEN];
	extern int errno;

	if (recdp == NULL)
		return 0;

	ret = recdp->cursor(recdp, NULL, &curs, 0);
	if (ret != 0)
	{
		return ret;
	}

	(void) memset(&data, 0, sizeof data);
	(void) memset(&key, 0, sizeof key);
	key.flags = DB_DBT_REALLOC;
	data.flags = DB_DBT_REALLOC;

	for (;;)
	{
		ret = curs->c_get(curs, &key, &data, DB_NEXT);
		if (ret == DB_NOTFOUND)
				break;
		if (ret != 0)
		{
			(void) curs->c_close(curs);
			return ret;
		}
		if (key.data == NULL)
			continue;

		/*
		 * Always silently skip the version.
		 */
		event = data.data;
		p = key.data;
		if (*p == DATABASE_VERSION)
			continue;
		if (ip != 0 && *p != ip)
			continue;
		(void) memcpy(&in.s_addr, (char *)p, sizeof(in.s_addr));
		(void) sprintf(ipbuf, "%s", inet_ntoa(in));
		fprintf(fp, "%s\n", event_to_str(ipbuf, event->date, event, FALSE));

	}
	(void) curs->c_close(curs);
	return 0;
}

/* ALIAS */
char *
alias_to_str(char *ipbuf, char *alias, int threading)
{
	char buf[BUFSIZ];

	if (ipbuf == NULL || alias == NULL)
		return NULL;

	(void) sprintf(buf, "%s aliased to %s", ipbuf, alias);
	
	return try_strdup(__FILE__, __LINE__, buf, threading);
}

char *
event_to_str(char *ipbuf, time_t date, SECONDARY_DATUM *event, int threading)
{
	char buf[BUFSIZ];
	struct tm *t;

	if (ipbuf == NULL)
		return NULL;

	if (date == TYPE_RECORD_WHITELIST)
	{
		(void) sprintf(buf, "%s: Whitelisted", ipbuf);
		return try_strdup(__FILE__, __LINE__, buf, threading);
	}
	t = localtime(&date);
	(void) sprintf(buf, "%s: %02d/%02d/%d.%02d:%02d:%02d ",
		ipbuf,
		t->tm_mon + 1, t->tm_mday, t->tm_year + 1900,
		t->tm_hour, t->tm_min, t->tm_sec);
	if (event == NULL)
	{
		if (errno != 0)
		{
			(void) sprintf(buf+strlen(buf), "%s",
				errno == DB_NOTFOUND ? "Item not in database (need to garbage collect)" :
				"Lookup error while processing item");
		}
		else
		{
			(void) sprintf(buf+strlen(buf), "Garbage Collected");
		}
		return try_strdup(__FILE__, __LINE__, buf, threading);
	}
	(void) sprintf(buf+strlen(buf), "er=%d,", event->envrcpts); 
	(void) sprintf(buf+strlen(buf), "hr=%d,", event->headrcpts);
	(void) sprintf(buf+strlen(buf), "ho=%d,", event->honeyrcpts);
	(void) sprintf(buf+strlen(buf), "br=%d,", event->badrcpts);
	if (event->eventmap != 0)
	{
		if (isbitset(event->eventmap, BIT_TOO_MANY_HDR_RCPTS))
			(void) strcat(buf, "hed,");
		if (isbitset(event->eventmap, BIT_TOO_MANY_ENV_RCPTS))
			(void) strcat(buf, "env,");
		if (isbitset(event->eventmap, BIT_GOT_HONEYPOT))
			(void) strcat(buf, "hon,");
		if (isbitset(event->eventmap, BIT_BAD_MSG_ID))
			(void) strcat(buf, "mid,");
		if (isbitset(event->eventmap, BIT_BAD_FROM_HEAD))
			(void) strcat(buf, "frm,");
		if (isbitset(event->eventmap, BIT_BAD_HOST))
			(void) strcat(buf, "bho,");
		if (isbitset(event->eventmap, BIT_RBL_BAD))
			(void) strcat(buf, "rbl,");
		if (isbitset(event->eventmap, BIT_MILTER_ABORTED))
			(void) strcat(buf, "abo,");
		if (isbitset(event->eventmap, BIT_ADVANCEWRITE))
			(void) strcat(buf, "pip,");
		if (isbitset(event->eventmap, BIT_FORGED))
			(void) strcat(buf, "for,");
		if (isbitset(event->eventmap, BIT_FROMMX))
			(void) strcat(buf, "mmx,");
		if (isbitset(event->eventmap, BIT_NOTINET))
			(void) strcat(buf, "net,");
		if (isbitset(event->eventmap, BIT_ADDEDIP))
			(void) strcat(buf, "aip,");
	}
	(void) sprintf(buf+strlen(buf), "msgid=%s", event->msgid);
	
	return try_strdup(__FILE__, __LINE__, buf, threading);
}


# Change User Description Committed
#7 4222 bryan_costales Massive rewrite to speed up the database writes.
Using a single database now with duplicate keys
where the keys are the IP numbers. Added a purge
command and removed the garbage command. Fixed
some leaking memory bugs and properly closed the
database in a few places were it was not
properly closed. Updated the docs to reflect
this and bumped both the database version and
release number. Running on a FreeBSD 3.x machine
and a Solaris 9 machine.
#6 4052 bryan_costales Implimented:
    whitelisting
    AddMXHost for MX servers that lie
    Converted to thread safe DNS routines
    garbage collection
    RunAsUser and RunAsGroup for root startups
    rebuild the database
    summarize by IP number
Finished all documentation.
Moved release from alpha to beta
#5 4030 bryan_costales Finished documenting the configuration file
Fixed a race condition and a core dump bug.
Added hooks for whitelisting and IP aliasing
Added support for Berkeley DB 4.2
Converted to htonl() and ntohl()

Known Bugs:
    ip.db cannot be shared over NFS
    IP tracking from MX hosts can fail
    A RunAsUser config option is needed.
#4 3998 bryan_costales Brought the whole distribution up to V0.9
Added a huge abount of documentation.
Added slowedit find
Created startup scripts to launch for testing
Fixed numerous bugs.
Fixed a few portablity issues.
Installed hooks for whitelisting and IP aliases.
#3 3957 bryan_costales Added rbl lookup support and testing for same.
Folded in support for smfi_stop().
Added lots of slowedit commands
Fixed a serious bug in MX lookups.
Added to documentation.
#2 3890 bryan_costales This is the 0.6 release. The following have been
added with the uses indicated:

    Source files: edit.c -- the slowedit functions
              compat.c -- missing system files
    Autoconf:     configure.ac, makefile.am config.h
              aclocal.m4 acinclude.m4 build/
    Documents:    doc/ -- html and man(1) documents
    Testing:      tests/ -- regressive testing
    TODO:          -- revised to show actual progress
#1 3838 bryan_costales This is pre-release 0.5 (rcs numbering) which includes:
    The milter source files and Makefile
    A regressive testing subdirectory with Makefile and /bin/sh scripts.
    A patches subdirectory with a patch file for V8.13 sendmail
All have been compiled and tested on a 64bit Sun Solaris 9 machine.