/*
* Copyright (c) 2005, 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: t-pmilter-1.c,v 1.65 2007/06/18 16:20:38 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/types.h"
#include "sm/sysexits.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/reccom.h"
#include "sm/mta.h"
#define PMILTER_DEBUG_DEFINE 1
#include "pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#include "sm/pmilter.h"
#include "util.h"
#include "t-pmilter.h"
#include "sm/test.h"
#if MTA_USE_PMILTER
static sm_hdrmodhd_P sm_hdrmodhd = NULL;
static char *rcpt_add_pa = NULL;
static char *rcpt_del_pa = NULL;
static rcpt_idx_T rcpt_del_idx = 0;
static char *mail_new = NULL;
/* replace all occurrences of a in str with b... just a hack */
static void
strreplace(char *str, char a, char b)
{
char *s;
if (NULL == str)
return;
if (a == b)
return;
s = str;
while (*s != '\0') {
if (*s == a)
*s = b;
++s;
}
}
/* hack: copy str, replace '_' with ' ', and append \r\n */
static char *
getreply(char *str)
{
size_t len;
char *new;
if (NULL == str)
return NULL;
len = strlen(str);
if (len > 1000)
return NULL;
new = malloc(len + 4);
if (NULL == new)
return NULL;
sm_memcpy(new, str, len + 1);
strreplace(new, '_', ' ');
new[len] = '\r';
new[len + 1] = '\n';
new[len + 2] = '\0';
return new;
}
/*
** pmilter test program; uses "native" API
*/
static pmt_ctx_T pmt_ctx;
#define SM_BUFSIZE 8192
static uint rbufsize = SM_BUFSIZE;
#define SETREPLY(str) do { \
if ((str) != NULL && *(str) != '\0') \
ret = sm_pmfi_setreply(pmse_ctx, str); \
} while (0)
#define T_DELAY(delay) do { \
if ((delay) > 0) \
sleep(delay); \
} while (0)
/* common function code; use only at the end of a function */
#define TPM_END(stage) \
if (PMT_EXIT(pmt_ctx.pmt_rcode[stage])) \
exit(0); \
SETREPLY(pmt_ctx.pmt_reply[stage]); \
T_DELAY(pmt_ctx.pmt_delay[stage]); \
return pmt_ctx.pmt_rcode[stage]
static sm_ret_T
tpm1_negotiate(pmss_ctx_P pmss_ctx, uint32_t srv_cap, uint32_t srv_fct, uint32_t srv_feat, uint32_t srv_misc, uint32_t *pm_cap, uint32_t *pm_fct, uint32_t *pm_feat, uint32_t *pm_misc)
{
sm_ret_T ret;
pmt_ctx_P pmt_ctx_l;
SM_REQUIRE(pm_cap != NULL);
SM_REQUIRE(pm_fct != NULL);
SM_REQUIRE(pm_feat != NULL);
SM_REQUIRE(pm_misc != NULL);
pmt_ctx_l = (pmt_ctx_P) sm_pmfi_get_ctx_g_ss(pmss_ctx);
SM_TEST(pmt_ctx_l != NULL);
SM_TEST(pmt_ctx_l == &pmt_ctx);
if (SM_IS_FLAG(pmt_ctx_l->pmt_flags, PMT_FL_M_HOSTN) ||
SM_IS_FLAG(pmt_ctx_l->pmt_flags, PMT_FL_M_SEID))
{
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_CONNECT,
PMM_SRVHOSTNAME, PMM_SEID, PMM_END);
SM_TEST(SM_SUCCESS == ret);
}
if (SM_IS_FLAG(pmt_ctx_l->pmt_flags, PMT_FL_M_MSGID)) {
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_DOT,
PMM_DOT_MSGID, PMM_END);
SM_TEST(SM_SUCCESS == ret);
}
if (SM_IS_FLAG(pmt_ctx_l->pmt_flags, PMT_FL_M_TAID)) {
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_MAIL,
PMM_MAIL_TAID, PMM_END);
SM_TEST(SM_SUCCESS == ret);
}
/* check that all bits in pmt_cap are also set in srv_cap... */
if ((pmt_ctx.pmt_cap & srv_cap) != pmt_ctx.pmt_cap)
sm_io_fprintf(smioerr,
"sev=WARN, where=tpm1_negotiate, pm_cap=%#X, srv_cap=%#X\n",
pmt_ctx_l->pmt_cap, srv_cap);
*pm_cap = pmt_ctx_l->pmt_cap;
*pm_fct = 0;
*pm_feat = 0;
*pm_misc = 0;
sm_io_fprintf(smioerr, "sev=DBG, where=tpm1_negotiate, cap=%#X\n",
pmt_ctx_l->pmt_cap);
return SM_SUCCESS;
}
static sfsistat_T
tpm1_connect(pmse_ctx_P pmse_ctx, const char *hostname, sm_sockaddr_T *hostaddr)
{
sfsistat_T ret;
if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_NSEID]))
exit(0);
/* HACK; should get pmt_ctx from pmse_ctx (indirectly) */
ret = sm_pmfi_set_ctx_se(pmse_ctx, &pmt_ctx);
SM_TEST(SM_SUCCESS == ret);
++pmt_ctx.pmt_se_cnt;
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_HOSTN)) {
char *srvhostname;
ret = sm_pmfi_getmac(pmse_ctx, PMM_SRVHOSTNAME, &srvhostname);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(srvhostname != NULL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, srvhostname=%s, where=connect, ret=%X\n",
pmse_ctx->pmse_se_id, srvhostname, ret);
}
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_SEID)) {
char *se_id;
ret = sm_pmfi_getmac(pmse_ctx, PMM_SEID, &se_id);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(se_id != NULL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, se_id=%s, where=connect, ret=%X\n",
pmse_ctx->pmse_se_id, se_id, ret);
}
ret = pmt_ctx.pmt_rcode[SM_STAGE_NSEID];
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, host=%s, where=connect, rc=%d\n",
pmse_ctx->pmse_se_id, hostname, ret);
TPM_END(SM_STAGE_NSEID);
}
static sm_ret_T
tpm1_close(pmse_ctx_P pmse_ctx)
{
void *actx;
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=close\n",
pmse_ctx->pmse_se_id);
actx = sm_pmfi_get_ctx_se(pmse_ctx);
SM_TEST(actx == &pmt_ctx);
return SM_SUCCESS;
}
static sfsistat_T
tpm1_helo(pmse_ctx_P pmse_ctx, const char *helohost, bool ehlo)
{
sfsistat_T ret;
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, helo=%s, ehlo=%d, rc=%d\n",
pmse_ctx->pmse_se_id, helohost, ehlo,
pmt_ctx.pmt_rcode[SM_STAGE_HELO]);
TPM_END(SM_STAGE_HELO);
}
static sfsistat_T
tpm1_mail(pmse_ctx_P pmse_ctx, const char *mail, char **argv)
{
sfsistat_T ret;
++pmt_ctx.pmt_ta_cnt;
pmt_rcpts_free(&pmt_ctx);
pmt_ctx.pmt_ta_rcpts_ok = 0;
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_TAID)) {
char *ta_id;
ret = sm_pmfi_getmac(pmse_ctx, PMM_MAIL_TAID, &ta_id);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(ta_id != NULL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, ta_id=%s, ret=%X\n",
pmse_ctx->pmse_se_id, ta_id, ret);
}
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_2ND) &&
pmt_ctx.pmt_ta_cnt > 1 &&
SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_MSGID))
{
char *msgid;
ret = sm_pmfi_getmac(pmse_ctx, PMM_DOT_MSGID, &msgid);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(NULL == msgid);
sm_io_fprintf(smioerr,
"sev=DBG, func=tpm1_mail, seid=%s, msgid=%s, ret=%X\n",
pmse_ctx->pmse_se_id, msgid, ret);
}
if (argv != NULL && argv[0] != NULL && strcasecmp(argv[0], "prdr") == 0)
PMT_SET_FLAG(&pmt_ctx, PMT_FL_RD_RSAD);
else
PMT_CLR_FLAG(&pmt_ctx, PMT_FL_RD_RSAD);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, mail=%s, argv[0]=%s, rc=%d\n",
pmse_ctx->pmse_se_id, mail,
(argv != NULL && argv[0] != NULL) ? argv[0] : "NULL",
pmt_ctx.pmt_rcode[SM_STAGE_MAIL]);
TPM_END(SM_STAGE_MAIL);
}
static sfsistat_T
tpm1_rcpt(pmse_ctx_P pmse_ctx, const char *rcpt, char **argv)
{
sm_ret_T ret, st, rcode;
st = -1;
ret = SMTP_OK;
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_R_ST))
ret = sm_pmfi_getstatus(pmse_ctx, &st);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rcpt=%s, argv[0]=%s, st=%d, rc=%d\n",
pmse_ctx->pmse_se_id, rcpt,
(argv != NULL && argv[0] != NULL) ? argv[0] : "NULL", st,
pmt_ctx.pmt_rcode[SM_STAGE_RCPT]);
if (rcpt != NULL && strlen(rcpt) > 5 &&
'r' == rcpt[1] && IS_SMTP_CODE(rcpt, 2))
rcode = strtoul(rcpt + 2, NULL, 10);
else
rcode = SMTP_NO_REPLY;
if ((smtp_reply_type(pmt_ctx.pmt_rcode[SM_STAGE_RCPT]) == SMTP_RTYPE_OK
|| pmt_ctx.pmt_rcode[SM_STAGE_RCPT] == SMTP_R_CONT
|| pmt_ctx.pmt_rcode[SM_STAGE_RCPT] == SMTP_OK) &&
(smtp_reply_type(ret) == SMTP_RTYPE_OK
|| SMTP_OK == ret) &&
(smtp_reply_type(rcode) == SMTP_RTYPE_OK
|| SMTP_NO_REPLY == rcode))
{
pmt_rcpt_P pmt_rcpt;
pmt_rcpt_new(&pmt_ctx, rcpt, &pmt_rcpt);
++pmt_ctx.pmt_ta_rcpts_ok;
}
TPM_END(SM_STAGE_RCPT);
}
static sfsistat_T
tpm1_data(pmse_ctx_P pmse_ctx)
{
sm_ret_T ret;
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=data, rc=%d\n",
pmse_ctx->pmse_se_id, pmt_ctx.pmt_rcode[SM_STAGE_DATA]);
TPM_END(SM_STAGE_DATA);
}
static void
pm_open_wr(pmse_ctx_P pmse_ctx)
{
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_FAIL))
{
pmt_ctx.pmt_fd = open(pmt_ctx.pmt_fname,
O_WRONLY|O_APPEND|O_CREAT,
0660);
if (pmt_ctx.pmt_fd >= 0)
PMT_SET_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
else
PMT_SET_FLAG(&pmt_ctx, PMT_FL_FD_FAIL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, where=pm_open_wr, open=%d\n",
pmse_ctx->pmse_se_id, pmt_ctx.pmt_fd);
}
}
static void
pm_close_wr(pmse_ctx_P pmse_ctx)
{
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN)) {
close(pmt_ctx.pmt_fd);
pmt_ctx.pmt_fd = INVALID_FD;
PMT_CLR_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
}
}
static sfsistat_T
tpm1_unknown(pmse_ctx_P pmse_ctx, const char *cmd)
{
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, where=unknown, cmd=%s\n",
pmse_ctx->pmse_se_id, cmd);
return SMTP_R_CONT;
}
static sfsistat_T
tpm1_msg(pmse_ctx_P pmse_ctx, unsigned char *buf, size_t len)
{
sm_ret_T ret;
pm_open_wr(pmse_ctx);
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL) &&
buf != NULL && len > 0
)
{
ssize_t written;
written = write(pmt_ctx.pmt_fd, buf, len);
if (written == -1) {
sm_io_fprintf(smioerr,
"sev=ERROR, seid=%s, where=msg, write=%d\n",
pmse_ctx->pmse_se_id, (int) written);
PMT_SET_FLAG(&pmt_ctx, PMT_FL_WR_FAIL);
pm_close_wr(pmse_ctx);
}
}
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=msg, len=%d, rc=%d\n",
pmse_ctx->pmse_se_id, (int) len, pmt_ctx.pmt_rcode[SM_STAGE_MSG]);
TPM_END(SM_STAGE_MSG);
}
static sfsistat_T
tpm1_eom(pmse_ctx_P pmse_ctx)
{
sm_ret_T ret;
bool writefailed;
int rcode;
sm_hdrmod_P sm_hdrmod;
writefailed = PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL);
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL))
{
pm_close_wr(pmse_ctx);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, where=dot, status=closed\n",
pmse_ctx->pmse_se_id);
}
if (SM_IS_FLAG(pmt_ctx.pmt_flags, PMT_FL_M_MSGID)) {
char *msgid;
ret = sm_pmfi_getmac(pmse_ctx, PMM_DOT_MSGID, &msgid);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(msgid != NULL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, msgid=%s, ret=%X\n",
pmse_ctx->pmse_se_id, msgid, ret);
}
rcode = pmt_ctx.pmt_rcode[SM_STAGE_DOT];
/* HACK !!! */
#define PMT_MAX_REPLIES 32
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_RD_RSAD)
&& SMTP_R_D_EOM == pmt_ctx.pmt_rcode[SM_STAGE_DOT]
&& pmt_ctx.pmt_ta_rcpts_ok < PMT_MAX_REPLIES)
{
uint u;
pmt_rcpt_P pmt_rcpt;
int rcodes[PMT_MAX_REPLIES];
for (u = 0, pmt_rcpt = PMT_RCPTS_FIRST(&pmt_ctx.pmt_rcpts);
u < pmt_ctx.pmt_ta_rcpts_ok
&& pmt_rcpt != PMT_RCPTS_END(&pmt_ctx.pmt_rcpts);
u++, pmt_rcpt = PMT_RCPTS_NEXT(pmt_rcpt))
{
char *pa;
rcodes[u] = pmt_ctx.pmt_rcode[SM_STAGE_RSAD];
pa = pmt_rcpt->pmtr_pa;
if (pa != NULL && strlen(pa) > 5 &&
'd' == pa[1] && IS_SMTP_CODE(pa, 2))
rcodes[u] = strtoul(pa + 2, NULL, 10);
}
ret = sm_pmfi_setreplies(pmse_ctx, pmt_ctx.pmt_ta_rcpts_ok, rcodes, NULL);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rcpts_ok=%d, rcode=%d, setreplies=%r\n"
, pmse_ctx->pmse_se_id, pmt_ctx.pmt_ta_rcpts_ok
, pmt_ctx.pmt_rcode[SM_STAGE_RSAD], ret);
}
else if (SMTP_R_OK == rcode && writefailed)
rcode = SMTP_R_TEMP;
else if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_RPLC_MSG))
rcode = SMTP_R_RPLCMSG;
if (mail_new != NULL) {
ret = sm_pmfi_mail_rplc(pmse_ctx, mail_new, NULL);
SM_TEST(SM_SUCCESS == ret);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, mail_new=%s, ret=%#x\n",
pmse_ctx->pmse_se_id, mail_new, ret);
}
if (rcpt_add_pa != NULL) {
ret = sm_pmfi_rcpt_add(pmse_ctx, rcpt_add_pa, NULL);
SM_TEST(SM_SUCCESS == ret);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rcpt_add_pa=%s, ret=%#x\n",
pmse_ctx->pmse_se_id, rcpt_add_pa, ret);
}
if (rcpt_del_pa != NULL) {
ret = sm_pmfi_rcpt_del(pmse_ctx, rcpt_del_pa, rcpt_del_idx);
SM_TEST(SM_SUCCESS == ret);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rcpt_del_pa=%s, ret=%#x\n",
pmse_ctx->pmse_se_id, rcpt_del_pa, ret);
}
sm_hdrmod = HDRMODL_FIRST(sm_hdrmodhd);
while (sm_hdrmod != NULL) {
ret = sm_pmfi_hdr_mod(pmse_ctx, sm_hdrmod->sm_hm_type,
sm_hdrmod->sm_hm_pos,
sm_hdrmod->sm_hm_hdr != NULL
? sm_cstr_data(sm_hdrmod->sm_hm_hdr)
: NULL);
SM_TEST(SM_SUCCESS == ret);
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, hdr=%#C, ret=%#x\n",
pmse_ctx->pmse_se_id, sm_hdrmod->sm_hm_hdr, ret);
sm_hdrmod_rm(&sm_hdrmodhd);
sm_hdrmod = HDRMODL_FIRST(sm_hdrmodhd);
}
sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=dot, rc=%d\n",
pmse_ctx->pmse_se_id, rcode);
if (PMT_EXIT(rcode))
exit(0);
pmt_rcpts_free(&pmt_ctx);
SETREPLY(pmt_ctx.pmt_reply[SM_STAGE_DOT]);
T_DELAY(pmt_ctx.pmt_delay[SM_STAGE_DOT]);
return rcode;
}
static int
pm_open_rd(pmse_ctx_P pmse_ctx)
{
int rcode;
rcode = SMTP_R_OK;
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_RPLC_MSG) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_RFD_OPEN) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_RFD_FAIL))
{
pmt_ctx.pmt_rfd = open(pmt_ctx.pmt_rfname, O_RDONLY);
if (pmt_ctx.pmt_rfd >= 0) {
if (NULL == pmt_ctx.pmt_buf)
pmt_ctx.pmt_buf = malloc(rbufsize);
if (NULL == pmt_ctx.pmt_buf) {
close(pmt_ctx.pmt_rfd);
pmt_ctx.pmt_rfd = INVALID_FD;
PMT_SET_FLAG(&pmt_ctx, PMT_FL_RFD_FAIL);
rcode = SMTP_R_TEMP;
}
else
PMT_SET_FLAG(&pmt_ctx, PMT_FL_RFD_OPEN);
}
else {
PMT_SET_FLAG(&pmt_ctx, PMT_FL_RFD_FAIL);
rcode = SMTP_R_TEMP;
}
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, where=pm_open_rd, open=%d\n",
pmse_ctx->pmse_se_id, pmt_ctx.pmt_rfd);
}
return rcode;
}
static void
pm_close_rd(pmse_ctx_P pmse_ctx)
{
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_RFD_OPEN)) {
close(pmt_ctx.pmt_rfd);
pmt_ctx.pmt_rfd = INVALID_FD;
PMT_CLR_FLAG(&pmt_ctx, PMT_FL_RFD_OPEN);
}
if (pmt_ctx.pmt_buf != NULL) {
free(pmt_ctx.pmt_buf);
pmt_ctx.pmt_buf = NULL;
}
}
static sfsistat_T
tpm1_msg_rplc_stat(pmse_ctx_P pmse_ctx, sm_ret_T status)
{
pm_close_rd(pmse_ctx);
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, tpm1_msg_rplc_stat=%#x\n",
pmse_ctx->pmse_se_id, status);
return SMTP_R_OK;
}
static sfsistat_T
tpm1_msg_rplc(pmse_ctx_P pmse_ctx, const unsigned char **pmsgchunk, size_t *pmsglen)
{
int rcode;
if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_RPLC_MSG])) {
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, tpm1_msg_rplc=exiting\n",
pmse_ctx->pmse_se_id);
exit(0);
}
SM_REQUIRE(pmsgchunk != NULL);
SM_REQUIRE(pmsglen != NULL);
*pmsgchunk = NULL;
*pmsglen = 0;
rcode = pm_open_rd(pmse_ctx);
sm_io_fprintf(smioerr,
"sev=DBG, func=tpm1_msg_rplc, seid=%s, flags=%#x\n",
pmse_ctx->pmse_se_id, pmt_ctx.pmt_flags);
if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_RPLC_MSG) &&
PMT_IS_FLAG(&pmt_ctx, PMT_FL_RFD_OPEN) &&
!PMT_IS_FLAG(&pmt_ctx, PMT_FL_RD_FAIL)
)
{
ssize_t rd;
char *msg;
msg = pmt_ctx.pmt_buf;
if (NULL == msg)
rd = 0;
else
rd = read(pmt_ctx.pmt_rfd, msg, rbufsize);
if (rd <= 0) {
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rd=%#x\n",
pmse_ctx->pmse_se_id, rd);
pm_close_rd(pmse_ctx);
}
else {
if (rd >= rbufsize)
rcode = SMTP_R_CONT;
*pmsgchunk = (unsigned char *) msg;
*pmsglen = rd;
if (rd < rbufsize) {
msg[rd] = '\0';
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, rplcmsg=%.*#s, rd=%#x, rcode=%d\n",
pmse_ctx->pmse_se_id, (int) rd, msg, rd, rcode);
}
}
}
return rcode;
}
static sm_ret_T
tpm1_signal(pmg_ctx_P pmg_ctx, int sig)
{
sm_io_fprintf(smioerr, "sev=DBG, signal=%d\n", sig);
return SM_SUCCESS;
}
static sm_ret_T
tpm1_abort(pmse_ctx_P pmse_ctx)
{
sm_io_fprintf(smioerr,
"sev=DBG, seid=%s, where=abort_ta\n",
pmse_ctx->pmse_se_id);
pm_close_wr(pmse_ctx);
pm_close_rd(pmse_ctx);
pmt_rcpts_free(&pmt_ctx);
return SM_SUCCESS;
}
/*
** USAGE -- Print usage message to smioerr
**
** Parameters:
** prg -- program name
**
** Returns:
** exits
*/
static void
usage(const char *prg)
{
sm_io_fprintf(smioerr, "usage: %s [options]\n"
"-A cap add capabilities\n"
" R get RCPT status\n"
" r send generated Received: header in message\n"
"-B file use content of file to replace message\n"
"-b n use n as buffer size when reading file (-B)\n"
"-C cap disable capabilities\n"
" cap is a sequence of characters:\n"
" C: CNNCT\n"
" E: EHLO\n"
#if 0
" S: STTLS\n"
" A: AUTH\n"
#endif
" M: MAIL\n"
" R: RCPT\n"
" D: DATA\n"
" B: MSG\n"
"-c n set requested capabilities\n"
"-D n delete header n\n"
#if PMILTER_DEBUG
"-d n set debug level\n"
#endif
"-e pos=header replace header at pos\n"
"-F sender change MAIL (must be in RFC 2821 format)\n"
"-f name write msg to file\n"
"-i pos=header insert a header at pos\n"
"-M action=header prepend or append a header\n"
" action=p/a\n"
"-m macros set list of macros to receive\n"
" H:hostname\n"
" M:message-id\n"
" S:session-id\n"
" T:transaction-id\n"
"-R stage=reply set SMTP reply text for stage\n"
" stage:\n"
" c: new session (connect)\n"
" h: HELO\n"
" m: MAIL\n"
" r: RCPT\n"
" d: DATA\n"
" D: Deferred RCPT status (RSAD)\n"
" B: Body\n"
" b: End of Body\n"
" R: message replacement\n"
"-r stage=rcode set SMTP reply code for stage to rcode\n"
" stage: see above\n"
" rcode=%d will invoke exit() to simulate a fatal error,\n"
" which can be applied to\n"
" R: replace message (see -B)\n"
"-T recipient add recipient (must be in RFC 2821 format)\n"
"-w stage=delay set a delay for replying in stage\n"
" stage: see above\n"
"-X idx=recipient remove recipient (must be in RFC 2821 format)\n"
, prg
, PMT_EXIT_CODE
);
exit(EX_USAGE);
}
static pmilter_T
pmilter =
{
"t-pmilter-1",
LPMILTER_VERSION,
SM_SCAP_PM_BASIC,
0,
0,
0,
tpm1_negotiate,
tpm1_connect,
tpm1_helo,
tpm1_mail,
tpm1_rcpt,
tpm1_data,
tpm1_msg,
tpm1_eom,
tpm1_abort,
tpm1_close,
tpm1_unknown,
tpm1_signal,
(pmfi_starttls_F) NULL,
(pmfi_auth_F) NULL,
tpm1_msg_rplc,
tpm1_msg_rplc_stat
};
/*
** MAIN -- PMILTER test server
**
** Parameters:
** argc -- number of arguments
** argv -- vector of arguments
**
** Returns:
** exit code
*/
int
main(int argc, char *argv[])
{
sm_ret_T ret;
int c, ch, stage;
unsigned int u, u2;
unsigned long l;
uint32_t major, minor, patchlevel;
pmg_ctx_P pmg_ctx;
char *prg, *str;
sm_hdrmod_P sm_hdrmod;
prg = argv[0];
pmg_ctx = NULL;
if (getuid() == 0 || geteuid() == 0) {
sm_io_fprintf(smioerr,
"%s: ERROR: do not run this as super-user!\n",
prg);
exit(EX_USAGE);
}
/* initialize test context */
sm_memzero(&pmt_ctx, sizeof(pmt_ctx));
for (c = 0; c < SM_STAGES; c++)
pmt_ctx.pmt_rcode[c] = SMTP_R_CONT;
pmt_ctx.pmt_fname[0] = '\0';
pmt_ctx.pmt_fd = INVALID_FD;
pmt_ctx.pmt_cap = SM_SCAP_PM_BASIC;
PMT_RCPTS_INIT(&pmt_ctx.pmt_rcpts);
#if SM_HEAP_CHECK
SmHeapCheck = 1;
if (HEAP_CHECK)
sm_heap_report(smioerr, 3);
#endif
while ((c = getopt(argc, argv, "2A:B:b:C:c:D:d:e:F:f:i:M:m:R:r:T:w:X:"))
!= -1)
{
switch (c) {
case '2':
pmt_ctx.pmt_flags |= PMT_FL_M_2ND;
break;
case 'B':
strlcpy(pmt_ctx.pmt_rfname, optarg,
sizeof(pmt_ctx.pmt_rfname));
PMT_SET_FLAG(&pmt_ctx, PMT_FL_RPLC_MSG);
pmt_ctx.pmt_cap |= SM_SCAP_PM_MSGREPLACE;
break;
case 'b':
rbufsize = strtoul(optarg, NULL, 0);
break;
case 'A':
for (u = 0, u2 = 0; (ch = optarg[u]) != '\0'; u++) {
switch (ch) {
case 'r': u2 |= SM_SCAP_PM_SND_RCVD;
break;
case 'R': u2 |= SM_SCAP_PM_RCPT_ST;
pmt_ctx.pmt_flags |= PMT_FL_R_ST;
break;
default: usage(prg); break;
}
}
pmt_ctx.pmt_cap |= u2;
break;
case 'C':
for (u = 0, u2 = 0; (ch = optarg[u]) != '\0'; u++) {
switch (ch) {
case 'C': u2 |= SM_SCAP_PM_CNNCT; break;
case 'E': u2 |= SM_SCAP_PM_EHLO; break;
case 'S': u2 |= SM_SCAP_PM_STTLS; break;
case 'A': u2 |= SM_SCAP_PM_AUTH; break;
case 'M': u2 |= SM_SCAP_PM_MAIL; break;
case 'R': u2 |= SM_SCAP_PM_RCPT; break;
case 'D': u2 |= SM_SCAP_PM_DATA; break;
case 'B': u2 |= SM_SCAP_PM_MSG; break;
default: usage(prg); break;
}
}
pmt_ctx.pmt_cap &= ~u2;
break;
case 'c':
pmt_ctx.pmt_cap = strtoul(optarg, NULL, 0);
break;
case 'D':
sm_hdrmod = NULL;
ret = sm_hdrmod_new(&sm_hdrmodhd, true, &sm_hdrmod);
SM_TEST(SM_SUCCESS == ret);
if (SM_SUCCESS != ret)
return(EX_OSERR);
SM_TEST(sm_hdrmod != NULL);
if (NULL == sm_hdrmod)
return(EX_OSERR);
sm_hdrmod->sm_hm_type = SM_HM_TYPE_REMOVE;
sm_hdrmod->sm_hm_pos = strtoul(optarg, NULL, 0);
SM_TEST(SM_SUCCESS == ret);
if (SM_SUCCESS != ret)
return(EX_OSERR);
pmt_ctx.pmt_cap |= SM_SCAP_PM_HDRMOD;
break;
case 'd':
#if PMILTER_DEBUG
pm_debug = strtoul(optarg, NULL, 0);
#endif
#if RCBCOMM_DEBUG
rcbcomm_debug = strtoul(optarg, NULL, 0);
#endif
break;
case 'e':
l = strtoul(optarg, &str, 0);
if (ULONG_MAX == l || l > UINT_MAX)
usage(argv[0]);
if (NULL == str || *str != '=')
usage(argv[0]);
sm_hdrmod = NULL;
str = getreply(str + 1);
ret = sm_hdrmod_new(&sm_hdrmodhd, true, &sm_hdrmod);
SM_TEST(SM_SUCCESS == ret);
if (SM_SUCCESS != ret)
return(EX_OSERR);
SM_TEST(sm_hdrmod != NULL);
if (NULL == sm_hdrmod)
return(EX_OSERR);
sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)str
, strlen(str));
sm_hdrmod->sm_hm_pos = l;
sm_hdrmod->sm_hm_type = SM_HM_TYPE_REPLACE;
free(str);
pmt_ctx.pmt_cap |= SM_SCAP_PM_HDRMOD;
break;
case 'F':
mail_new = strdup(optarg);
if (NULL == mail_new) {
sm_io_fprintf(smioerr,
"sev=ERROR, strdup=NULL");
exit(EX_OSERR);
}
pmt_ctx.pmt_cap |= SM_SCAP_PM_MAILMOD;
break;
case 'f':
strlcpy(pmt_ctx.pmt_fname, optarg,
sizeof(pmt_ctx.pmt_fname));
PMT_SET_FLAG(&pmt_ctx, PMT_FL_W2F);
break;
case 'i':
l = strtoul(optarg, &str, 0);
if (ULONG_MAX == l || l > UINT_MAX)
usage(argv[0]);
if (NULL == str || *str != '=')
usage(argv[0]);
sm_hdrmod = NULL;
str = getreply(str + 1);
ret = sm_hdrmod_new(&sm_hdrmodhd, true, &sm_hdrmod);
SM_TEST(SM_SUCCESS == ret);
if (SM_SUCCESS != ret)
return(EX_OSERR);
SM_TEST(sm_hdrmod != NULL);
if (NULL == sm_hdrmod)
return(EX_OSERR);
sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)str
, strlen(str));
sm_hdrmod->sm_hm_pos = l;
sm_hdrmod->sm_hm_type = SM_HM_TYPE_INSERT;
free(str);
pmt_ctx.pmt_cap |= SM_SCAP_PM_HDRMOD;
break;
case 'M':
if (NULL == optarg ||
(optarg[0] != 'p' && optarg[0] != 'a')
|| optarg[1] != '=')
{
usage(prg);
break;
}
str = getreply(optarg + 2);
SM_TEST(str != NULL);
if (NULL == str)
return(EX_OSERR);
sm_hdrmod = NULL;
ret = sm_hdrmod_new(&sm_hdrmodhd, true, &sm_hdrmod);
SM_TEST(SM_SUCCESS == ret);
if (SM_SUCCESS != ret)
return(EX_OSERR);
SM_TEST(sm_hdrmod != NULL);
if (NULL == sm_hdrmod)
return(EX_OSERR);
sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)str
, strlen(str));
SM_TEST(sm_hdrmod->sm_hm_hdr != NULL);
if (NULL == sm_hdrmod->sm_hm_hdr)
return(EX_OSERR);
if (optarg[0] == 'p')
sm_hdrmod->sm_hm_type = SM_HM_TYPE_PREPEND;
else
sm_hdrmod->sm_hm_type = SM_HM_TYPE_APPEND;
free(str);
pmt_ctx.pmt_cap |= SM_SCAP_PM_HDRMOD;
break;
case 'm':
for (u = 0; (ch = optarg[u]) != '\0'; u++) {
switch (ch) {
case 'H':
pmt_ctx.pmt_flags |= PMT_FL_M_HOSTN;
break;
case 'M':
pmt_ctx.pmt_flags |= PMT_FL_M_MSGID;
break;
case 'S':
pmt_ctx.pmt_flags |= PMT_FL_M_SEID;
break;
case 'T':
pmt_ctx.pmt_flags |= PMT_FL_M_TAID;
break;
default: usage(prg); break;
}
}
break;
case 'R':
if (optarg != NULL && ISALPHA(optarg[0])
&& optarg[1] == '=')
u2 = 2;
else {
usage(prg);
break;
}
str = getreply(optarg + u2);
if (NULL == str) {
sm_io_fprintf(smioerr,
"sev=ERROR, getreply=NULL");
exit(1);
}
stage = sm_getstage(optarg[0]);
if (stage < 0 || stage >= SM_STAGES) {
usage(prg);
/* NOTREACHED */
SM_ASSERT(false);
}
pmt_ctx.pmt_reply[stage] = str;
if (SM_STAGE_MSG == stage)
pmt_ctx.pmt_cap |= SM_SCAP_PM_MSG_RC;
break;
case 'r':
if (optarg != NULL && ISALPHA(optarg[0])
&& optarg[1] == '=')
u2 = 2;
else {
usage(prg);
break;
}
u = (unsigned int) strtoul(optarg + u2, NULL, 0);
stage = sm_getstage(optarg[0]);
if (stage < 0 || stage >= SM_STAGES) {
usage(prg);
/* NOTREACHED */
SM_ASSERT(false);
}
pmt_ctx.pmt_rcode[stage] = u;
if (SM_STAGE_MSG == stage)
pmt_ctx.pmt_cap |= SM_SCAP_PM_MSG_RC;
break;
case 'T':
rcpt_add_pa = strdup(optarg);
if (NULL == rcpt_add_pa) {
sm_io_fprintf(smioerr,
"sev=ERROR, strdup=NULL");
exit(EX_OSERR);
}
pmt_ctx.pmt_cap |= SM_SCAP_PM_RCPTMOD;
break;
case 'w':
if (optarg != NULL && ISALPHA(optarg[0])
&& optarg[1] == '=')
u2 = 2;
else {
usage(prg);
break;
}
u = (unsigned int) strtoul(optarg + u2, NULL, 0);
stage = sm_getstage(optarg[0]);
if (stage < 0 || stage >= SM_STAGES) {
usage(prg);
/* NOTREACHED */
SM_ASSERT(false);
}
pmt_ctx.pmt_delay[stage] = u;
break;
case 'X':
rcpt_del_idx = strtoul(optarg, &str, 0);
if (NULL == str || *str != '=') {
usage(prg);
/* NOTREACHED */
SM_ASSERT(false);
}
rcpt_del_pa = strdup(str + 1);
if (NULL == rcpt_del_pa) {
sm_io_fprintf(smioerr,
"sev=ERROR, strdup=NULL");
exit(EX_OSERR);
}
pmt_ctx.pmt_cap |= SM_SCAP_PM_RCPTMOD;
break;
default:
usage(prg);
break;
}
}
ret = sm_pmfi_init(&pmg_ctx);
if (sm_is_err(ret)) {
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_init=%x\n", ret);
goto error;
}
sm_test_begin(argc, argv, "test pmilter 1");
argc -= optind;
argv += optind;
/* XXX HACK */
if (argc > 0)
pmg_ctx->pmg_sockname = argv[0];
else
pmg_ctx->pmg_sockname = "pmilter.sock";
ret = sm_pmfi_version(pmg_ctx, &major, &minor, &patchlevel);
SM_TEST(SM_SUCCESS == ret);
SM_TEST(1 == major);
SM_TEST(0 == minor);
SM_TEST(0 == patchlevel);
ret = sm_pmfi_set_ctx_g(pmg_ctx, &pmt_ctx);
SM_TEST(SM_SUCCESS == ret);
ret = sm_pmfi_start(pmg_ctx, &pmilter);
if (sm_is_err(ret)) {
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_start=%x\n", ret);
goto error;
}
#if SM_HEAP_CHECK
if (HEAP_CHECK)
sm_heap_report(smioerr, 3);
#endif
return sm_test_end();
error:
/* ignore shutdown errors... */
(void) sm_pmilt_stop(pmg_ctx);
/* select an appropriate error here... */
return sm_error_value(ret);
}
#else /* MTA_USE_PMILTER */
int
main(int argc, char *argv[])
{
return 0;
}
#endif /* MTA_USE_PMILTER */
syntax highlighted by Code2HTML, v. 0.9.1