/* * 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 */