/* * 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; }