/* $Id: milter-regex.c,v 1.24 2007/06/10 16:49:55 ca Exp $ */
/*
* Copyright (c) 2003-2004 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
static const char rcsid[] = "$Id: milter-regex.c,v 1.24 2007/06/10 16:49:55 ca Exp $";
#include "sm/generic.h"
SM_RCSID("@(#)$Id: milter-regex.c,v 1.24 2007/06/10 16:49:55 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/types.h"
#include "sm/ctype.h"
#include "sm/sysexits.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/log.h"
#include "sm/regex.h"
#include "sm/signal.h"
#include "sm/sm_extracthdr.h"
#include "sm/smreplycodes.h"
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#if MTA_USE_PMILTER
#include <stdio.h>
#include "milter-regex-eval.h"
extern int parse_ruleset(const char *, struct ruleset **, char *, size_t);
static const char *rule_file_name = "/etc/meta1/milter-regex.conf";
static int debug = 0;
static int Xhdr_perm = 0;
struct context {
struct ruleset *rs;
int *res;
char buf[2048]; /* longer body lines are wrapped */
unsigned pos; /* write position within buf */
char host_name[128];
char host_addr[64];
char hdr_from[128];
char hdr_to[128];
char hdr_subject[128];
char ta_id[20];
char *quarantine;
uint32_t pmexse_flags;
sm_xhdr_ctx_P pmexse_xhdr_ctx;
};
typedef struct context context_T, *context_P, *pmexse_ctx_P;
#define PMSE_FL_NONE 0x0000
#define PMSE_FL_MSG_INIT 0x0001
#define PMSE_FL_MSG_HDR 0x0002
#define PMSE_FL_MSG_EOH 0x0004
#define PMSE_FL_MSG_BODY 0x0008
#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)
#define sfsistat sfsistat_T
#define SMFICTX pmse_ctx_T
#define MI_SUCCESS SM_PMI_SUCCESS
#define MI_FAILURE SM_PMI_FAILURE
#define _SOCK_ADDR sm_sockaddr_T
#define smfi_getpriv sm_pmfi_get_ctx_se
#define smfi_setpriv sm_pmfi_set_ctx_se
static pmg_ctx_P pmg_ctx;
#define smfi_stop() sm_pmfi_stop(pmg_ctx)
void die(const char *);
static sfsistat setreply(SMFICTX *, struct context *,
const struct action *);
static struct ruleset *get_ruleset(void);
static sfsistat cb_connect(SMFICTX *, const char *, _SOCK_ADDR *);
static sfsistat cb_helo(SMFICTX *, const char *, bool);
static sfsistat cb_envfrom(SMFICTX *, const char *, char **);
static sfsistat cb_envrcpt(SMFICTX *, const char *, char **);
static sfsistat cb_header(SMFICTX *, char *, char *);
static sfsistat cb_eoh(SMFICTX *);
static sfsistat cb_body(SMFICTX *, u_char *, size_t);
static sfsistat cb_eom(SMFICTX *);
static sfsistat cb_close(SMFICTX *);
static void usage(const char *);
static void msg(int, struct context *, const char *, ...);
#define OCONN "/var/spool/milter-regex/sock"
#define RCODE_REJECT "554"
#define RCODE_TEMPFAIL "451"
#define XCODE_REJECT "5.7.1"
#define XCODE_TEMPFAIL "4.7.1"
#define MAXRS 16
#if SM_STAT_st_mtimespec
#define ST_MTIME st_mtimespec
#else
#define ST_MTIME st_mtime
#endif
static sfsistat
setreply(SMFICTX *ctx, struct context *context, const struct action *action)
{
int result = SMFIS_CONTINUE;
char *what, *rhs;
char m[64];
/*
** strlen("454 4.7.1 \r\n")=12, hence restrict len for reply to 64-12=52
** (2 for paranoia's sake)
*/
#define MR_TXT_LEN "50"
what = rhs = NULL;
switch (action->type) {
case ACTION_REJECT:
what = "REJECT";
rhs = action->msg;
result = SMFIS_REJECT;
break;
case ACTION_REJECT_QUICK:
what = "REJECT_QUICK";
rhs = action->msg;
result = SMTP_R_SET_QUICK(SMFIS_REJECT);
break;
case ACTION_TEMPFAIL:
what = "TEMPFAIL";
rhs = action->msg;
result = SMFIS_TEMPFAIL;
break;
case ACTION_QUARANTINE:
if (context->quarantine != NULL)
free(context->quarantine);
context->quarantine = strdup(action->msg);
break;
case ACTION_DISCARD:
what = "status";
rhs = "DISCARD";
result = SMFIS_DISCARD;
break;
case ACTION_ACCEPT:
#if 0
what = "status";
rhs = "ACCEPT";
#endif
result = SMFIS_ACCEPT;
break;
}
if (what != NULL && msg != NULL)
{
msg(LOG_NOTICE, context,
"ta_id=%s, %s=%s, From=%@s, To=%@s, Subject=%@s",
context->ta_id, what, rhs, context->hdr_from,
context->hdr_to, context->hdr_subject);
}
if (action->type == ACTION_REJECT)
{
snprintf(m, sizeof(m), "554 5.7.1 %." MR_TXT_LEN "s\r\n",
(char *)action->msg);
if (sm_pmfi_setreply(ctx, m) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
}
if (action->type == ACTION_TEMPFAIL)
{
snprintf(m, sizeof(m), "454 4.7.1 %." MR_TXT_LEN "s\r\n",
(char *)action->msg);
if (sm_pmfi_setreply(ctx, m) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
}
return (result);
}
static struct ruleset *
get_ruleset(void)
{
static struct ruleset *rs[MAXRS] = { NULL, NULL, 0, 0 };
static int cur = 0;
static time_t last_check = 0;
static struct stat sbo;
time_t t = time(NULL);
int load = 0;
if (!last_check)
memset(&sbo, 0, sizeof(sbo));
if (t - last_check >= 10) {
struct stat sb;
last_check = t;
memset(&sb, 0, sizeof(sb));
if (stat(rule_file_name, &sb))
msg(LOG_ERR, NULL, "get_ruleset: stat: %s: %s",
rule_file_name, strerror(errno));
else if (memcmp(&sb.ST_MTIME, &sbo.ST_MTIME,
sizeof(sb.ST_MTIME))) {
memcpy(&sbo.ST_MTIME, &sb.ST_MTIME,
sizeof(sb.ST_MTIME));
load = 1;
}
}
if (load || rs[cur] == NULL) {
int i;
char err[8192];
msg(LOG_DEBUG, NULL, "loading new configuration file");
for (i = 0; i < MAXRS; ++i)
if (rs[i] != NULL && rs[i]->refcnt == 0) {
msg(LOG_DEBUG, NULL, "freeing unused ruleset "
"%d/%d", i, MAXRS);
free_ruleset(rs[i]);
rs[i] = NULL;
}
for (i = 0; i < MAXRS; ++i)
if (rs[i] == NULL)
break;
if (i == MAXRS)
msg(LOG_ERR, NULL, "all rulesets are in use, cannot "
"load new one", MAXRS);
else if (parse_ruleset(rule_file_name, &rs[i], err,
sizeof(err)) || rs[i] == NULL)
msg(LOG_ERR, NULL, "parse_ruleset: %s", err);
else {
msg(LOG_INFO, NULL, "configuration file %s loaded "
"successfully", rule_file_name);
cur = i;
}
}
return (rs[cur]);
}
static sm_ret_T
cb_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;
*pm_cap = SM_SCAP_PM_ALL;
*pm_fct = 0;
*pm_feat = 0;
*pm_misc = 0;
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_CONNECT,
PMM_SRVHOSTNAME, PMM_END);
if (sm_is_err(ret))
msg(LOG_ERR, NULL,
"sev=ERROR, func=cb_negotiate, sm_pmfi_setmaclist=%#x, macro=hostname\n",
ret);
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_MAIL,
PMM_MAIL_TAID, PMM_END);
if (sm_is_err(ret))
msg(LOG_ERR, NULL,
"sev=ERROR, func=cb_negotiate, sm_pmfi_setmaclist=%#x, macro=taid\n",
ret);
return SM_SUCCESS;
}
static sfsistat
cb_connect(SMFICTX *ctx, const char *name, _SOCK_ADDR *sa)
{
struct context *context;
context = calloc(1, sizeof(*context));
if (context == NULL) {
msg(LOG_ERR, NULL, "cb_connect: calloc: %s", strerror(errno));
return (SMFIS_ACCEPT);
}
context->rs = get_ruleset();
if (context->rs == NULL) {
free(context);
msg(LOG_ERR, NULL, "cb_connect: get_ruleset");
return (SMFIS_ACCEPT);
}
context->res = calloc(context->rs->maxidx, sizeof(*context->res));
if (context->res == NULL) {
free(context);
msg(LOG_ERR, NULL, "cb_connect: calloc: %s", strerror(errno));
return (SMFIS_ACCEPT);
}
if (smfi_setpriv(ctx, context) != MI_SUCCESS) {
free(context->res);
free(context);
msg(LOG_ERR, NULL, "cb_connect: smfi_setpriv");
return (SMFIS_ACCEPT);
}
context->rs->refcnt++;
context->pmexse_flags = 0;
strlcpy(context->host_name, name, sizeof(context->host_name));
strlcpy(context->host_addr, "unknown", sizeof(context->host_addr));
switch (sa->sa.sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr.s_addr,
context->host_addr, sizeof(context->host_addr)) ==
NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
#if HAVE_INET6
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop(AF_INET6, &sin6->sin6_addr,
context->host_addr, sizeof(context->host_addr)) ==
NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
#endif
}
msg(LOG_DEBUG, context, "cb_connect('%s', '%s')",
context->host_name, context->host_addr);
return (SMFIS_CONTINUE);
}
static sfsistat
cb_helo(SMFICTX *ctx, const char *arg, bool ehlo)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_helo: smfi_getpriv");
return (SMFIS_ACCEPT);
}
/* multiple HELO imply RSET in sendmail */
/* evaluate connect arguments here, because we can't call */
/* setreply from cb_connect */
eval_clear(context->rs, context->res, COND_CONNECT);
if ((action = eval_cond(context->rs, context->res, COND_CONNECT,
context->host_name, context->host_addr)) != NULL)
return (setreply(ctx, context, action));
if ((action = eval_end(context->rs, context->res, COND_CONNECT)) !=
NULL)
return (setreply(ctx, context, action));
msg(LOG_DEBUG, context, "cb_helo('%@s')", arg);
eval_clear(context->rs, context->res, COND_HELO);
if ((action = eval_cond(context->rs, context->res, COND_HELO,
arg, NULL)) != NULL)
return (setreply(ctx, context, action));
if ((action = eval_end(context->rs, context->res, COND_HELO)) !=
NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_envfrom(SMFICTX *ctx, const char *mail, char **args)
{
struct context *context;
const struct action *action;
char *ta_id;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envfrom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
context->pmexse_flags = 0;
/* multiple MAIL FROM indicate separate messages */
eval_clear(context->rs, context->res, COND_ENVFROM);
ta_id = NULL;
sm_pmfi_getmac(ctx, PMM_MAIL_TAID, &ta_id);
if (ta_id == NULL)
ta_id = "unknown_ta_id";
strlcpy(context->ta_id, ta_id, sizeof(context->ta_id));
if (mail != NULL) {
msg(LOG_DEBUG, context, "cb_envfrom('%@s')", mail);
if ((action = eval_cond(context->rs, context->res, COND_ENVFROM,
mail, NULL)) != NULL)
return (setreply(ctx, context, action));
}
if ((action = eval_end(context->rs, context->res, COND_ENVFROM)) !=
NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_envrcpt(SMFICTX *ctx, const char *rcpt, char **args)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envrcpt: smfi_getpriv");
return (SMFIS_ACCEPT);
}
/* multiple RCPT TO: possible */
eval_clear(context->rs, context->res, COND_ENVRCPT);
if (rcpt != NULL) {
msg(LOG_DEBUG, context, "cb_envrcpt('%@s')", rcpt);
if ((action = eval_cond(context->rs, context->res, COND_ENVRCPT,
rcpt, NULL)) != NULL)
return (setreply(ctx, context, action));
}
if ((action = eval_end(context->rs, context->res, COND_ENVRCPT)) !=
NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_header(SMFICTX *ctx, char *name, char *value)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, context, "cb_header: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_header('%@s', '%@s')", name, value);
if (!strcasecmp(name, "From"))
strlcpy(context->hdr_from, value, sizeof(context->hdr_from));
else if (!strcasecmp(name, "To"))
strlcpy(context->hdr_to, value, sizeof(context->hdr_to));
else if (!strcasecmp(name, "Subject"))
strlcpy(context->hdr_subject, value,
sizeof(context->hdr_subject));
if ((action = eval_cond(context->rs, context->res, COND_HEADER,
name, value)) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_eoh(SMFICTX *ctx)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_eoh: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_eoh()");
memset(context->buf, 0, sizeof(context->buf));
context->pos = 0;
if ((action = eval_end(context->rs, context->res, COND_HEADER)) !=
NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_body(SMFICTX *ctx, u_char *chunk, size_t size)
{
struct context *context;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_body: smfi_getpriv");
return (SMFIS_ACCEPT);
}
for (; size > 0; size--, chunk++) {
context->buf[context->pos] = *chunk;
if (context->buf[context->pos] == '\n' ||
context->pos == sizeof(context->buf) - 1) {
const struct action *action;
if (context->pos > 0 &&
context->buf[context->pos - 1] == '\r')
context->buf[context->pos - 1] = 0;
else
context->buf[context->pos] = 0;
context->pos = 0;
msg(LOG_DEBUG, context, "cb_body('%@s')", context->buf);
if ((action = eval_cond(context->rs, context->res,
COND_BODY, context->buf, NULL)) != NULL)
return (setreply(ctx, context, action));
} else
context->pos++;
}
return (SMFIS_CONTINUE);
}
static sfsistat
cb_eom(SMFICTX *ctx)
{
struct context *context;
const struct action *action;
int result = SMFIS_ACCEPT;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_eom()");
if ((action = eval_end(context->rs, context->res, COND_BODY)) !=
NULL)
result = setreply(ctx, context, action);
else
msg(LOG_DEBUG, context, "ACCEPT, From: %@s, To: %@s, "
"Subject: %s", context->hdr_from, context->hdr_to,
context->hdr_subject);
#ifdef SMFIF_QUARANTINE
if (context->quarantine != NULL) {
msg(LOG_NOTICE, context, "QUARANTINE: %@s, From: %@s, To: %@s, "
"Subject: %@s", action->msg, context->hdr_from,
context->hdr_to, context->hdr_subject);
if (smfi_quarantine(ctx, context->quarantine) != MI_SUCCESS)
msg(LOG_ERR, context, "cb_eom: smfi_quarantine");
}
#endif
context->pmexse_flags = 0;
return (result);
}
static sfsistat
cb_close(SMFICTX *ctx)
{
struct context *context;
context = (struct context *)smfi_getpriv(ctx);
msg(LOG_DEBUG, context, "cb_close()");
if (context != NULL) {
smfi_setpriv(ctx, NULL);
free(context->res);
if (context->quarantine != NULL)
free(context->quarantine);
context->rs->refcnt--;
free(context);
}
return (SMFIS_CONTINUE);
}
static sfsistat_T
pm_hdr(pmse_ctx_P pmse_ctx, pmexse_ctx_P pmexse_ctx, unsigned char *buf, size_t len)
{
sm_ret_T ret, rv;
bool newchunk;
sm_xhdr_ctx_P sm_xhdr_ctx;
struct context *context;
sm_xhdr_ctx = pmexse_ctx->pmexse_xhdr_ctx;
if ((context = (struct context *)smfi_getpriv(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "pm_hdr: smfi_getpriv");
return (SMFIS_ACCEPT);
}
sm_xhdr_ctx->sm_xhdr_chunk_off = 0;
newchunk = true;
do
{
ret = sm_xhdr(buf, len, newchunk, sm_xhdr_ctx);
newchunk = false;
if (sm_is_err(ret))
{
int plen;
bool perm;
char m[64];
plen = (len > 256) ? 256 : (int) len;
msg(LOG_NOTICE, context,
"ta_id=%s, sm_xhdr=%m, buf=\"%.*@s\", len=%lu"
", off=%lu, state=%#x, name=%@S, value=%@S",
context->ta_id, ret, plen, buf, (unsigned long) len,
(unsigned long) sm_xhdr_ctx->sm_xhdr_chunk_off,
sm_xhdr_ctx->sm_xhdr_state,
sm_xhdr_ctx->sm_xhdr_name,
sm_xhdr_ctx->sm_xhdr_value);
perm = sm_is_perm_err(ret) && Xhdr_perm;
snprintf(m, sizeof(m),
"%d %d.7.1 error parsing headers\r\n",
perm ? 554 : 451, perm ? 5 : 4);
if (sm_pmfi_setreply(pmse_ctx, m) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
return perm ? SMFIS_REJECT : SMFIS_TEMPFAIL;
}
if (ret == SM_XHDR_GOT1 || ret == SM_XHDR_GOTA ||
ret == SM_XHDR_GOTL)
{
char *hdr_name, *hdr_value;
hdr_name = (char *) sm_str_getdata(
sm_xhdr_ctx->sm_xhdr_name);
hdr_value = (char *) sm_str_getdata(
sm_xhdr_ctx->sm_xhdr_value);
if (hdr_name == NULL || hdr_value == NULL)
{
msg(LOG_NOTICE, context,
"ta_id=%s, hdr_name=%p, hdr_value=%p",
context->ta_id, hdr_name, hdr_value);
return SMFIS_TEMPFAIL;
}
rv = cb_header(pmse_ctx, hdr_name, hdr_value);
if (rv != SMFIS_CONTINUE)
return rv;
}
} while (!sm_is_err(ret) && ret == SM_XHDR_GOTA);
if (ret == SM_XHDR_EOHDR || ret == SM_XHDR_GOTL)
{
PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_MSG_HDR);
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_MSG_EOH);
/* invoke end-of-header function */
rv = cb_eoh(pmse_ctx);
PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_MSG_EOH);
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_MSG_BODY);
if (rv != SMFIS_CONTINUE)
return rv;
}
return SMFIS_CONTINUE;
}
static sfsistat_T
pm_msg(pmse_ctx_P pmse_ctx, unsigned char *buf, size_t len)
{
sm_ret_T ret;
pmexse_ctx_P pmexse_ctx;
pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx);
if (pmexse_ctx == NULL)
return SMTP_R_TEMP;
ret = SMTP_R_CONT;
if (!PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_MSG_INIT))
{
ret = sm_xhdr_init(SM_XHDR_FL_SKIP_FIRST_BLANK,
&(pmexse_ctx->pmexse_xhdr_ctx));
if (sm_is_err(ret))
{
return SMTP_R_TEMP;
}
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_MSG_INIT|PMSE_FL_MSG_HDR);
}
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_MSG_HDR))
{
ret = pm_hdr(pmse_ctx, pmexse_ctx, buf, len);
if (ret != SMFIS_CONTINUE)
return ret;
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_MSG_BODY))
{
sm_xhdr_ctx_P sm_xhdr_ctx;
sm_xhdr_ctx = pmexse_ctx->pmexse_xhdr_ctx;
if (sm_xhdr_ctx->sm_xhdr_chunk_off < len)
{
len -= sm_xhdr_ctx->sm_xhdr_chunk_off;
buf += sm_xhdr_ctx->sm_xhdr_chunk_off;
ret = sm_xhdr_end(sm_xhdr_ctx);
}
else
len = 0;
}
}
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_MSG_BODY) && len > 0)
ret = cb_body(pmse_ctx, buf, len);
return ret;
}
#define smfilter pmilter
static pmilter_T
pmilter =
{
"milter-regex", /* filter name */
LPMILTER_VERSION,
SM_SCAP_PM_CNNCT|SM_SCAP_PM_EHLO|SM_SCAP_PM_DATA|SM_SCAP_PM_MSG,
0,
0,
0,
cb_negotiate,
cb_connect,
cb_helo,
cb_envfrom,
cb_envrcpt,
NULL,
pm_msg,
cb_eom,
NULL,
cb_close,
NULL, /* cb_unknown, */
NULL /* cb_signal */
};
static void
msg(int priority, struct context *context, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (debug)
{
char msg[8192];
sm_vsnprintf(msg, sizeof(msg), fmt, ap);
sm_io_fprintf(smioerr, "syslog: %s\n", msg);
}
else
sm_log_vwrite(NULL, NULL, NULL, priority, 1, fmt, ap);
va_end(ap);
}
static void
usage(const char *argv0)
{
sm_io_fprintf(smioerr, "usage: %s [-ds] [-c config] [-p socket]\n",
argv0);
exit(EX_USAGE);
}
void
die(const char *reason)
{
msg(LOG_ERR, NULL, "die: %s", reason);
smfi_stop();
sleep(60);
/* not reached, smfi_stop() kills thread */
abort();
}
int
main(int argc, char **argv)
{
int ch, dbg;
sfsistat_T ret = SM_PMI_FAILURE;
const char *oconn = OCONN;
const char *ofile = NULL;
uint32_t major, minor, patchlevel;
char *prog;
prog = argv[0];
if (getuid() == 0 || geteuid() == 0)
{
sm_io_fprintf(smioerr,
"%s: ERROR: do not run this as super-user!\n",
prog);
exit(EX_USAGE);
}
tzset();
(void) sm_log_opensyslog("milter-regex", LOG_PID|LOG_NDELAY, LOG_MAIL);
dbg = 0;
while ((ch = getopt(argc, argv, "c:D:dp:s")) != -1) {
switch (ch) {
case 'c':
rule_file_name = optarg;
break;
case 'D':
dbg = strtoul(optarg, NULL, 0);
break;
case 'd':
debug = 1;
break;
case 'p':
oconn = optarg;
break;
case 's':
Xhdr_perm = 1;
break;
default:
usage(argv[0]);
}
}
if (argc != optind) {
sm_io_fprintf(smioerr, "unknown command line argument: %s ...",
argv[optind]);
usage(argv[0]);
}
if (!strncmp(oconn, "unix:", 5))
ofile = oconn + 5;
else if (!strncmp(oconn, "local:", 6))
ofile = oconn + 6;
else
ofile = oconn;
if (ofile != NULL)
(void) unlink(ofile);
pmg_ctx = NULL;
ret = sm_pmfi_init(&pmg_ctx);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_init=%m\n", ret);
goto done;
}
ret = sm_pmfi_version(pmg_ctx, &major, &minor, &patchlevel);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_version=%m\n", ret);
goto done;
}
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 done;
}
if (dbg > 0)
ret = sm_pmfi_setdbg(pmg_ctx, dbg);
ret = sm_pmfi_setconn(pmg_ctx, (char *)ofile);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_setconn=%m\n", ret);
goto done;
}
if (eval_init(ACTION_ACCEPT)) {
sm_io_fprintf(smioerr, "eval_init: failed\n");
goto done;
}
umask(0177);
signal(SIGPIPE, SIG_IGN);
msg(LOG_INFO, NULL, "started: %s", rcsid);
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 done;
}
if (ret != MI_SUCCESS)
msg(LOG_ERR, NULL, "smfi_main: terminating due to error");
else
msg(LOG_INFO, NULL, "smfi_main: terminating without error");
done:
return 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