/* * 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: qss_ctaid.c,v 1.81 2006/11/27 03:24:52 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/io.h" #include "sm/rcb.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "sm/reccom.h" #include "qmgr.h" #include "log.h" /* ** QM_SS_CTAID -- close SMTPS transaction: write IBDB record ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** qss_ta -- transaction ** ** Returns: ** <0: usual sm_error code: only for severe problems! ** >=0: acceptance/rejection code ** ** Side Effects: ** writes transaction to IBDB ** adds transaction to AQ; does not handle error properly: ** see comments below. ** ** Last code review: 2003-10-22 20:18:56, see comments below! */ #define SM_IS_IBDB_DISK_FULL(ret) \ (sm_error_value(ret) == ENOSPC \ || sm_error_value(ret) == EFBIG \ || sm_error_value(ret) == EDQUOT) sm_ret_T qm_ss_ctaid(qss_ctx_P qss_ctx, qss_ta_P qss_ta) { sm_ret_T ret; ibdb_ta_T ibdb_ta; const char *func; SM_IS_QSS_CTX(qss_ctx); SM_IS_QS_TA(qss_ta); ret = SM_SUCCESS; /* Do some checks here: is it ok to accept this mail? */ /* ** assign data to ibdb_ta just for ibdb_ta_status, ibdb_ta is not ** used at all afterwards. */ ibdb_ta.ibt_ta_id = qss_ta->qssta_id; ibdb_ta.ibt_mail_pa = qss_ta->qssta_mail->qsm_pa; ibdb_ta.ibt_cdb_id = qss_ta->qssta_cdb_id; ibdb_ta.ibt_nrcpts = qss_ta->qssta_rcpts_tot; ret = ibdb_ta_status(qss_ctx->qss_qmgr_ctx->qmgr_ibdb, &ibdb_ta, IBDB_TA_NEW, IBDB_FL_NONE, 0, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { func = "ibdb_ta_status"; goto err_write; } ret = ibdb_hdrmodl_wr(qss_ctx->qss_qmgr_ctx->qmgr_ibdb, qss_ta->qssta_hdrmodhd, qss_ta->qssta_id, IBDB_FL_NONE, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { func = "ibdb_hdrmodl_wr"; goto err_write; } QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_IBDB); /* copy transaction into AQ immediately */ ret = aq_env_add_iqdb(qss_ctx->qss_qmgr_ctx->qmgr_aq, qss_ta, qss_ctx->qss_qmgr_ctx); /* ** XXX Error handling is far from optimal! ** Current solution: discard TA (see below). ** Alternatives: ** If the data couldn't be transferred to AQ because it is full ** - try DEFEDB ** - throttle SMTPS and try to add the TA later on to AQ. ** (Note: the transaction is still in IQDB!) ** If none of that works: reject the mail (we can't deal with it). */ if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP, "sev=ERROR, func=qm_ss_ctaid, aq_env_add_iqdb=%r\n", ret)); sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 0, "sev=ERROR, func=qm_ss_ctaid, aq_env_add_iqdb=%m", ret); if (sm_error_value(ret) == SM_E_FULL || sm_error_value(ret) == ENOMEM) (void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_AQ_I, THR_LOCK_UNLOCK); /* ** Simply discard the TA for now, see above. ** This will be done in the caller because we return ** a temporary error, except for ibdb changes. */ ret = ibdb_ta_status(qss_ctx->qss_qmgr_ctx->qmgr_ibdb, &ibdb_ta, IBDB_TA_CANCEL, IBDB_FL_NONE, 0, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 0, "sev=ERROR, func=qm_ss_ctaid, ibdb_ta_status=%m", ret); } ret = SMTP_R_TEMP; goto error; } else { /* currently not really required because of the goto above */ /* ** No error; why don't we remove the entry from IQDB now? ** See qda_upd_iqdb(), currently it uses qss_ta to update ** IBDB; maybe this can be changed to use data from AQ? */ (void) qss_control(qss_ctx, QMGR_THROTTLE, ret, QMGR_RFL_AQ_I, THR_LOCK_UNLOCK); QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_AQ); } return SMTP_R_OK; err_write: /* ** Disk full? Need to clean up IBDB or otherwise "wait" for free space. */ if (SM_IS_IBDB_DISK_FULL(ret)) { (void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_IBD_I, THR_LOCK_UNLOCK); sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ALERT, 0, "sev=ALERT, func=qm_ss_ctaid, %s=%m, status=disk_space_exceeded" , func, ret); } else if (sm_error_value(ret) == ENOMEM) { (void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_MEM_I, THR_LOCK_UNLOCK); sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ALERT, 0, "sev=ALERT, func=qm_ss_ctaid, %s=%m, status=ENOMEM" , func, ret); } else { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 0, "sev=ERROR, func=qm_ss_ctaid, %s=%m" , func, ret); } /* change the error code to a temporary rejection XXX */ ret = SMTP_R_TEMP; error: sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 1, "sev=ERROR, func=qm_ss_ctaid, ret=%m", ret); return ret; } /* ** QM_2SS_CTAID -- Compose reply to "close TA", put data into RCB entry ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** qss_ta -- transaction ** rcbe -- RCB entry (must be open for encoding) ** status -- status code ** ** Returns: ** usual sm_error code ** ** Called by: ** q_ibdb_commit() (group commit) ** qm_fr_ss_react() (if something goes wrong when CTAID is received ** from SMTPS) ** ** Last code review: 2003-10-23 00:57:57 */ sm_ret_T qm_2ss_ctaid(qss_ctx_P qss_ctx, qss_ta_P qss_ta, sm_rcbe_P rcbe, int status) { sm_rcb_P rcb; sm_ret_T ret; SM_IS_QSS_CTX(qss_ctx); SM_IS_QS_TA(qss_ta); rcb = &rcbe->rcbe_rcb; ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_Q2S_ID, qss_ctx->qss_id, SM_RCBV_BUF, RT_Q2S_TAID, qss_ta->qssta_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_Q2S_STAT, (uint32_t) status, SM_RCBV_END); /* can be simplified [return ret;] if debug output below is removed */ if (sm_is_err(ret)) goto error; QM_LEV_DPRINTTC(QDC_Q2S, 1, (QM_DEBFP, "func=qm_2ss_ctaid, cdb-id=%C, stat=%d\n", qss_ta->qssta_cdb_id, status), qss_ctx->qss_qmgr_ctx->qmgr_ev_ctx->evthr_c_time); return ret; error: return ret; }