diff options
Diffstat (limited to 'contrib/sendmail/libmilter/engine.c')
-rw-r--r-- | contrib/sendmail/libmilter/engine.c | 1777 |
1 files changed, 0 insertions, 1777 deletions
diff --git a/contrib/sendmail/libmilter/engine.c b/contrib/sendmail/libmilter/engine.c deleted file mode 100644 index a024312..0000000 --- a/contrib/sendmail/libmilter/engine.c +++ /dev/null @@ -1,1777 +0,0 @@ -/* - * Copyright (c) 1999-2004, 2006, 2007 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/gen.h> -SM_RCSID("@(#)$Id: engine.c,v 8.159 2007/04/23 22:22:50 ca Exp $") - -#include "libmilter.h" - -#if NETINET || NETINET6 -# include <arpa/inet.h> -#endif /* NETINET || NETINET6 */ - -/* generic argument for functions in the command table */ -struct arg_struct -{ - size_t a_len; /* length of buffer */ - char *a_buf; /* argument string */ - int a_idx; /* index for macro array */ - SMFICTX_PTR a_ctx; /* context */ -}; - -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 */ - int (*cm_fct) __P((genarg *)); /* function to execute */ -}; - -typedef struct cmdfct_t cmdfct; - -/* possible values for cm_argt */ -#define CM_ARG0 0 /* no args */ -#define CM_ARG1 1 /* one arg (string) */ -#define CM_ARG2 2 /* two args (strings) */ -#define CM_ARGA 4 /* one string and _SOCK_ADDR */ -#define CM_ARGO 5 /* two integers */ -#define CM_ARGV 8 /* \0 separated list of args, NULL-terminated */ -#define CM_ARGN 9 /* \0 separated list of args (strings) */ - -/* possible values for cm_todo */ -#define CT_CONT 0x0000 /* continue reading commands */ -#define CT_IGNO 0x0001 /* continue even when error */ - -/* not needed right now, done via return code instead */ -#define CT_KEEP 0x0004 /* keep buffer (contains symbols) */ -#define CT_END 0x0008 /* last command of session, stop replying */ - -/* 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_DATA 4 -#define CI_EOM 5 -#define CI_EOH 6 -#define CI_LAST CI_EOH -#if CI_LAST < CI_DATA -ERROR: do not compile with CI_LAST < CI_DATA -#endif -#if CI_LAST < CI_EOM -ERROR: do not compile with CI_LAST < CI_EOM -#endif -#if CI_LAST < CI_EOH -ERROR: do not compile with CI_LAST < CI_EOH -#endif -#if CI_LAST < CI_ENVRCPT -ERROR: do not compile with CI_LAST < CI_ENVRCPT -#endif -#if CI_LAST < CI_ENVFROM -ERROR: do not compile with CI_LAST < CI_ENVFROM -#endif -#if CI_LAST < CI_HELO -ERROR: do not compile with CI_LAST < CI_HELO -#endif -#if CI_LAST < CI_CONNECT -ERROR: do not compile with CI_LAST < CI_CONNECT -#endif -#if CI_LAST >= MAX_MACROS_ENTRIES -ERROR: do not compile with CI_LAST >= MAX_MACROS_ENTRIES -#endif - -/* function prototypes */ -static int st_abortfct __P((genarg *)); -static int st_macros __P((genarg *)); -static int st_optionneg __P((genarg *)); -static int st_bodychunk __P((genarg *)); -static int st_connectinfo __P((genarg *)); -static int st_bodyend __P((genarg *)); -static int st_helo __P((genarg *)); -static int st_header __P((genarg *)); -static int st_sender __P((genarg *)); -static int st_rcpt __P((genarg *)); -static int st_unknown __P((genarg *)); -static int st_data __P((genarg *)); -static int st_eoh __P((genarg *)); -static int st_quit __P((genarg *)); -static int sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR)); -static void fix_stm __P((SMFICTX_PTR)); -static bool trans_ok __P((int, int)); -static char **dec_argv __P((char *, size_t)); -static int dec_arg2 __P((char *, size_t, char **, char **)); - -#if _FFR_WORKERS_POOL -static bool mi_rd_socket_ready __P((int)); -#endif /* _FFR_WORKERS_POOL */ - -/* states */ -#define ST_NONE (-1) -#define ST_INIT 0 /* initial state */ -#define ST_OPTS 1 /* option negotiation */ -#define ST_CONN 2 /* connection info */ -#define ST_HELO 3 /* helo */ -#define ST_MAIL 4 /* mail from */ -#define ST_RCPT 5 /* rcpt to */ -#define ST_DATA 6 /* data */ -#define ST_HDRS 7 /* headers */ -#define ST_EOHS 8 /* end of headers */ -#define ST_BODY 9 /* body */ -#define ST_ENDM 10 /* end of message */ -#define ST_QUIT 11 /* quit */ -#define ST_ABRT 12 /* abort */ -#define ST_UNKN 13 /* unknown SMTP command */ -#define ST_Q_NC 14 /* quit, new connection follows */ -#define ST_LAST ST_Q_NC /* last valid state */ -#define ST_SKIP 16 /* not a state but required for the state table */ - -/* in a mail transaction? must be before eom according to spec. */ -#define ST_IN_MAIL(st) ((st) >= ST_MAIL && (st) < ST_ENDM) - -/* -** 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 MI_MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */ -#define NX_INIT (MI_MASK(ST_OPTS)) -#define NX_OPTS (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN)) -#define NX_CONN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN)) -#define NX_HELO (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN)) -#define NX_MAIL (MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN)) -#define NX_RCPT (MI_MASK(ST_HDRS) | MI_MASK(ST_EOHS) | MI_MASK(ST_DATA) | \ - MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | \ - MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN)) -#define NX_DATA (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT)) -#define NX_HDRS (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT)) -#define NX_EOHS (MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | MI_MASK(ST_ABRT)) -#define NX_BODY (MI_MASK(ST_ENDM) | MI_MASK(ST_BODY) | MI_MASK(ST_ABRT)) -#define NX_ENDM (MI_MASK(ST_QUIT) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN) | \ - MI_MASK(ST_Q_NC)) -#define NX_QUIT 0 -#define NX_ABRT 0 -#define NX_UNKN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | \ - MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | \ - MI_MASK(ST_DATA) | \ - MI_MASK(ST_BODY) | MI_MASK(ST_UNKN) | \ - MI_MASK(ST_ABRT) | MI_MASK(ST_QUIT) | MI_MASK(ST_Q_NC)) -#define NX_Q_NC (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN)) -#define NX_SKIP MI_MASK(ST_SKIP) - -static int next_states[] = -{ - NX_INIT - , NX_OPTS - , NX_CONN - , NX_HELO - , NX_MAIL - , NX_RCPT - , NX_DATA - , NX_HDRS - , NX_EOHS - , NX_BODY - , NX_ENDM - , NX_QUIT - , NX_ABRT - , NX_UNKN - , NX_Q_NC -}; - -#define SIZE_NEXT_STATES (sizeof(next_states) / sizeof(next_states[0])) - -/* commands received by milter */ -static cmdfct cmds[] = -{ - {SMFIC_ABORT, CM_ARG0, ST_ABRT, CT_CONT, CI_NONE, st_abortfct } -, {SMFIC_MACRO, CM_ARGV, ST_NONE, CT_KEEP, CI_NONE, st_macros } -, {SMFIC_BODY, CM_ARG1, ST_BODY, CT_CONT, CI_NONE, st_bodychunk } -, {SMFIC_CONNECT, CM_ARG2, ST_CONN, CT_CONT, CI_CONN, st_connectinfo } -, {SMFIC_BODYEOB, CM_ARG1, ST_ENDM, CT_CONT, CI_EOM, st_bodyend } -, {SMFIC_HELO, CM_ARG1, ST_HELO, CT_CONT, CI_HELO, st_helo } -, {SMFIC_HEADER, CM_ARG2, ST_HDRS, CT_CONT, CI_NONE, st_header } -, {SMFIC_MAIL, CM_ARGV, ST_MAIL, CT_CONT, CI_MAIL, st_sender } -, {SMFIC_OPTNEG, CM_ARGO, ST_OPTS, CT_CONT, CI_NONE, st_optionneg } -, {SMFIC_EOH, CM_ARG0, ST_EOHS, CT_CONT, CI_EOH, st_eoh } -, {SMFIC_QUIT, CM_ARG0, ST_QUIT, CT_END, CI_NONE, st_quit } -, {SMFIC_DATA, CM_ARG0, ST_DATA, CT_CONT, CI_DATA, st_data } -, {SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt } -, {SMFIC_UNKNOWN, CM_ARG1, ST_UNKN, CT_IGNO, CI_NONE, st_unknown } -, {SMFIC_QUIT_NC, CM_ARG0, ST_Q_NC, CT_CONT, CI_NONE, st_quit } -}; - -/* -** Additional (internal) reply codes; -** must be coordinated wit libmilter/mfapi.h -*/ - -#define _SMFIS_KEEP 20 -#define _SMFIS_ABORT 21 -#define _SMFIS_OPTIONS 22 -#define _SMFIS_NOREPLY SMFIS_NOREPLY -#define _SMFIS_FAIL (-1) -#define _SMFIS_NONE (-2) - -/* -** MI_ENGINE -- receive commands and process them -** -** Parameters: -** ctx -- context structure -** -** Returns: -** MI_FAILURE/MI_SUCCESS -*/ - -int -mi_engine(ctx) - SMFICTX_PTR ctx; -{ - size_t len; - int i; - socket_t sd; - int ret = MI_SUCCESS; - int ncmds = sizeof(cmds) / sizeof(cmdfct); - int curstate = ST_INIT; - int newstate; - bool call_abort; - sfsistat r; - char cmd; - char *buf = NULL; - genarg arg; - struct timeval timeout; - int (*f) __P((genarg *)); - sfsistat (*fi_abort) __P((SMFICTX *)); - sfsistat (*fi_close) __P((SMFICTX *)); - - arg.a_ctx = ctx; - sd = ctx->ctx_sd; - fi_abort = ctx->ctx_smfi->xxfi_abort; -#if _FFR_WORKERS_POOL - curstate = ctx->ctx_state; - if (curstate == ST_INIT) - { - mi_clr_macros(ctx, 0); - fix_stm(ctx); - } -#else /* _FFR_WORKERS_POOL */ - mi_clr_macros(ctx, 0); - fix_stm(ctx); -#endif /* _FFR_WORKERS_POOL */ - r = _SMFIS_NONE; - do - { - /* call abort only if in a mail transaction */ - call_abort = ST_IN_MAIL(curstate); - timeout.tv_sec = ctx->ctx_timeout; - timeout.tv_usec = 0; - if (mi_stop() == MILTER_ABRT) - { - if (ctx->ctx_dbg > 3) - sm_dprintf("[%ld] milter_abort\n", - (long) ctx->ctx_id); - 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. - */ - -#if _FFR_WORKERS_POOL - /* Is the socket ready to be read ??? */ - if (!mi_rd_socket_ready(sd)) - { - ret = MI_CONTINUE; - break; - } -#endif /* _FFR_WORKERS_POOL */ - - r = _SMFIS_NONE; - if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len, - ctx->ctx_smfi->xxfi_name)) == NULL && - cmd < SMFIC_VALIDCMD) - { - if (ctx->ctx_dbg > 5) - sm_dprintf("[%ld] mi_engine: mi_rd_cmd error (%x)\n", - (long) ctx->ctx_id, (int) cmd); - - /* - ** eof is currently treated as failure -> - ** abort() instead of close(), otherwise use: - ** if (cmd != SMFIC_EOF) - */ - - ret = MI_FAILURE; - break; - } - if (ctx->ctx_dbg > 4) - sm_dprintf("[%ld] got cmd '%c' len %d\n", - (long) ctx->ctx_id, cmd, (int) len); - for (i = 0; i < ncmds; i++) - { - if (cmd == cmds[i].cm_cmd) - break; - } - if (i >= ncmds) - { - /* unknown command */ - if (ctx->ctx_dbg > 1) - sm_dprintf("[%ld] cmd '%c' unknown\n", - (long) ctx->ctx_id, cmd); - ret = MI_FAILURE; - break; - } - if ((f = cmds[i].cm_fct) == NULL) - { - /* stop for now */ - if (ctx->ctx_dbg > 1) - sm_dprintf("[%ld] cmd '%c' not impl\n", - (long) ctx->ctx_id, cmd); - ret = MI_FAILURE; - break; - } - - /* is new state ok? */ - newstate = cmds[i].cm_next; - if (ctx->ctx_dbg > 5) - sm_dprintf("[%ld] cur %x new %x nextmask %x\n", - (long) ctx->ctx_id, - curstate, newstate, next_states[curstate]); - - if (newstate != ST_NONE && !trans_ok(curstate, newstate)) - { - if (ctx->ctx_dbg > 1) - sm_dprintf("[%ld] abort: cur %d (%x) new %d (%x) next %x\n", - (long) ctx->ctx_id, - curstate, MI_MASK(curstate), - newstate, MI_MASK(newstate), - next_states[curstate]); - - /* call abort only if in a mail transaction */ - if (fi_abort != NULL && call_abort) - (void) (*fi_abort)(ctx); - - /* - ** try to reach the new state from HELO - ** if it can't be reached, ignore the command. - */ - - curstate = ST_HELO; - if (!trans_ok(curstate, newstate)) - { - if (buf != NULL) - { - free(buf); - buf = NULL; - } - continue; - } - } - arg.a_len = len; - arg.a_buf = buf; - if (newstate != ST_NONE) - { - curstate = newstate; - ctx->ctx_state = curstate; - } - arg.a_idx = cmds[i].cm_macros; - call_abort = ST_IN_MAIL(curstate); - - /* call function to deal with command */ - MI_MONITOR_BEGIN(ctx, cmd); - r = (*f)(&arg); - MI_MONITOR_END(ctx, cmd); - if (r != _SMFIS_KEEP && buf != NULL) - { - free(buf); - buf = NULL; - } - if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS) - { - ret = MI_FAILURE; - break; - } - - if (r == SMFIS_ACCEPT) - { - /* accept mail, no further actions taken */ - curstate = ST_HELO; - } - 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 - */ - if (!bitset(CT_IGNO, cmds[i].cm_todo)) - curstate = ST_HELO; - } - else if (r == _SMFIS_ABORT) - { - if (ctx->ctx_dbg > 5) - sm_dprintf("[%ld] function returned abort\n", - (long) ctx->ctx_id); - ret = MI_FAILURE; - break; - } - } while (!bitset(CT_END, cmds[i].cm_todo)); - - ctx->ctx_state = curstate; - - if (ret == MI_FAILURE) - { - /* call abort only if in a mail transaction */ - if (fi_abort != NULL && call_abort) - (void) (*fi_abort)(ctx); - } - - /* has close been called? */ - if (ctx->ctx_state != ST_QUIT -#if _FFR_WORKERS_POOL - && ret != MI_CONTINUE -#endif /* _FFR_WORKERS_POOL */ - ) - { - if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL) - (void) (*fi_close)(ctx); - } - if (r != _SMFIS_KEEP && buf != NULL) - free(buf); -#if !_FFR_WORKERS_POOL - mi_clr_macros(ctx, 0); -#endif /* _FFR_WORKERS_POOL */ - return ret; -} - -static size_t milter_addsymlist __P((SMFICTX_PTR, char *, char **)); - -static size_t -milter_addsymlist(ctx, buf, newbuf) - SMFICTX_PTR ctx; - char *buf; - char **newbuf; -{ - size_t len; - int i; - mi_int32 v; - char *buffer; - - SM_ASSERT(ctx != NULL); - SM_ASSERT(buf != NULL); - SM_ASSERT(newbuf != NULL); - len = 0; - for (i = 0; i < MAX_MACROS_ENTRIES; i++) - { - if (ctx->ctx_mac_list[i] != NULL) - { - len += strlen(ctx->ctx_mac_list[i]) + 1 + - MILTER_LEN_BYTES; - } - } - if (len > 0) - { - size_t offset; - - SM_ASSERT(len + MILTER_OPTLEN > len); - len += MILTER_OPTLEN; - buffer = malloc(len); - if (buffer != NULL) - { - (void) memcpy(buffer, buf, MILTER_OPTLEN); - offset = MILTER_OPTLEN; - for (i = 0; i < MAX_MACROS_ENTRIES; i++) - { - size_t l; - - if (ctx->ctx_mac_list[i] == NULL) - continue; - - SM_ASSERT(offset + MILTER_LEN_BYTES < len); - v = htonl(i); - (void) memcpy(buffer + offset, (void *) &v, - MILTER_LEN_BYTES); - offset += MILTER_LEN_BYTES; - l = strlen(ctx->ctx_mac_list[i]) + 1; - SM_ASSERT(offset + l <= len); - (void) memcpy(buffer + offset, - ctx->ctx_mac_list[i], l); - offset += l; - } - } - else - { - /* oops ... */ - } - } - else - { - len = MILTER_OPTLEN; - buffer = buf; - } - *newbuf = buffer; - return len; -} - -/* -** GET_NR_BIT -- get "no reply" bit matching state -** -** Parameters: -** state -- current protocol stage -** -** Returns: -** 0: no matching bit -** >0: the matching "no reply" bit -*/ - -static unsigned long get_nr_bit __P((int)); - -static unsigned long -get_nr_bit(state) - int state; -{ - unsigned long bit; - - switch (state) - { - case ST_CONN: - bit = SMFIP_NR_CONN; - break; - case ST_HELO: - bit = SMFIP_NR_HELO; - break; - case ST_MAIL: - bit = SMFIP_NR_MAIL; - break; - case ST_RCPT: - bit = SMFIP_NR_RCPT; - break; - case ST_DATA: - bit = SMFIP_NR_DATA; - break; - case ST_UNKN: - bit = SMFIP_NR_UNKN; - break; - case ST_HDRS: - bit = SMFIP_NR_HDR; - break; - case ST_EOHS: - bit = SMFIP_NR_EOH; - break; - case ST_BODY: - bit = SMFIP_NR_BODY; - break; - default: - bit = 0; - break; - } - return bit; -} - -/* -** 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: -** MI_SUCCESS/MI_FAILURE -*/ - -static int -sendreply(r, sd, timeout_ptr, ctx) - sfsistat r; - socket_t sd; - struct timeval *timeout_ptr; - SMFICTX_PTR ctx; -{ - int ret; - unsigned long bit; - - ret = MI_SUCCESS; - - bit = get_nr_bit(ctx->ctx_state); - if (bit != 0 && (ctx->ctx_pflags & bit) != 0 && r != SMFIS_NOREPLY) - { - if (r >= SMFIS_CONTINUE && r < _SMFIS_KEEP) - { - /* milter said it wouldn't reply, but it lied... */ - smi_log(SMI_LOG_ERR, - "%s: milter claimed not to reply in state %d but did anyway %d\n", - ctx->ctx_smfi->xxfi_name, - ctx->ctx_state, r); - - } - - /* - ** Force specified behavior, otherwise libmilter - ** and MTA will fail to communicate properly. - */ - - switch (r) - { - case SMFIS_CONTINUE: - case SMFIS_TEMPFAIL: - case SMFIS_REJECT: - case SMFIS_DISCARD: - case SMFIS_ACCEPT: - case SMFIS_SKIP: - case _SMFIS_OPTIONS: - r = SMFIS_NOREPLY; - break; - } - } - - switch (r) - { - case SMFIS_CONTINUE: - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0); - break; - case SMFIS_TEMPFAIL: - case SMFIS_REJECT: - if (ctx->ctx_reply != NULL && - ((r == SMFIS_TEMPFAIL && *ctx->ctx_reply == '4') || - (r == SMFIS_REJECT && *ctx->ctx_reply == '5'))) - { - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE, - ctx->ctx_reply, - strlen(ctx->ctx_reply) + 1); - free(ctx->ctx_reply); - ctx->ctx_reply = NULL; - } - else - { - ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ? - SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0); - } - break; - case SMFIS_DISCARD: - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0); - break; - case SMFIS_ACCEPT: - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0); - break; - case SMFIS_SKIP: - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_SKIP, NULL, 0); - break; - case _SMFIS_OPTIONS: - { - mi_int32 v; - size_t len; - char *buffer; - char buf[MILTER_OPTLEN]; - - v = htonl(ctx->ctx_prot_vers2mta); - (void) memcpy(&(buf[0]), (void *) &v, - MILTER_LEN_BYTES); - v = htonl(ctx->ctx_aflags); - (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v, - MILTER_LEN_BYTES); - v = htonl(ctx->ctx_pflags2mta); - (void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), - (void *) &v, MILTER_LEN_BYTES); - len = milter_addsymlist(ctx, buf, &buffer); - if (buffer != NULL) - ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, - buffer, len); - else - ret = MI_FAILURE; - } - break; - case SMFIS_NOREPLY: - if (bit != 0 && - (ctx->ctx_pflags & bit) != 0 && - (ctx->ctx_mta_pflags & bit) == 0) - { - /* - ** milter doesn't want to send a reply, - ** but the MTA doesn't have that feature: fake it. - */ - - ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, - 0); - } - 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) - SMFICTX_PTR ctx; - int m; -{ - int i; - - for (i = m; i < MAX_MACROS_ENTRIES; i++) - { - if (ctx->ctx_mac_ptr[i] != NULL) - { - free(ctx->ctx_mac_ptr[i]); - ctx->ctx_mac_ptr[i] = NULL; - } - if (ctx->ctx_mac_buf[i] != NULL) - { - free(ctx->ctx_mac_buf[i]); - ctx->ctx_mac_buf[i] = NULL; - } - } -} - -/* -** ST_OPTIONNEG -- negotiate options -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** abort/send options/continue -*/ - -static int -st_optionneg(g) - genarg *g; -{ - mi_int32 i, v, fake_pflags; - SMFICTX_PTR ctx; - int (*fi_negotiate) __P((SMFICTX *, - unsigned long, unsigned long, - unsigned long, unsigned long, - unsigned long *, unsigned long *, - unsigned long *, unsigned long *)); - - if (g == NULL || g->a_ctx->ctx_smfi == NULL) - return SMFIS_CONTINUE; - ctx = g->a_ctx; - mi_clr_macros(ctx, g->a_idx + 1); - ctx->ctx_prot_vers = SMFI_PROT_VERSION; - - /* check for minimum length */ - if (g->a_len < MILTER_OPTLEN) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%ld]: len too short %d < %d", - ctx->ctx_smfi->xxfi_name, - (long) ctx->ctx_id, (int) g->a_len, - MILTER_OPTLEN); - return _SMFIS_ABORT; - } - - /* protocol version */ - (void) memcpy((void *) &i, (void *) &(g->a_buf[0]), MILTER_LEN_BYTES); - v = ntohl(i); - -#define SMFI_PROT_VERSION_MIN 2 - - /* check for minimum version */ - if (v < SMFI_PROT_VERSION_MIN) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%ld]: protocol version too old %d < %d", - ctx->ctx_smfi->xxfi_name, - (long) ctx->ctx_id, v, SMFI_PROT_VERSION_MIN); - return _SMFIS_ABORT; - } - ctx->ctx_mta_prot_vers = v; - if (ctx->ctx_prot_vers < ctx->ctx_mta_prot_vers) - ctx->ctx_prot_vers2mta = ctx->ctx_prot_vers; - else - ctx->ctx_prot_vers2mta = ctx->ctx_mta_prot_vers; - - (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]), - MILTER_LEN_BYTES); - v = ntohl(i); - - /* no flags? set to default value for V1 actions */ - if (v == 0) - v = SMFI_V1_ACTS; - ctx->ctx_mta_aflags = v; /* MTA action flags */ - - (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]), - MILTER_LEN_BYTES); - v = ntohl(i); - - /* no flags? set to default value for V1 protocol */ - if (v == 0) - v = SMFI_V1_PROT; - ctx->ctx_mta_pflags = v; /* MTA protocol flags */ - - /* - ** Copy flags from milter struct into libmilter context; - ** this variable will be used later on to check whether - ** the MTA "actions" can fulfill the milter requirements, - ** but it may be overwritten by the negotiate callback. - */ - - ctx->ctx_aflags = ctx->ctx_smfi->xxfi_flags; - fake_pflags = SMFIP_NR_CONN - |SMFIP_NR_HELO - |SMFIP_NR_MAIL - |SMFIP_NR_RCPT - |SMFIP_NR_DATA - |SMFIP_NR_UNKN - |SMFIP_NR_HDR - |SMFIP_NR_EOH - |SMFIP_NR_BODY - ; - - if (g->a_ctx->ctx_smfi != NULL && - g->a_ctx->ctx_smfi->xxfi_version > 4 && - (fi_negotiate = g->a_ctx->ctx_smfi->xxfi_negotiate) != NULL) - { - int r; - unsigned long m_aflags, m_pflags, m_f2, m_f3; - - /* - ** let milter decide whether the features offered by the - ** MTA are "good enough". - ** Notes: - ** - libmilter can "fake" some features (e.g., SMFIP_NR_HDR) - ** - m_f2, m_f3 are for future extensions - */ - - m_f2 = m_f3 = 0; - m_aflags = ctx->ctx_mta_aflags; - m_pflags = ctx->ctx_pflags; - if ((SMFIP_SKIP & ctx->ctx_mta_pflags) != 0) - m_pflags |= SMFIP_SKIP; - r = fi_negotiate(g->a_ctx, - ctx->ctx_mta_aflags, - ctx->ctx_mta_pflags|fake_pflags, - 0, 0, - &m_aflags, &m_pflags, &m_f2, &m_f3); - - /* - ** Types of protocol flags (pflags): - ** 1. do NOT send protocol step X - ** 2. MTA can do/understand something extra (SKIP, - ** send unknown RCPTs) - ** 3. MTA can deal with "no reply" for various protocol steps - ** Note: this mean that it isn't possible to simply set all - ** flags to get "everything": - ** setting a flag of type 1 turns off a step - ** (it should be the other way around: - ** a flag means a protocol step can be sent) - ** setting a flag of type 3 requires that milter - ** never sends a reply for the corresponding step. - ** Summary: the "negation" of protocol flags is causing - ** problems, but at least for type 3 there is no simple - ** solution. - ** - ** What should "all options" mean? - ** send all protocol steps _except_ those for which there is - ** no callback (currently registered in ctx_pflags) - ** expect SKIP as return code? Yes - ** send unknown RCPTs? No, - ** must be explicitly requested? - ** "no reply" for some protocol steps? No, - ** must be explicitly requested. - */ - - if (SMFIS_ALL_OPTS == r) - { - ctx->ctx_aflags = ctx->ctx_mta_aflags; - ctx->ctx_pflags2mta = ctx->ctx_pflags; - if ((SMFIP_SKIP & ctx->ctx_mta_pflags) != 0) - ctx->ctx_pflags2mta |= SMFIP_SKIP; - } - else if (r != SMFIS_CONTINUE) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%ld]: xxfi_negotiate returned %d (protocol options=0x%lx, actions=0x%lx)", - ctx->ctx_smfi->xxfi_name, - (long) ctx->ctx_id, r, ctx->ctx_mta_pflags, - ctx->ctx_mta_aflags); - return _SMFIS_ABORT; - } - else - { - ctx->ctx_aflags = m_aflags; - ctx->ctx_pflags = m_pflags; - ctx->ctx_pflags2mta = m_pflags; - } - - /* check whether some flags need to be "faked" */ - i = ctx->ctx_pflags2mta; - if ((ctx->ctx_mta_pflags & i) != i) - { - unsigned int idx; - unsigned long b; - - /* - ** If some behavior can be faked (set in fake_pflags), - ** but the MTA doesn't support it, then unset - ** that flag in the value that is sent to the MTA. - */ - - for (idx = 0; idx < 32; idx++) - { - b = 1 << idx; - if ((ctx->ctx_mta_pflags & b) != b && - (fake_pflags & b) == b) - ctx->ctx_pflags2mta &= ~b; - } - } - } - else - { - /* - ** Set the protocol flags based on the values determined - ** in mi_listener() which checked the defined callbacks. - */ - - ctx->ctx_pflags2mta = ctx->ctx_pflags; - } - - /* check whether actions and protocol requirements can be satisfied */ - i = ctx->ctx_aflags; - if ((i & ctx->ctx_mta_aflags) != i) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%ld]: 0x%lx does not fulfill action requirements 0x%x", - ctx->ctx_smfi->xxfi_name, - (long) ctx->ctx_id, ctx->ctx_mta_aflags, i); - return _SMFIS_ABORT; - } - - i = ctx->ctx_pflags2mta; - if ((ctx->ctx_mta_pflags & i) != i) - { - /* - ** Older MTAs do not support some protocol steps. - ** As this protocol is a bit "wierd" (it asks for steps - ** NOT to be taken/sent) we have to check whether we - ** should turn off those "negative" requests. - ** Currently these are only SMFIP_NODATA and SMFIP_NOUNKNOWN. - */ - - if (bitset(SMFIP_NODATA, ctx->ctx_pflags2mta) && - !bitset(SMFIP_NODATA, ctx->ctx_mta_pflags)) - ctx->ctx_pflags2mta &= ~SMFIP_NODATA; - if (bitset(SMFIP_NOUNKNOWN, ctx->ctx_pflags2mta) && - !bitset(SMFIP_NOUNKNOWN, ctx->ctx_mta_pflags)) - ctx->ctx_pflags2mta &= ~SMFIP_NOUNKNOWN; - i = ctx->ctx_pflags2mta; - } - - if ((ctx->ctx_mta_pflags & i) != i) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%ld]: 0x%lx does not fulfill protocol requirements 0x%x", - ctx->ctx_smfi->xxfi_name, - (long) ctx->ctx_id, ctx->ctx_mta_pflags, i); - return _SMFIS_ABORT; - } - - if (ctx->ctx_dbg > 3) - sm_dprintf("[%ld] milter_negotiate:" - " mta_actions=0x%lx, mta_flags=0x%lx" - " actions=0x%lx, flags=0x%lx\n" - , (long) ctx->ctx_id - , ctx->ctx_mta_aflags, ctx->ctx_mta_pflags - , ctx->ctx_aflags, ctx->ctx_pflags); - - return _SMFIS_OPTIONS; -} - -/* -** ST_CONNECTINFO -- receive connection information -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_connectinfo(g) - genarg *g; -{ - size_t l; - size_t i; - char *s, family; - unsigned short port = 0; - _SOCK_ADDR sockaddr; - sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *)); - - if (g == NULL) - return _SMFIS_ABORT; - mi_clr_macros(g->a_ctx, g->a_idx + 1); - if (g->a_ctx->ctx_smfi == NULL || - (fi_connect = g->a_ctx->ctx_smfi->xxfi_connect) == NULL) - 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++]; - (void) memset(&sockaddr, '\0', sizeof sockaddr); - if (family != SMFIA_UNKNOWN) - { - if (i + sizeof port >= l) - { - smi_log(SMI_LOG_ERR, - "%s: connect[%ld]: wrong len %d >= %d", - g->a_ctx->ctx_smfi->xxfi_name, - (long) g->a_ctx->ctx_id, (int) i, (int) l); - return _SMFIS_ABORT; - } - (void) memcpy((void *) &port, (void *) (s + i), - sizeof port); - i += sizeof port; - - /* make sure string is terminated */ - if (s[l - 1] != '\0') - return _SMFIS_ABORT; -# if NETINET - if (family == SMFIA_INET) - { - if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr) - != 1) - { - smi_log(SMI_LOG_ERR, - "%s: connect[%ld]: inet_aton failed", - g->a_ctx->ctx_smfi->xxfi_name, - (long) g->a_ctx->ctx_id); - return _SMFIS_ABORT; - } - sockaddr.sa.sa_family = AF_INET; - if (port > 0) - sockaddr.sin.sin_port = port; - } - else -# endif /* NETINET */ -# if NETINET6 - if (family == SMFIA_INET6) - { - if (mi_inet_pton(AF_INET6, s + i, - &sockaddr.sin6.sin6_addr) != 1) - { - smi_log(SMI_LOG_ERR, - "%s: connect[%ld]: mi_inet_pton failed", - g->a_ctx->ctx_smfi->xxfi_name, - (long) g->a_ctx->ctx_id); - return _SMFIS_ABORT; - } - sockaddr.sa.sa_family = AF_INET6; - if (port > 0) - sockaddr.sin6.sin6_port = port; - } - else -# endif /* NETINET6 */ -# if NETUNIX - if (family == SMFIA_UNIX) - { - if (sm_strlcpy(sockaddr.sunix.sun_path, s + i, - sizeof sockaddr.sunix.sun_path) >= - sizeof sockaddr.sunix.sun_path) - { - smi_log(SMI_LOG_ERR, - "%s: connect[%ld]: path too long", - g->a_ctx->ctx_smfi->xxfi_name, - (long) g->a_ctx->ctx_id); - return _SMFIS_ABORT; - } - sockaddr.sunix.sun_family = AF_UNIX; - } - else -# endif /* NETUNIX */ - { - smi_log(SMI_LOG_ERR, - "%s: connect[%ld]: unknown family %d", - g->a_ctx->ctx_smfi->xxfi_name, - (long) g->a_ctx->ctx_id, family); - return _SMFIS_ABORT; - } - } - return (*fi_connect)(g->a_ctx, g->a_buf, - family != SMFIA_UNKNOWN ? &sockaddr : NULL); -} - -/* -** ST_EOH -- end of headers -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_eoh(g) - genarg *g; -{ - sfsistat (*fi_eoh) __P((SMFICTX *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi != NULL && - (fi_eoh = g->a_ctx->ctx_smfi->xxfi_eoh) != NULL) - return (*fi_eoh)(g->a_ctx); - return SMFIS_CONTINUE; -} - -/* -** ST_DATA -- DATA command -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_data(g) - genarg *g; -{ - sfsistat (*fi_data) __P((SMFICTX *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi != NULL && - g->a_ctx->ctx_smfi->xxfi_version > 3 && - (fi_data = g->a_ctx->ctx_smfi->xxfi_data) != NULL) - return (*fi_data)(g->a_ctx); - return SMFIS_CONTINUE; -} - -/* -** ST_HELO -- helo/ehlo command -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_helo(g) - genarg *g; -{ - sfsistat (*fi_helo) __P((SMFICTX *, char *)); - - if (g == NULL) - return _SMFIS_ABORT; - mi_clr_macros(g->a_ctx, g->a_idx + 1); - if (g->a_ctx->ctx_smfi != NULL && - (fi_helo = g->a_ctx->ctx_smfi->xxfi_helo) != NULL) - { - /* paranoia: check for terminating '\0' */ - if (g->a_len == 0 || g->a_buf[g->a_len - 1] != '\0') - return MI_FAILURE; - return (*fi_helo)(g->a_ctx, g->a_buf); - } - 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; -{ - char *hf, *hv; - sfsistat (*fi_header) __P((SMFICTX *, char *, char *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi == NULL || - (fi_header = g->a_ctx->ctx_smfi->xxfi_header) == NULL) - return SMFIS_CONTINUE; - if (dec_arg2(g->a_buf, g->a_len, &hf, &hv) == MI_SUCCESS) - return (*fi_header)(g->a_ctx, hf, hv); - else - return _SMFIS_ABORT; -} - -#define ARGV_FCT(lf, rf, idx) \ - char **argv; \ - sfsistat (*lf) __P((SMFICTX *, char **)); \ - int r; \ - \ - if (g == NULL) \ - return _SMFIS_ABORT; \ - mi_clr_macros(g->a_ctx, g->a_idx + 1); \ - if (g->a_ctx->ctx_smfi == NULL || \ - (lf = g->a_ctx->ctx_smfi->rf) == NULL) \ - return SMFIS_CONTINUE; \ - if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL) \ - return _SMFIS_ABORT; \ - r = (*lf)(g->a_ctx, argv); \ - free(argv); \ - 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; -{ - ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL) -} - -/* -** ST_RCPT -- RCPT TO command -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_rcpt(g) - genarg *g; -{ - ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT) -} - -/* -** ST_UNKNOWN -- unrecognized or unimplemented command -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue or filter-specified value -*/ - -static int -st_unknown(g) - genarg *g; -{ - sfsistat (*fi_unknown) __P((SMFICTX *, const char *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi != NULL && - g->a_ctx->ctx_smfi->xxfi_version > 2 && - (fi_unknown = g->a_ctx->ctx_smfi->xxfi_unknown) != NULL) - return (*fi_unknown)(g->a_ctx, (const char *) g->a_buf); - return SMFIS_CONTINUE; -} - -/* -** ST_MACROS -- deal with macros received from the MTA -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** continue/keep -** -** Side effects: -** set pointer in macro array to current values. -*/ - -static int -st_macros(g) - genarg *g; -{ - int i; - char **argv; - - if (g == NULL || g->a_len < 1) - return _SMFIS_FAIL; - if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL) - 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_DATA: - i = CI_DATA; - break; - case SMFIC_BODYEOB: - i = CI_EOM; - break; - case SMFIC_EOH: - i = CI_EOH; - break; - default: - free(argv); - return _SMFIS_FAIL; - } - if (g->a_ctx->ctx_mac_ptr[i] != NULL) - free(g->a_ctx->ctx_mac_ptr[i]); - if (g->a_ctx->ctx_mac_buf[i] != NULL) - free(g->a_ctx->ctx_mac_buf[i]); - g->a_ctx->ctx_mac_ptr[i] = argv; - g->a_ctx->ctx_mac_buf[i] = g->a_buf; - return _SMFIS_KEEP; -} - -/* -** ST_QUIT -- quit command -** -** Parameters: -** g -- generic argument structure -** -** Returns: -** noreply -*/ - -/* ARGSUSED */ -static int -st_quit(g) - genarg *g; -{ - sfsistat (*fi_close) __P((SMFICTX *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi != NULL && - (fi_close = g->a_ctx->ctx_smfi->xxfi_close) != NULL) - (void) (*fi_close)(g->a_ctx); - mi_clr_macros(g->a_ctx, 0); - 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; -{ - sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g->a_ctx->ctx_smfi != NULL && - (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL) - return (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, - 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; - sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); - sfsistat (*fi_eom) __P((SMFICTX *)); - - if (g == NULL) - return _SMFIS_ABORT; - r = SMFIS_CONTINUE; - if (g->a_ctx->ctx_smfi != NULL) - { - if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL && - g->a_len > 0) - { - socket_t sd; - struct timeval timeout; - - timeout.tv_sec = g->a_ctx->ctx_timeout; - timeout.tv_usec = 0; - sd = g->a_ctx->ctx_sd; - r = (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, - g->a_len); - if (r != SMFIS_CONTINUE && - sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS) - return _SMFIS_ABORT; - } - } - if (r == SMFIS_CONTINUE && - (fi_eom = g->a_ctx->ctx_smfi->xxfi_eom) != NULL) - return (*fi_eom)(g->a_ctx); - 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; -{ - sfsistat (*fi_abort) __P((SMFICTX *)); - - if (g == NULL) - return _SMFIS_ABORT; - if (g != NULL && g->a_ctx->ctx_smfi != NULL && - (fi_abort = g->a_ctx->ctx_smfi->xxfi_abort) != NULL) - (void) (*fi_abort)(g->a_ctx); - return _SMFIS_NOREPLY; -} - -/* -** TRANS_OK -- is the state transition ok? -** -** Parameters: -** old -- old state -** new -- new state -** -** Returns: -** state transition ok -*/ - -static bool -trans_ok(old, new) - int old, new; -{ - int s, n; - - s = old; - if (s >= SIZE_NEXT_STATES) - return false; - do - { - /* is this state transition allowed? */ - if ((MI_MASK(new) & next_states[s]) != 0) - 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 - */ - - if (bitset(NX_SKIP, next_states[n])) - 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 -fix_stm(ctx) - SMFICTX_PTR ctx; -{ - unsigned long fl; - - if (ctx == NULL || ctx->ctx_smfi == NULL) - return; - fl = ctx->ctx_pflags; - if (bitset(SMFIP_NOCONNECT, fl)) - next_states[ST_CONN] |= NX_SKIP; - if (bitset(SMFIP_NOHELO, fl)) - next_states[ST_HELO] |= NX_SKIP; - if (bitset(SMFIP_NOMAIL, fl)) - next_states[ST_MAIL] |= NX_SKIP; - if (bitset(SMFIP_NORCPT, fl)) - next_states[ST_RCPT] |= NX_SKIP; - if (bitset(SMFIP_NOHDRS, fl)) - next_states[ST_HDRS] |= NX_SKIP; - if (bitset(SMFIP_NOEOH, fl)) - next_states[ST_EOHS] |= NX_SKIP; - if (bitset(SMFIP_NOBODY, fl)) - next_states[ST_BODY] |= NX_SKIP; - if (bitset(SMFIP_NODATA, fl)) - next_states[ST_DATA] |= NX_SKIP; - if (bitset(SMFIP_NOUNKNOWN, fl)) - next_states[ST_UNKN] |= NX_SKIP; -} - -/* -** 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 ** -dec_argv(buf, len) - char *buf; - size_t len; -{ - char **s; - size_t i; - int elem, nelem; - - 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 */ - s = (char **)malloc((nelem + 1) * (sizeof *s)); - if (s == NULL) - return NULL; - s[0] = buf; - for (i = 0, elem = 0; i < len && elem < nelem; i++) - { - if (buf[i] == '\0') - { - ++elem; - if (i + 1 >= len) - s[elem] = NULL; - else - s[elem] = &(buf[i + 1]); - } - } - - /* overwrite last entry (already done above, just paranoia) */ - s[elem] = NULL; - 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: -** MI_FAILURE/MI_SUCCESS -*/ - -static int -dec_arg2(buf, len, s1, s2) - char *buf; - size_t len; - char **s1; - char **s2; -{ - size_t i; - - /* paranoia: check for terminating '\0' */ - if (len == 0 || buf[len - 1] != '\0') - return MI_FAILURE; - *s1 = buf; - for (i = 1; i < len && buf[i] != '\0'; i++) - continue; - if (i >= len - 1) - return MI_FAILURE; - *s2 = buf + i + 1; - 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 -mi_sendok(ctx, flag) - SMFICTX_PTR ctx; - int flag; -{ - if (ctx == NULL || ctx->ctx_smfi == NULL) - return false; - - /* did the milter request this operation? */ - if (flag != 0 && !bitset(flag, ctx->ctx_aflags)) - return false; - - /* are we in the correct state? It must be "End of Message". */ - return ctx->ctx_state == ST_ENDM; -} - -#if _FFR_WORKERS_POOL -/* -** MI_RD_SOCKET_READY - checks if the socket is ready for read(2) -** -** Parameters: -** sd -- socket_t -** -** Returns: -** true iff socket is ready for read(2) -*/ - -#define MI_RD_CMD_TO 1 -#define MI_RD_MAX_ERR 16 - -static bool -mi_rd_socket_ready (sd) - socket_t sd; -{ - int n; - int nerr = 0; -#if SM_CONF_POLL - struct pollfd pfd; -#else /* SM_CONF_POLL */ - fd_set rd_set, exc_set; -#endif /* SM_CONF_POLL */ - - do - { -#if SM_CONF_POLL - pfd.fd = sd; - pfd.events = POLLIN; - pfd.revents = 0; - - n = poll(&pfd, 1, MI_RD_CMD_TO); -#else /* SM_CONF_POLL */ - struct timeval timeout; - - FD_ZERO(&rd_set); - FD_ZERO(&exc_set); - FD_SET(sd, &rd_set); - FD_SET(sd, &exc_set); - - timeout.tv_sec = MI_RD_CMD_TO / 1000; - timeout.tv_usec = 0; - n = select(sd + 1, &rd_set, NULL, &exc_set, &timeout); -#endif /* SM_CONF_POLL */ - - if (n < 0) - { - if (errno == EINTR) - { - nerr++; - continue; - } - return true; - } - - if (n == 0) - return false; - break; - } while (nerr < MI_RD_MAX_ERR); - if (nerr >= MI_RD_MAX_ERR) - return false; - -#if SM_CONF_POLL - return (pfd.revents != 0); -#else /* SM_CONF_POLL */ - return FD_ISSET(sd, &rd_set) || FD_ISSET(sd, &exc_set); -#endif /* SM_CONF_POLL */ -} -#endif /* _FFR_WORKERS_POOL */ |