/* * 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: qm_to_ar.c,v 1.69 2007/06/04 04:58:39 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 "sm/smar.h" #include "sm/da.h" #include "qmgr.h" /* ** QM2AR -- Send recipient data to AR, i.e., put it into RCB ** ** Parameters: ** qmgr_ctx -- QMGR context ** aq_rcpt -- aq_rcpt ** rcbe -- RCB entry ** ** Returns: ** usual sm_error code (from sm_rcb_putv()) ** ** Side Effects: fills in rcb ** if error: rcb might have partial content ** ** Locking: aq_ctx (aq_rcpt) must be locked by caller (to read aq_rcpt) ** ** Last code review: 2003-10-17 17:22:58 ** Last code change: */ static sm_ret_T qm2ar(qmgr_ctx_P qmgr_ctx, aq_rcpt_P aq_rcpt, sm_rcbe_P rcbe) { sm_rcb_P rcb; sm_ret_T ret; rcpt_id_T rcpt_id; rcb = &rcbe->rcbe_rcb; sm_snprintf(rcpt_id, sizeof(rcpt_id), SMTP_RCPTID_FORMAT, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_idx); /* send rcpt_idx and ta_id separately?? */ ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_BUF, RT_Q2R_RCPT_ID, rcpt_id, SMTP_RCPTID_SIZE, SM_RCBV_STR, RT_Q2R_RCPT_PA, aq_rcpt->aqr_pa, SM_RCBV_INT, RT_Q2R_FLAGS, (AQR_IS_FLAG(aq_rcpt, AQR_FL_ALIAS) ? SMARRQ_FL_NONE : SMARRQ_FL_ALIAS|SMARRQ_FL_OWNER|SMARRQ_FL_VERP)| #if MTA_USE_TLS (QCNF_IS_FLAG(qmgr_ctx, QCNF_FL_SC_LKP_RCPT_CONF) ? SMARRQ_FL_CONF : SMARRQ_FL_NONE)| #endif SMARRQ_FL_CHK_LU, #if MTA_USE_TLS SM_RCBV_INT, (qmgr_ctx->qmgr_cnf.q_cnf_sc_lfl != SMMAP_LFL_NONE) ? RT_R2Q_RCPT_CNF_LKP_FL : RT_NOSEND, qmgr_ctx->qmgr_cnf.q_cnf_sc_lfl, #endif SM_RCBV_INT, RT_Q2R_TIMEOUT, (aq_rcpt->aqr_tries == 0 || aq_rcpt->aqr_err_st != DA_AR_ERR || aq_rcpt->aqr_status != SMTP_AR_TEMP) ? 0 : (aq_rcpt->aqr_tries < 5) ? (qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar / 6 - aq_rcpt->aqr_tries) : (qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar / 2) , SM_RCBV_END); if (sm_is_err(ret)) goto error; return ret; error: /* XXX leave rcb in a consistent state? */ QM_LEV_DPRINTFC(QDC_Q2A, 1, (QM_DEBFP, "sev=ERROR, func=qr2ar, ret=%r, rcpt_id=%s\n", ret, rcpt_id)); return ret; } /* ** QMGR_RCPT2AR -- send a recipient to SMAR; append to wait queue ** ** Parameters: ** qmgr_ctx -- QMGR context ** aq_rcpt -- aq_rcpt ** locktype -- kind of locking ** ** Returns: ** usual sm_error code; SM_E_NO_AR, ENOMEM, SM_E_RANGE, ** SM_E_OVFLW_NS, etc ** ** Side Effects: always add rcpt to wait queue ** if ok: increase aq_ta->aqt_rcpts_ar ** ** Locking: ** aq_ctx (aq_rcpt/aq_ta) can be locked here (locktype), ** otherwise it must be locked by caller. ** ** ** Note: Caller has to enable WR for qar_task, e.g., ** evthr_en_wr(qmgr_ctx->qmgr_ar_tsk) ** ** Last code review: ** Last code change: 2006-03-30 02:44:09 */ sm_ret_T qmgr_rcpt2ar(qmgr_ctx_P qmgr_ctx, aq_rcpt_P aq_rcpt, thr_lock_T locktype) { sm_ret_T ret; int r; sm_evthr_task_P qar_tsk; qar_ctx_P qar_ctx; sm_rcbe_P rcbe; aq_ta_P aq_ta; aq_ctx_P aq_ctx; time_T exp; SM_IS_QMGR_CTX(qmgr_ctx); SM_IS_AQ_RCPT(aq_rcpt); qar_ctx = qmgr_ctx->qmgr_ar_ctx; SM_IS_QAR_CTX(qar_ctx); qar_tsk = qmgr_ctx->qmgr_ar_tsk; aq_ctx = qmgr_ctx->qmgr_aq; SM_IS_AQ(aq_ctx); rcbe = NULL; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&aq_ctx->aq_mutex); SM_LOCK_OK(r); if (r != 0) return sm_error_perm(SM_EM_AQ, r); } /* always add recipient to wait queue for cleanup to work */ (void) aq_waitq_add(qmgr_ctx->qmgr_aq, aq_rcpt, 0, AQWQ_AR, false); exp = aq_waitq_first_tmo(qmgr_ctx->qmgr_aq, AQWQ_AR, false); if (exp > 0) (void) qmgr_set_aq_cleanup(qmgr_ctx->qmgr_cleanup_ctx, exp, true); else QM_LEV_DPRINTFC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, exp=%7ld\n", (long) exp)); /* there might be no task... */ if (NULL == qar_tsk) { /* ** XXX Need to ask MCP to start SMAR? ** Not really, MCP notices when a component fails and ** starts it itself (unless it fails too often). ** ** XXX Need to (un)throttle SMTPS, probably somewhere in ** qm_fr_ss.c; use qss_control(). */ QM_LEV_DPRINTTC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, qar_tsk=NULL\n"), qmgr_ctx->qmgr_ev_ctx->evthr_c_time); ret = sm_error_temp(SM_EM_Q_Q2AR, SM_E_NO_AR); QMGR_SET_SFLAG(qmgr_ctx, QMGR_SFL_AR); goto error; } if (QMGR_IS_SFLAG(qmgr_ctx, QMGR_SFL_AR)) QMGR_CLR_SFLAG(qmgr_ctx, QMGR_SFL_AR); aq_ta = aq_rcpt->aqr_ss_ta; SM_IS_AQ_TA(aq_ta); ret = sm_rcbe_new_enc(&rcbe, -1, 0); if (sm_is_err(ret)) goto error; ret = qm2ar(qmgr_ctx, aq_rcpt, rcbe); if (sm_is_err(ret)) goto error; ret = sm_rcbcom_endrep(&qar_ctx->qar_com, qar_tsk, true, &rcbe); if (sm_is_err(ret)) goto error; AQR_SET_FLAG(aq_rcpt, AQR_FL_SENT2AR); if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC|AQR_FL_IS_DBNC)) ++aq_ta->aqt_rcpts_ar; QM_LEV_DPRINTTC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, status=ok, aqt_rcpts_ar=%u, rcpt_id=%s, rcpt_pa=%S\n" , aq_ta->aqt_rcpts_ar, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_pa), qmgr_ctx->qmgr_ev_ctx->evthr_c_time); if (thr_unl_no_err(locktype)) { r = pthread_mutex_unlock(&aq_ctx->aq_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_AQ, r); } return SM_SUCCESS; error: if (thr_unl_if_err(locktype)) { r = pthread_mutex_unlock(&aq_ctx->aq_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_AQ, r); } if (rcbe != NULL) sm_rcbe_free(rcbe); QM_LEV_DPRINTFC(QDC_Q2A, 1, (QM_DEBFP, "sev=ERROR, func=qmgr_rcpt2ar, ret=%r\n", ret)); return ret; } /* ** QMGR2AR -- QMGR - AR interface ** ** Parameters: ** tsk -- evthr task ** ** Returns: ** usual sm_error code */ sm_ret_T qm_to_ar(sm_evthr_task_P tsk) { qar_ctx_P qar_ctx; SM_IS_EVTHR_TSK(tsk); qar_ctx = (qar_ctx_P) tsk->evthr_t_actx; SM_IS_QAR_CTX(qar_ctx); QM_LEV_DPRINTFC(QDC_Q2A, 6, (QM_DEBFP, "func=qm_to_ar, tsk=%p\n", tsk)); return sm_rcbcom2mod(tsk, &qar_ctx->qar_com); }