/*
 * Copyright (c) 2002-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: dadbh.c,v 1.39 2007/06/14 05:12:27 ca Exp $")
#include "sm/types.h"
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/str.h"
#include "sm/time.h"
#include "sm/mta.h"
#include "sm/memops.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "sm/dadb.h"
#include "sm/io.h"
#include "dadb.h"

/*
**  DADB_SET_LIMIT -- Set current maximum number of entries in DA DB
**	The limit may change due to resource restrictions.
**
**	Parameters:
**		dadb_ctx -- DADB context
**		entries_lim -- new limit
**		locktype -- kind of locking
**
**	Returns:
**		usual sm_error code; SM_E_RANGE, (un)lock
**
**	Side Effects: none on error (except if unlock fails)
**
**	Locking: locks dadb_ctx if requested
**
**	Last code review: 2005-03-17 00:35:27
**	Last code change:
*/

sm_ret_T
dadb_set_limit(dadb_ctx_P dadb_ctx, size_t entries_lim, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "dadb_set_limit"
	sm_ret_T ret;
	int r;

	SM_IS_DADB(dadb_ctx);
	if (thr_lock_it(locktype)) {
		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
			return sm_error_perm(SM_EM_DA, r);
	}

	/*
	**  Don't check against current number of entries as lower limit,
	**  the system will simple make sure that the new limit isn't
	**  exceeded, but it must not check against the new limit to detect
	**  "inconsistencies", entries_max is used for that purpose.
	*/

	if (entries_lim >= dadb_ctx->dadb_entries_max)
		ret = sm_error_perm(SM_EM_DA, SM_E_RANGE);
	else {
		dadb_ctx->dadb_entries_lim = entries_lim;
		ret = SM_SUCCESS;
	}
	if ((!sm_is_err(ret) && thr_unl_no_err(locktype))
	    || (sm_is_err(ret) && thr_unl_if_err(locktype)))
	{
		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
		SM_ASSERT(r == 0);
		if (r != 0 && sm_is_success(ret))
			ret = sm_error_perm(SM_EM_DA, r);
	}
	return ret;
}

/*
**  DADB_ENTRY_AVAIL -- Is there a free entry in DA DB?
**
**	Parameters:
**		dadb_ctx -- DADB context
**		pda_avail -- (pointer to) how many entries are free? (output)
**		pda_idle -- (pointer to) how many entries are idle? (output)
**		locktype -- kind of locking
**
**	Returns:
**		SM_SUCCESS except for (un)lock errors
**
**	Note: it might be useful to return counters instead of bool
**
**	Side Effects: none on error
**
**	Locking: locks dadb_ctx if requested
**
**	Last code review: 2005-03-17 00:48:15
**	Last code change: 2006-02-22 00:25:40
*/

sm_ret_T
dadb_entry_avail(dadb_ctx_P dadb_ctx, uint *pda_avail, uint *pda_idle, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "dadb_entry_avail"
	int r;

	SM_IS_DADB(dadb_ctx);
	SM_REQUIRE(pda_avail != NULL);
	*pda_avail = 0;
	SM_REQUIRE(pda_idle != NULL);
	*pda_idle = 0;
	if (thr_lock_it(locktype)) {
		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
			return sm_error_perm(SM_EM_DA, r);
	}
	if (DADB_IS_OK(dadb_ctx)) {
		*pda_avail = dadb_ctx->dadb_entries_cur - dadb_ctx->dadb_entries_lim;
		*pda_idle = dadb_ctx->dadb_entries_idle;
	}
	if (thr_unl_no_err(locktype)) {
		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
		SM_ASSERT(r == 0);
		if (r != 0)
			return sm_error_perm(SM_EM_DA, r);
	}
	return SM_SUCCESS;
}

/*
**  DADB_ENTRY_GET -- get a free entry in DA DB (or allocate a new one)
**
**	Parameters:
**		dadb_ctx -- DADB context
**		pdadb_entry -- pointer to DADB entry (output)
**		pidx -- pointer to idx (output)
**		locktype -- kind of locking
**
**	Returns:
**		usual sm_error code; ENOMEM, SM_E_FULL,
**
**	Side Effects: none on error (except if unlock fails)
**
**	Locking: locks dadb_ctx if requested
**		*pdadb_entry will be locked (if successful)
**
**	Last code review: 2005-03-17 00:55:16
**	Last code change: 2006-02-18 20:21:00
*/

sm_ret_T
dadb_entry_get(dadb_ctx_P dadb_ctx, dadb_entry_P *pdadb_entry, uint *pidx, thr_lock_T locktype)
{
#undef SMFCT
#define SMFCT "dadb_entry_get"
	uint i;
	sm_ret_T ret;
	dadb_entry_P dadb_entry;
	int r;

	SM_IS_DADB(dadb_ctx);
	SM_REQUIRE(pdadb_entry != NULL);
	SM_REQUIRE(pidx != NULL);
	ret = sm_error_temp(SM_EM_DA, SM_E_FULL);
	if (thr_lock_it(locktype)) {
		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
			return sm_error_perm(SM_EM_DA, r);
	}

	/* too many entries already? (get rid of goto for "nicer" structure?) */
	if (dadb_ctx->dadb_entries_cur >= dadb_ctx->dadb_entries_lim)
		goto unl;
	if (!DADB_IS_OK(dadb_ctx))
		goto unl;
	SM_ASSERT(dadb_ctx->dadb_entries != NULL);

	/* search for a free DA DB entry */
	for (i = 0; i < dadb_ctx->dadb_entries_max; i++) {
		dadb_entry = (dadb_ctx->dadb_entries)[i];
		if (NULL == dadb_entry) {
			dadb_entry = (dadb_entry_P) sm_zalloc(sizeof(*dadb_entry));
			if (NULL == dadb_entry) {
				ret = sm_error_temp(SM_EM_DA, ENOMEM);
				goto unl;
			}
#if DADB_CHECK
			dadb_entry->sm_magic = SM_DADBE_MAGIC;
#endif
#if DADBE_MUTEX
			r = pthread_mutex_init(&dadb_entry->dadbe_mutex,
						SM_PTHREAD_MUTEXATTR);
			if (r != 0) {
				ret = sm_error_perm(SM_EM_DA, r);
				goto unl;
			}
#endif

			/* fill in more data?? */
			(dadb_ctx->dadb_entries)[i] = dadb_entry;
#if DADBE_MUTEX
			r = pthread_mutex_lock(&dadb_entry->dadbe_mutex);
			SM_LOCK_OK(r);
			if (r != 0) {
				ret = sm_error_perm(SM_EM_DA, r);
				goto unl;
			}
#endif
			break;
		}
		else {
#if DADBE_MUTEX
			r = pthread_mutex_trylock(&dadb_entry->dadbe_mutex);
#else
			r = 0;
#endif
			if (r == 0 && DADBE_IS_FREE(dadb_entry))
				break;
		}
	}
	if (i < dadb_ctx->dadb_entries_max) {
		dadb_ctx->dadb_entries_cur++;
#if DADB_STATS
		if (dadb_ctx->dadb_entries_cur > dadb_ctx->dadb_entries_max_used)
			dadb_ctx->dadb_entries_max_used = dadb_ctx->dadb_entries_cur;
#endif /* DA_DB_STATS */
		*pdadb_entry = dadb_entry;
		*pidx = i;
		ret = SM_SUCCESS;
	}
  unl:
	if ((!sm_is_err(ret) && thr_unl_no_err(locktype))
	    || (sm_is_err(ret) && thr_unl_if_err(locktype)))
	{
		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
		SM_ASSERT(r == 0);
		if (r != 0 && sm_is_success(ret))
			ret = sm_error_perm(SM_EM_DA, r);
	}
	return ret;
}

/*
**  DADB_SE_FIND_BY_IPV4 -- find a free session in DADB by IPv4 address
**	HACK, see smX doc for proper index to access connection cache
**	Note: this is NOT a connection cache as described in the doc;
**		it is missing several data entries.
**	Use a hash table, see connctl.c?
**
**	Parameters:
**		dadb_ctx -- DADB context
**		ipv4 -- IPv4 address
**		time_now -- current time
**		pdadb_entry -- pointer to DA DB entry (output)
**
**	Returns:
**		>=0: number of entries found (that is not the actual number
**			of entries for that IPv4 address as the algorithm
**			stops as soon as it finds a free entry).
**		usual sm_error code; only (un)lock errors
**
**	Locking: locks entire dadb_ctx during operation, returns unlocked
**
**	Last code review: 2005-03-17 00:59:40
**	Last code change: 2006-02-20 18:02:17
*/

sm_ret_T
dadb_se_find_by_ipv4(dadb_ctx_P dadb_ctx, ipv4_T ipv4, time_T time_now, dadb_entry_P *pdadb_entry)
{
#undef SMFCT
#define SMFCT "dadb_se_find_by_ipv4"
	uint i;
	sm_ret_T ret;
	int r;
	dadb_entry_P dadb_entry;

	SM_IS_DADB(dadb_ctx);
	SM_REQUIRE(pdadb_entry != NULL);
	*pdadb_entry = NULL;

	/* do we really need to lock this?? */
	r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
	SM_LOCK_OK(r);
	if (r != 0)
		return sm_error_perm(SM_EM_DA, r);

	SM_ASSERT(dadb_ctx->dadb_entries != NULL);
	ret = 0;
	for (i = 0; i < dadb_ctx->dadb_entries_max; i++) {
		dadb_entry = (dadb_ctx->dadb_entries)[i];
		if (dadb_entry != NULL && DADBE_IS_ACTIVE(dadb_entry)
		    && ipv4_addr_eq(dadb_entry->dadbe_srv_ipv4, ipv4))
		{
			++ret;

			/* use first free entry that is not too old */
			if (*pdadb_entry == NULL && DADBE_IS_CONN(dadb_entry)
			    && (0 == dadb_entry->dadbe_lastuse ||
				dadb_entry->dadbe_lastuse >= time_now ||
				time_now  - dadb_entry->dadbe_lastuse <= SM_SESSION_TMO))
			{
				SM_IS_DADBE(dadb_entry);
				*pdadb_entry = dadb_entry;
				break;
			}
		}
	}

	r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
	SM_ASSERT(r == 0);
	if (r != 0 && sm_is_success(ret))
		ret = sm_error_perm(SM_EM_DA, r);
	return ret;
}

#if 0
///*
//**  DADB_ENTRY_FREE -- free a DA DB entry
//**
//**	Parameters:
//**		dadb_ctx -- DADB context
//**		dadb_entry -- DADB entry
//**		locktype -- kind of locking
//**
//**	Returns:
//**		usual sm_error code
//*/
//
//sm_ret_T
//dadb_entry_free(dadb_ctx_P dadb_ctx, dadb_entry_P dadb_entry, thr_lock_T locktype)
//{
//	int r;
//
//	if (NULL == dadb_entry)
//		return SM_SUCCESS;
//	SM_IS_DADB(dadb_ctx);
//	if (thr_lock_it(locktype))
//	{
//		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
//		SM_LOCK_OK(r);
//		if (r != 0)
//			return sm_error_perm(SM_EM_DA, r);
//	}
//
//	/* remove it from array?? otherwise there's no need for locking dadb */
//	sm_free(dadb_entry);
//	if ((!sm_is_err(ret) && thr_unl_no_err(locktype))
//	    || (sm_is_err(ret) && thr_unl_if_err(locktype)))
//	{
//		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
//		SM_ASSERT(r == 0);
//		if (r != 0 && sm_is_success(ret))
//			ret = sm_error_perm(SM_EM_DA, r);
//	}
//	return SM_SUCCESS;
//}
#endif /* 0 */

#if 0
///*
//**  DADB_ENTRY_NEW -- create new DA DB entry
//**
//**	Parameters:
//**		dadb_ctx -- DADB context
//**		pdadb_entry -- pointer to DADB entry (output)
//**		locktype -- kind of locking
//**
//**	Returns:
//**		usual sm_error code
//*/
//
//sm_ret_T
//dadb_entry_new(dadb_ctx_P dadb_ctx, dadb_entry_P *pdadb_entry, thr_lock_T locktype)
//{
//	sm_ret_T ret;
//	int r;
//	dadb_entry_P dadb_entry;
//
//	SM_IS_DADB(dadb_ctx);
//	if (thr_lock_it(locktype))
//	{
//		r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
//		SM_LOCK_OK(r);
//		if (r != 0)
//			return sm_error_perm(SM_EM_DA, r);
//	}
//	if (dadb_ctx->dadb_entries_cur >= dadb_ctx->dadb_entries_max)
//	{
//		DADB_DPRINTF((smioerr, "dadb_entry_new: cur=%d, max=%d\n", dadb_ctx->dadb_entries_cur, dadb_ctx->dadb_entries_max));
//		ret = sm_error_temp(SM_EM_DA, SM_E_FULL);
//		goto errunl;
//	}
//	dadb_entry = (dadb_entry_P) sm_zalloc(sizeof(*dadb_entry));
//	if (NULL == dadb_entry)
//		goto enomem;
//#if DADB_CHECK
//	/* set this early, otherwise the rest of the routines will fail */
//	dadb_entry->sm_magic = SM_DADBE_MAGIC;
//#endif /* DADB_CHECK */
//
//	/* fill in more data?? */
//
//	dadb_ctx->dadb_entries_cur++;
//	*pdadb_entry = dadb_entry;
//	if (thr_unl_no_err(locktype))
//	{
//		r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
//		SM_ASSERT(r == 0);
//		if (r != 0 && sm_is_success(ret))
//			ret = sm_error_perm(SM_EM_DA, r);
//	}
//	return SM_SUCCESS;
//
//  enomem:
//	ret = sm_error_temp(SM_EM_DA, ENOMEM);
//
//	/* clean up...? nothing to do right now */
//  errunl:
//	if (thr_unl_if_err(locktype))
//		(void) pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
//	return ret;
//}
#endif /* 0 */

#if 0
///*
//**  DADB_ENTRY_UPDATE -- update DA DB entry
//**
//**	Parameters:
//**		dadb_ctx -- DADB context
//**		sess_id -- session id from SMTPS
//**		ta_id -- transaction id from SMTPS
//**		dadb_entry -- entry
//**
//**	Returns:
//**		usual sm_error code
//**
//**	Locking: locks entire dadb_ctx during operation, returns unlocked
//*/
//
//sm_ret_T
//dadb_entry_update(dadb_ctx_P dadb_ctx, sessta_id_P sess_id, sessta_id_P ta_id,
//	dadb_entry_P dadb_entry)
//{
//#if 0
//	sm_ret_T ret;
//#endif /* 0 */
//	int r;
//
//	SM_IS_DADB(dadb_ctx);
//	r = pthread_mutex_lock(&dadb_ctx->dadb_mutex);
//	SM_LOCK_OK(r);
//	if (r != 0)
//		return sm_error_perm(SM_EM_DA, r);
//
//#if 0
//	ret = bht2_add(dadb_ctx->dadb_bht,
//		sess_id, SMTP_STID_SIZE,
//		ta_id, SMTP_STID_SIZE,
//		dadb_entry,
//		&bht2e);
//	if (sm_is_err(ret))
//		goto errunl;
//#endif /* 0 */
//
//	dadb_ctx->dadb_entries_cur++;
//	r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
//	SM_ASSERT(r == 0);
//	if (r != 0 && sm_is_success(ret))
//		ret = sm_error_perm(SM_EM_DA, r);
//	return SM_SUCCESS;
//
//#if 0
//  errunl:
//	r = pthread_mutex_unlock(&dadb_ctx->dadb_mutex);
//	return ret;
//#endif /* 0 */
//}
#endif /* 0 */


syntax highlighted by Code2HTML, v. 0.9.1