/* * 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: qm_ssh.c,v 1.74 2007/03/27 03:04:41 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/memops.h" #include "sm/hdrmod.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "qmgr.h" /* ** This module contains (de)allocation functions for QMGR/SMTPS related data. */ /* ** QSS_MAIL_NEW -- create new QMGR/SMTPS mail entry ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** ** Returns: ** usual sm_error code ** ** Last code review: ** Last code change: */ sm_ret_T qss_mail_new(qss_ta_P qss_ta) { qss_mail_P qss_mail; SM_IS_QS_TA(qss_ta); qss_mail = (qss_mail_P) sm_rpool_zalloc(qss_ta->qssta_rpool, sizeof(*qss_mail)); if (NULL == qss_mail) return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM); qss_ta->qssta_mail = qss_mail; return SM_SUCCESS; } /* ** QSS_MAIL_FREE -- free QMGR/SMTPS mail entry ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-04-01 18:09:25 ** Last code change: */ sm_ret_T qss_mail_free(qss_ta_P qss_ta) { SM_IS_QS_TA(qss_ta); if (NULL == qss_ta->qssta_mail) return SM_SUCCESS; SM_STR_FREE(qss_ta->qssta_mail->qsm_pa); SM_RPOOL_FREE(qss_ta->qssta_rpool, qss_ta->qssta_mail); return SM_SUCCESS; } /* ** QSS_RCPTS_NEW -- create new QMGR/SMTPS recipient and add it to rcpt list ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** prcpt_pa -- (pointer to) recipient printable address ** rcpt_idx -- recipient index ** prcpt -- (pointer to) recipient (output) ** ** Returns: ** usual sm_error code ** ** Last code review: ** Last code change: */ sm_ret_T qss_rcpts_new(qss_ta_P qss_ta, sm_str_P *prcpt_pa, rcpt_idx_T rcpt_idx, qss_rcpt_P *prcpt) { qss_rcpt_P qss_rcpt; SM_IS_QS_TA(qss_ta); SM_REQUIRE(prcpt_pa != NULL && *prcpt_pa != NULL); SM_REQUIRE(prcpt != NULL); if (qss_ta->qssta_rcpts_tot >= SMTP_RCPTIDX_MAX) return sm_error_temp(SM_EM_Q_Q2SS, SM_E_FULL); qss_rcpt = (qss_rcpt_P) sm_rpool_zalloc(qss_ta->qssta_rpool, sizeof(*qss_rcpt)); if (NULL == qss_rcpt) goto error; qss_rcpt->qsr_pa = *prcpt_pa; *prcpt_pa = NULL; qss_rcpt->qsr_idx = rcpt_idx; sm_snprintf(qss_rcpt->qsr_id, sizeof(qss_rcpt->qsr_id), SMTP_RCPTID_FORMAT, qss_ta->qssta_id, rcpt_idx); QSRCPTS_INSERT_TAIL(&qss_ta->qssta_rcpts, qss_rcpt); ++qss_ta->qssta_rcpts_tot; *prcpt = qss_rcpt; return SM_SUCCESS; error: SM_RPOOL_FREE(qss_ta->qssta_rpool, qss_rcpt); return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM); } /* ** QSS_RCPT_FREE -- free a single QMGR/SMTPS recipient address ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** qss_rcpt -- QMGR/SMTPS recipient ** rmflags -- various flags (qmgr/qmgr.h: QSS_) ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-04-01 18:12:03 ** Last code change: 2005-04-01 18:10:42 */ sm_ret_T qss_rcpt_free(qss_ta_P qss_ta, qss_rcpt_P qss_rcpt, uint rmflags) { if (NULL == qss_rcpt) return SM_SUCCESS; SM_IS_QS_TA(qss_ta); sm_str_free(qss_rcpt->qsr_pa); QSRCPTS_REMOVE(&qss_ta->qssta_rcpts, qss_rcpt); if (SM_IS_FLAG(rmflags, QSS_RMFIQDB)) { qmgr_ctx_P qmgr_ctx; qmgr_ctx = qss_ta->qssta_ssctx->qss_qmgr_ctx; iqdb_rcpt_rm(qmgr_ctx->qmgr_iqdb, qss_rcpt->qsr_id, SMTP_RCPTID_SIZE, SM_IS_FLAG(rmflags, QSS_IQDB_NOLOCK) ? THR_NO_LOCK : THR_LOCK_UNLOCK); if (QMGR_IS_RFLAG_I(qmgr_ctx, QMGR_RFL_IQD)) { (void) qm_resource(qmgr_ctx, QMGR_UN_THROTTLE, iqdb_usage(qmgr_ctx->qmgr_iqdb), QMGR_RFL_IQD); } } sm_rpool_free(qss_ta->qssta_rpool, qss_rcpt); if (SM_IS_FLAG(rmflags, QSS_DECR_RCPTS_TOT)) { SM_ASSERT(qss_ta->qssta_rcpts_tot > 0); --qss_ta->qssta_rcpts_tot; } return SM_SUCCESS; } /* ** QSS_RCPTS_FREE -- free an entire QMGR/SMTPS recipient list ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** rmflags -- various flags (for qss_rcpt_free()) ** prcpt_idx_high -- (pointer to) highest rcpt idx (output) ** ** Returns: ** SM_SUCCESS (or SM_NOTDONE) ** ** Last code review: 2005-04-01 18:12:30 ** Last code change: 2006-08-05 04:02:31 */ sm_ret_T qss_rcpts_free(qss_ta_P qss_ta, uint rmflags, rcpt_idx_T *prcpt_idx_high) { qss_rcpts_P qss_rcpts; qss_rcpt_P qss_rcpt, qss_nxt_rcpt; rcpt_idx_T rcpt_idx_high; SM_IS_QS_TA(qss_ta); qss_rcpts = &qss_ta->qssta_rcpts; if (QSRCPTS_EMPTY(qss_rcpts)) return SM_NOTDONE; rcpt_idx_high = 0; for (qss_rcpt = QSRCPTS_FIRST(qss_rcpts); qss_rcpt != QSRCPTS_END(qss_rcpts); qss_rcpt = qss_nxt_rcpt) { qss_nxt_rcpt = QSRCPTS_NEXT(qss_rcpt); if (rcpt_idx_high < qss_rcpt->qsr_idx) rcpt_idx_high = qss_rcpt->qsr_idx; qss_rcpt_free(qss_ta, qss_rcpt, rmflags); } QSRCPTS_INIT(&qss_ta->qssta_rcpts); if (prcpt_idx_high != NULL) *prcpt_idx_high = rcpt_idx_high; return SM_SUCCESS; } /* ** QSS_TA_CLR -- clear out a QMGR/SMTPS transaction ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** rmflags -- various flags (for qss_rcpt_free()) ** ** Returns: ** SM_SUCCESS ** ** Last code review: 2005-04-01 18:12:44 ** Last code change: */ static sm_ret_T qss_ta_clr(qss_ta_P qss_ta, uint rmflags) { SM_IS_QS_TA(qss_ta); /* ** If all data inside qss_ta is allocated via rpool, ** we could simply free the rpool. ** Cleanup functions (resources) could be registered with ** the rpool... future enhancement?? */ qss_mail_free(qss_ta); qss_rcpts_free(qss_ta, rmflags, NULL); if (qss_ta->qssta_rpool != NULL) { sm_rpool_delete(qss_ta->qssta_rpool); qss_ta->qssta_rpool = NULL; } SM_CSTR_FREE(qss_ta->qssta_cdb_id); sm_hdrmodl_free(&qss_ta->qssta_hdrmodhd); /* clear transaction */ sm_memzero(qss_ta, sizeof(*qss_ta)); QSRCPTS_INIT(&qss_ta->qssta_rcpts); return SM_SUCCESS; } #if 0 ///* //** QSS_TA_ABORT -- abort a QMGR/SMTPS transaction //** //** Parameters: //** qss_ta -- QMGR/SMTPS transaction //** //** Returns: //** usual sm_error code //** //** Comments: //** this is currently not really used. //*/ // //sm_ret_T //qss_ta_abort(qss_ta_P qss_ta) //{ // SM_IS_QS_TA(qss_ta); // //#if 0 // /* check existing transaction */ // if (qss_ta->qssta_state != STA_NONE) // { // /* XXX ... */ // /* abort all performed actions */ // // } //#endif // // /* XXX should this be dependent on the state? */ // qss_ta_clr(qss_ta, QSS_RMFIQDB); // return SM_SUCCESS; //} #endif /* 0 */ /* ** QSS_TA_FREE -- free a QMGR/SMTPS transaction ** ** Parameters: ** qss_ta -- QMGR/SMTPS transaction ** unlock -- transaction is locked: unlock it before freeing ** flag -- from where is qss_ta removed? ** rmflags -- various flags (qmgr/qmgr.h: QSS_) ** ** Returns: ** SM_SUCCESS except for (un)lock errors ** ** Last code review: 2005-04-01 18:17:03 ** Last code change: 2005-04-01 18:06:05 */ sm_ret_T qss_ta_free(qss_ta_P qss_ta, bool unlock, uint flag, uint rmflags) { int r; uint flags; if (NULL == qss_ta) { if (unlock) QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP, "sev=ERROR, func=qss_ta_free, status=qss_ta_is_NULL_but_unlock_is_set\n")); SM_ASSERT(!unlock); return SM_SUCCESS; } SM_IS_QS_TA(qss_ta); /* ** Get a copy of flags before unlocking qss_ta. ** Alternatively unlock qss_ta after the check down below. */ flags = qss_ta->qssta_flags; QM_LEV_DPRINTFC(QDC_Q2S, 1, (QM_DEBFP, "sev=DBG, func=qss_ta_free, qss_ta=%p, flags=%#x, flag=%#x, unlock=%d, free=%d\n", qss_ta, flags, flag, unlock, QSS_TA_OK_FREE(flags) )); if (unlock) { r = pthread_mutex_unlock(&qss_ta->qssta_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP, "sev=ERROR, func=qss_ta_free, unlock=%d\n", r)); SM_ASSERT(r == 0); return sm_error_perm(SM_EM_Q_Q2SS, r); } } /* more conditions when to remove qss_ta? */ if (SM_IS_FLAG(flag, QSS_TA_FREE_ALWAYS) || QSS_TA_OK_FREE(flags)) { (void) pthread_mutex_destroy(&qss_ta->qssta_mutex); (void) qss_ta_clr(qss_ta, rmflags); #if QS_TA_CHECK qss_ta->sm_magic = SM_MAGIC_NULL; #endif sm_free_size(qss_ta, sizeof(*qss_ta)); } return SM_SUCCESS; } /* ** QSS_TA_NEW -- create new QMGR/SMTPS transaction ** Note: qssta_cdb_id is not allocated here, it's done when an RCB is read ** ** Parameters: ** pqss_ta -- QMGR/SMTPS transaction (output) ** qss_ctx -- QMGR/SMTPS context ** rpool -- rpool: only for data in qss_ta, not for qss_ta itself ** ** Returns: ** usual sm_error code ** ** Last code review: ** Last code change: */ sm_ret_T qss_ta_new(qss_ta_P *pqss_ta, qss_ctx_P qss_ctx, sm_rpool_P rpool) { int r; sm_ret_T ret; qss_ta_P qss_ta; SM_REQUIRE(pqss_ta != NULL); SM_IS_QSS_CTX(qss_ctx); qss_ta = (qss_ta_P) sm_zalloc(sizeof(*qss_ta)); if (NULL == qss_ta) return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM); r = pthread_mutex_init(&qss_ta->qssta_mutex, SM_PTHREAD_MUTEXATTR); if (r != 0) { ret = sm_error_perm(SM_EM_Q_Q2SS, r); goto error; } qss_ta->qssta_rpool = rpool; qss_ta->qssta_ssctx = qss_ctx; QSRCPTS_INIT(&qss_ta->qssta_rcpts); #if 0 qss_ta->qssta_sess = sess; #endif #if QS_TA_CHECK qss_ta->sm_magic = SM_QSS_TA_MAGIC; #endif *pqss_ta = qss_ta; return SM_SUCCESS; error: if (*pqss_ta != NULL) sm_free_size(*pqss_ta, sizeof(**pqss_ta)); *pqss_ta = NULL; return ret; } /* ** QSS_SESS_FREE -- free QMGR/SMTPS session ** ** Parameters: ** qss_sess -- QMGR/SMTPS session ** ** Returns: ** usual sm_error code ** ** Last code review: 2005-04-10 05:28:30 ** Last code change: 2005-04-10 05:28:26 */ sm_ret_T qss_sess_free(qss_sess_P qss_sess) { if (NULL == qss_sess) return SM_SUCCESS; SM_IS_QS_SE(qss_sess); #if 0 /* should this free the ta inside? we don't know the current ta! */ qss_ta = qss_sess->sss_ta; if (qss_ta != NULL) qss_ta_abort(qss_ta); #endif #if QS_SE_CHECK qss_sess->sm_magic = SM_MAGIC_NULL; #endif sm_free_size(qss_sess, sizeof(*qss_sess)); return SM_SUCCESS; } /* ** QSS_SESS_NEW -- create new QMGR/SMTPS session ** ** Parameters: ** pqss_sess -- (pointer to) QMGR/SMTPS session (output) ** rpool -- rpool: for data in qss_sess, not for qss_sess itself ** ** Returns: ** usual sm_error code ** ** Last code review: ** Last code change: */ /* pass it an rpool or let it create one?? */ sm_ret_T qss_sess_new(qss_sess_P *pqss_sess, sm_rpool_P rpool) { SM_REQUIRE(pqss_sess != NULL); *pqss_sess = (qss_sess_P) sm_zalloc(sizeof(**pqss_sess)); if (*pqss_sess == NULL) goto error; (*pqss_sess)->qsess_rpool = rpool; #if QS_SE_CHECK (*pqss_sess)->sm_magic = SM_QSS_SE_MAGIC; #endif return SM_SUCCESS; error: /* complain?? */ /* qss_sess_free(*pqss_sess); not needed now */ return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM); }