/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1