engine.c revision 3ee0e49223f178da635734759b9167f924321ff0
/*
* Copyright (c) 1999-2004, 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.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "libmilter.h"
#endif /* NETINET || NETINET6 */
/* generic argument for functions in the command table */
struct arg_struct
{
char *a_buf; /* argument string */
int a_idx; /* index for macro array */
};
typedef struct arg_struct genarg;
/* structure for commands received from MTA */
struct cmdfct_t
{
char cm_cmd; /* command */
int cm_argt; /* type of arguments expected */
int cm_next; /* next state */
int cm_todo; /* what to do next */
int cm_macros; /* index for macros */
};
/* possible values for cm_argt */
#define CM_ARG0 0 /* no args */
/* possible values for cm_todo */
/* not needed right now, done via return code instead */
/* index in macro array: macros only for these commands */
#define CI_NONE (-1)
#define CI_CONN 0
#define CI_HELO 1
#define CI_MAIL 2
#define CI_RCPT 3
#define CI_EOM 4
#if CI_EOM >= MAX_MACROS_ENTRIES
#endif
/* function prototypes */
#if SMFI_VERSION > 2
#endif /* SMFI_VERSION > 2 */
#if SMFI_VERSION > 3
#endif /* SMFI_VERSION > 3 */
/* states */
#define ST_NONE (-1)
#define ST_INIT 0 /* initial state */
/* in a mail transaction? must be before eom according to spec. */
/*
** set of next states
** each state (ST_*) corresponds to bit in an int value (1 << state)
** each state has a set of allowed transitions ('or' of bits of states)
** so a state transition is valid if the mask of the next state
** is set in the NX_* value
** this function is coded in trans_ok(), see below.
*/
#define NX_QUIT 0
#define NX_ABRT 0
static int next_states[] =
{
};
/* commands received by milter */
{
#if SMFI_VERSION > 3
#endif /* SMFI_VERSION > 3 */
#if SMFI_VERSION > 2
#endif /* SMFI_VERSION > 2 */
};
/* additional (internal) reply codes */
#define _SMFIS_KEEP 20
#define _SMFIS_ABORT 21
#define _SMFIS_OPTIONS 22
#define _SMFIS_NOREPLY 23
#define _SMFIS_FAIL (-1)
#define _SMFIS_NONE (-2)
/*
** MI_ENGINE -- receive commands and process them
**
** Parameters:
** ctx -- context structure
**
** Returns:
*/
int
{
int i;
int ret = MI_SUCCESS;
int newstate;
bool call_abort;
sfsistat r;
char cmd;
mi_clr_macros(ctx, 0);
r = _SMFIS_NONE;
do
{
/* call abort only if in a mail transaction */
if (mi_stop() == MILTER_ABRT)
{
sm_dprintf("[%d] milter_abort\n",
ret = MI_FAILURE;
break;
}
/*
** Notice: buf is allocated by mi_rd_cmd() and it will
** usually be free()d after it has been used in f().
** However, if the function returns _SMFIS_KEEP then buf
** contains macros and will not be free()d.
** Hence r must be set to _SMFIS_NONE if a new buf is
** allocated to avoid problem with housekeeping, esp.
** if the code "break"s out of the loop.
*/
r = _SMFIS_NONE;
{
sm_dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n",
/*
** eof is currently treated as failure ->
** abort() instead of close(), otherwise use:
** if (cmd != SMFIC_EOF)
*/
ret = MI_FAILURE;
break;
}
sm_dprintf("[%d] got cmd '%c' len %d\n",
for (i = 0; i < ncmds; i++)
{
break;
}
if (i >= ncmds)
{
/* unknown command */
sm_dprintf("[%d] cmd '%c' unknown\n",
ret = MI_FAILURE;
break;
}
{
/* stop for now */
sm_dprintf("[%d] cmd '%c' not impl\n",
ret = MI_FAILURE;
break;
}
/* is new state ok? */
sm_dprintf("[%d] cur %x new %x nextmask %x\n",
{
sm_dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n",
/* call abort only if in a mail transaction */
/*
** try to reach the new state from HELO
** if it can't be reached, ignore the command.
*/
{
{
}
continue;
}
}
{
}
/* call function to deal with command */
r = (*f)(&arg);
{
}
{
ret = MI_FAILURE;
break;
}
if (r == SMFIS_ACCEPT)
{
/* accept mail, no further actions taken */
}
else if (r == SMFIS_REJECT || r == SMFIS_DISCARD ||
r == SMFIS_TEMPFAIL)
{
/*
** further actions depend on current state
** if the IGNO bit is set: "ignore" the error,
** i.e., stay in the current state
*/
}
else if (r == _SMFIS_ABORT)
{
sm_dprintf("[%d] function returned abort\n",
ret = MI_FAILURE;
break;
}
if (ret != MI_SUCCESS)
{
/* call abort only if in a mail transaction */
}
/* close must always be called */
mi_clr_macros(ctx, 0);
return ret;
}
/*
** SENDREPLY -- send a reply to the MTA
**
** Parameters:
** r -- reply code
** sd -- socket descriptor
** timeout_ptr -- (ptr to) timeout to use for sending
** ctx -- context structure
**
** Returns:
*/
static int
sfsistat r;
struct timeval *timeout_ptr;
{
int ret = MI_SUCCESS;
switch (r)
{
case SMFIS_CONTINUE:
break;
case SMFIS_TEMPFAIL:
case SMFIS_REJECT:
{
}
else
{
}
break;
case SMFIS_DISCARD:
break;
case SMFIS_ACCEPT:
break;
case _SMFIS_OPTIONS:
{
char buf[MILTER_OPTLEN];
mi_int32 v;
}
break;
default: /* don't send a reply */
break;
}
return ret;
}
/*
** CLR_MACROS -- clear set of macros starting from a given index
**
** Parameters:
** ctx -- context structure
** m -- index from which to clear all macros
**
** Returns:
** None.
*/
void
mi_clr_macros(ctx, m)
int m;
{
int i;
for (i = m; i < MAX_MACROS_ENTRIES; i++)
{
{
}
{
}
}
}
/*
** ST_OPTIONNEG -- negotiate options
**
** Parameters:
** g -- generic argument structure
**
** Returns:
*/
static int
st_optionneg(g)
genarg *g;
{
mi_int32 i, v;
return SMFIS_CONTINUE;
/* check for minimum length */
if (g->a_len < MILTER_OPTLEN)
{
"%s: st_optionneg[%d]: len too short %d < %d",
return _SMFIS_ABORT;
}
v = ntohl(i);
{
/* hard failure for now! */
"%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d",
return _SMFIS_ABORT;
}
v = ntohl(i);
/* no flags? set to default value for V1 actions */
if (v == 0)
v = SMFI_V1_ACTS;
if ((v & i) != i)
{
"%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x",
return _SMFIS_ABORT;
}
v = ntohl(i);
/* no flags? set to default value for V1 protocol */
if (v == 0)
v = SMFI_V1_PROT;
i = g->a_ctx->ctx_pflags;
if ((v & i) != i)
{
"%s: st_optionneg[%d]: 0x%x does not fulfill protocol requirements 0x%x",
return _SMFIS_ABORT;
}
return _SMFIS_OPTIONS;
}
/*
** ST_CONNECTINFO -- receive connection information
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
genarg *g;
{
size_t l;
size_t i;
char *s, family;
unsigned short port = 0;
if (g == NULL)
return _SMFIS_ABORT;
return SMFIS_CONTINUE;
s = g->a_buf;
i = 0;
l = g->a_len;
while (s[i] != '\0' && i <= l)
++i;
if (i + 1 >= l)
return _SMFIS_ABORT;
/* Move past trailing \0 in host string */
i++;
family = s[i++];
if (family != SMFIA_UNKNOWN)
{
if (i + sizeof port >= l)
{
"%s: connect[%d]: wrong len %d >= %d",
return _SMFIS_ABORT;
}
sizeof port);
i += sizeof port;
/* make sure string is terminated */
if (s[l - 1] != '\0')
return _SMFIS_ABORT;
# if NETINET
if (family == SMFIA_INET)
{
!= 1)
{
"%s: connect[%d]: inet_aton failed",
return _SMFIS_ABORT;
}
if (port > 0)
}
else
# endif /* NETINET */
# if NETINET6
if (family == SMFIA_INET6)
{
if (mi_inet_pton(AF_INET6, s + i,
{
"%s: connect[%d]: mi_inet_pton failed",
return _SMFIS_ABORT;
}
if (port > 0)
}
else
# endif /* NETINET6 */
# if NETUNIX
if (family == SMFIA_UNIX)
{
{
"%s: connect[%d]: path too long",
return _SMFIS_ABORT;
}
}
else
# endif /* NETUNIX */
{
"%s: connect[%d]: unknown family %d",
return _SMFIS_ABORT;
}
}
}
/*
** ST_EOH -- end of headers
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_eoh(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
return SMFIS_CONTINUE;
}
#if SMFI_VERSION > 3
/*
** ST_DATA -- DATA command
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_data(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
return SMFIS_CONTINUE;
}
#endif /* SMFI_VERSION > 3 */
/*
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_helo(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
{
/* paranoia: check for terminating '\0' */
return MI_FAILURE;
}
return SMFIS_CONTINUE;
}
/*
** ST_HEADER -- header line
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_header(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
return SMFIS_CONTINUE;
else
return _SMFIS_ABORT;
}
char **argv; \
int r; \
\
if (g == NULL) \
return _SMFIS_ABORT; \
return SMFIS_CONTINUE; \
return _SMFIS_ABORT; \
return r;
/*
** ST_SENDER -- MAIL FROM command
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_sender(g)
genarg *g;
{
}
/*
** ST_RCPT -- RCPT TO command
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_rcpt(g)
genarg *g;
{
}
#if SMFI_VERSION > 2
/*
** ST_UNKNOWN -- unrecognized or unimplemented command
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_unknown(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
return SMFIS_CONTINUE;
}
#endif /* SMFI_VERSION > 2 */
/*
** ST_MACROS -- deal with macros received from the MTA
**
** Parameters:
** g -- generic argument structure
**
** Returns:
**
** Side effects:
** set pointer in macro array to current values.
*/
static int
st_macros(g)
genarg *g;
{
int i;
char **argv;
return _SMFIS_FAIL;
return _SMFIS_FAIL;
switch (g->a_buf[0])
{
case SMFIC_CONNECT:
i = CI_CONN;
break;
case SMFIC_HELO:
i = CI_HELO;
break;
case SMFIC_MAIL:
i = CI_MAIL;
break;
case SMFIC_RCPT:
i = CI_RCPT;
break;
case SMFIC_BODYEOB:
i = CI_EOM;
break;
default:
return _SMFIS_FAIL;
}
return _SMFIS_KEEP;
}
/*
** ST_QUIT -- quit command
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** noreply
*/
/* ARGSUSED */
static int
st_quit(g)
genarg *g;
{
return _SMFIS_NOREPLY;
}
/*
** ST_BODYCHUNK -- deal with a piece of the mail body
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
*/
static int
st_bodychunk(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
g->a_len);
return SMFIS_CONTINUE;
}
/*
** ST_BODYEND -- deal with the last piece of the mail body
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** continue or filter-specified value
**
** Side effects:
** sends a reply for the body part (if non-empty).
*/
static int
st_bodyend(g)
genarg *g;
{
sfsistat r;
if (g == NULL)
return _SMFIS_ABORT;
r = SMFIS_CONTINUE;
{
g->a_len > 0)
{
g->a_len);
if (r != SMFIS_CONTINUE &&
return _SMFIS_ABORT;
}
}
if (r == SMFIS_CONTINUE &&
return r;
}
/*
** ST_ABORTFCT -- deal with aborts
**
** Parameters:
** g -- generic argument structure
**
** Returns:
** abort or filter-specified value
*/
static int
st_abortfct(g)
genarg *g;
{
if (g == NULL)
return _SMFIS_ABORT;
return _SMFIS_NOREPLY;
}
/*
** TRANS_OK -- is the state transition ok?
**
** Parameters:
** old -- old state
** new -- new state
**
** Returns:
** state transition ok
*/
static bool
{
int s, n;
s = old;
if (s >= SIZE_NEXT_STATES)
return false;
do
{
/* is this state transition allowed? */
return true;
/*
** no: try next state;
** this works since the relevant states are ordered
** strict sequentially
*/
n = s + 1;
if (n >= SIZE_NEXT_STATES)
return false;
/*
** can we actually "skip" this state?
** see fix_stm() which sets this bit for those
** states which the filter program is not interested in
*/
s = n;
else
return false;
} while (s < SIZE_NEXT_STATES);
return false;
}
/*
** FIX_STM -- add "skip" bits to the state transition table
**
** Parameters:
** ctx -- context structure
**
** Returns:
** None.
**
** Side effects:
** may change state transition table.
*/
static void
{
unsigned long fl;
return;
}
/*
** DEC_ARGV -- split a buffer into a list of strings, NULL terminated
**
** Parameters:
** buf -- buffer with several strings
** len -- length of buffer
**
** Returns:
** array of pointers to the individual strings
*/
static char **
char *buf;
{
char **s;
size_t i;
nelem = 0;
for (i = 0; i < len; i++)
{
if (buf[i] == '\0')
++nelem;
}
if (nelem == 0)
return NULL;
/* last entry is only for the name */
if (s == NULL)
return NULL;
s[0] = buf;
{
if (buf[i] == '\0')
{
++elem;
if (i + 1 >= len)
else
}
}
/* overwrite last entry (already done above, just paranoia) */
return s;
}
/*
** DEC_ARG2 -- split a buffer into two strings
**
** Parameters:
** buf -- buffer with two strings
** len -- length of buffer
** s1,s2 -- pointer to result strings
**
** Returns:
*/
static int
char *buf;
char **s1;
char **s2;
{
size_t i;
/* paranoia: check for terminating '\0' */
return MI_FAILURE;
continue;
if (i >= len - 1)
return MI_FAILURE;
return MI_SUCCESS;
}
/*
** SENDOK -- is it ok for the filter to send stuff to the MTA?
**
** Parameters:
** ctx -- context structure
** flag -- flag to check
**
** Returns:
** sending allowed (in current state)
*/
bool
int flag;
{
return false;
/* did the milter request this operation? */
return false;
/* are we in the correct state? It must be "End of Message". */
}