499 lines
11 KiB
HTML
499 lines
11 KiB
HTML
<html>
|
|
<head><title>A Sample Filter</title></head>
|
|
<body>
|
|
<h1>A Sample Filter</h1>
|
|
|
|
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>
|
|
<center>
|
|
<table border="1" cellpadding=2 cellspacing=1>
|
|
<tr><td><code>-p port</code></td><td>The port through which the MTA will connect to the filter.</td></tr>
|
|
<tr><td><code>-t sec</code></td><td>The timeout value.</td></tr>
|
|
<tr><td><code>-r addr</code></td><td>A recipient to reject.</td></tr>
|
|
<tr><td><code>-a addr</code></td><td>A recipient to add.</td></tr>
|
|
</table>
|
|
</center>
|
|
<hr>
|
|
<pre>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
#include <unistd.h>
|
|
|
|
#include "libmilter/mfapi.h"
|
|
|
|
#ifndef bool
|
|
# define bool int
|
|
# define TRUE 1
|
|
# define FALSE 0
|
|
#endif /* ! bool */
|
|
|
|
|
|
struct mlfiPriv
|
|
{
|
|
char *mlfi_fname;
|
|
char *mlfi_connectfrom;
|
|
char *mlfi_helofrom;
|
|
FILE *mlfi_fp;
|
|
};
|
|
|
|
#define MLFIPRIV ((struct mlfiPriv *) <a href="smfi_getpriv.html">smfi_getpriv</a>(ctx))
|
|
|
|
extern sfsistat mlfi_cleanup(SMFICTX *, bool);
|
|
|
|
/* recipients to add and reject (set with -a and -r options) */
|
|
char *add = NULL;
|
|
char *reject = NULL;
|
|
|
|
sfsistat
|
|
<a href="xxfi_connect.html">mlfi_connect</a>(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 */
|
|
<a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, priv);
|
|
|
|
ident = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "_");
|
|
if (ident == NULL)
|
|
ident = "???";
|
|
if ((priv->mlfi_connectfrom = strdup(ident)) == NULL)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
/* continue processing */
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_helo.html">mlfi_helo</a>(ctx, helohost)
|
|
SMFICTX *ctx;
|
|
char *helohost;
|
|
{
|
|
size_t len;
|
|
char *tls;
|
|
char *buf;
|
|
struct mlfiPriv *priv = MLFIPRIV;
|
|
|
|
tls = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{tls_version}");
|
|
if (tls == NULL)
|
|
tls = "No TLS";
|
|
if (helohost == NULL)
|
|
helohost = "???";
|
|
len = strlen(tls) + strlen(helohost) + 3;
|
|
if ((buf = (char*) malloc(len)) == NULL)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
snprintf(buf, len, "%s, %s", helohost, tls);
|
|
if (priv->mlfi_helofrom != NULL)
|
|
free(priv->mlfi_helofrom);
|
|
priv->mlfi_helofrom = buf;
|
|
|
|
/* continue processing */
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_envfrom.html">mlfi_envfrom</a>(ctx, argv)
|
|
SMFICTX *ctx;
|
|
char **argv;
|
|
{
|
|
struct mlfiPriv *priv = MLFIPRIV;
|
|
char *mailaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{mail_addr}");
|
|
int argc = 0;
|
|
|
|
/* open a file to store this message */
|
|
if ((priv->mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
if (mkstemp(priv->mlfi_fname) == -1)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
/* count the arguments */
|
|
while (*argv++ != NULL)
|
|
++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
|
|
<a href="xxfi_envrcpt.html">mlfi_envrcpt</a>(ctx, argv)
|
|
SMFICTX *ctx;
|
|
char **argv;
|
|
{
|
|
struct mlfiPriv *priv = MLFIPRIV;
|
|
char *rcptaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{rcpt_addr}");
|
|
int argc = 0;
|
|
|
|
/* count the arguments */
|
|
while (*argv++ != NULL)
|
|
++argc;
|
|
|
|
/* log this recipient */
|
|
if (reject != NULL && rcptaddr != NULL &&
|
|
(strcasecmp(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
|
|
<a href="xxfi_header.html">mlfi_header</a>(ctx, headerf, headerv)
|
|
SMFICTX *ctx;
|
|
char *headerf;
|
|
unsigned char *headerv;
|
|
{
|
|
/* write the header to the log file */
|
|
if (fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
/* continue processing */
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_eoh.html">mlfi_eoh</a>(ctx)
|
|
SMFICTX *ctx;
|
|
{
|
|
/* output the blank line between the header and the body */
|
|
if (fprintf(MLFIPRIV->mlfi_fp, "\n") == EOF)
|
|
{
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
/* continue processing */
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_body.html">mlfi_body</a>(ctx, bodyp, bodylen)
|
|
SMFICTX *ctx;
|
|
unsigned char *bodyp;
|
|
size_t bodylen;
|
|
{
|
|
struct mlfiPriv *priv = MLFIPRIV;
|
|
|
|
/* output body block to log file */
|
|
if (fwrite(bodyp, bodylen, 1, priv->mlfi_fp) != 1)
|
|
{
|
|
/* write failed */
|
|
fprintf(stderr, "Couldn't write file %s: %s\n",
|
|
priv->mlfi_fname, strerror(errno));
|
|
(void) mlfi_cleanup(ctx, FALSE);
|
|
return SMFIS_TEMPFAIL;
|
|
}
|
|
|
|
/* continue processing */
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_eom.html">mlfi_eom</a>(ctx)
|
|
SMFICTX *ctx;
|
|
{
|
|
bool ok = TRUE;
|
|
|
|
/* change recipients, if requested */
|
|
if (add != NULL)
|
|
ok = (<a href="smfi_addrcpt.html">smfi_addrcpt</a>(ctx, add) == MI_SUCCESS);
|
|
return mlfi_cleanup(ctx, ok);
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_abort.html">mlfi_abort</a>(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)
|
|
snprintf(host, sizeof host, "localhost");
|
|
p = strrchr(priv->mlfi_fname, '/');
|
|
if (p == NULL)
|
|
p = priv->mlfi_fname;
|
|
else
|
|
p++;
|
|
snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
|
|
if (<a href="smfi_addheader.html">smfi_addheader</a>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
|
|
{
|
|
/* failed; we have to wait until later */
|
|
fprintf(stderr,
|
|
"Couldn't add header: X-Archived: %s\n",
|
|
hbuf);
|
|
ok = FALSE;
|
|
rstat = SMFIS_TEMPFAIL;
|
|
(void) unlink(priv->mlfi_fname);
|
|
}
|
|
}
|
|
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 */
|
|
if (priv->mlfi_fname != NULL)
|
|
free(priv->mlfi_fname);
|
|
|
|
/* return status */
|
|
return rstat;
|
|
}
|
|
|
|
sfsistat
|
|
<a href="xxfi_close.html">mlfi_close</a>(ctx)
|
|
SMFICTX *ctx;
|
|
{
|
|
struct mlfiPriv *priv = MLFIPRIV;
|
|
|
|
if (priv == NULL)
|
|
return SMFIS_CONTINUE;
|
|
if (priv->mlfi_connectfrom != NULL)
|
|
free(priv->mlfi_connectfrom);
|
|
if (priv->mlfi_helofrom != NULL)
|
|
free(priv->mlfi_helofrom);
|
|
free(priv);
|
|
<a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, NULL);
|
|
return SMFIS_CONTINUE;
|
|
}
|
|
|
|
struct smfiDesc smfilter =
|
|
{
|
|
"SampleFilter", /* filter name */
|
|
SMFI_VERSION, /* version code -- do not change */
|
|
SMFIF_ADDHDRS, /* flags */
|
|
<a href="xxfi_connect.html">mlfi_connect</a>, /* connection info filter */
|
|
<a href="xxfi_helo.html">mlfi_helo</a>, /* SMTP HELO command filter */
|
|
<a href="xxfi_envfrom.html">mlfi_envfrom</a>, /* envelope sender filter */
|
|
<a href="xxfi_envrcpt.html">mlfi_envrcpt</a>, /* envelope recipient filter */
|
|
<a href="xxfi_header.html">mlfi_header</a>, /* header filter */
|
|
<a href="xxfi_eoh.html">mlfi_eoh</a>, /* end of header */
|
|
<a href="xxfi_body.html">mlfi_body</a>, /* body block filter */
|
|
<a href="xxfi_eom.html">mlfi_eom</a>, /* end of message */
|
|
<a href="xxfi_abort.html">mlfi_abort</a>, /* message aborted */
|
|
<a href="xxfi_close.html">mlfi_close</a>, /* connection cleanup */
|
|
};
|
|
|
|
static void
|
|
usage(prog)
|
|
char *prog;
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
|
|
prog);
|
|
}
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
bool setconn = FALSE;
|
|
int c;
|
|
const char *args = "p:t:r:a:h";
|
|
extern char *optarg;
|
|
|
|
/* Process command line options */
|
|
while ((c = getopt(argc, argv, args)) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'p':
|
|
if (optarg == NULL || *optarg == '\0')
|
|
{
|
|
(void) fprintf(stderr, "Illegal conn: %s\n",
|
|
optarg);
|
|
exit(EX_USAGE);
|
|
}
|
|
if (<a href="smfi_setconn.html">smfi_setconn</a>(optarg) == MI_FAILURE)
|
|
{
|
|
(void) fprintf(stderr,
|
|
"smfi_setconn failed\n");
|
|
exit(EX_SOFTWARE);
|
|
}
|
|
|
|
/*
|
|
** If we're using a local socket, make sure it
|
|
** doesn't already exist. Don't ever run this
|
|
** code as root!!
|
|
*/
|
|
|
|
if (strncasecmp(optarg, "unix:", 5) == 0)
|
|
unlink(optarg + 5);
|
|
else if (strncasecmp(optarg, "local:", 6) == 0)
|
|
unlink(optarg + 6);
|
|
setconn = TRUE;
|
|
break;
|
|
|
|
case 't':
|
|
if (optarg == NULL || *optarg == '\0')
|
|
{
|
|
(void) fprintf(stderr, "Illegal timeout: %s\n",
|
|
optarg);
|
|
exit(EX_USAGE);
|
|
}
|
|
if (<a href="smfi_settimeout.html">smfi_settimeout</a>(atoi(optarg)) == MI_FAILURE)
|
|
{
|
|
(void) fprintf(stderr,
|
|
"smfi_settimeout failed\n");
|
|
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(argv[0]);
|
|
exit(EX_USAGE);
|
|
}
|
|
}
|
|
if (!setconn)
|
|
{
|
|
fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
|
|
usage(argv[0]);
|
|
exit(EX_USAGE);
|
|
}
|
|
if (<a href="smfi_register.html">smfi_register</a>(smfilter) == MI_FAILURE)
|
|
{
|
|
fprintf(stderr, "smfi_register failed\n");
|
|
exit(EX_UNAVAILABLE);
|
|
}
|
|
return <a href="smfi_main.html">smfi_main</a>();
|
|
}
|
|
|
|
/* eof */
|
|
|
|
</pre>
|
|
<hr size="1">
|
|
<font size="-1">
|
|
Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers.
|
|
All rights reserved.
|
|
<br>
|
|
By using this file, you agree to the terms and conditions set
|
|
forth in the LICENSE.
|
|
</font>
|
|
</body>
|
|
</html>
|