/*
* Copyright (c) 2002-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"
SM_RCSID("@(#)$Id: edb.c,v 1.134 2007/06/18 04:42:31 ca Exp $")
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/stat.h"
#include "sm/time.h"
#include "sm/heap.h"
#include "sm/assert.h"
#include "sm/str.h"
#include "sm/io.h"
#include "sm/log.h"
#include "sm/fdset.h"
#include "sm/reccom.h"
#include "sm/rcb.h"
#include "sm/cdb.h"
#include "sm/qmgr-int.h"
#include "sm/actdb-int.h"
#include "sm/edb.h"
#include "edb-int.h"
/* Should we provide our own locking or let BDB do it? for now: own */
#include "sm/pthread.h"
#define EDB_LOG_DEFINES 1
#include "log.h"
#define EDB_VRS_NONE 0x00
#define EDB_VRS_WR 0x01
#define EDB_VRS_TXN 0x02
/*
** Error messages? Use logging instead of printf?
** dbp->set_errfile(dbp, stderr);
** dbp->set_errpfx(dbp, EDB_PREFIX);
** dbp->err(dbp, r, "...");
*/
/*
** EDB_ERR_CB -- called by BDB in case of errors
**
** Parameters:
** dbenv -- BDB environment
** errpfx -- error prefix
** msg -- error message
**
** Returns:
** none
**
** Note: this is a HACK. errpfx is expected to be part of edb_ctx_T
** and hence edb_ctx is passed as context to this function;
** there doesn't seem to be another way to get a user context.
**
** Last code review: 2005-03-23 21:21:50; see note.
** Last code change:
*/
static void
edb_err_cb(
#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
const DB_ENV *dbenv, const char *errpfx, const char *msg)
#else
const char *errpfx, char *msg)
#endif
{
edb_ctx_P edb_ctx;
if (NULL == errpfx)
return; /* ERROR */
/* HACK; get edb_ctx */
edb_ctx = (edb_ctx_P) (errpfx - offsetof(edb_ctx_T, edb_pfx));
if (NULL == edb_ctx)
return; /* ERROR */
/* "clean up" msg? it doesn't follow the smX logging format... */
sm_log_write(edb_ctx->edb_lctx,
EDB_LCAT_EDB, EDB_LMOD_EDB, SM_LOG_ERR, 1,
"sev=ERROR, %s", msg);
return;
}
/*
** EDB_CTX_FREE -- free deferred envelope database context
**
** Parameters:
** edb_ctx -- EDB context
**
** Returns:
** SM_SUCCESS
**
** Last code review: 2005-03-23 21:22:12
** Last code change:
*/
static sm_ret_T
edb_ctx_free(edb_ctx_P edb_ctx)
{
if (NULL == edb_ctx)
return SM_SUCCESS;
SM_IS_EDB_CTX(edb_ctx);
edb_ctx->sm_magic = SM_MAGIC_NULL;
sm_free_size(edb_ctx, sizeof(*edb_ctx));
return SM_SUCCESS;
}
/*
** EDB_RW_VERSION -- read or write version of EDB
**
** Parameters:
** edb_ctx -- EDB context
** flags -- read/write etc
**
** Returns:
** usual sm_error code; ENOMEM, SM_E_VER_MIX, et.al.
**
** Locking:
** none (must be provided by caller)
**
** Last code review: 2005-03-23 22:04:08
** Last code change: 2005-03-23 22:03:34
*/
static sm_ret_T
edb_rw_version(edb_ctx_P edb_ctx, uint flags)
{
sm_ret_T ret;
int r;
DBT db_key, db_data;
DB_TXN *db_txn;
sm_rcb_P rcb;
SM_IS_EDB_CTX(edb_ctx);
ret = SM_SUCCESS;
db_txn = NULL;
rcb = NULL;
sm_memzero(&db_key, sizeof(db_key));
sm_memzero(&db_data, sizeof(db_data));
db_data.flags = DB_DBT_USERMEM;
db_key.data = EDB_VRS_KEY;
db_key.size = sizeof(EDB_VRS_KEY);
#define EDB_RCB_VERS_LEN 32
#define EDB_RCB_VERS_MAX 64
rcb = sm_rcb_new(NULL, EDB_RCB_VERS_LEN, EDB_RCB_VERS_MAX);
if (NULL == rcb)
return sm_error_temp(SM_EM_EDB, ENOMEM);
if (SM_IS_FLAG(flags, EDB_VRS_TXN)) {
r = edb_ctx->edb_bdbenv->txn_begin(edb_ctx->edb_bdbenv, NULL, &db_txn, 0);
if (r != 0) {
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
}
if (SM_IS_FLAG(flags, EDB_VRS_WR)) {
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, EDB_RCB_VERS_LEN,
SM_RCBV_INT, RT_EDB_VERSION, EDB_VERSION,
SM_RCBV_END);
if (sm_is_err(ret))
goto err0;
db_data.data = sm_rcb_data(rcb);
db_data.size = sm_rcb_getlen(rcb);
r = edb_ctx->edb_bdb->put(edb_ctx->edb_bdb, db_txn, &db_key, &db_data, 0);
}
else {
uint32_t rt, l, v;
db_data.data = sm_rcb_data(rcb);
db_data.ulen = sm_rcb_getsize(rcb);
r = edb_ctx->edb_bdb->get(edb_ctx->edb_bdb, db_txn, &db_key, &db_data, 0);
if (0 == r) {
l = db_data.size;
if (l < sm_rcb_getmax(rcb))
sm_rcb_setlen(rcb, db_data.size);
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret))
goto err0;
/* Total length of record */
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret) || v > EDB_RCB_VERS_MAX ||
v > sm_rcb_getlen(rcb))
goto err0;
/* version number */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != EDB_VERSION)
goto err0;
if (!EDB_VRS_COMPAT(v, EDB_VERSION))
ret = sm_error_perm(SM_EM_EDB, SM_E_VER_MIX);
}
}
if (r != 0)
ret = sm_error_perm(SM_EM_EDB, r); /* always perm? */
err0:
if (db_txn != NULL) {
if (sm_is_err(ret)) {
(void) db_txn->abort(db_txn);
}
else {
r = db_txn->commit(db_txn, 0);
db_txn = NULL;
if (r != 0)
ret = sm_error_perm(SM_EM_EDB, r);
/* always perm? */
}
}
error:
if (rcb != NULL)
sm_rcb_free(rcb);
return ret;
}
#if SM_REQL_FLE
/*
** EDB_REQ_PRE -- allocate some edb requests
**
** Parameters:
** edb_ctx -- EDB context
** n -- number of entries to allocate
** lockit -- lock edb_ctx? (reql_pool)
**
** Returns:
** usual sm_error code; ENOMEM
**
** Side Effects: edb_reql_fle may have some entries even on error
**
** Last code review:
** Last code change:
*/
static sm_ret_T
edb_req_pre(edb_ctx_P edb_ctx, uint n)
{
uint u;
sm_ret_T ret;
edb_req_P edb_req;
ret = SM_SUCCESS;
for (u = 0; u < n; u++) {
ret = edb_req_new(edb_ctx, EDB_RQF_ALLOC, &edb_req, false);
if (sm_is_err(ret))
break;
SM_ASSERT(edb_req != NULL);
EDBREQL_PRE(&edb_ctx->edb_reql_fle, edb_req);
}
return ret;
}
#endif
/*
** EDB_OPEN -- open deferred envelope database on disk
**
** Parameters:
** edb_cnf -- EDB configuration data
** base_path -- path to base directory
** lctx -- log context
** pedb_ctx -- pointer to EDB context (output)
**
** Returns:
** usual sm_error code; ENOMEM, SM_E_VER_MIX, et.al.
**
** Last code review:
** Last code change:
*/
sm_ret_T
edb_open(edb_cnf_P edb_cnf, const char *base_path, sm_log_ctx_P lctx, edb_ctx_P *pedb_ctx)
{
sm_ret_T ret;
int r;
uint flags, oflags;
int major_v, minor_v, patch_v;
bool exists;
char *err_prefix, *dbfile;
edb_ctx_P edb_ctx;
DB *dbp;
DB_ENV *dbenv;
DB_TXN *db_txn;
struct stat sb;
#define EDB_PREFIX "libedb"
#define EDB_CNF_DEF_NULL(field, dflt) \
(NULL == edb_cnf || NULL == edb_cnf->field) ? (dflt) : edb_cnf->field
#define EDB_CNF_DEF_0(field, dflt) \
(NULL == edb_cnf || edb_cnf->field == 0) ? (dflt) : edb_cnf->field
SM_ASSERT(pedb_ctx != NULL);
*pedb_ctx = NULL;
dbp = NULL;
dbenv = NULL;
db_txn = NULL;
dbfile = NULL;
exists = false;
(void) db_version(&major_v, &minor_v, &patch_v);
if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR)
return sm_error_perm(SM_EM_EDB, SM_E_VER_MIX);
edb_ctx = (edb_ctx_P) sm_zalloc(sizeof(*edb_ctx));
if (NULL == edb_ctx)
return sm_error_temp(SM_EM_EDB, ENOMEM);
strlcat(edb_ctx->edb_pfx, "edb", sizeof(edb_ctx->edb_pfx));
edb_ctx->edb_lctx = lctx;
r = pthread_mutex_init(&edb_ctx->edb_mutex, SM_PTHREAD_MUTEXATTR);
if (r != 0) {
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
oflags = EDB_CNF_DEF_0(edbcnf_oflags, 0);
EDBREQL_INIT(&edb_ctx->edb_reql_wr);
EDBREQL_INIT(&edb_ctx->edb_reql_fls);
EDBREQL_INIT(&edb_ctx->edb_reql_fln);
#if SM_REQL_FLE
EDBREQL_INIT(&edb_ctx->edb_reql_fle);
/* preallocate some entries */
ret = edb_req_pre(edb_ctx, 2);
if (sm_is_err(ret))
goto error;
#endif
/* does DB exist? */
r = stat(EDB_NAME_RD, &sb);
exists = (0 == r); /* check for other errors? */
err_prefix = EDB_CNF_DEF_NULL(edbcnf_err_prefix, EDB_PREFIX);
if (!SM_IS_FLAG(oflags, EDB_OPEN_NOENV)) {
size_t l;
char dbh[PATH_MAX];
dbh[0] = '\0';
r = db_env_create(&dbenv, 0);
if (r != 0) {
dbenv->err(dbenv, r, "db_env_create");
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
#if 0
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, err_prefix);
#else
dbenv->set_errpfx(dbenv, edb_ctx->edb_pfx);
dbenv->set_errcall(dbenv, edb_err_cb);
#endif
r = dbenv->set_cachesize(dbenv, 0,
EDB_CNF_DEF_0(edbcnf_cachesize, EDB_CACHE), 0);
if (SM_IS_FLAG(oflags, EDB_OPEN_RDONLY))
flags = DB_INIT_MPOOL | DB_PRIVATE /*| DB_INIT_LOCK*/;
else {
flags = DB_CREATE | DB_INIT_MPOOL | DB_INIT_TXN |
/* DB_INIT_LOCK | */ DB_PRIVATE;
if (SM_IS_FLAG(oflags, EDB_OPEN_RECOVER))
flags |= DB_RECOVER;
if (SM_IS_FLAG(oflags, EDB_OPEN_RECOVER_FATAL))
flags |= DB_RECOVER_FATAL;
}
if (base_path != NULL && *base_path != '\0' &&
(NULL == edb_cnf || NULL == edb_cnf->edbcnf_basedir ||
*edb_cnf->edbcnf_basedir != '/'))
{
l = strlcpy(dbh, base_path, sizeof(dbh));
if (l >= sizeof(dbh)) {
ret = sm_error_perm(SM_EM_EDB, SM_E_2BIG);
goto error;
}
}
l = strlcat(dbh,
(NULL == edb_cnf || NULL == edb_cnf->edbcnf_basedir) ?
EDB_HOME : edb_cnf->edbcnf_basedir,
sizeof(dbh));
if (l >= sizeof(dbh)) {
ret = sm_error_perm(SM_EM_EDB, SM_E_2BIG);
goto error;
}
r = dbenv->open(dbenv, dbh, flags, 0600);
if (r != 0) {
dbenv->err(dbenv, r,
"where=DB_ENV->open, dbhome=%s", dbh);
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
if (edb_cnf != NULL && edb_cnf->edbcnf_logdir != NULL &&
*edb_cnf->edbcnf_logdir != '\0' &&
(r = dbenv->set_lg_dir(dbenv, edb_cnf->edbcnf_logdir)) != 0)
{
dbenv->err(dbenv, r, "where=DB_ENV->set_lg_dir, logdir=%s",
edb_cnf->edbcnf_logdir);
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
if (SM_IS_FLAG(oflags, EDB_LOG_AUTOREMOVE) &&
(r = dbenv->set_flags(dbenv, DB_LOG_AUTOREMOVE, 1)) != 0)
{
dbenv->err(dbenv, r,
"where=DB_ENV->set_flags, flags=DB_LOG_AUTOREMOVE",
edb_cnf->edbcnf_logdir);
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
}
/* Create and initialize database object, open the database. */
r = db_create(&dbp, dbenv, 0);
if (r != 0) {
sm_log_write(lctx,
EDB_LCAT_EDB, EDB_LMOD_EDB, SM_LOG_ERR, 4,
"sev=ERROR, func=edb_open, db_create=%s",
db_strerror(r));
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
if (SM_IS_FLAG(oflags, EDB_OPEN_NOENV)) {
dbp->set_errpfx(dbp, edb_ctx->edb_pfx);
dbp->set_errcall(dbp, edb_err_cb);
}
if (edb_cnf != NULL && edb_cnf->edbcnf_pagesize != 0
&& (r = dbp->set_pagesize(dbp, edb_cnf->edbcnf_pagesize)) != 0)
{
dbp->err(dbp, r, "where=set_pagesize");
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
db_txn = NULL;
if (!SM_IS_FLAG(oflags, EDB_OPEN_NOENV)) {
r = dbenv->txn_begin(dbenv, NULL, &db_txn, 0);
if (r != 0) {
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
}
dbfile = SM_IS_FLAG(oflags, EDB_OPEN_NOENV) ? EDB_NAME_RD : EDB_NAME;
flags = SM_IS_FLAG(oflags, EDB_OPEN_RDONLY) ? DB_RDONLY : DB_CREATE;
r = dbp->open(dbp, db_txn, dbfile, NULL, DB_BTREE, flags, 0600);
if (r != 0) {
dbp->err(dbp, r, "where=db->open, db=%s", EDB_NAME);
if (db_txn != NULL) {
(void) db_txn->abort(db_txn);
db_txn = NULL;
}
ret = sm_error_perm(SM_EM_EDB, r);
goto err2;
}
/* for edb_rw_version() */
flags = exists ? EDB_VRS_NONE : EDB_VRS_WR;
if (db_txn != NULL) {
flags |= EDB_VRS_TXN;
r = db_txn->commit(db_txn, 0);
db_txn = NULL;
if (r != 0) {
ret = sm_error_perm(SM_EM_EDB, r);
goto err3;
}
}
edb_ctx->sm_magic = SM_EDB_CTX_MAGIC;
edb_ctx->edb_bdb = dbp;
edb_ctx->edb_bdbenv = dbenv;
ret = edb_rw_version(edb_ctx, flags);
if (sm_is_err(ret) && !SM_IS_FLAG(oflags, EDB_OPEN_NOVERSION))
goto err3;
#if 0
/* only if writing? */
edb_ctx->edb_version = EDB_VERSION;
edb_ctx->edb_maxsize = size;
edb_ctx->edb_mode = mode;
/*
edb_ctx->edb_created;
edb_ctx->edb_touched;
*/
#endif /* 0 */
edb_ctx->edb_chkpt_kb = (NULL == edb_cnf) ? 0 :
edb_cnf->edbcnf_chkpt_kb;
edb_ctx->edb_chkpt_delay = (NULL == edb_cnf) ? 0 :
edb_cnf->edbcnf_chkpt_delay;
*pedb_ctx = edb_ctx;
return SM_SUCCESS;
err3:
if (dbp != NULL)
(void) dbp->close(dbp, 0);
err2:
if (dbenv != NULL)
(void) dbenv->close(dbenv, 0);
error:
/* clean up... close DB etc */
/* XXX clean up mutex, cond, ... */
edb_ctx->sm_magic = SM_MAGIC_NULL;
sm_free_size(edb_ctx, sizeof(*edb_ctx));
return ret;
}
/*
** EDB_HDRMODL_WR -- write header modification list to EDB
**
** Parameters:
** sm_hdrmodhd -- header of header modification list
** rcb -- RCB to fill in
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
edb_hdrmodl_wr(sm_hdrmodhd_P sm_hdrmodhd, sm_rcb_P rcb)
{
return sm_hdrmodl_wr(sm_hdrmodhd, rcb, RT_EDB_HM_T_P, RT_EDB_HM_HDR);
}
/*
** EDB_TA_APP -- append transaction (status) to request list
**
** Parameters:
** edb_ctx -- EDB context
** aq_ta -- transaction (sender) data
** edb_req_hd -- EDB request list (used if not NULL)
** status -- transaction status
**
** Returns:
** usual sm_error code; ENOMEM, EINVAL, SM_E_OVFLW_SC,
** SM_E_OVFLW_NS, etc
**
** Side Effects: none on error (except if unlock fails)
** if ok: appends new rcb to edb_req_hd or edb_ctx list
**
** Locking:
** locks edb_ctx during operation
**
** Last code review: 2005-03-23 22:43:59
** Last code change:
*/
sm_ret_T
edb_ta_app(edb_ctx_P edb_ctx, aq_ta_P aq_ta, edb_req_hd_P edb_req_hd, int status)
{
sm_ret_T ret;
int r;
uint u;
sm_rcb_P rcb;
edb_req_P edb_req;
SM_IS_EDB_CTX(edb_ctx);
SM_IS_AQ_TA(aq_ta);
r = pthread_mutex_lock(&edb_ctx->edb_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG? */
return sm_error_perm(SM_EM_EDB, r);
}
edb_req = NULL;
ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, false);
if (sm_is_err(ret))
goto error;
rcb = edb_req->edb_req_rcb;
ret = sm_rcb_open_enc(rcb, -1);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
/* transaction status */
SM_RCBV_INT, RT_EDB_TA_ST, status,
/* CDB ID */
SM_RCBV_CSTR, RT_EDB_CDBID, aq_ta->aqt_cdb_id,
SM_RCBV_OFF, RT_EDB_SIZE_B, aq_ta->aqt_msg_sz_b,
SM_RCBV_INT, RT_EDB_TA_ST_TI, aq_ta->aqt_st_time,
SM_RCBV_INT, RT_EDB_TA_FL,
(aq_ta->aqt_flags & AQ_TA_FL_MASK) | AQ_TA_FL_DEFEDB,
/* nrcpts */
SM_RCBV_INT, RT_EDB_TA_R_TOT, aq_ta->aqt_rcpts_tot,
SM_RCBV_INT, RT_EDB_TA_R_LEFT, aq_ta->aqt_rcpts_left,
SM_RCBV_INT, RT_EDB_TA_R_TEMP, aq_ta->aqt_rcpts_temp,
SM_RCBV_INT, RT_EDB_TA_R_PERM, aq_ta->aqt_rcpts_perm,
SM_RCBV_INT, RT_EDB_TA_R_TRIED, aq_ta->aqt_rcpts_tried,
SM_RCBV_INT, RT_EDB_TA_R_NXT, (uint32_t) aq_ta->aqt_nxt_idx,
/* RT_EDB_TA_R_TRD */
/* mail XXX this may exceed the record size??? */
SM_RCBV_STR, RT_EDB_MAIL_PA,
aq_ta->aqt_mail->aqm_pa,
SM_RCBV_INT, RT_EDB_OWN_N, (uint32_t) aq_ta->aqt_owners_n,
SM_RCBV_END);
for (u = 0; SM_SUCCESS == ret && u < aq_ta->aqt_owners_n; u++) {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_STR, RT_EDB_OWN_PA, aq_ta->aqt_owners_pa[u],
SM_RCBV_END);
}
ret = edb_hdrmodl_wr(aq_ta->aqt_hdrmodhd, rcb);
(void) sm_rcb_close_enc(rcb);
if (sm_is_err(ret))
goto error;
SESSTA_COPY(edb_req->edb_req_id, aq_ta->aqt_ss_ta_id);
edb_req->edb_req_type = EDB_REQ_TA;
if (NULL == edb_req_hd)
EDBREQL_APP(&edb_ctx->edb_reql_wr, edb_req);
else
EDBREQL_APP(edb_req_hd, edb_req);
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
return ret;
error:
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
if (edb_req != NULL) {
if (sm_is_err(ret) && sm_error_value(ret) == ENOMEM)
(void) edb_req_free(edb_req);
else
(void) edb_req_rel(edb_ctx, edb_req, 0, THR_NO_LOCK);
}
/* cleanup? */
return ret;
}
/*
** EDB_RCPT_APP -- append recipient (status) to request list
**
** Parameters:
** edb_ctx -- EDB context
** aq_rcpt -- recipient data
** edb_req_hd -- EDB request list (used if not NULL)
** status -- recipient status
**
** Returns:
** usual sm_error code; ENOMEM, EINVAL, SM_E_OVFLW_SC,
** SM_E_OVFLW_NS, etc
**
** Side Effects: none on error (except if unlock fails)
** if ok: appends new rcb to edb_req_hd or edb_ctx list
**
** Locking:
** locks edb_ctx during operation
**
** Last code review: 2005-03-23 22:56:17
** Last code change:
*/
sm_ret_T
edb_rcpt_app(edb_ctx_P edb_ctx, aq_rcpt_P aq_rcpt, edb_req_hd_P edb_req_hd, int status)
{
edb_req_P edb_req;
sm_rcb_P rcb;
sm_ret_T ret;
int r;
SM_IS_EDB_CTX(edb_ctx);
SM_IS_AQ_RCPT(aq_rcpt);
r = pthread_mutex_lock(&edb_ctx->edb_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG? */
return sm_error_perm(SM_EM_EDB, r);
}
edb_req = NULL;
ret = edb_req_new(edb_ctx, EDB_RQF_NONE, &edb_req, THR_NO_LOCK);
if (sm_is_err(ret))
goto error;
rcb = edb_req->edb_req_rcb;
ret = sm_rcb_open_enc(rcb, -1);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
/* recipient status */
SM_RCBV_INT, RT_EDBR_ST, status,
/* recipient index */
SM_RCBV_INT, RT_EDBR_IDX, aq_rcpt->aqr_idx,
/* reference to owner */
SM_RCBV_INT, RT_EDBR_OWN, aq_rcpt->aqr_owner_idx,
/* transaction ID */
SM_RCBV_BUF, RT_EDBR_TAID,
(uchar *) aq_rcpt->aqr_ss_ta_id,
SMTP_STID_SIZE,
/* aq_rcpt XXX this may exceed the record size??? put strs at end? */
SM_RCBV_STR, RT_EDBR_PA, aq_rcpt->aqr_pa,
SM_RCBV_INT, RT_EDBR_TRIES, aq_rcpt->aqr_tries,
SM_RCBV_INT, RT_EDBR_ST_TI, aq_rcpt->aqr_st_time,
SM_RCBV_INT, RT_EDBR_LA_TI, aq_rcpt->aqr_last_try,
SM_RCBV_INT, RT_EDBR_NX_TI, aq_rcpt->aqr_next_try,
SM_RCBV_INT, RT_EDBR_FL,
(aq_rcpt->aqr_flags & AQR_FL_MASK) | AQR_FL_DEFEDB,
SM_RCBV_INT, RT_EDBR_DA, aq_rcpt->aqr_da_idx,
SM_RCBV_INT,
(0 == aq_rcpt->aqr_port)
? RT_NOSEND : RT_EDBR_PORT,
(uint32_t) aq_rcpt->aqr_port,
SM_RCBV_END);
/*
** Resolved address... XXX needs more details
** Note: this depends on whether QMGR does DNS lookups (MX, A).
** In that case only the "resolved address" from SMAR must be
** written to DEFEDB.
*/
if (!sm_is_err(ret)) {
uint u;
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_ADDRS, aq_rcpt->aqr_addr_max);
if (!sm_is_err(ret) && aq_rcpt->aqr_addrs != NULL) {
for (u = 0; u < aq_rcpt->aqr_addr_max && !sm_is_err(ret); ++u) {
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_IPV4,
aq_rcpt->aqr_addrs[u].aqra_ipv4);
if (sm_is_err(ret))
break;
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_EXPT,
aq_rcpt->aqr_addrs[u].aqra_expt);
if (sm_is_err(ret))
break;
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_PREF,
(uint32_t) aq_rcpt->aqr_addrs[u].aqra_pref);
}
}
}
if (!sm_is_err(ret))
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_DSNFL, aq_rcpt->aqr_dsn_flags);
if (!sm_is_err(ret) && aq_rcpt->aqr_msg != NULL) {
/* recipient status text */
ret = sm_rcb_put2uint32(rcb, sm_str_getlen(aq_rcpt->aqr_msg),
RT_EDBR_STT);
if (!sm_is_err(ret))
ret = sm_rcb_putn(rcb, sm_str_data(aq_rcpt->aqr_msg),
sm_str_getlen(aq_rcpt->aqr_msg));
}
if (!sm_is_err(ret) && aq_rcpt->aqr_err_st != 0) {
/* error state */
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_ERR_ST, aq_rcpt->aqr_err_st);
}
if (!sm_is_err(ret) && aq_rcpt->aqr_addr_fail != 0) {
/* contacted host */
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_F_IPV4, aq_rcpt->aqr_addr_fail);
}
if (!sm_is_err(ret) && aq_rcpt_is_alias(aq_rcpt)) {
/* recipient index of original address */
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_IDX_A, aq_rcpt->aqr_alias_idx);
}
if (!sm_is_err(ret) && aq_rcpt_has_bounce(aq_rcpt)) {
/* bounce recipient index */
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_IDX_B, aq_rcpt->aqr_dsn_idx);
}
if (!sm_is_err(ret) && aq_rcpt_has_delay(aq_rcpt)) {
/* delay recipient index */
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_IDX_D, aq_rcpt->aqr_dly_idx);
}
if (!sm_is_err(ret) && aq_rcpt->aqr_dsn_msg != NULL) {
/* bounce message (for bounce recipient) */
ret = sm_rcb_put2uint32(rcb, sm_str_getlen(aq_rcpt->aqr_dsn_msg),
RT_EDBR_B_MSG);
if (!sm_is_err(ret))
ret = sm_rcb_putn(rcb, sm_str_data(aq_rcpt->aqr_dsn_msg),
sm_str_getlen(aq_rcpt->aqr_dsn_msg));
}
#if MTA_USE_TLS
if (!sm_is_err(ret) && aq_rcpt->aqr_conf != NULL) {
ret = sm_rcb_put3uint32(rcb, 4, RT_EDBR_MAP_RES_CNF_RCPT,
aq_rcpt->aqr_maprescnf);
ret = sm_rcb_put2uint32(rcb, sm_str_getlen(aq_rcpt->aqr_conf),
RT_EDBR_RHS_CNF_RCPT);
if (!sm_is_err(ret))
ret = sm_rcb_putn(rcb, sm_str_data(aq_rcpt->aqr_conf),
sm_str_getlen(aq_rcpt->aqr_conf));
}
#endif /* MTA_USE_TLS */
(void) sm_rcb_close_enc(rcb);
if (sm_is_err(ret))
goto error;
SESSTA_CLR(edb_req->edb_req_id);
sm_snprintf(edb_req->edb_req_id, SMTP_RCPTID_SIZE,
SMTP_RCPTID_FORMAT, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_idx);
edb_req->edb_req_type = EDB_REQ_RCPT;
if (NULL == edb_req_hd)
EDBREQL_APP(&edb_ctx->edb_reql_wr, edb_req);
else
EDBREQL_APP(edb_req_hd, edb_req);
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
return ret;
error:
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
if (edb_req != NULL) {
if (sm_is_err(ret) && sm_error_value(ret) == ENOMEM)
(void) edb_req_free(edb_req);
else
(void) edb_req_rel(edb_ctx, edb_req, 0, THR_NO_LOCK);
}
/* more cleanup? */
return ret;
}
/*
** EDB_WR_STATUS -- write status (request list)
**
** Parameters:
** edb_ctx -- EDB context
** edb_req_hd -- EDB request list (used if not NULL)
**
** Returns:
** usual sm_error code; DB errors,
**
** Side Effects: none on error (except if unlock or BDB transaction fails)
**
** Locking:
** locks edb_ctx during operation
**
** Last code review: 2005-03-23 23:25:14
** Last code change: 2005-03-23 23:16:05
*/
sm_ret_T
edb_wr_status(edb_ctx_P edb_ctx, edb_req_hd_P edb_req_hd)
{
sm_ret_T ret;
int r;
uint fct_state;
bool delete;
edb_req_P edb_req;
sm_rcb_P rcb;
DBT db_key, db_data;
DB_TXN *db_txn;
/* function state flags */
#define FST_EDB_CTX_LCK 0x01 /* edb_ctx is locked */
#define FST_REQ_TYPE 0x02 /* bogus request type: abort */
SM_IS_EDB_CTX(edb_ctx);
fct_state = 0;
edb_req = NULL;
ret = SM_SUCCESS;
if (edb_req_hd != NULL && EDBREQL_EMPTY(edb_req_hd))
return SM_SUCCESS;
if (NULL == edb_req_hd) {
r = pthread_mutex_lock(&edb_ctx->edb_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG? */
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
SM_SET_FLAG(fct_state, FST_EDB_CTX_LCK);
edb_req_hd = &edb_ctx->edb_reql_wr;
}
if (EDBREQL_EMPTY(edb_req_hd))
goto done;
if (!SM_IS_FLAG(fct_state, FST_EDB_CTX_LCK)) {
r = pthread_mutex_lock(&edb_ctx->edb_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG? */
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
SM_SET_FLAG(fct_state, FST_EDB_CTX_LCK);
}
db_txn = NULL;
r = edb_ctx->edb_bdbenv->txn_begin(edb_ctx->edb_bdbenv, NULL, &db_txn, 0);
if (r != 0 || NULL == db_txn) {
ret = sm_error_perm(SM_EM_EDB, r == 0 ? SM_E_UNEXPECTED : r);
goto error;
}
/* walk through request list and write them to DEFEDB */
for (edb_req = EDBREQL_FIRST(edb_req_hd);
edb_req != EDBREQL_END(edb_req_hd);
edb_req = EDBREQL_NEXT(edb_req))
{
rcb = edb_req->edb_req_rcb;
sm_memzero(&db_key, sizeof(db_key));
sm_memzero(&db_data, sizeof(db_data));
delete = false;
db_key.data = edb_req->edb_req_id;
/* Check type */
switch (edb_req->edb_req_type) {
case EDB_REQ_RCPT_DEL:
delete = true;
/* FALLTHROUGH */
case EDB_REQ_RCPT:
db_key.size = SMTP_RCPTID_SIZE;
break;
case EDB_REQ_TA_DEL:
delete = true;
/* FALLTHROUGH */
case EDB_REQ_TA:
db_key.size = SMTP_STID_SIZE;
break;
default:
ret = sm_error_perm(SM_EM_EDB, SM_E_UNEXPECTED);
SM_SET_FLAG(fct_state, FST_REQ_TYPE);
(void) db_txn->abort(db_txn);
goto error;
}
if (delete)
r = edb_ctx->edb_bdb->del(edb_ctx->edb_bdb, db_txn, &db_key, 0);
else {
db_data.data = sm_rcb_data(rcb);
db_data.size = sm_rcb_getlen(rcb);
r = edb_ctx->edb_bdb->put(edb_ctx->edb_bdb, db_txn, &db_key, &db_data, 0);
}
if (r != 0) {
EDB_DPRINTF((smioerr, "sev=ERROR, func=edb_wr_status, delete=%d, op=%d, key=%s\n", delete, r, (char *) db_key.data));
ret = sm_error_perm(SM_EM_EDB, r);
break;
}
else
++edb_ctx->edb_writes;
}
if (sm_is_err(ret)) {
/* check result? */
(void) db_txn->abort(db_txn);
goto error;
}
r = db_txn->commit(db_txn, 0);
db_txn = NULL;
if (r != 0) {
ret = sm_error_perm(SM_EM_EDB, r);
goto error;
}
#if 0
else {
/* currently not used anywhere */
/* better way to get time? */
edb_ctx->edb_last_write = time(NULLT);
}
#endif /* 0 */
/* Everything is ok: remove requests from list */
while (!EDBREQL_EMPTY(edb_req_hd)) {
edb_req = EDBREQL_FIRST(edb_req_hd);
EDBREQL_REMOVE(edb_req_hd);
(void) edb_req_rel(edb_ctx, edb_req, 0, THR_NO_LOCK);
/* always ok */
}
/* Fall through for unlocking etc */
done:
edb_req = NULL;
error:
if (SM_IS_FLAG(fct_state, FST_EDB_CTX_LCK)) {
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
SM_CLR_FLAG(fct_state, FST_EDB_CTX_LCK);
}
if (SM_IS_FLAG(fct_state, FST_REQ_TYPE)) {
sm_abort("wrong edb_req_type %u",
edb_req != NULL ? edb_req->edb_req_type : UINT_MAX);
}
return ret;
}
/*
** EDB_HDRMODL_RD -- read header modification list
**
** Parameters:
** sm_hdrmodhd -- header of header modification list
** rcb -- RCB
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
edb_hdrmodl_rd(sm_hdrmodhd_P sm_hdrmodhd, sm_rcb_P rcb)
{
return sm_hdrmodl_rd(sm_hdrmodhd, rcb, RT_EDB_HM_T_P, RT_EDB_HM_HDR);
}
/*
** EDB_TA_DEC -- decode edb_req into transaction structure
**
** Parameters:
** edb_req -- request: contains data to decode
** aq_ta -- TA data (must exist, will be populated) (output)
**
** Returns:
** usual sm_error code; ENOMEM, EINVAL (protocol errors)
**
** Side Effects: none on error
** allocates: aqt_cdb_id, aqt_mail->aqm_pa,
** maybe: aqt_owners_pa and strings in that array
**
** Last code review: 2005-03-18 23:57:11
** Last code change:
*/
sm_ret_T
edb_ta_dec(edb_req_P edb_req, aq_ta_P aq_ta)
{
sm_rcb_P rcb;
sm_ret_T ret;
uint u;
uint32_t v, l, rt, tl;
off_t off;
size_t owners_size;
sm_str_P *owners_pa;
SM_ASSERT(edb_req != NULL);
SM_IS_AQ_TA(aq_ta);
rcb = edb_req->edb_req_rcb;
(void) sm_rcb_open_dec(rcb); /* OK */
owners_pa = NULL;
owners_size = 0;
/* Total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
if (sm_is_err(ret) || tl > EDB_RC_MAXSZ || tl > sm_rcb_getlen(rcb))
goto error;
/* transaction status */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_ST)
goto error;
aq_ta->aqt_state = v;
/* CDB ID */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || l >= tl || rt != RT_EDB_CDBID)
goto error;
ret = sm_rcb_getncstr(rcb, &aq_ta->aqt_cdb_id, l);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_get3off_t(rcb, &l, &rt, &off);
if (sm_is_err(ret) || l != SIZEOF_OFF_T || rt != RT_EDB_SIZE_B)
goto error;
aq_ta->aqt_msg_sz_b = off;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_ST_TI)
goto error;
aq_ta->aqt_st_time = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_FL)
goto error;
aq_ta->aqt_flags = v;
/* nrcpts */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_TOT)
goto error;
aq_ta->aqt_rcpts_tot = v;
/* rcpts_left */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_LEFT)
goto error;
aq_ta->aqt_rcpts_left = v;
/* rcpts_temp */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_TEMP)
goto error;
aq_ta->aqt_rcpts_temp = v;
/* rcpts_perm */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_PERM)
goto error;
aq_ta->aqt_rcpts_perm = v;
/* rcpts_tried */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_TRIED)
goto error;
aq_ta->aqt_rcpts_tried = v;
/* next rcpt idx */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_TA_R_NXT)
goto error;
aq_ta->aqt_nxt_idx = v;
/* mail_pa */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || l >= tl || rt != RT_EDB_MAIL_PA)
goto error;
ret = sm_rcb_getnstr(rcb, &aq_ta->aqt_mail->aqm_pa, l);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDB_OWN_N)
goto error;
aq_ta->aqt_owners_n = v;
if (v > 0) {
owners_size = aq_ta->aqt_owners_n * sizeof(*owners_pa);
if (owners_size < aq_ta->aqt_owners_n) {
ret = sm_error_perm(SM_EM_EDB, SM_E_OVFLW_SC);
goto error;
}
owners_pa = sm_zalloc(owners_size);
if (NULL == owners_pa) {
ret = sm_error_temp(SM_EM_EDB, ENOMEM);
goto error;
}
for (u = 0; SM_SUCCESS == ret && u < aq_ta->aqt_owners_n; u++) {
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || l >= tl || rt != RT_EDB_OWN_PA)
goto error;
ret = sm_rcb_getnstr(rcb, &owners_pa[u], l);
if (sm_is_err(ret))
goto error;
}
aq_ta->aqt_owners_pa = owners_pa;
owners_pa = NULL;
}
ret = edb_hdrmodl_rd(aq_ta->aqt_hdrmodhd, rcb);
(void) sm_rcb_close_dec(rcb);
if (sm_is_err(ret))
goto error;
SESSTA_COPY(aq_ta->aqt_ss_ta_id, edb_req->edb_req_id);
return ret;
error:
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_EDB, EINVAL);
(void) sm_rcb_close_decn(rcb);
/* cleanup? */
if (owners_pa != NULL) {
for (u = 0; u < aq_ta->aqt_owners_n; u++)
SM_STR_FREE(owners_pa[u]);
SM_ASSERT(owners_size > 0);
SM_FREE_SIZE(owners_pa, owners_size);
}
SM_STR_FREE(aq_ta->aqt_mail->aqm_pa);
SM_CSTR_FREE(aq_ta->aqt_cdb_id);
return ret;
}
/*
** EDB_RCPT_DEC -- decode edb_req into recipient structure
**
** Parameters:
** edb_req -- request: contains data to decode
** aq_rcpt -- RCPT data (must exist, will be populated) (output)
**
** Returns:
** usual sm_error code; ENOMEM, EINVAL (protocol errors)
**
** Side Effects: some scalars in aq_rcpt might be set
** if ok: allocates: aqr_pa, aqr_addrs, maybe: aqr_dsn_msg, aqr_msg
**
** Last code review: 2005-03-23 23:40:23
** Last code change: 2005-03-18 18:46:17
*/
sm_ret_T
edb_rcpt_dec(edb_req_P edb_req, aq_rcpt_P aq_rcpt)
{
sm_rcb_P rcb;
sm_ret_T ret;
uint32_t v, l, rt, tl, naddrs;
uint u;
bool expired;
time_T now;
size_t addrs_size;
SM_ASSERT(edb_req != NULL);
SM_IS_AQ_RCPT(aq_rcpt);
now = time(NULLT); /* better way to get time? */
addrs_size = 0;
rcb = edb_req->edb_req_rcb;
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret))
goto error;
/* Total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
if (sm_is_err(ret) || tl > EDB_RC_MAXSZ || tl > sm_rcb_getlen(rcb))
goto error;
/* recipient status */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_ST)
goto error;
aq_rcpt->aqr_status = v;
/* recipient index */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_IDX)
goto error;
aq_rcpt->aqr_idx = v; /* XXX check with rcpt_id? */
/* reference to owner */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_OWN)
goto error;
aq_rcpt->aqr_owner_idx = v;
/* transaction ID */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || l != SMTP_STID_SIZE || rt != RT_EDBR_TAID)
goto error;
ret = sm_rcb_getn(rcb, (uchar *) aq_rcpt->aqr_ss_ta_id, l);
if (sm_is_err(ret))
goto error;
/* aq_rcpt_pa */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || l >= tl || rt != RT_EDBR_PA)
goto error;
ret = sm_rcb_getnstr(rcb, &aq_rcpt->aqr_pa, l);
if (sm_is_err(ret))
goto error;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_TRIES)
goto error;
aq_rcpt->aqr_tries = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_ST_TI)
goto error;
aq_rcpt->aqr_st_time = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_LA_TI)
goto error;
aq_rcpt->aqr_last_try = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_NX_TI)
goto error;
aq_rcpt->aqr_next_try = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_FL)
goto error;
aq_rcpt->aqr_flags = v;
/* optional: RT_EDBR_DA */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4)
goto error;
if (RT_EDBR_DA == rt) {
aq_rcpt->aqr_da_idx = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
}
if (RT_EDBR_PORT == rt) {
aq_rcpt->aqr_port = (short) v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
}
/* resolved address... needs more details?? which? */
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_ADDRS)
goto error;
naddrs = v;
aq_rcpt->aqr_addr_cur = 0; /* XXX (why??) */
if (0 == naddrs) {
aq_rcpt->aqr_addr_max = 0;
aq_rcpt->aqr_addrs = NULL;
}
else {
addrs_size = naddrs * sizeof(*(aq_rcpt->aqr_addrs));
if (addrs_size < naddrs) {
ret = sm_error_perm(SM_EM_EDB, SM_E_OVFLW_SC);
goto error;
}
aq_rcpt->aqr_addrs = (aq_raddr_P) sm_malloc(addrs_size);
if (NULL == aq_rcpt->aqr_addrs) {
AQR_SET_FLAG(aq_rcpt, AQR_FL_MEMAR);
aq_rcpt->aqr_addr_max = 1;
aq_rcpt->aqr_addrs = &aq_rcpt->aqr_addr_mf;
addrs_size = 0;
}
expired = false;
for (u = 0; u < naddrs; u++) {
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_IPV4)
goto error;
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR) || u == 0)
aq_rcpt->aqr_addrs[u].aqra_ipv4 = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_EXPT)
goto error;
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR) || u == 0) {
aq_rcpt->aqr_addrs[u].aqra_expt = v;
if (!expired)
expired = v < now;
}
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_EDBR_PREF)
goto error;
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR) || u == 0)
aq_rcpt->aqr_addrs[u].aqra_pref = v;
}
/* is one of the addresses expired? */
if (expired) {
/* yes: just ignore everything */
if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR))
sm_free_size(aq_rcpt->aqr_addrs, addrs_size);
aq_rcpt->aqr_addr_max = 0;
aq_rcpt->aqr_addrs = NULL;
addrs_size = 0;
}
else if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR)) {
aq_rcpt->aqr_addr_max = naddrs;
if (naddrs > 0)
AQR_SET_FLAG(aq_rcpt,
AQR_FL_RCVD4AR|AQR_FL_RDY4DLVRY);
}
}
while (!SM_RCB_ISEOB(rcb)) {
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret))
goto error;
switch (rt) {
case RT_EDBR_DSNFL:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_dsn_flags = v;
break;
case RT_EDBR_B_MSG:
if (l >= tl)
goto error;
ret = sm_rcb_getnstr(rcb, &aq_rcpt->aqr_dsn_msg, l);
if (sm_is_err(ret))
goto error;
break;
case RT_EDBR_STT:
if (l >= tl)
goto error;
ret = sm_rcb_getnstr(rcb, &aq_rcpt->aqr_msg, l);
if (sm_is_err(ret))
goto error;
break;
case RT_EDBR_ERR_ST:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_err_st = v;
break;
case RT_EDBR_F_IPV4:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_addr_fail = v;
break;
case RT_EDBR_IDX_A:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_alias_idx = v;
break;
case RT_EDBR_IDX_B:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_dsn_idx = v;
break;
case RT_EDBR_IDX_D:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_dly_idx = v;
break;
#if MTA_USE_TLS
case RT_EDBR_MAP_RES_CNF_RCPT:
if (l != 4)
goto error;
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret))
goto error;
aq_rcpt->aqr_maprescnf = v;
break;
case RT_EDBR_RHS_CNF_RCPT:
if (l >= tl)
goto error;
ret = sm_rcb_getnstr(rcb, &aq_rcpt->aqr_conf, l);
if (sm_is_err(ret))
goto error;
break;
#endif /* MTA_USE_TLS */
default:
/* COMPLAIN */
goto error;
break;
}
}
(void) sm_rcb_close_dec(rcb);
if (sm_is_err(ret))
goto error;
return ret;
error:
if (!sm_is_err(ret))
ret = sm_error_perm(SM_EM_EDB, EINVAL);
if (aq_rcpt->aqr_addrs != NULL
&& !AQR_IS_FLAG(aq_rcpt, AQR_FL_MEMAR)
&& aq_rcpt->aqr_addrs != &aq_rcpt->aqr_addr_mf)
SM_FREE_SIZE(aq_rcpt->aqr_addrs, addrs_size);
aq_rcpt->aqr_addrs = NULL;
SM_STR_FREE(aq_rcpt->aqr_pa);
SM_STR_FREE(aq_rcpt->aqr_dsn_msg);
SM_STR_FREE(aq_rcpt->aqr_msg);
(void) sm_rcb_close_decn(rcb);
/* cleanup? */
return ret;
}
/*
** EDB_RD_REQ -- read one entry from EDB
**
** Parameters:
** edb_ctx -- EDB context
** edb_req -- request: contains key to read, rcb to fill (I/O)
** edb_req_type and edb_req_id must be set.
**
** Returns:
** usual sm_error code; BDB error
**
** Side Effects: writes data into rcb of edb_req (maybe even on error)
**
** Locking:
** locks edb_ctx during operation
**
** Last code review: 2005-03-18 05:46:49
** Last code change:
*/
sm_ret_T
edb_rd_req(edb_ctx_P edb_ctx, edb_req_P edb_req)
{
sm_rcb_P rcb;
sm_ret_T ret;
int r;
size_t l;
DBT db_key, db_data;
DB_TXN *db_txn;
SM_IS_EDB_CTX(edb_ctx);
SM_REQUIRE(edb_req != NULL);
r = pthread_mutex_lock(&edb_ctx->edb_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG? */
return sm_error_perm(SM_EM_EDB, r);
}
/* Should this be a transaction?? Check BDB doc */
db_txn = NULL;
ret = SM_SUCCESS;
rcb = edb_req->edb_req_rcb;
sm_memzero(&db_key, sizeof(db_key));
sm_memzero(&db_data, sizeof(db_data));
switch (edb_req->edb_req_type) {
case EDB_REQ_RCPT:
db_key.data = edb_req->edb_req_id;
db_key.size = SMTP_RCPTID_SIZE;
break;
case EDB_REQ_TA:
db_key.data = edb_req->edb_req_id;
db_key.size = SMTP_STID_SIZE;
break;
default:
sm_abort("edb_rd_req: unknown edb_req_type %d", edb_req->edb_req_type);
break;
}
db_data.flags = DB_DBT_USERMEM;
db_data.data = sm_rcb_data(rcb);
db_data.ulen = sm_rcb_getsize(rcb);
ret = SM_SUCCESS;
r = edb_ctx->edb_bdb->get(edb_ctx->edb_bdb, db_txn, &db_key, &db_data, 0);
l = db_data.size;
if (DB_BUFFER_SMALL == r && l < EDB_RC_MAXSZ) {
l = (l + 1023) & ~1023; /* round to 1024; see BDB docs */
if (!sm_is_err(sm_rcb_resize_data(rcb, l))) {
db_data.data = sm_rcb_data(rcb);
db_data.ulen = sm_rcb_getsize(rcb);
r = edb_ctx->edb_bdb->get(edb_ctx->edb_bdb, db_txn,
&db_key, &db_data, 0);
l = db_data.size;
}
}
if (r != 0)
ret = sm_error_perm(SM_EM_EDB, r);
else if (l < sm_rcb_getmax(rcb))
sm_rcb_setlen(edb_req->edb_req_rcb, db_data.size);
r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_EDB, r);
return ret;
}
/*
** EDB_CLOSE -- close edb
**
** Parameters:
** edb_ctx -- edb context
**
** Returns:
** usual sm_error code; BDB error
**
** Last code review: 2005-03-23 23:45:55
** Last code change: 2005-03-23 23:45:39
*/
sm_ret_T
edb_close(edb_ctx_P edb_ctx)
{
sm_ret_T ret;
int r;
if (NULL == edb_ctx)
return SM_SUCCESS;
ret = edb_reqls_free(edb_ctx); /* OK */
(void) pthread_mutex_destroy(&edb_ctx->edb_mutex);
/* Close DB */
if (edb_ctx->edb_bdb != NULL) {
r = edb_ctx->edb_bdb->close(edb_ctx->edb_bdb, 0);
edb_ctx->edb_bdb = NULL;
if (r != 0) {
sm_log_write(edb_ctx->edb_lctx,
EDB_LCAT_EDB, EDB_LMOD_EDB, SM_LOG_ERR, 4,
"sev=ERROR, func=edb_close, db->close=%s",
db_strerror(r));
ret = sm_error_perm(SM_EM_EDB, r);
}
}
/* Close DB Environment */
if (edb_ctx->edb_bdbenv != NULL) {
r = edb_ctx->edb_bdbenv->close(edb_ctx->edb_bdbenv, 0);
edb_ctx->edb_bdbenv = NULL;
if (r != 0) {
sm_log_write(edb_ctx->edb_lctx,
EDB_LCAT_EDB, EDB_LMOD_EDB, SM_LOG_ERR, 4,
"sev=ERROR, func=edb_close, dbenv->close=%s",
db_strerror(r));
ret = sm_error_perm(SM_EM_EDB, r);
}
}
(void) edb_ctx_free(edb_ctx); /* OK */
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1