/* * 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: example-pmilter-0.c,v 1.12 2006/10/05 04:27:37 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/smreplycodes.h" #include "sm/pmilter.h" #include "sm/pmfdef.h" #include "sm/pmfapi.h" #if MTA_USE_PMILTER /* global context */ typedef struct pmexg_ctx_S pmexg_ctx_T, *pmexg_ctx_P; struct pmexg_ctx_S { uint32_t pmexg_cap; uint32_t pmexg_flags; }; /* context per SMTP server */ typedef struct pmexss_ctx_S pmexss_ctx_T, *pmexss_ctx_P; struct pmexss_ctx_S { pmexg_ctx_P pmexss_pmexg_ctx; uint32_t pmexss_flags; }; /* context per SMTP session */ typedef struct pmexse_ctx_S pmexse_ctx_T, *pmexse_ctx_P; struct pmexse_ctx_S { pmexss_ctx_P pmexse_pmexss_ctx; uint32_t pmexse_flags; int pmexse_fd; char pmexse_fname[PATH_MAX]; }; #define PMSE_FL_NONE 0x0000 #define PMG_FL_W2F 0x0001 /* write to file */ #define PMSE_FL_W2F PMG_FL_W2F #define PMSE_FL_FD_OPEN 0x0002 /* fd is ok to use */ #define PMSE_FL_FD_FAIL 0x0004 /* don't try to open file */ #define PMSE_FL_WR_FAIL 0x0008 /* write failed before */ #define PMSE_SET_FLAG(pmexse_ctx, fl) (pmexse_ctx)->pmexse_flags |= (fl) #define PMSE_CLR_FLAG(pmexse_ctx, fl) (pmexse_ctx)->pmexse_flags &= ~(fl) #define PMSE_IS_FLAG(pmexse_ctx, fl) (((pmexse_ctx)->pmexse_flags & (fl)) != 0) static sm_ret_T expm1_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; pmexss_ctx_P pmexss_ctx; pmexg_ctx_P pmexg_ctx; pmexg_ctx = sm_pmfi_get_ctx_g_ss(pmss_ctx); pmexss_ctx = (pmexss_ctx_P) malloc(sizeof(*pmexss_ctx)); if (pmexss_ctx == NULL) return SMTP_R_TEMP; sm_pmfi_set_ctx_ss(pmss_ctx, pmexss_ctx); pmexss_ctx->pmexss_pmexg_ctx = pmexg_ctx; pmexss_ctx->pmexss_flags = pmexg_ctx->pmexg_flags; *pm_cap = pmexg_ctx->pmexg_cap; *pm_fct = 0; *pm_feat = 0; *pm_misc = 0; ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_CONNECT, PMM_SRVHOSTNAME, PMM_SEID, PMM_END); if (sm_is_err(ret)) sm_io_fprintf(smioerr, "sev=ERROR, where=expm1_negotiate, sm_pmfi_setmaclist=%#x, macro=hostname\n", ret); ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_DOT, PMM_DOT_MSGID, PMM_END); if (sm_is_err(ret)) sm_io_fprintf(smioerr, "sev=ERROR, where=expm1_negotiate, sm_pmfi_setmaclist=%#x, macro=msg_id\n", ret); sm_io_fprintf(smioerr, "where=expm1_negotiate, cap=%#X\n", pmexg_ctx->pmexg_cap); return SM_SUCCESS; } static sfsistat_T expm1_connect(pmse_ctx_P pmse_ctx, const char *hostname, sm_sockaddr_T *hostaddr) { sm_ret_T ret; pmexse_ctx_P pmexse_ctx; pmexss_ctx_P pmexss_ctx; char *srvhostname; /* create session context */ pmexse_ctx = (pmexse_ctx_P) malloc(sizeof(*pmexse_ctx)); if (pmexse_ctx == NULL) return SMTP_R_TEMP; pmexss_ctx = sm_pmfi_get_ctx_ss_se(pmse_ctx); pmexse_ctx->pmexse_pmexss_ctx = pmexss_ctx; pmexse_ctx->pmexse_flags = pmexss_ctx->pmexss_flags; sm_pmfi_set_ctx_se(pmse_ctx, pmexse_ctx); sm_io_fprintf(smioerr, "client=%s\n", hostname); ret = sm_pmfi_getmac(pmse_ctx, PMM_SRVHOSTNAME, &srvhostname); sm_io_fprintf(smioerr, "srvhostname=%s, ret=%#X\n", srvhostname, ret); return SMTP_R_CONT; } static sm_ret_T expm1_close(pmse_ctx_P pmse_ctx) { pmexse_ctx_P pmexse_ctx; sm_io_fprintf(smioerr, "where=close\n"); pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx); if (pmexse_ctx != NULL) sm_free(pmexse_ctx); sm_pmfi_set_ctx_se(pmse_ctx, NULL); return SM_SUCCESS; } static sfsistat_T expm1_helo(pmse_ctx_P pmse_ctx, const char *helohost, bool ehlo) { sm_io_fprintf(smioerr, "helo=%s\n", helohost); return SMTP_R_CONT; } static sfsistat_T expm1_mail(pmse_ctx_P pmse_ctx, const char *mail, char **argv) { sm_io_fprintf(smioerr, "mail=%s, argv[0]=%s\n", mail, (argv != NULL && argv[0] != NULL) ? argv[0] : "NULL"); return SMTP_R_CONT; } static sfsistat_T expm1_rcpt(pmse_ctx_P pmse_ctx, const char *rcpt, char **argv) { sm_io_fprintf(smioerr, "rcpt=%s, argv[0]=%s\n", rcpt, (argv != NULL && argv[0] != NULL) ? argv[0] : "NULL"); return SMTP_R_CONT; } static sfsistat_T expm1_data(pmse_ctx_P pmse_ctx) { sm_io_fprintf(smioerr, "where=data\n"); return SMTP_R_CONT; } static void pm_open(pmse_ctx_P pmse_ctx, pmexse_ctx_P pmexse_ctx) { if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) && !PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) && !PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_FAIL)) { (void) strlcpy(pmexse_ctx->pmexse_fname, "temp.XXXXXX", sizeof(pmexse_ctx->pmexse_fname)); pmexse_ctx->pmexse_fd = mkstemp(pmexse_ctx->pmexse_fname); if (pmexse_ctx->pmexse_fd >= 0) PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN); else PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_FD_FAIL); sm_io_fprintf(smioerr, "where=open, mkstemp=%d\n", pmexse_ctx->pmexse_fd); } } static sfsistat_T expm1_unknown(pmse_ctx_P pmse_ctx, const char *cmd) { sm_io_fprintf(smioerr, "where=unknown, cmd=%s\n", cmd); return SMTP_R_CONT; } static sm_ret_T expm1_abort(pmse_ctx_P pmse_ctx) { sm_io_fprintf(smioerr, "where=abort_ta\n"); return SM_SUCCESS; } static sfsistat_T expm1_msg(pmse_ctx_P pmse_ctx, unsigned char *buf, size_t len) { pmexse_ctx_P pmexse_ctx; pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx); pm_open(pmse_ctx, pmexse_ctx); if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) && PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) && !PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL) && buf != NULL && len > 0 ) { ssize_t written; written = write(pmexse_ctx->pmexse_fd, buf, len); if (written == -1) { sm_io_fprintf(smioerr, "sev=ERROR, where=msg, write=%d\n", (int) written); PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL); close(pmexse_ctx->pmexse_fd); pmexse_ctx->pmexse_fd = INVALID_FD; PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN); } } sm_io_fprintf(smioerr, "where=msg, len=%lu\n", (unsigned long) len); return SM_SUCCESS; } static sfsistat_T expm1_eom(pmse_ctx_P pmse_ctx) { sm_ret_T ret; bool writefailed; pmexse_ctx_P pmexse_ctx; char *msgid; pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx); writefailed = PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) && PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL); if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) && PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) && !PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL)) { int r; r = close(pmexse_ctx->pmexse_fd); sm_io_fprintf(smioerr, "where=dot, write=%d\n", r); pmexse_ctx->pmexse_fd = INVALID_FD; PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN); } ret = sm_pmfi_getmac(pmse_ctx, PMM_DOT_MSGID, &msgid); sm_io_fprintf(smioerr, "msgid=%s, ret=%#X\n", msgid, ret); sm_io_fprintf(smioerr, "where=dot\n"); return SMTP_R_CONT; } static sm_ret_T expm1_signal(pmg_ctx_P pmg_ctx, int sig) { sm_io_fprintf(smioerr, "signal=%d\n", sig); 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" "-f name write msg to file\n" , prg ); exit(EX_USAGE); } static pmilter_T pmilter = { "example-pmilter-0", LPMILTER_VERSION, SM_SCAP_PM_ALL, 0, 0, 0, expm1_negotiate, expm1_connect, expm1_helo, expm1_mail, expm1_rcpt, expm1_data, expm1_msg, expm1_eom, expm1_abort, expm1_close, expm1_unknown, expm1_signal }; /* ** 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; uint32_t major, minor, patchlevel; pmg_ctx_P pmg_ctx; char *prg; pmexg_ctx_T pmexg_ctx; 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(&pmexg_ctx, sizeof(pmexg_ctx)); pmexg_ctx.pmexg_cap = SM_SCAP_PM_ALL; while ((c = getopt(argc, argv, "fh")) != -1) { switch (c) { case 'f': pmexg_ctx.pmexg_flags |= PMG_FL_W2F; break; default: usage(prg); break; } } argc -= optind; argv += optind; 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; } ret = sm_pmfi_version(pmg_ctx, &major, &minor, &patchlevel); if (sm_is_err(ret)) { sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_version=%#x\n", ret); goto error; } if (major != LPMILTER_VERSION_MAJOR) { sm_io_fprintf(smioerr, "sev=ERROR, status=version_mismatch, compile_time=%d, run_time=%d\n" , LPMILTER_VERSION_MAJOR, major); goto error; } ret = sm_pmfi_setconn(pmg_ctx, "pmilter.sock"); if (sm_is_err(ret)) { sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_setconn=%#x\n", ret); goto error; } ret = sm_pmfi_set_ctx_g(pmg_ctx, &pmexg_ctx); 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; } return 0; error: /* select an appropriate error here... */ return sm_error_value(ret); } #else /* MTA_USE_PMILTER */ int main(int argc, char *argv[]) { sm_io_fprintf(smioerr, "sev=ERROR, MTA_USE_PMILTER=not_set\n"); return -1; } #endif /* MTA_USE_PMILTER */