A Sample Filter
The following sample logs each message to a separate temporary file,
adds a recipient given with the -a flag, and rejects a disallowed
recipient address given with the -r flag. It recognizes the following
options:
-p port | The port through which the MTA will connect to the filter. |
-t sec | The timeout value. |
-r addr | A recipient to reject. |
-a addr | A recipient to add. |
#include "mfapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <unistd.h>
#ifndef bool
#define bool char
#define TRUE 1
#define FALSE 0
#endif
extern int errno;
struct mlfiPriv
{
char *mlfi_fname;
char *mlfi_connectfrom;
char *mlfi_helofrom;
FILE *mlfi_fp;
};
#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
extern sfsistat mlfi_cleanup(SMFICTX *, bool);
/* recipients to add and reject (set with -a and -r options) */
char *add, *reject;
sfsistat
mlfi_connect(ctx, hostname, hostaddr)
SMFICTX *ctx;
char *hostname;
_SOCK_ADDR *hostaddr;
{
struct mlfiPriv *priv;
char *ident;
/* allocate some private memory */
priv = malloc(sizeof *priv);
if (priv == NULL)
{
/* can't accept this message right now */
return SMFIS_TEMPFAIL;
}
memset(priv, '\0', sizeof *priv);
/* save the private data */
smfi_setpriv(ctx, priv);
ident = smfi_getsymval(ctx, "_");
if(!ident) ident = "???";
if(!(priv->mlfi_connectfrom = strdup(ident))) {
return SMFIS_TEMPFAIL;
}
/* Continue processing. */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_helo(ctx, helohost)
SMFICTX *ctx;
char *helohost;
{
char *tls;
char *buf;
struct mlfiPriv *priv = MLFIPRIV;
tls = smfi_getsymval(ctx, "{tls_version}");
if(!tls) tls = "No TLS";
if(!helohost) helohost = "???";
if(!(buf = (char*)malloc(strlen(tls) + strlen(helohost) + 3))) {
return SMFIS_TEMPFAIL;
}
sprintf(buf, "%s, %s", helohost, tls);
if(priv->mlfi_helofrom)
free(priv->mlfi_helofrom);
priv->mlfi_helofrom = buf;
/* Continue processing. */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_envfrom(ctx, argv)
SMFICTX *ctx;
char **argv;
{
struct mlfiPriv *priv = MLFIPRIV;
char *mailaddr = smfi_getsymval(ctx, "{mail_addr}");
int argc = 0;
/* open a file to store this message */
priv->mlfi_fname = strdup("/tmp/msg.XXXXXX");
mkstemp(priv->mlfi_fname);
if (priv->mlfi_fname == NULL)
return SMFIS_TEMPFAIL;
if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL)
{
free(priv->mlfi_fname);
return SMFIS_TEMPFAIL;
}
/* count the arguments */
while(*argv++) ++argc;
/* log the connection information we stored earlier: */
if(fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n",
priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF) {
(void) mlfi_cleanup(ctx, FALSE);
return SMFIS_TEMPFAIL;
}
/* log the sender */
if(fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n",
mailaddr?mailaddr:"???", argc,
(argc == 1)?"":"s")
== EOF) {
(void) mlfi_cleanup(ctx, FALSE);
return SMFIS_TEMPFAIL;
}
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_envrcpt(ctx, argv)
SMFICTX *ctx;
char **argv;
{
struct mlfiPriv *priv = MLFIPRIV;
char *rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}");
int argc = 0;
/* count the arguments */
while(*argv++) ++argc;
/* log this recipient */
if(reject && rcptaddr && (strcmp(rcptaddr, reject) == 0)) {
if(fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n", rcptaddr)
== EOF) {
(void) mlfi_cleanup(ctx, FALSE);
return SMFIS_TEMPFAIL;
}
return SMFIS_REJECT;
}
if(fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n",
rcptaddr?rcptaddr:"???", argc,
(argc == 1)?"":"s")
== EOF) {
(void) mlfi_cleanup(ctx, FALSE);
return SMFIS_TEMPFAIL;
}
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_header(ctx, headerf, headerv)
SMFICTX *ctx;
char *headerf;
unsigned char *headerv;
{
/* write the header to the log file */
fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv);
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_eoh(ctx)
SMFICTX *ctx;
{
/* output the blank line between the header and the body */
fprintf(MLFIPRIV->mlfi_fp, "\n");
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_body(ctx, bodyp, bodylen)
SMFICTX *ctx;
unsigned char *bodyp;
size_t bodylen;
{
/* output body block to log file */
int nwritten;
if ((nwritten = fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp)) != 1)
{
/* write failed */
perror("error logging body");
(void) mlfi_cleanup(ctx, FALSE);
return SMFIS_TEMPFAIL;
}
/* continue processing */
return SMFIS_CONTINUE;
}
sfsistat
mlfi_eom(ctx)
SMFICTX *ctx;
{
bool ok = TRUE;
/* change recipients, if requested */
if(add)
ok = ok && (smfi_addrcpt(ctx, add) == MI_SUCCESS);
return mlfi_cleanup(ctx, ok);
}
sfsistat
mlfi_abort(ctx)
SMFICTX *ctx;
{
return mlfi_cleanup(ctx, FALSE);
}
sfsistat
mlfi_cleanup(ctx, ok)
SMFICTX *ctx;
bool ok;
{
sfsistat rstat = SMFIS_CONTINUE;
struct mlfiPriv *priv = MLFIPRIV;
char *p;
char host[512];
char hbuf[1024];
if (priv == NULL)
return rstat;
/* close the archive file */
if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
{
/* failed; we have to wait until later */
fprintf(stderr, "Couldn't close archive file %s: %s\n",
priv->mlfi_fname, strerror(errno));
rstat = SMFIS_TEMPFAIL;
(void) unlink(priv->mlfi_fname);
}
else if (ok)
{
/* add a header to the message announcing our presence */
if (gethostname(host, sizeof host) < 0)
strncpy(host, "localhost", sizeof host);
p = strrchr(priv->mlfi_fname, '/');
if (p == NULL)
p = priv->mlfi_fname;
else
p++;
snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
smfi_addheader(ctx, "X-Archived", hbuf);
}
else
{
/* message was aborted -- delete the archive file */
fprintf(stderr, "Message aborted. Removing %s\n",
priv->mlfi_fname);
rstat = SMFIS_TEMPFAIL;
(void) unlink(priv->mlfi_fname);
}
/* release private memory */
free(priv->mlfi_fname);
/* return status */
return rstat;
}
sfsistat
mlfi_close(ctx)
SMFICTX *ctx;
{
struct mlfiPriv *priv = MLFIPRIV;
if(priv->mlfi_connectfrom)
free(priv->mlfi_connectfrom);
if(priv->mlfi_helofrom)
free(priv->mlfi_helofrom);
free(priv);
smfi_setpriv(ctx, NULL);
return SMFIS_CONTINUE;
}
struct smfiDesc smfilter =
{
"SampleFilter", /* filter name */
SMFI_VERSION, /* version code -- do not change */
SMFIF_ADDHDRS, /* flags */
mlfi_connect, /* connection info filter */
mlfi_helo, /* SMTP HELO command filter */
mlfi_envfrom, /* envelope sender filter */
mlfi_envrcpt, /* envelope recipient filter */
mlfi_header, /* header filter */
mlfi_eoh, /* end of header */
mlfi_body, /* body block filter */
mlfi_eom, /* end of message */
mlfi_abort, /* message aborted */
mlfi_close, /* connection cleanup */
};
static void
usage()
{
fprintf(stderr,
"Usage: sample [-p socket-addr] [-t timeout] [-r reject-addr] \n\
\t[-a accept-addr]\n");
}
int
main(argc, argv)
int argc;
char *argv[];
{
int retval;
char c;
const char *args = "p:t:r:a:h";
extern char *optarg;
/* Process command line options */
while ((c = getopt(argc, argv, args)) != (char)EOF)
{
switch (c)
{
case 'p':
if (optarg == NULL || *optarg == '\0')
{
(void) fprintf(stderr, "Illegal conn: %s\n",
optarg);
exit(EX_USAGE);
}
if(smfi_setconn(optarg) == MI_FAILURE)
{
(void) fputs("smfi_setconn failed", stderr);
exit(EX_SOFTWARE);
}
/*
** If we're using a local socket, make sure it doesn't
** already exist.
*/
if(strncmp(optarg, "unix:", 5) == 0)
unlink(optarg + 5);
else if(strncmp(optarg, "local:", 6) == 0)
unlink(optarg + 6);
break;
case 't':
if (optarg == NULL || *optarg == '\0')
{
(void) fprintf(stderr, "Illegal timeout: %s\n",
optarg);
exit(EX_USAGE);
}
if(smfi_settimeout(atoi(optarg)) == MI_FAILURE)
{
(void) fputs("smfi_settimeout failed", stderr);
exit(EX_SOFTWARE);
}
break;
case 'r':
if (optarg == NULL)
{
(void) fprintf(stderr, "Illegal reject rcpt: %s\n",
optarg);
exit(EX_USAGE);
}
reject = optarg;
break;
case 'a':
if (optarg == NULL)
{
(void) fprintf(stderr, "Illegal add rcpt: %s\n",
optarg);
exit(EX_USAGE);
}
add = optarg;
smfilter.xxfi_flags |= SMFIF_ADDRCPT;
break;
case 'h':
default:
usage();
exit(0);
}
}
if (smfi_register(smfilter) == MI_FAILURE)
{
fprintf(stderr, "smfi_register failed\n");
exit(EX_UNAVAILABLE);
}
retval = smfi_main();
return retval;
}
/* eof */
Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
All rights reserved.
By using this file, you agree to the terms and conditions set
forth in the LICENSE.