/*
* 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: pmilter_engine.c,v 1.34 2007/02/13 03:58:18 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/ctype.h"
#include "sm/reccom.h"
#include "sm/pmilter.h"
#include "pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#include "util.h"
#if MTA_USE_PMILTER
/*
** How to implement session timeouts?
** That is, when the server aborts a connection will pmilter be informed?
**
** Note: currently a reply text is only sent if it
** - has an SMTP reply code
** - the reply code matches the return value of the function
** This means it can't be used for other things right now, e.g.,
** changing an address.
*/
#define CHKREPLYTEXT \
sm_str_getlen(pmse_ctx->pmse_reply_text) > 0 && \
IS_SMTP_REPLY(rcode) && \
SMTP_REPLY_MATCHES_RCODE(pmse_ctx->pmse_reply_text, rcode, 0, rc)
/*
** SM_PMILT_NSEID -- New SMTP session
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_nseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode;
bool sendreplytext;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
sendreplytext = false;
if (pmilter->pmfi_connect != NULL &&
SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_CNNCT))
{
int rc;
sm_sockaddr_T sm_sockaddr;
sm_memzero(&sm_sockaddr, sizeof(sm_sockaddr));
sm_sockaddr.sin.sin_addr.s_addr = pmse_ctx->pmse_cltipv4;
sm_sockaddr.sin.sin_family = AF_INET;
sm_str_clr(pmse_ctx->pmse_reply_text);
pm_clrreplies(pmse_ctx);
rcode = (*pmilter->pmfi_connect)(pmse_ctx,
(char *) sm_str_getdata(pmse_ctx->pmse_arg1),
&sm_sockaddr);
sendreplytext = CHKREPLYTEXT;
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
return ret;
}
/*
** SM_PMILT_CSEID -- Close SMTP session
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_cseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx)
{
sm_ret_T ret;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
if (pmilter->pmfi_close != NULL)
ret = (*pmilter->pmfi_close)(pmse_ctx);
return SM_SUCCESS;
}
/*
** SM_PMILT_HELO -- EHLO/HELO
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** rcbe -- RCB for reply
** ehlo -- was EHLO used?
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_helo(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe, bool ehlo)
{
sm_ret_T ret;
int rcode;
bool sendreplytext;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
sendreplytext = false;
if (pmilter->pmfi_helo != NULL) {
int rc;
sm_str_clr(pmse_ctx->pmse_reply_text);
rcode = (*pmilter->pmfi_helo)(pmse_ctx,
(char *) sm_str_getdata(pmse_ctx->pmse_arg1), ehlo);
sendreplytext = CHKREPLYTEXT;
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
return ret;
}
/*
** SM_PMILT_MAIL -- MAIL
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_mail(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode;
unsigned int nelem;
bool sendreplytext;
char **argv;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
argv = NULL;
sendreplytext = false;
ret = args2argv(pmse_ctx->pmse_arg2, &nelem, &argv);
if (sm_is_err(ret))
goto error;
if (pmilter->pmfi_mail != NULL) {
int rc;
sm_str_clr(pmse_ctx->pmse_reply_text);
rcode = (*pmilter->pmfi_mail)(pmse_ctx,
(char *) sm_str_getdata(pmse_ctx->pmse_arg1),
argv);
sendreplytext = CHKREPLYTEXT;
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
error:
if (argv != NULL)
sm_free(argv);
return ret;
}
/*
** SM_PMILT_RCPT -- RCPT
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_rcpt(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode;
unsigned int nelem;
bool sendreplytext;
char **argv;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
argv = NULL;
sendreplytext = false;
ret = args2argv(pmse_ctx->pmse_arg2, &nelem, &argv);
if (sm_is_err(ret))
goto error;
if (pmilter->pmfi_rcpt != NULL) {
int rc;
sm_str_clr(pmse_ctx->pmse_reply_text);
rcode = (*pmilter->pmfi_rcpt)(pmse_ctx,
(char *) sm_str_getdata(pmse_ctx->pmse_arg1),
argv);
sendreplytext = CHKREPLYTEXT;
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
error:
if (argv != NULL)
sm_free(argv);
return ret;
}
/*
** SM_PMILT_DATA -- DATA
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_data(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode;
bool sendreplytext;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
sendreplytext = false;
if (pmilter->pmfi_data != NULL) {
int rc;
sm_str_clr(pmse_ctx->pmse_reply_text);
rcode = (*pmilter->pmfi_data)(pmse_ctx);
sendreplytext = CHKREPLYTEXT;
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
return ret;
}
/*
** SM_PMILT_MSG -- message chunck
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** buf -- message chunck
** len -- size of message chunck
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_msg(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode;
bool sendreplytext;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
sendreplytext = false;
if (pmilter->pmfi_msg != NULL) {
int rc;
sm_str_clr(pmse_ctx->pmse_reply_text);
rcode = (*pmilter->pmfi_msg)(pmse_ctx, buf, len);
sendreplytext = SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC) &&
CHKREPLYTEXT;
}
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
SM_RCBV_END);
}
return ret;
}
/*
** SM_PMILT_DOT -- final message chunck
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** buf -- message chunck
** len -- size of message chunck
** rcbe -- RCB for reply
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_dot(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe)
{
sm_ret_T ret;
int rcode, rc;
bool sendreplytext;
#if MTA_USE_RSAD
bool sendreplies;
#endif
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
rcode = SMTP_R_OK;
sendreplytext = false;
#if MTA_USE_RSAD
sendreplies = false;
#endif
if (pmilter->pmfi_msg != NULL) {
sm_str_clr(pmse_ctx->pmse_reply_text);
pmse_ctx->pmse_state = PMSE_ST_MSG;
rcode = (*pmilter->pmfi_msg)(pmse_ctx, buf, len);
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
sendreplytext = CHKREPLYTEXT;
}
else
rcode = SMTP_R_OK;
}
if (!(SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC) &&
(IS_SMTP_REPLY(rcode) || rcode == SMTP_R_ACCEPT)) &&
pmilter->pmfi_eom != NULL)
{
sm_str_clr(pmse_ctx->pmse_reply_text);
pm_clrreplies(pmse_ctx);
pmse_ctx->pmse_state = PMSE_ST_DOT;
rcode = (*pmilter->pmfi_eom)(pmse_ctx);
if (SMTP_R_RPLCMSG == rcode &&
SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSGREPLACE) &&
pmilter->pmfi_msg_rplc != NULL)
{
pmse_ctx->pmse_cmd_status = SM_SUCCESS;
rcbe->rcbe_wr_cb = sm_pmilt_msg_rplc;
rcbe->rcbe_ctx = pmse_ctx;
}
pmse_ctx->pmse_state = PMSE_ST_NONE;
sendreplytext = CHKREPLYTEXT;
#if MTA_USE_RSAD
sendreplies = (pmse_ctx->pmse_nreplies > 0
&& pmse_ctx->pmse_rcodes != NULL);
#endif
}
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_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
SM_RCBV_INT, RT_M2S_RCODE, rcode,
SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND,
pmse_ctx->pmse_reply_text,
#if MTA_USE_RSAD
SM_RCBV_INTA, sendreplies ? RT_M2S_RCODES : RT_NOSEND,
pmse_ctx->pmse_nreplies, pmse_ctx->pmse_rcodes,
#endif
SM_RCBV_END);
if (pmse_ctx->pmse_mail_new != NULL) {
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MAILMOD)) {
ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0,
SM_RCBV_STR, RT_M2S_MAIL_NEW, pmse_ctx->pmse_mail_new,
SM_RCBV_END);
}
else if (sm_is_success(ret))
ret = sm_error_perm(SM_EM_PMILTER, EINVAL);
SM_STR_FREE(pmse_ctx->pmse_mail_new);
}
/* send recipient mods */
if (!PM_RCPTS_EMPTY(&pmse_ctx->pmse_rcpts_hd)) {
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_RCPTMOD)) {
pm_rcpt_P pm_rcpt;
for (pm_rcpt = PM_RCPTS_FIRST(&pmse_ctx->pmse_rcpts_hd);
pm_rcpt != PM_RCPTS_END(&pmse_ctx->pmse_rcpts_hd)
&& sm_is_success(ret);
pm_rcpt = PM_RCPTS_NEXT(pm_rcpt))
{
if (PM_RCPT_ADD == pm_rcpt->pmr_type)
ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0,
SM_RCBV_STR, RT_M2S_RCPT_ADD, pm_rcpt->pmr_pa,
SM_RCBV_END);
else
ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0,
SM_RCBV_STR, RT_M2S_RCPT_DEL, pm_rcpt->pmr_pa,
SM_RCBV_INT, RT_M2S_RCPT_IDX, pm_rcpt->pmr_idx,
SM_RCBV_END);
}
}
else if (sm_is_success(ret))
ret = sm_error_perm(SM_EM_PMILTER, EINVAL);
pm_rcpts_free(pmse_ctx);
}
/* send header mods */
if (!HDRMODL_EMPTY(pmse_ctx->pmse_hdrmodhd)) {
if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_HDRMOD)) {
sm_hdrmodl_wr(pmse_ctx->pmse_hdrmodhd, &rcbe->rcbe_rcb,
RT_M2S_HM_T_P, RT_M2S_HM_HDR);
}
else if (sm_is_success(ret))
ret = sm_error_perm(SM_EM_PMILTER, EINVAL);
sm_hdrmodl_free(&pmse_ctx->pmse_hdrmodhd);
}
return ret;
}
/*
** SM_PMILT_ABORT_TA -- abort current transaction
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_abort_ta(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx)
{
sm_ret_T ret;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
if (pmilter->pmfi_abort != NULL)
ret = (*pmilter->pmfi_abort)(pmse_ctx);
return ret;
}
/*
** SM_PMILT_MSG_RPLC_STAT -- message replacement status
**
** Parameters:
** pmss_ctx -- task context pmilter/SMTP server
** pmse_ctx -- pmilter/SMTP server session context
** status -- message replacement status
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_pmilt_msg_rplc_stat(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_ret_T status)
{
sm_ret_T ret;
pmilter_P pmilter;
SM_IS_PMSE_CTX(pmse_ctx);
SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL);
pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter;
SM_REQUIRE(pmilter != NULL);
ret = SM_SUCCESS;
if (pmilter->pmfi_msg_rplc_stat != NULL)
ret = (*pmilter->pmfi_msg_rplc_stat)(pmse_ctx, status);
return ret;
}
#endif /* MTA_USE_PMILTER */
syntax highlighted by Code2HTML, v. 0.9.1