/*
* Copyright (c) 2004-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: smtps2pmilter.c,v 1.35 2007/02/03 03:23:04 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/reccom.h"
#include "pmilter.h"
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#define PMSS_RC_MINSZ 28
#define PMSS_MAX_REC_LEN (65 * 1024)
#if 0
generic reply code:
/* prepare reply (async) */
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errPART;
inwaitq = true;
/* call milter function (rcbe required for response) */
ret2 = sm_pmilt_PART(pmss_ctx, pmse_ctx, rcbe);
if (sm_is_err(ret2))
goto errPART;
/* put reply into write queue */
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errPART;
#endif /* 0 */
/*
** SM_PMILT_SMTPSFIND -- Find SMTPS id
**
** Parameters:
** pmg_ctx -- pmilter (library) context
** pmss_ctx -- task context pmilter/SMTP server
**
** Returns:
** >=0: index
** <0: usual sm_error code
**
** Last code review:
*/
static sm_ret_T
sm_pmilt_smtpsfind(pmg_ctx_P pmg_ctx, pmss_ctx_P pmss_ctx)
{
int r;
uint u;
uint32_t j;
sm_ret_T ret;
pmss_ctx_P pmss2_ctx;
r = pthread_mutex_lock(&pmg_ctx->pmg_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG */
PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmilt_smtpsfind, lock=%d\n", r));
return sm_error_temp(SM_EM_PMILTER, r);
}
ret = sm_error_perm(SM_EM_PMILTER, SM_E_NOTFOUND);
/* search for sss ctx */
for (j = 1, u = 0; u < SM_ARRAY_SIZE(pmg_ctx->pmg_sctx); u++, j *= 2) {
if ((pmg_ctx->pmg_sused & j) != 0) {
pmss2_ctx = pmg_ctx->pmg_sctx[u];
if (pmss2_ctx != pmss_ctx &&
pmss2_ctx->pmss_id == pmss_ctx->pmss_id)
{
ret = u;
break;
}
}
}
r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
SM_ASSERT(r == 0);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_PMILTER, r);
return ret;
}
/*
** SM_PMILT_SENDMACLIST -- Send list of macros to SMTPS
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** rcb -- RCB to send to SMTPS
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
sm_pmilt_sendmaclist(pmss_ctx_P pmss_ctx, sm_rcb_P rcb)
{
sm_ret_T ret;
uint i, j;
uint32_t macm;
bool first;
ret = SM_SUCCESS;
for (i = 0; !sm_is_err(ret) && i < PM_SMST_MAX; i++) {
first = true;
for (j = 0;
!sm_is_err(ret) && j < PM_MAX_MACROS &&
(macm = pmss_ctx->pmss_mac_names[i][j]) != PMM_END;
j++)
{
#if PM_SE_TA_ID_LOCAL
if (PMM_SEID == macm || PMM_MAIL_TAID == macm)
continue;
#endif
if (first) {
ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACW, i);
first = false;
if (sm_is_err(ret))
break;
}
ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACM, macm);
}
}
return ret;
}
/*
** SM_PMSS_REACT -- Decode data received from SMTPS
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** tsk -- evthr task
**
** Returns:
** usual sm_error code
**
** Locking:
** XXX who locks pmss_ctx?
**
** Called by: sm_smtps2pmilt
*/
static sm_ret_T
sm_pmss_react(pmss_ctx_P pmss_ctx, sm_evthr_task_P tsk)
{
uint32_t v, l, rt, tl;
sm_ret_T ret, ret2, rv;
bool inwaitq;
sm_rcb_P rcb;
sm_rcbe_P rcbe;
pmg_ctx_P pmg_ctx;
pmse_ctx_P pmse_ctx;
sessta_id_T se_id;
SM_IS_PMSS_CTX(pmss_ctx);
ret = rv = SM_SUCCESS;
inwaitq = false;
rcbe = NULL;
pmse_ctx = NULL;
/*
** 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 PMILTER can't allocate memory
** then SMTPS must be told about it...
**
** In general PMILTER 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 = pmss_ctx->pmss_com.rcbcom_rdrcb;
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret)) {
rv = ret;
goto error;
}
pmg_ctx = pmss_ctx->pmss_pmg_ctx;
SM_IS_PMG_CTX(pmg_ctx);
/* Total length of record */
ret = sm_rcb_getuint32(rcb, &tl);
if (sm_is_err(ret) || tl > PMSS_MAX_REC_LEN || tl > sm_rcb_getlen(rcb)) {
rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, 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_PMILTER, SM_E_PR_V_MISM);
goto err2;
}
/* SMTPS (new/existing/close) ID */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, rt=%X, l=%d, smtpsid=%d\n", rt, l, v));
if (sm_is_err(ret) || l != 4) {
rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
if (RT_S2M_NID == rt) {
pmilter_P pmilter;
/* New SMTPS */
if (pmss_ctx->pmss_status != PMSS_ST_NONE ||
pmss_ctx->pmss_id != PMSS_ID_NONE)
{
/* XXX Internal error? Stop task? SMTPS id exists */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto err2;
}
pmss_ctx->pmss_id = v;
pmss_ctx->pmss_cur_session = 0;
ret = sm_pmilt_smtpsfind(pmg_ctx, pmss_ctx);
if (ret >= 0) {
PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, nid=%d, exists=%d\n", pmss_ctx->pmss_id, ret));
/* XXX Internal error? Stop task? SMTPS id exists */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto err2;
}
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PROT) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
/* XXX check protocol version */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_S2M_MAXTHRDS) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
pmss_ctx->pmss_max_thrs = v;
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_S2M_CAP) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
pmss_ctx->pmss_cap = v & SM_SCAP_PM_ALL;
#if 0
if ((v & SM_SCAP_PM_ALL) != v)
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 4,
"sev=INFO, func=sm_pmss_react, server_cap=%#x, libpmilter_cap=%#X"
, v, SM_SCAP_PM_ALL);
#endif
while (!SM_RCB_ISEOB(rcb)) {
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_PMILTER, SM_E_PR_ERR);
goto err2;
}
switch (rt) {
case RT_S2M_FCT:
pmss_ctx->pmss_fct = v & SM_SFCT_PM_ALL;
#if 0
if ((v & SM_SFEAT_PM_ALL) != v)
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 4,
"sev=INFO, func=sm_pmss_react, server_fct=%#x, libpmilter_fct=%#X"
, v, SM_SFCT_PM_ALL);
#endif
break;
case RT_S2M_FEAT:
pmss_ctx->pmss_feat = v & SM_SFEAT_PM_ALL;
#if 0
if ((v & SM_SFEAT_PM_ALL) != v)
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 4,
"sev=INFO, func=sm_pmss_react, server_feat=%#x, libpmilter_feat=%#X"
, v, SM_SFEAT_PM_ALL);
#endif
break;
case RT_S2M_MISC:
pmss_ctx->pmss_misc = v & SM_SMISC_PM_ALL;
#if 0
if ((v & SM_SMISC_PM_ALL) != v)
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 4,
"sev=INFO, func=sm_pmss_react, server_misc=%#x, libpmilter_misc=%#X"
, v, SM_SMISC_PM_ALL);
#endif
break;
default:
ret = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
}
pmss_ctx->pmss_status = PMSS_ST_START;
SM_ASSERT(pmss_ctx->pmss_pmg_ctx != NULL);
pmilter = pmss_ctx->pmss_pmg_ctx->pmg_pmilter;
SM_ASSERT(pmilter != NULL);
pmss_ctx->pmss_pmcap = pmilter->pmfi_dfl_cap;
pmss_ctx->pmss_pmfct = pmilter->pmfi_dfl_fct;
pmss_ctx->pmss_pmfeat = pmilter->pmfi_dfl_feat;
pmss_ctx->pmss_pmmisc = pmilter->pmfi_dfl_misc;
if (pmilter->pmfi_negotiate != NULL) {
/* check protocol, capabilities, requirements, etc */
ret = (*pmilter->pmfi_negotiate)(pmss_ctx,
pmss_ctx->pmss_cap,
pmss_ctx->pmss_fct,
pmss_ctx->pmss_feat,
pmss_ctx->pmss_misc,
&pmss_ctx->pmss_pmcap,
&pmss_ctx->pmss_pmfct,
&pmss_ctx->pmss_pmfeat,
&pmss_ctx->pmss_pmmisc);
#if 0
if (sm_is_err(ret))
/* XXX */ ;
#endif
}
/*
** Send a reply to let client know that the connection
** has been accepted.
*/
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto err2;
inwaitq = true;
/* this doesn't need to be async ... */
ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
SM_RCBV_INT, RT_M2S_PROT, SM_PMILTER_PROT,
SM_RCBV_INT, RT_M2S_CAP, pmss_ctx->pmss_pmcap,
SM_RCBV_INT, RT_M2S_FCT, pmss_ctx->pmss_pmfct,
SM_RCBV_INT, RT_M2S_FEAT, pmss_ctx->pmss_pmfeat,
SM_RCBV_END);
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_pcb_putv=%x\n", pmss_ctx->pmss_id, v, ret));
goto err2;
}
ret = sm_pmilt_sendmaclist(pmss_ctx, &rcbe->rcbe_rcb);
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, pm_sendmaclist=%r\n", pmss_ctx->pmss_id, v, ret));
goto err2;
}
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, v, ret));
goto err2;
}
PM_LEV_DPRINTF(5, (PM_DEBFP, "sev=DBG, nid=ok\n"));
goto done;
}
else if (RT_S2M_ID == rt) {
/* SMTPS id just for identification */
if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) {
/* XXX Internal error? Stop task? SMTPS id exists */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto err2;
}
}
else if (RT_S2M_CID == rt) {
/* SMTPS shuts down */
if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) {
/* XXX Internal error? Stop task? SMTPS id exists */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto err2;
}
/* check for EOB? not really: we shut down anyway */
pmss_ctx->pmss_status = PMSS_ST_SH_DOWN;
(void) sm_rcb_close_dec(pmss_ctx->pmss_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, pmss_ctx is cleaned in caller */
return EVTHR_DEL;
}
else
goto err2;
/* RT_S2M_ID == rt is the only case in which we continue here */
/* what's next? */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret)) {
rv = ret;
goto err2;
}
/* XXX always session id? */
if (l != SMTP_STID_SIZE ||
(rt != RT_S2M_NSEID && rt != RT_S2M_CSEID && rt != RT_S2M_SEID))
{
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
/* session id */
ret = sm_rcb_getn(rcb, (uchar *) se_id, l);
if (sm_is_err(ret)) {
rv = ret;
goto errcseid;
}
if (RT_S2M_CSEID == rt || RT_S2M_SEID == rt) {
/* lookup session id to find session context */
ret = sm_pmse_find(pmss_ctx, se_id, &pmse_ctx);
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, sess-id=%s, sm_pmse_find=not_found\n", pmss_ctx->pmss_id, se_id));
goto errhelo;
}
pmse_ctx->pmse_cmd_status = 0;
}
if (RT_S2M_SEID == rt) {
/* get next record */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret)) {
rv = ret;
goto err2;
}
}
if (RT_S2M_NSEID == rt) {
ret = sm_pmse_new(pmg_ctx, pmss_ctx, &pmse_ctx);
if (sm_is_err(ret))
goto err2;
strlcpy(pmse_ctx->pmse_se_id, se_id, sizeof(pmse_ctx->pmse_se_id));
ret = sm_getsmtpsid(pmse_ctx->pmse_se_id);
if (ret != pmss_ctx->pmss_id) {
/* XXX SMTPS id doesn't match */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto errnseid;
}
/* 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_S2M_IPV4) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errnseid;
}
pmse_ctx->pmse_cltipv4 = v;
++pmss_ctx->pmss_cur_session;
PM_LEV_DPRINTF(3, (PM_DEBFP, "func=sm_pmss_react, id=%d, nsess-id=%s, IP=%A\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, v));
/* port */
ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PORT) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errnseid;
}
/* populate SOCKADDR for call to nseid() */
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_S2M_HOST) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errnseid;
}
SM_STR_FREE(pmse_ctx->pmse_arg1);
ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l);
if (sm_is_err(ret))
goto errnseid;
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_CONNECT);
ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_CONNECT, rcb);
if (sm_is_err(ret))
goto errnseid;
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errnseid;
inwaitq = true;
ret = sm_pmilt_nseid(pmss_ctx, pmse_ctx, rcbe);
SM_STR_FREE(pmse_ctx->pmse_arg1);
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_pmilt_nseid=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret));
goto errnseid2;
}
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, evthr_en_wr=%p, done=%x\n", &pmss_ctx->pmss_com, ret));
if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret));
goto errnseid2;
}
return PMILT_R_ASYNC;
errnseid2:
/* XXX this needs to undo all operations from sm_pmilt_nseid() */
errnseid:
if (pmse_ctx != NULL) {
(void) sm_pmse_free(pmss_ctx, pmse_ctx);
pmse_ctx = NULL;
}
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_ERR, 4,
"sev=ERROR, func=sm_pmss_react, sess_new=%d", ret);
#endif /* 0 */
goto err2;
}
else if (RT_S2M_CSEID == rt) {
ret = sm_getsmtpsid(se_id);
if (ret != pmss_ctx->pmss_id) {
/* XXX SMTPS id doesn't match */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto errcseid;
}
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, csess-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id));
/* check for EOR? */
/* call milter function */
ret2 = sm_pmilt_cseid(pmss_ctx, pmse_ctx);
if (sm_is_err(ret2))
goto errcseid;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 14,
"func=sm_pmss_react, id=%d, close_sess=%s",
pmss_ctx->pmss_id, pmse_ctx->pmse_se_id);
#endif /* 0 */
sm_pmse_free(pmss_ctx, pmse_ctx);
--pmss_ctx->pmss_cur_session;
/* XXX no reply??? */
goto done;
errcseid:
/* more cleanup? (pmse_ctx isn't allocated, don't free it!) */
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_ERR, 4,
"sev=ERROR, func=sm_pmss_react, sess_close=%d", ret);
#endif /* 0 */
goto err2;
}
else if (RT_S2M_HELO == rt || RT_S2M_EHLO == rt) {
bool ehlo;
ehlo = RT_S2M_EHLO == rt;
ret = sm_getsmtpsid(se_id);
if (ret != pmss_ctx->pmss_id) {
/* XXX SMTPS id doesn't match */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto errhelo;
}
SM_STR_FREE(pmse_ctx->pmse_arg1);
ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l);
if (sm_is_err(ret))
goto errhelo;
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_EHLO);
ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_EHLO, rcb);
if (sm_is_err(ret))
goto errnseid;
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errhelo;
inwaitq = true;
ret2 = sm_pmilt_helo(pmss_ctx, pmse_ctx, rcbe, ehlo);
SM_STR_FREE(pmse_ctx->pmse_arg1);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_helo=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
/* check for EOR? */
if (sm_is_err(ret2))
goto errhelo;
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errhelo;
return PMILT_R_ASYNC;
errhelo:
goto err2;
}
else if (RT_S2M_NTAID == rt) {
if (l != SMTP_STID_SIZE) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
/* transaction id */
ret = sm_rcb_getn(rcb, (uchar *) pmse_ctx->pmse_ta_id, l);
if (sm_is_err(ret)) {
rv = ret;
goto errntaid;
}
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id));
ret = sm_getsmtpsid(pmse_ctx->pmse_ta_id);
if (ret != pmss_ctx->pmss_id) {
/* XXX SMTPS id doesn't match */
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
/* XXX use different error! */
goto errntaid;
}
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_S2M_MAIL || l >= tl) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errntaid;
}
SM_STR_FREE(pmse_ctx->pmse_arg1);
ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l);
if (sm_is_err(ret))
goto errntaid;
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, l=%d\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, l));
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_MAIL);
while (!SM_RCB_ISEOB(rcb)) {
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret))
goto errntaid;
switch (rt) {
case RT_S2M_ARG:
if (l >= tl) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errntaid;
}
SM_STR_FREE(pmse_ctx->pmse_arg2);
ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l);
if (sm_is_err(ret))
goto errntaid;
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, args=%S\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, pmse_ctx->pmse_arg2));
break;
case RT_S2M_MACM:
ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_MAIL, rcb);
if (sm_is_err(ret))
goto errntaid;
break;
default:
/* skip over other data */
ret = sm_rcb_skip(rcb, l);
if (sm_is_err(ret))
goto errntaid;
break;
}
}
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errntaid2;
inwaitq = true;
ret2 = sm_pmilt_mail(pmss_ctx, pmse_ctx, rcbe);
SM_STR_FREE(pmse_ctx->pmse_arg1);
SM_STR_FREE(pmse_ctx->pmse_arg2);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_mail=%r\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
if (sm_is_err(ret2))
goto errntaid;
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errntaid2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_mail_pa);
#endif /* 0 */
return PMILT_R_ASYNC;
errntaid2:
errntaid:
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_ERR, 4,
"sev=ERROR, func=sm_pmss_react, ta_new=%d", ret);
#endif /* 0 */
SM_STR_FREE(pmse_ctx->pmse_arg1);
SM_STR_FREE(pmse_ctx->pmse_arg2);
goto err2;
}
else if (RT_S2M_RCPT_IDX == rt) {
SM_STR_FREE(pmse_ctx->pmse_arg1);
if (l != 4) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errrcpt;
}
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret)) {
rv = ret;
goto errrcpt;
}
pmse_ctx->pmse_rcpt_idx = v;
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret) || rt != RT_S2M_RCPT || l > tl) {
rv = sm_is_err(ret) ? ret
: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errrcpt;
}
ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l);
if (sm_is_err(ret))
goto errrcpt;
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_RCPT);
while (!SM_RCB_ISEOB(rcb)) {
ret = sm_rcb_get2uint32(rcb, &l, &rt);
if (sm_is_err(ret))
goto errrcpt;
switch (rt) {
case RT_S2M_ARG:
if (l >= tl) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errrcpt;
}
SM_STR_FREE(pmse_ctx->pmse_arg2);
ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l);
if (sm_is_err(ret))
goto errrcpt;
break;
case RT_S2M_MACM:
ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_RCPT, rcb);
if (sm_is_err(ret))
goto errrcpt;
break;
case RT_S2M_RCPT_ST:
if (l != 4) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errrcpt;
}
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret)) {
rv = ret;
goto errrcpt;
}
pmse_ctx->pmse_cmd_status = v;
break;
default:
/* skip over other data */
ret = sm_rcb_skip(rcb, l);
if (sm_is_err(ret))
goto errrcpt;
break;
}
}
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errrcpt2;
inwaitq = true;
ret2 = sm_pmilt_rcpt(pmss_ctx, pmse_ctx, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_rcpt=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
SM_STR_FREE(pmse_ctx->pmse_arg1);
SM_STR_FREE(pmse_ctx->pmse_arg2);
pmse_ctx->pmse_cmd_status = 0;
if (sm_is_err(ret2))
goto errrcpt2;
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errrcpt2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
return PMILT_R_ASYNC;
errrcpt2:
/*
** More cleanup? See below.
*/
/* fall through ... */
errrcpt:
SM_STR_FREE(pmse_ctx->pmse_arg1);
SM_STR_FREE(pmse_ctx->pmse_arg2);
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_ERR, 4,
"sev=ERROR, func=sm_pmss_react, in_ss_ta=%x, rv=%x"
, ret, rv);
#endif /* 0 */
goto err2;
}
else if (RT_S2M_DATA == rt) {
if (l != 4) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errdata;
}
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret)) {
rv = ret;
goto errdata;
}
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DATA);
ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DATA, rcb);
if (sm_is_err(ret))
goto errdata;
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errdata2;
inwaitq = true;
ret2 = sm_pmilt_data(pmss_ctx, pmse_ctx, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_data=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
if (sm_is_err(ret2))
goto errdata2;
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errdata2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
return PMILT_R_ASYNC;
errdata2:
/*
** More cleanup? See below.
*/
/* fall through ... */
errdata:
goto err2;
}
else if (RT_S2M_MSG == rt) {
uchar *msg;
msg = NULL;
if (l >= tl) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errmsg;
}
msg = (uchar *) sm_malloc(l);
if (NULL == msg) {
rv = sm_error_temp(SM_EM_PMILTER, ENOMEM);
goto errmsg;
}
ret = sm_rcb_getn(rcb, msg, l);
if (sm_is_err(ret)) {
rv = ret;
goto errmsg;
}
/*
** usually no reply ... however: how to make this "async",
** i.e., put task back into queue?
*/
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errmsg2;
inwaitq = true;
}
ret2 = sm_pmilt_msg(pmss_ctx, pmse_ctx, msg, l, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_msg=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
/* check for EOR? */
if (sm_is_err(ret2))
goto errmsg2;
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errmsg2;
}
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
SM_FREE(msg);
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC))
return PMILT_R_ASYNC;
goto done;
errmsg2:
/*
** More cleanup? See below.
*/
/* fall through ... */
errmsg:
SM_FREE(msg);
goto err2;
}
else if (RT_S2M_DOT == rt) {
uchar *msg;
msg = NULL;
if (l >= tl) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errdot;
}
msg = (uchar *) sm_malloc(l);
if (NULL == msg) {
rv = sm_error_temp(SM_EM_PMILTER, ENOMEM);
goto errdot;
}
ret = sm_rcb_getn(rcb, msg, l);
if (sm_is_err(ret)) {
rv = ret;
goto errdot;
}
(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DOT);
ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DOT, rcb);
if (sm_is_err(ret))
goto errdot;
ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
if (sm_is_err(ret))
goto errdot2;
inwaitq = true;
ret2 = sm_pmilt_dot(pmss_ctx, pmse_ctx, msg, l, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_dot=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
/* check for EOR? */
if (sm_is_err(ret2))
goto errdot2;
ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
if (sm_is_err(ret))
goto errdot2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
SM_FREE(msg);
return PMILT_R_ASYNC;
errdot2:
/*
** More cleanup? See below.
*/
/* fall through ... */
errdot:
SM_FREE(msg);
goto err2;
}
else if (RT_S2M_ABORT_TA == rt) {
if (l != 4) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto errabortta;
}
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret)) {
rv = ret;
goto errabortta;
}
ret2 = sm_pmilt_abort_ta(pmss_ctx, pmse_ctx);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_abort_ta=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
/* check for EOR? */
if (sm_is_err(ret2))
goto errabortta2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
goto done;
errabortta2:
/*
** More cleanup? See below.
*/
/* fall through ... */
errabortta:
goto err2;
}
else if (RT_S2M_MSG_RPLC_STAT == rt) {
if (l != 4) {
rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
goto err2;
}
ret = sm_rcb_getuint32(rcb, &v);
if (sm_is_err(ret)) {
rv = ret;
goto err2;
}
ret2 = sm_pmilt_msg_rplc_stat(pmss_ctx, pmse_ctx, v);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, ta-id=%s, status=%#x, sm_sm_pmilt_msg_rplc_stat=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, v, ret2));
/* check for EOR? */
if (sm_is_err(ret2))
goto err2;
#if 0
sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
SM_LOG_INFO, 10,
"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */
goto done;
}
#if 0
else if (RT_S2M_CTAID == rt) {
}
#endif /* 0 */
else
goto err2;
done:
if (!inwaitq) {
ret = sm_rcb_close_dec(pmss_ctx->pmss_com.rcbcom_rdrcb);
(void) sm_rcb_open_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
}
if (SM_SUCCESS == ret && inwaitq)
return PMILT_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(pmss_ctx->pmss_com.rcbcom_rdrcb);
error:
/* open rcb for receiving next record */
if (!inwaitq)
(void) sm_rcb_open_rcvn(pmss_ctx->pmss_com.rcbcom_rdrcb);
#if 0
errret:
#endif /* 0 */
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, stat=%d, rt=%x, l=%d, v=%d, ret=%x, rv=%x, inwaitq=%d\n",
pmss_ctx->pmss_id, pmss_ctx->pmss_status, rt, l, v, ret, rv, inwaitq));
if (rcbe != NULL)
sm_rcbe_free(rcbe);
if (inwaitq) {
if (sm_is_err(rv)) /* shouldn't happen! */
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, rv=%x, inwaitq=%d\n", rv, inwaitq));
return PMILT_R_ASYNC;
}
return rv;
}
/*
** SM_SMTPS2PMILT -- PMILT - SMTPS interface
**
** Parameters:
** tsk -- evthr task
**
** Returns:
** usual sm_error code
**
** Called by: sm_pmilt_smtps() for read events
*/
sm_ret_T
sm_smtps2pmilt(sm_evthr_task_P tsk)
{
int fd, r;
#if PMILT_STORE_FDS
int i;
#endif /* PMILT_STORE_FDS */
sm_ret_T ret;
pmg_ctx_P pmg_ctx;
pmss_ctx_P pmss_ctx;
SM_IS_EVTHR_TSK(tsk);
pmss_ctx = (pmss_ctx_P) tsk->evthr_t_actx;
pmg_ctx = pmss_ctx->pmss_pmg_ctx;
fd = tsk->evthr_t_fd; /* checked in caller */
ret = sm_rcb_rcv(fd, pmss_ctx->pmss_com.rcbcom_rdrcb, PMSS_RC_MINSZ);
PM_LEV_DPRINTF(6, (PM_DEBFP, "sev=DBG, func=sm_smtps2pmilt, fd=%d, got ret=%x, buf=%d, len=%d\n", fd, ret,
pmss_ctx->pmss_com.rcbcom_rdrcb->sm_rcb_base[0], sm_rcb_getlen(pmss_ctx->pmss_com.rcbcom_rdrcb)));
if (ret > 0)
return EVTHR_WAITQ;
else if (ret == 0) {
ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
/* start appropriate function ... */
ret = sm_pmss_react(pmss_ctx, tsk);
if (sm_is_err(ret))
goto termit; /* too harsh? */
else if (PMILT_R_WAITQ == ret)
return EVTHR_WAITQ;
else if (PMILT_R_ASYNC == ret)
return EVTHR_OK;
else if (EVTHR_DEL == ret)
goto termit; /* terminate this SMTPS */
else
return ret;
}
else if (SM_IO_EOF == ret) {
ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
termit:
close(fd);
r = pthread_mutex_lock(&pmg_ctx->pmg_mutex);
SM_LOCK_OK(r);
if (r != 0) {
/* LOG */
goto error;
}
/* Need to abort all open sessions */
(void) sm_pmss_close(pmss_ctx);
/* XXX don't crash while holding lock? */
SM_ASSERT(pmg_ctx->pmg_ssnfd > 0);
/* free sss ctx */
pmg_ctx->pmg_ssnfd--;
pmg_ctx->pmg_sused &= ~(pmss_ctx->pmss_bit);
if (pmss_ctx->pmss_com.rcbcom_rdrcb != NULL)
(void) sm_rcb_close_decn(pmss_ctx->pmss_com.rcbcom_rdrcb);
r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
return EVTHR_DEL;
}
else { /* if (ret < 0) */
PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, ret=%x, errno=%d\n", ret, errno));
}
PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, fd=%d\n", fd));
error:
return EVTHR_DEL;
}
syntax highlighted by Code2HTML, v. 0.9.1