/* * 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_fr_ss.c,v 1.206 2007/08/18 16:53:00 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/ssocc.h" #include "sm/reccom.h" #include "qmgr.h" #include "qm_throttle.h" #include "log.h" /* ** QM_SS_COMP_CONTROL -- (un)throttle SMTPS based on a component as needed ** (currently the only component checked is SMAR) ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** unthrottle -- unthrottle only ** ** Returns: ** usual sm_error code ** ** Called by: qm_fr_ss_react() ** Calls: qss_control() ** ** Locking: ** qss_ctx should be "locked", i.e., under control of the caller. ** XXX qmgr_ctx should be locked here. */ static sm_ret_T qm_ss_comp_control(qss_ctx_P qss_ctx, bool unthrottle) { sm_ret_T ret; qmgr_ctx_P qmgr_ctx; SM_IS_QSS_CTX(qss_ctx); qmgr_ctx = qss_ctx->qss_qmgr_ctx; SM_IS_QMGR_CTX(qmgr_ctx); ret = SM_SUCCESS; /* check whether some component (SMAR) is MIA... */ QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "func=qm_ss_comp_control, sflags=%#x, usage[%d]=%d\n", qmgr_ctx->qmgr_sflags, QMGR_RFL_AR_I, qmgr_ctx->qmgr_usage[QMGR_RFL_AR_I])); if (!unthrottle && QMGR_IS_SFLAG(qmgr_ctx, QMGR_SFL_AR) && qmgr_ctx->qmgr_usage[QMGR_RFL_AR_I] != QMGR_R_USE_FULL) { qmgr_ctx->qmgr_usage[QMGR_RFL_AR_I] = QMGR_R_USE_FULL; ret = qss_control(qss_ctx, QMGR_THROTTLE, QMGR_R_USE_FULL, QMGR_RFL_AR_I, THR_LOCK_UNLOCK); return ret; } /* check whether component (SMAR) is back */ if ((!QMGR_IS_SFLAG(qmgr_ctx, QMGR_SFL_AR) || qmgr_ctx->qmgr_ar_tsk != NULL) && qmgr_ctx->qmgr_usage[QMGR_RFL_AR_I] != QMGR_R_USE_NONE) { QMGR_CLR_SFLAG(qmgr_ctx, QMGR_SFL_AR); qmgr_ctx->qmgr_usage[QMGR_RFL_AR_I] = QMGR_R_USE_NONE; ret = qss_control(qss_ctx, QMGR_UN_THROTTLE, QMGR_R_USE_NONE, QMGR_RFL_AR_I, THR_LOCK_UNLOCK); } return ret; } /* ** QM_SS_FIND -- Find SMTPS id ** ** Parameters: ** qmgr_ctx -- QMGR context ** qss_ctx -- QMGR/SMTPS context ** ** Returns: ** >=0: index ** <0: usual sm_error code ** ** Last code review: */ static sm_ret_T qm_ss_find(qmgr_ctx_P qmgr_ctx, qss_ctx_P qss_ctx) { int i, r; uint8_t j; sm_ret_T ret; qss_ctx_P qss2_ctx; r = pthread_mutex_lock(&qmgr_ctx->qmgr_mutex); SM_LOCK_OK(r); if (r != 0) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_CRIT, 4, "sev=CRIT, func=qm_ss_find, lock=%d", r); return sm_error_temp(SM_EM_Q_SS2Q, r); } ret = sm_error_perm(SM_EM_Q_SS2Q, SM_E_NOTFOUND); /* search for ss ctx */ for (j = 1, i = 0; i < QM_N_SS_GLI(qmgr_ctx); i++, j *= 2) { if ((qmgr_ctx->qmgr_ss_li.qm_gli_used & j) != 0) { qss2_ctx = qmgr_li_ss(qmgr_ctx, i); if (qss2_ctx != qss_ctx && qss2_ctx->qss_id == qss_ctx->qss_id) { ret = i; break; } } } r = pthread_mutex_unlock(&qmgr_ctx->qmgr_mutex); SM_ASSERT(0 == r); return ret; } /* ** QM_SS_FINDFREEID -- Find free SMTPS id ** ** Parameters: ** qmgr_ctx -- QMGR context ** qss_ctx -- QMGR/SMTPS context ** ** Returns: ** usual sm_error code ** ** Last code review: */ static sm_ret_T qm_ss_findfreeid(qmgr_ctx_P qmgr_ctx, qss_ctx_P qss_ctx) { int i, r, smtps_id; uint8_t j; sm_ret_T ret; qss_ctx_P qss2_ctx; r = pthread_mutex_lock(&qmgr_ctx->qmgr_mutex); SM_LOCK_OK(r); if (r != 0) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_CRIT, 4, "sev=CRIT, func=qm_ss_findfreeid, lock=%d", r); return sm_error_temp(SM_EM_Q_SS2Q, r); } ret = SM_SUCCESS; smtps_id = 0; /* * find a free id; current "hack": biggest id +1 * note: this needs to be done better because the id is only * allowed to SMTPS_MAX_SMTPS_ID (255) * if smtps always ask qmgr for an id, then we could simply use the * index in this array (could we?) * we could use a bitfield to indicate which ids are used. */ for (j = 1, i = 0; i < QM_N_SS_GLI(qmgr_ctx); i++, j *= 2) { if ((qmgr_ctx->qmgr_ss_li.qm_gli_used & j) != 0) { qss2_ctx = qmgr_li_ss(qmgr_ctx, i); if (qss2_ctx->qss_id >= smtps_id) smtps_id = qss2_ctx->qss_id + 1; } } r = pthread_mutex_unlock(&qmgr_ctx->qmgr_mutex); SM_ASSERT(0 == r); qss_ctx->qss_id = smtps_id; return ret; } /* ** QM_RD_HDRMODS -- read header modification list ** ** Parameters: ** qmgr_ctx -- QMGR context ** qss_ta -- QMGR/SMTPS transaction ** rcb -- RCB from which to read data ** ** Returns: ** usual sm_error code */ static sm_ret_T qm_rd_hdrmods(qmgr_ctx_P qmgr_ctx, qss_ta_P qss_ta, sm_rcb_P rcb) { sm_hdrmod_P sm_hdrmod; uint32_t l, rt, n1, n2; sm_ret_T ret; ret = SM_SUCCESS; while (!SM_RCB_ISEOB(rcb)) { ret = sm_hdrmod_new(&qss_ta->qssta_hdrmodhd, true, &sm_hdrmod); if (sm_is_err(ret)) return ret; ret = sm_rcb_get4uint32(rcb, &l, &rt, &n1, &n2); if (sm_is_err(ret) || rt != RT_S2Q_HM_T_P || l != 8) { return sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); } sm_hdrmod->sm_hm_type = n1; sm_hdrmod->sm_hm_pos = n2; if (SM_RCB_ISEOB(rcb)) break; ret = sm_rcb_peek2uint32(rcb, &l, &rt); if (sm_is_err(ret)) { sm_hdrmod_rm_last(&qss_ta->qssta_hdrmodhd); return ret; } if (RT_S2Q_HM_T_P == rt && 8 == l) continue; if (rt != RT_S2Q_HM_HDR) break; ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2Q_HM_HDR || l > SM_MAXHDRLEN) { return sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); } ret = sm_rcb_getncstr(rcb, &sm_hdrmod->sm_hm_hdr, l); if (sm_is_err(ret)) return ret; } return ret; } /* ** SM_FR_SS_REACT -- Decode data received from SMTPS ** ** Parameters: ** qss_ctx -- QMGR/SMTPS context ** tsk -- evthr task ** ** Returns: ** usual sm_error code ** ** Locking: ** XXX who locks qss_ctx? ** ** Called by: qm_fr_ss */ static sm_ret_T qm_fr_ss_react(qss_ctx_P qss_ctx, sm_evthr_task_P tsk #if QM_TIMING , uint32_t *pfrt #endif ) { uint32_t v, l, rt, tl; off_t off; sm_ret_T ret, ret2, rv; qss_status_T oldstatus; bool inwaitq; int r; uint32_t fct_state; sm_rcb_P rcb; sm_rcbe_P rcbe; qmgr_ctx_P qmgr_ctx; #define FST_QSS_TA_LOCKED 0x0001 /* qss_ta is locked */ SM_IS_QSS_CTX(qss_ctx); ret = rv = SM_SUCCESS; inwaitq = false; fct_state = 0; rcbe = NULL; #if QM_TIMING *pfrt = 0; #endif /* ** Generic component control XXX Is it ok to do this here? ** Not really: this will at most throttle SMTPS and then ** this function will not be called again. ** There needs to be some other place to reactivate SMTPS! ** XXX who does it? */ ret = qm_ss_comp_control(qss_ctx, false); if (sm_is_err(ret)) QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qm_ss_comp_control=%r\n", ret)); /* What to do on error? */ /* ** XXX Todo: add error codes before each goto err* ** this is necessary to figure out what error happened ** (at least for debugging...) ** it is also necessary in some cases to send a reply ** back to SMTPS, e.g., if QMGR can't allocate memory ** then SMTPS should be told about it... ** ** In general the QMGR doesn't reply to data it can't decode. ** We assume that the sender is fubared and a reply won't help. ** Let it figure out itself that something went wrong. ** However, we should log an error (logging is completely missing). */ /* Decode rcb */ rcb = qss_ctx->qss_com.rcbcom_rdrcb; ret = sm_rcb_open_dec(rcb); if (sm_is_err(ret)) { rv = ret; goto error; } qmgr_ctx = qss_ctx->qss_qmgr_ctx; SM_IS_QMGR_CTX(qmgr_ctx); /* Total length of record */ ret = sm_rcb_getuint32(rcb, &tl); if (sm_is_err(ret) || tl > QM_SS_MAX_REC_LEN || tl > sm_rcb_getlen(rcb)) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_RCB2LONG); goto err2; } /* Decode data, act accordingly... */ /* Protocol header: version */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret)) { rv = ret; goto err2; } if (l != 4 || rt != RT_PROT_VER || v != PROT_VER_RT) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_V_MISM); goto err2; } /* queuemanager.func.tex: \subsubsection{QMGR - SMTPS API} \label{func:QMGR:QMGRSMTPSAPI} Should we change the records such that it simpler to distinguish the various types? On one side having a common "header" makes it simpler to avoid duplicate code, however, having distinct headers allow for "early" selection of the right case instead of having a deeply nested if else structure. Moreover, if we "know" the data type, we don't need to do: get2int(); check record type; getint() or getn(). [read] qss_ctx_open(IN smtps-info, OUT status): RT_S2Q_NID: int smtps-id [ok] XXX should this also transfer an initial status? [partially implemented] qmgr_session_open(IN connection-info, IN session-id, OUT status): RT_S2Q_ID: int smtps-id [ok] RT_S2Q_NSEID: str session-id [ok] RT_S2Q_CLTIPV4/RT_S2Q_CLTIPV6: str client IP address [-] [not yet implemented] qmgr_session_status(IN connection-info, IN session-id, IN session-status, OUT status): RT_S2Q_ID: int smtps-id RT_S2Q_SEID: str session-id XXX: AUTH, STARTTLS, others? [read] qmgr_1trans(IN session-id, IN sender-env-info, IN trans-id, ..., OUT status): RT_S2Q_ID: int smtps-id RT_S2Q_1TAID: str transaction id RT_S2Q_MAIL: str mail from RT_S2Q_MAIL_FL: int mail flags [optional] RT_S2Q_RCPT_IDX: int rcpt idx (?) RT_S2Q_RCPT: str rcpt to the last two values can be repeated to transmit a list. RT_S2Q_CDBID: str cdb-id [read] qmgr_session_close(IN session-id, OUT status): RT_S2Q_ID: int smtps-id RT_S2Q_CSEID: str session-id [read] qss_ctx_close(IN smtps-info, OUT status): RT_S2Q_CID: int smtps-id */ /* SMTPS (new/existing/close) ID */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } if (RT_S2Q_NID == rt || RT_S2Q_QID == rt) { bool query_id; /* New SMTPS */ query_id = RT_S2Q_QID == rt; if (qss_ctx->qss_status != QSS_ST_NONE || qss_ctx->qss_id != QSS_ID_NONE) { /* XXX Internal error? Stop task? SMTPS id exists */ sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERROR, 4, "sev=ERROR, func=qm_fr_ss_react, rt=%#x, qss_status=%d, qss_id=%d, status=unexpected_state" , rt, qss_ctx->qss_status, qss_ctx->qss_id); rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } if (RT_S2Q_QID == rt && (int) v < 0) { ret = qm_ss_findfreeid(qmgr_ctx, qss_ctx); if (sm_is_err(ret)) { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERROR, 4, "sev=ERROR, func=qm_fr_ss_react, rt=RT_S2Q_QID, qss_status=%d, qss_id=%d, qm_ss_findfreeid=%m" , qss_ctx->qss_status, qss_ctx->qss_id, ret); rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_OVFLW_SC); /* XXX use different error! */ goto err2; } QM_LEV_DPRINTFC(QDC_S2Q, 2, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, nid=%d\n", (int) v, qss_ctx->qss_id, ret)); } else qss_ctx->qss_id = v; qss_ctx->qss_cur_session = 0; #if QMGR_STATS qss_ctx->qss_max_session = 0; #endif ret = qm_ss_find(qmgr_ctx, qss_ctx); if (ret >= 0) { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERROR, 4, "sev=ERROR, func=qm_fr_ss_react, rt=RT_S2Q_NID, qss_status=%d, qss_id=%d, qm_ss_find=%m" , qss_ctx->qss_status, qss_ctx->qss_id, ret); /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } qss_ctx->qss_status = QSS_ST_START; if (SM_RCB_ISEOB(rcb)) goto done; ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2Q_MAXTHRDS) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } qss_ctx->qss_max_thrs = v; qss_ctx->qss_max_cur_thrs = v; if (!SM_RCB_ISEOB(rcb)) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } /* ** Send a reply to let client know that the connection ** has been accepted. */ ret = qm_rcbcom_prerep(qmgr_ctx, &qss_ctx->qss_com, tsk, &rcbe); if (sm_is_err(ret)) goto err2; inwaitq = true; /* XXX get real initial id (from IBDB?) */ ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, query_id ? RT_Q2S_NID : RT_Q2S_ID, qss_ctx->qss_id, SM_RCBV_INT64, RT_Q2S_IIDC, qmgr_ctx->qmgr_idc + 1, SM_RCBV_END); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, nid=%d, sm_pcb_putv=%r\n", qss_ctx->qss_id, v, ret)); goto err2; } ret = sm_rcbcom_endrep(&qss_ctx->qss_com, tsk, false, &rcbe); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, nid=%d, sm_rcbcom_endrep=%r\n", qss_ctx->qss_id, v, ret)); goto err2; } goto done; } else if (RT_S2Q_ID == rt) { /* SMTPS id just for identification */ if (qss_ctx->qss_status == QSS_ST_NONE || qss_ctx->qss_id != v) { /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } } else if (RT_S2Q_CID == rt) { /* SMTPS shuts down */ if (qss_ctx->qss_status == QSS_ST_NONE || qss_ctx->qss_id != v) { /* Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* use different error?? */ goto err2; } /* check for EOB? not really: we shut down anyway */ qss_ctx->qss_status = QSS_ST_SH_DOWN; (void) sm_rcb_close_dec(qss_ctx->qss_com.rcbcom_rdrcb); /* ** We assume that all no open sessions/transactions exist, ** i.e., SMTPS properly terminated them before sending this ** message. */ /* Terminate (delete) this task, qss_ctx is cleaned in caller */ return EVTHR_DEL; } else goto err2; /* rt == RT_S2Q_ID is the only case in which we continue here */ oldstatus = qss_ctx->qss_status; /* what's next? */ ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret)) { rv = ret; goto err2; } #if QM_TIMING *pfrt = rt; #endif if (RT_S2Q_STAT == rt) { if (l != 4) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } /* status */ ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } qss_ctx->qss_status = v; QM_LEV_DPRINTFC(QDC_S2Q, 1, (QM_DEBFP, "func=qm_fr_ss_react, id=%d, stat=%d\n", qss_ctx->qss_id, qss_ctx->qss_status)); if (QSS_ST_NONE == oldstatus) { if (!SM_RCB_ISEOB(rcb)) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } goto done; } /* XXX what to do? just be happy? */ goto done; } else if (RT_S2Q_NSEID == rt) { qss_sess_P qss_sess; qss_sess = NULL; if (l != SMTP_STID_SIZE) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } /* allocate new session? rpool??? */ ret = qss_sess_new(&qss_sess, NULL); if (sm_is_err(ret)) goto err2; qss_sess->qsses_st_time = evthr_time(qmgr_ctx->qmgr_ev_ctx); /* session id */ ret = sm_rcb_getn(rcb, (uchar *) qss_sess->qsses_id, l); if (sm_is_err(ret)) { rv = ret; goto errnseid; } ret = sm_getsmtpsid(qss_sess->qsses_id); if (ret != qss_ctx->qss_id) { /* SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* use different error?? */ goto errnseid; } QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "func=qm_fr_ss_react, id=%d, nsess-id=%s\n", qss_ctx->qss_id, qss_sess->qsses_id)); /* XXX client IP address; must match structure later on! */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2Q_CLTIPV4) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errnseid; } qss_sess->qsess_client.s_addr = v; QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "func=qm_fr_ss_react, id=%d, nsess-id=%s, IP=%s\n", qss_ctx->qss_id, qss_sess->qsses_id, inet_ntoa(qss_sess->qsess_client))); ret = qm_rcbcom_prerep(qmgr_ctx, &qss_ctx->qss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errnseid; inwaitq = true; ret2 = qm_ss_nseid(qss_ctx, qss_sess); QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "func=qm_fr_ss_react, id=%d, nsess-id=%s, qm_ss_nseid=%d\n", qss_ctx->qss_id, qss_sess->qsses_id, ret2)); if (sm_is_err(ret2)) goto errnseid; ret = qm_2ss_nseid(qss_ctx, rcbe, (uchar *) qss_sess->qsses_id, (int) ret2); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, nsess-id=%s, qm_2ss_nseid=%r\n", qss_ctx->qss_id, qss_sess->qsses_id, ret)); goto errnseid2; } QM_LEV_DPRINTFC(QDC_S2Q, 7, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, evthr_en_wr=%p\n", &qss_ctx->qss_com)); ret = sm_rcbcom_endrep(&qss_ctx->qss_com, tsk, false, &rcbe); QM_LEV_DPRINTFC(QDC_S2Q, 6, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, evthr_en_wr=%p, done=%r\n", &qss_ctx->qss_com, ret)); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, nsess-id=%s, sm_rcbcom_endrep=%r\n", qss_ctx->qss_id, qss_sess->qsses_id, ret)); goto errnseid2; } ret = qss_control(qss_ctx, QMGR_THROTTLE, iqdb_usage(qmgr_ctx->qmgr_iqdb), QMGR_RFL_IQD_I, THR_LOCK_UNLOCK); sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 14, "func=qm_fr_ss_react, id=%d, new_sess=%s, client_ipv4=%s, stat=%m", qss_ctx->qss_id, qss_sess->qsses_id, inet_ntoa(qss_sess->qsess_client), ret2); return QMGR_R_ASYNC; errnseid2: /* remove the session from the iqdb */ /* XXX this needs to undo all operations from qm_ss_nseid() */ (void) iqdb_session_rm(qmgr_ctx->qmgr_iqdb, qss_sess->qsses_id, SMTP_STID_SIZE, THR_LOCK_UNLOCK); errnseid: if (qss_sess != NULL) { (void) qss_sess_free(qss_sess); qss_sess = NULL; } sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=qm_fr_ss_react, sess_new=%d", ret); goto err2; } else if (RT_S2Q_CSEID == rt) { sessta_id_T sessid; qss_sess_P qss_sess; ssocc_entry_P ssocc_entry; qss_sess = NULL; if (l != SMTP_STID_SIZE) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } /* session id */ ret = sm_rcb_getn(rcb, (uchar *) sessid, l); if (sm_is_err(ret)) { rv = ret; goto errcseid; } ret = sm_getsmtpsid(sessid); if (ret != qss_ctx->qss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* XXX use different error! */ goto errcseid; } qss_sess = (qss_sess_P) iqdb_lookup(qmgr_ctx->qmgr_iqdb, sessid, SMTP_STID_SIZE, THR_LOCK_UNLOCK); if (NULL == qss_sess) { ret = sm_error_perm(SM_EM_Q_SS2Q, SM_E_NOTFOUND); QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, sess-id=%s, siqdb_lookup=not_found\n", qss_ctx->qss_id, sessid)); goto errcseid; } QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, csess-id=%s\n", qss_ctx->qss_id, qss_sess->qsses_id)); /* check for EOR? */ /* XXX this should be in some other function? (qm_ss_cseid()) */ /* XXX this should be in some other function? (ssocc_new_se()) */ ssocc_entry = NULL; ret = ssocc_entry_find(qmgr_ctx->qmgr_ssocc_ctx, qss_sess->qsess_client.s_addr, &ssocc_entry, THR_LOCK_UNLERR); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, ss_sess=%s, ssocc_entry_find=failed, ip=%A, ret=%r\n", qss_sess->qsses_id, (ipv4_T) qss_sess->qsess_client.s_addr, ret)); } else { SM_ASSERT(ssocc_entry->ssocce_open_se > 0); --ssocc_entry->ssocce_open_se; if (0 == ssocc_entry->ssocce_open_se) { ret = ssocc_entry_free(qmgr_ctx->qmgr_ssocc_ctx, ssocc_entry, THR_NO_LOCK); } r = pthread_mutex_unlock(&qmgr_ctx->qmgr_ssocc_ctx->ssocc_mutex); if (r != 0) { /* XXX ??? COMPLAIN */ QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, unlock(ssocc_ctx)=%d\n", r)); SM_ASSERT(0 == r); } } /* ** XXX just remove it? do anything else with it first? ** e.g., check for open transactions (and abort those)? */ ret = iqdb_session_rm(qmgr_ctx->qmgr_iqdb, sessid, SMTP_STID_SIZE, THR_LOCK_UNLOCK); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, csess-id=%s, rm=%r\n", qss_ctx->qss_id, qss_sess->qsses_id, ret)); goto errcseid; } sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 14, "func=qm_fr_ss_react, id=%d, close_sess=%s", qss_ctx->qss_id, qss_sess->qsses_id); qss_sess_free(qss_sess); SM_ASSERT(qss_ctx->qss_cur_session > 0); --qss_ctx->qss_cur_session; ret = qm_control(qmgr_ctx, QMGR_UN_THROTTLE, iqdb_usage(qmgr_ctx->qmgr_iqdb), QMGR_RFL_IQD_I, THR_LOCK_UNLOCK); /* XXX no reply??? */ goto done; errcseid: /* more cleanup? (qss_sess isn't allocated, don't free it!) */ sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=qm_fr_ss_react, sess_close=%m", ret); goto err2; } else if (RT_S2Q_1TAID == rt) { qss_ta_P qss_ta; if (l != SMTP_STID_SIZE) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto err2; } /* XXX allocate new transaction? rpool? */ ret = qss_ta_new(&qss_ta, qss_ctx, NULL); if (sm_is_err(ret)) goto err2; qss_ta->qssta_st_time = evthr_time(qmgr_ctx->qmgr_ev_ctx); /* transaction id */ ret = sm_rcb_getn(rcb, (uchar *) qss_ta->qssta_id, l); if (sm_is_err(ret)) { rv = ret; goto errntaid; } QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, 1ta-id=%s\n", qss_ctx->qss_id, qss_ta->qssta_id)); ret = sm_getsmtpsid(qss_ta->qssta_id); if (ret != qss_ctx->qss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } if (sm_idcmp(qss_ctx->qss_ta_id, qss_ta->qssta_id) < 0) SESSTA_COPY(qss_ctx->qss_ta_id, qss_ta->qssta_id); ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2Q_MAIL || l >= tl) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid; } ret = qss_mail_new(qss_ta); if (sm_is_err(ret)) goto errntaid; ret = sm_rcb_getnstr(rcb, &qss_ta->qssta_mail->qsm_pa, l); if (sm_is_err(ret)) goto errntaid; QM_LEV_DPRINTFC(QDC_S2Q, 2, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, 1ta-id=%s, mail from=%S, l=%d\n", qss_ctx->qss_id, qss_ta->qssta_id, qss_ta->qssta_mail->qsm_pa, l)); ret = sm_rcb_peek2uint32(rcb, &l, &rt); if (sm_is_success(ret) && 4 == l && RT_S2Q_MAIL_FL == rt) { ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2Q_MAIL_FL) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid; } /* "translate" flags */ if (SM_IS_FLAG(v, SM_TA_FL_VERP)) QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_VERP); } ret = sm_rcb_get2uint32(rcb, &l, &rt); while (RT_S2Q_RCPT_IDX == rt) { rcpt_idx_T rcpt_idx; qss_rcpt_P qss_rcpt; sm_str_P rcpt_pa; qss_rcpt = NULL; if (l != 4) { rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid2; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto errntaid2; } rcpt_idx = v; ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2Q_RCPT || l > tl) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid2; } ret = sm_rcb_getnstr(rcb, &rcpt_pa, l); if (sm_is_err(ret)) goto errntaid2; /* add recipient to list */ ret = qss_rcpts_new(qss_ta, &rcpt_pa, rcpt_idx, &qss_rcpt); if (sm_is_err(ret)) goto errntaid2; QSRCPT_SET_FLAG(qss_rcpt, QSRCPT_FL_TA); QM_LEV_DPRINTFC(QDC_S2Q, 2, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, qss_ta-id=%s, rcpt_idx=%d, rcptaddr=%S, l=%d, nrctps=%d\n", qss_ctx->qss_id, qss_ta->qssta_id, rcpt_idx, qss_rcpt->qsr_pa, l, qss_ta->qssta_rcpts_tot)); /* add recipient to iqdb, ibdb; ret2 used down below */ /* 2003-10-20 03:23:06 XXX ret2 used where? (except for logging) */ /* XXX split here: this function will be async. */ ret2 = qm_ss_rcptid(qss_ctx, qss_ta, tsk, qss_rcpt); qss_rcpt = NULL; /* qm_ss_rcptid took care of it */ /* qss_rcpt will be in qss_ta only if accepted */ if (sm_is_err(ret2)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qm_ss_rcptid=%r\n", ret2)); goto errntaid2; } ret = sm_rcb_get2uint32(rcb, &l, &rt); continue; } if (rt != RT_S2Q_CDBID || l > tl) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, rt=%#x, l=%u, tl=%u\n", rt, l, tl)); rv = sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid2; } ret = sm_rcb_getncstr(rcb, &qss_ta->qssta_cdb_id, l); QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, cdb_id=%C, ret=%r\n", qss_ta->qssta_cdb_id, ret)); if (sm_is_err(ret)) { /* ** XXX Throttle SMTPS? Send error back saying that ** transaction can't be accepted because QMGR is ** out of memory? */ if (qss_ta != NULL) { /* ** XXX Remove from DBs? ** It's not in IBDB nor in AQ. ** Use qss_ta_abort instead? */ ret = qss_ta_free(qss_ta, false, QSS_TA_FREE_ALWAYS, 0); qss_ta = NULL; } goto errntaid2; } ret = sm_rcb_get3off_t(rcb, &l, &rt, &off); if (sm_is_err(ret) || rt != RT_S2Q_SIZE_B || l != SIZEOF_OFF_T) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_Q_SS2Q, SM_E_PR_ERR); goto errntaid2; } qss_ta->qssta_msg_sz_b = off; QM_LEV_DPRINTFC(QDC_S2Q, 3, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, size=%lu\n", qss_ctx->qss_id, (ulong) off)); ret = qm_rd_hdrmods(qmgr_ctx, qss_ta, rcb); if (sm_is_err(ret)) goto errntaid2; ret = qm_rcbcom_prerep(qmgr_ctx, &qss_ctx->qss_com, tsk, &rcbe); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta=%p, qm_rcbcom_prerep=%r\n", qss_ta, ret)); goto errntaid2; } inwaitq = true; r = pthread_mutex_lock(&qss_ta->qssta_mutex); SM_LOCK_OK(r); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta=%p, lock=%d\n", qss_ta, r)); goto errntaid2; } SM_SET_FLAG(fct_state, FST_QSS_TA_LOCKED); /* all data read, store it internally, prepare reply */ ret2 = qm_ss_1taid(qss_ctx, qss_ta); QM_LEV_DPRINTFC(QDC_S2Q, 1, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, 1ta-id=%s, qm_ss_1taid=%r\n", qss_ctx->qss_id, qss_ta->qssta_id, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto errntaid; ret2 = qm_ss_ctaid(qss_ctx, qss_ta); if (sm_is_err(ret2)) { QM_LEV_DPRINTFC(QDC_S2Q, 1, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qm_ss_ctaid=%r, id=%d, cta-id=%s, cdb=%C\n", ret2, qss_ctx->qss_id, qss_ta->qssta_id, qss_ta->qssta_cdb_id)); goto errcdb2; } /* ** If we get an return code other than SMTP_R_OK ** then we can immediately tell SMTPS about; ** we don't need to schedule this for a group commit. */ if (ret2 != SMTP_R_OK) { sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 8, "sev=INFO, func=qm_fr_ss_react, smtps_id=%d, ss_ta=%s, stat=%d", qss_ctx->qss_id, qss_ta->qssta_id, ret2); ret = qm_2ss_ctaid(qss_ctx, qss_ta, rcbe, ret2); if (sm_is_err(ret)) goto errcdb2; ret = sm_rcbcom_endrep(&qss_ctx->qss_com, tsk, false, &rcbe); /* ret checked below */ QM_LEV_DPRINTFC(QDC_S2Q, 6, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, 3 evthr_en_wr=%p, done=%r\n", &qss_ctx->qss_com, ret)); } else { #if QMGR_STATS /* XXX not protected by mutex */ qmgr_ctx->qmgr_rcpts_rcvd += qss_ta->qssta_rcpts_tot; ++qmgr_ctx->qmgr_tas_rcvd; #endif /* don't need rcbe here: a reply scheduled in qm_ss_schedctaid() */ sm_rcbe_free(rcbe); rcbe = NULL; /* protected by fs_* mutex */ ret = cdb_sz_add(qmgr_ctx->qmgr_cdb_fsctx, qss_ta->qssta_id, SM_B2KB(qss_ta->qssta_msg_sz_b), &qmgr_ctx->qmgr_cdb_kbfree); QM_LEV_DPRINTFC(QDC_S2Q, 4, (QM_DEBFP, "cdb_sz_add=%r, size=%lu, kbfree=%lu\n", ret, (ulong) qss_ta->qssta_msg_sz_b, qmgr_ctx->qmgr_cdb_kbfree)); if (sm_is_err(ret)) { QM_LEV_DPRINTFC(QDC_S2Q, 1, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, cdb_sz_add=%r\n", ret)); } else if (qmgr_ctx->qmgr_cdb_kbfree < qmgr_ctx->qmgr_cnf.q_cnf_ok_df) { ret = qss_control(qss_ctx, QMGR_THROTTLE, DISK_USAGE(qmgr_ctx->qmgr_cdb_kbfree, qmgr_ctx), QMGR_RFL_CDB_I, THR_LOCK_UNLOCK); } ret = qm_ss_schedctaid(qmgr_ctx, qss_ta); /* ret checked below */ } if (sm_is_err(ret)) goto errcdb2; SM_ASSERT(SM_IS_FLAG(fct_state, FST_QSS_TA_LOCKED)); r = pthread_mutex_unlock(&qss_ta->qssta_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta=%p, unlock=%d\n", qss_ta, r)); SM_ASSERT(0 == r); } SM_CLR_FLAG(fct_state, FST_QSS_TA_LOCKED); /* ** if qm_ss_ctaid failed then qss_ta must be removed */ if (ret2 != SMTP_R_OK && qss_ta != NULL) { ret = qss_ta_free(qss_ta, false, QSS_TA_FREE_ALWAYS, 0); if (sm_is_err(ret)) QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta_free=%r\n", ret)); qss_ta = NULL; } ret = qss_control(qss_ctx, QMGR_THROTTLE, iqdb_usage(qmgr_ctx->qmgr_iqdb), QMGR_RFL_IQD_I, THR_LOCK_UNLOCK); return QMGR_R_ASYNC; /* check: proper cleanup functionality??? */ errcdb2: SM_ASSERT(SM_IS_FLAG(fct_state, FST_QSS_TA_LOCKED)); r = pthread_mutex_unlock(&qss_ta->qssta_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta=%p, unlock=%d\n", qss_ta, r)); SM_ASSERT(0 == r); } SM_CLR_FLAG(fct_state, FST_QSS_TA_LOCKED); QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, id=%d, cta-id=%s, ret=%r\n", qss_ctx->qss_id, qss_ta->qssta_id, ret)); sm_rcbe_free(rcbe); rcbe = NULL; if (qss_ta != NULL) { /* ** XXX Remove from iqdb? XXX Really? Always? ** Maybe use the flags to denote what should happen ** with qss_ta? (indicate the "progress", i.e., where ** is qss_ta stored and whether something bad happened ** such that it must be freed). */ (void) qss_ta_free(qss_ta, false, QSS_TA_FREE_ALWAYS, 0); qss_ta = NULL; } sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=qm_fr_ss_react, ss_ta=%s, close_ss_ta=%m", qss_ta != NULL ? qss_ta->qssta_id : "unknown", ret); goto err2; /* check: proper cleanup functionality??? */ errntaid2: if (qss_ta != NULL && QSS_TA_IS_FLAG(qss_ta, QSS_TA_FL_IQDB)) { /* remove the transaction from iqdb */ (void) iqdb_trans_rm(qmgr_ctx->qmgr_iqdb, qss_ta->qssta_id, SMTP_STID_SIZE, THR_LOCK_UNLOCK); QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_IQDB_RM); } errntaid: if (SM_IS_FLAG(fct_state, FST_QSS_TA_LOCKED)) { r = pthread_mutex_unlock(&qss_ta->qssta_mutex); if (r != 0) { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, qss_ta=%p, unlock=%d\n", qss_ta, r)); SM_ASSERT(0 == r); } SM_CLR_FLAG(fct_state, FST_QSS_TA_LOCKED); } if (qss_ta != NULL) { /* qss_ta is not in any DB */ if (!QSS_TA_OK_FREE(qss_ta->qssta_flags)) QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, ERROR=unexpected_state, flags=%#x, id=%d, qss_ta-id=%s\n", qss_ta->qssta_flags, qss_ctx->qss_id, qss_ta->qssta_id)); (void) qss_ta_free(qss_ta, false, QSS_TA_FREE_ALWAYS, 0); qss_ta = NULL; } sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=qm_fr_ss_react, 1ta_new=%r", ret); goto err2; } #if 0 /* other cases? */ else if (XXX == rt) RT_S2Q_CLTIP /* client IP address */ RT_S2Q_CID /* close SMTPS (id) */ RT_S2Q_CLTIPV4 /* client IPv4 address */ RT_S2Q_CLTIPV6 /* client IPv6 address */ #endif /* 0 */ else goto err2; done: if (!inwaitq) { ret = sm_rcb_close_dec(qss_ctx->qss_com.rcbcom_rdrcb); (void) sm_rcb_open_rcv(qss_ctx->qss_com.rcbcom_rdrcb); } if (ret == SM_SUCCESS && inwaitq) return QMGR_R_ASYNC; return rv; /* preserve original error code! */ err2: /* use rcb functions that don't do check the state */ if (!inwaitq) (void) sm_rcb_close_decn(qss_ctx->qss_com.rcbcom_rdrcb); error: /* open rcb for receiving next record */ if (!inwaitq) (void) sm_rcb_open_rcvn(qss_ctx->qss_com.rcbcom_rdrcb); #if 0 errret: #endif QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=DBG, func=qm_fr_ss_react, id=%d, stat=%d, error out, rt=%#x, v=%d, ret=%r, rv=%r, inwaitq=%d\n", qss_ctx->qss_id, qss_ctx->qss_status, rt, v, ret, rv, inwaitq)); if (rcbe != NULL) sm_rcbe_free(rcbe); if (inwaitq) { if (sm_is_err(rv)) /* shouldn't happen! */ QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss_react, rv=%r, inwaitq=%d\n", rv, inwaitq)); return QMGR_R_ASYNC; } return rv; #undef FST_QSS_TA_LOCKED } /* ** QM_FR_SS -- SMTPS to QMGR interface ** ** Parameters: ** tsk -- evthr task ** ** Returns: ** usual sm_error code ** ** Called by: sm_qmgr_smtps() for read events */ sm_ret_T qm_fr_ss(sm_evthr_task_P tsk) { int fd, r; sm_ret_T ret; qmgr_ctx_P qmgr_ctx; qss_ctx_P qss_ctx; #if QM_TIMING uint32_t frt; #endif id_count_T idc; SM_IS_EVTHR_TSK(tsk); qss_ctx = (qss_ctx_P) tsk->evthr_t_actx; SM_IS_QSS_CTX(qss_ctx); qmgr_ctx = qss_ctx->qss_qmgr_ctx; SM_IS_QMGR_CTX(qmgr_ctx); fd = tsk->evthr_t_fd; /* checked in caller */ ret = sm_rcb_rcv(fd, qss_ctx->qss_com.rcbcom_rdrcb, QSS_RC_MINSZ); QM_LEV_DPRINTTC(QDC_S2Q, 4, (QM_DEBFP, "sev=DBG, func=qm_fr_ss, fd=%d, got ret=%r, buf=%d, len=%d\n", fd, ret, qss_ctx->qss_com.rcbcom_rdrcb->sm_rcb_base[0], sm_rcb_getlen(qss_ctx->qss_com.rcbcom_rdrcb)), qmgr_ctx->qmgr_ev_ctx->evthr_c_time); if (ret > 0) return EVTHR_WAITQ; else if (0 == ret) { ret = sm_rcb_close_rcv(qss_ctx->qss_com.rcbcom_rdrcb); /* start appropriate function ... */ QM_HRBT_DPRINTF(QDC_S2Q_TM, 3, (QM_DEBFP, "func=qm_fr_ss, qm_fr_ss_react=before\n")); ret = qm_fr_ss_react(qss_ctx, tsk #if QM_TIMING , &frt #endif ); QM_HRBT_DPRINTF(QDC_S2Q_TM, 3, (QM_DEBFP, "func=qm_fr_ss, qm_fr_ss_react=after, rt=%x\n", frt)); if (sm_is_err(ret)) goto termit; /* too harsh? */ else if (QMGR_R_WAITQ == ret) return EVTHR_WAITQ; else if (QMGR_R_ASYNC == ret) return EVTHR_OK; else if (EVTHR_DEL == ret) goto termit; /* terminate this client */ else return ret; } else if (SM_IO_EOF == ret) { ret = sm_rcb_close_rcv(qss_ctx->qss_com.rcbcom_rdrcb); termit: QM_LEV_DPRINTTC(QDC_S2Q, 1, (QM_DEBFP, "sev=DBG, func=qm_fr_ss, task=%p, status=terminate, rcbcom_tsk=%p, ret=%r\n", tsk, qss_ctx->qss_com.rcbcom_tsk, ret), qmgr_ctx->qmgr_ev_ctx->evthr_c_time); close(fd); /* ** XXX don't do this if we returned the task already! ** we need to lock the task first and invalidate it ** then. How to do this properly? ** It seems we need another evthr function... */ #if 0 tsk->evthr_t_fd = INVALID_FD; /* make it invalid */ #endif r = pthread_mutex_lock(&qmgr_ctx->qmgr_mutex); SM_LOCK_OK(r); if (r != 0) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_CRIT, 4, "sev=CRIT, func=qm_fr_ss, lock=%d", r); goto error; } ret = sm_id2idc(qss_ctx->qss_ta_id, &idc); if (ret == SM_SUCCESS && qmgr_ctx->qmgr_idc < idc) qmgr_ctx->qmgr_idc = idc; /* Close all outstanding sessions and update internal data */ (void) qss_ctx_close(qss_ctx); /* Don't crash while holding lock?? */ SM_ASSERT(qmgr_ctx->qmgr_ss_li.qm_gli_nfd > 0); /* free sss ctx */ qmgr_ctx->qmgr_ss_li.qm_gli_nfd--; qmgr_ctx->qmgr_ss_li.qm_gli_used &= ~(qss_ctx->qss_bit); if (qss_ctx->qss_com.rcbcom_rdrcb != NULL) (void) sm_rcb_close_decn(qss_ctx->qss_com.rcbcom_rdrcb); r = pthread_mutex_unlock(&qmgr_ctx->qmgr_mutex); SM_ASSERT(0 == r); return EVTHR_DEL; } else /* if (ret < 0) */ { QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss, ret=%r, errno=%d\n", ret, errno)); } QM_LEV_DPRINTFC(QDC_S2Q, 0, (QM_DEBFP, "sev=ERROR, func=qm_fr_ss, fd=%d\n", fd)); error: return EVTHR_DEL; }