diff options
Diffstat (limited to 'contrib/sendmail/libmilter/engine.c')
-rw-r--r-- | contrib/sendmail/libmilter/engine.c | 762 |
1 files changed, 640 insertions, 122 deletions
diff --git a/contrib/sendmail/libmilter/engine.c b/contrib/sendmail/libmilter/engine.c index 8b4bed07adf3..a61cf56130c0 100644 --- a/contrib/sendmail/libmilter/engine.c +++ b/contrib/sendmail/libmilter/engine.c @@ -9,7 +9,7 @@ */ #include <sm/gen.h> -SM_RCSID("@(#)$Id: engine.c,v 8.121 2006/04/18 21:01:46 ca Exp $") +SM_RCSID("@(#)$Id: engine.c,v 8.157 2007/03/26 18:10:04 ca Exp $") #include "libmilter.h" @@ -56,7 +56,7 @@ typedef struct cmdfct_t cmdfct; /* not needed right now, done via return code instead */ #define CT_KEEP 0x0004 /* keep buffer (contains symbols) */ -#define CT_END 0x0008 /* start replying */ +#define CT_END 0x0008 /* last command of session, stop replying */ /* index in macro array: macros only for these commands */ #define CI_NONE (-1) @@ -64,9 +64,33 @@ typedef struct cmdfct_t cmdfct; #define CI_HELO 1 #define CI_MAIL 2 #define CI_RCPT 3 -#define CI_EOM 4 -#if CI_EOM >= MAX_MACROS_ENTRIES -ERROR: do not compile with CI_EOM >= MAX_MACROS_ENTRIES +#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 */ @@ -80,12 +104,8 @@ static int st_helo __P((genarg *)); static int st_header __P((genarg *)); static int st_sender __P((genarg *)); static int st_rcpt __P((genarg *)); -#if SMFI_VERSION > 2 static int st_unknown __P((genarg *)); -#endif /* SMFI_VERSION > 2 */ -#if SMFI_VERSION > 3 static int st_data __P((genarg *)); -#endif /* SMFI_VERSION > 3 */ static int st_eoh __P((genarg *)); static int st_quit __P((genarg *)); static int sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR)); @@ -94,6 +114,10 @@ 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 */ @@ -110,8 +134,9 @@ static int dec_arg2 __P((char *, size_t, char **, char **)); #define ST_QUIT 11 /* quit */ #define ST_ABRT 12 /* abort */ #define ST_UNKN 13 /* unknown SMTP command */ -#define ST_LAST ST_UNKN /* last valid state */ -#define ST_SKIP 15 /* not a state but required for the state table */ +#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) @@ -138,32 +163,35 @@ static int dec_arg2 __P((char *, size_t, char **, char **)); #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)) +#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_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_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])) @@ -171,31 +199,32 @@ static int next_states[] = /* 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_NONE, st_eoh }, -{SMFIC_QUIT, CM_ARG0, ST_QUIT, CT_END, CI_NONE, st_quit }, -#if SMFI_VERSION > 3 -{SMFIC_DATA, CM_ARG0, ST_DATA, CT_CONT, CI_NONE, st_data }, -#endif /* SMFI_VERSION > 3 */ -{SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt } -#if SMFI_VERSION > 2 -,{SMFIC_UNKNOWN,CM_ARG1, ST_UNKN, CT_IGNO, CI_NONE, st_unknown } -#endif /* SMFI_VERSION > 2 */ + {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 */ +/* +** 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 23 +#define _SMFIS_NOREPLY SMFIS_NOREPLY #define _SMFIS_FAIL (-1) #define _SMFIS_NONE (-2) @@ -208,6 +237,7 @@ static cmdfct cmds[] = ** Returns: ** MI_FAILURE/MI_SUCCESS */ + int mi_engine(ctx) SMFICTX_PTR ctx; @@ -232,8 +262,17 @@ mi_engine(ctx) 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 { @@ -244,8 +283,8 @@ mi_engine(ctx) if (mi_stop() == MILTER_ABRT) { if (ctx->ctx_dbg > 3) - sm_dprintf("[%d] milter_abort\n", - (int) ctx->ctx_id); + sm_dprintf("[%ld] milter_abort\n", + (long) ctx->ctx_id); ret = MI_FAILURE; break; } @@ -260,14 +299,23 @@ mi_engine(ctx) ** 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("[%d] mi_engine: mi_rd_cmd error (%x)\n", - (int) ctx->ctx_id, (int) cmd); + sm_dprintf("[%ld] mi_engine: mi_rd_cmd error (%x)\n", + (long) ctx->ctx_id, (int) cmd); /* ** eof is currently treated as failure -> @@ -279,8 +327,8 @@ mi_engine(ctx) break; } if (ctx->ctx_dbg > 4) - sm_dprintf("[%d] got cmd '%c' len %d\n", - (int) ctx->ctx_id, cmd, (int) len); + 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) @@ -290,8 +338,8 @@ mi_engine(ctx) { /* unknown command */ if (ctx->ctx_dbg > 1) - sm_dprintf("[%d] cmd '%c' unknown\n", - (int) ctx->ctx_id, cmd); + sm_dprintf("[%ld] cmd '%c' unknown\n", + (long) ctx->ctx_id, cmd); ret = MI_FAILURE; break; } @@ -299,8 +347,8 @@ mi_engine(ctx) { /* stop for now */ if (ctx->ctx_dbg > 1) - sm_dprintf("[%d] cmd '%c' not impl\n", - (int) ctx->ctx_id, cmd); + sm_dprintf("[%ld] cmd '%c' not impl\n", + (long) ctx->ctx_id, cmd); ret = MI_FAILURE; break; } @@ -308,15 +356,15 @@ mi_engine(ctx) /* is new state ok? */ newstate = cmds[i].cm_next; if (ctx->ctx_dbg > 5) - sm_dprintf("[%d] cur %x new %x nextmask %x\n", - (int) ctx->ctx_id, + 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("[%d] abort: cur %d (%x) new %d (%x) next %x\n", - (int) ctx->ctx_id, + 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]); @@ -352,7 +400,9 @@ mi_engine(ctx) 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); @@ -383,28 +433,164 @@ mi_engine(ctx) else if (r == _SMFIS_ABORT) { if (ctx->ctx_dbg > 5) - sm_dprintf("[%d] function returned abort\n", - (int) ctx->ctx_id); + sm_dprintf("[%ld] function returned abort\n", + (long) ctx->ctx_id); ret = MI_FAILURE; break; } } while (!bitset(CT_END, cmds[i].cm_todo)); - if (ret != MI_SUCCESS) + 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); } - /* close must always be called */ - if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL) - (void) (*fi_close)(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 ** @@ -425,7 +611,42 @@ sendreply(r, sd, timeout_ptr, ctx) struct timeval *timeout_ptr; SMFICTX_PTR ctx; { - int ret = MI_SUCCESS; + 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) { @@ -456,21 +677,45 @@ sendreply(r, sd, timeout_ptr, ctx) 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: { - char buf[MILTER_OPTLEN]; mi_int32 v; + size_t len; + char *buffer; + char buf[MILTER_OPTLEN]; - v = htonl(ctx->ctx_smfi->xxfi_version); - (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); - v = htonl(ctx->ctx_smfi->xxfi_flags); - (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v, + v = htonl(ctx->ctx_prot_vers2mta); + (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); - v = htonl(ctx->ctx_pflags); - (void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), (void *) &v, + v = htonl(ctx->ctx_aflags); + (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v, MILTER_LEN_BYTES); - ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, buf, - MILTER_OPTLEN); + 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 */ @@ -489,6 +734,7 @@ sendreply(r, sd, timeout_ptr, ctx) ** Returns: ** None. */ + void mi_clr_macros(ctx, m) SMFICTX_PTR ctx; @@ -510,6 +756,7 @@ mi_clr_macros(ctx, m) } } } + /* ** ST_OPTIONNEG -- negotiate options ** @@ -524,36 +771,51 @@ static int st_optionneg(g) genarg *g; { - mi_int32 i, v; + 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; - mi_clr_macros(g->a_ctx, g->a_idx + 1); + 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[%d]: len too short %d < %d", - g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, (int) g->a_len, + "%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; } - (void) memcpy((void *) &i, (void *) &(g->a_buf[0]), - MILTER_LEN_BYTES); + /* protocol version */ + (void) memcpy((void *) &i, (void *) &(g->a_buf[0]), MILTER_LEN_BYTES); v = ntohl(i); - if (v < g->a_ctx->ctx_smfi->xxfi_version) + +#define SMFI_PROT_VERSION_MIN 2 + + /* check for minimum version */ + if (v < SMFI_PROT_VERSION_MIN) { - /* hard failure for now! */ smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d", - g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, (int) v, - g->a_ctx->ctx_smfi->xxfi_version); + "%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); @@ -562,15 +824,7 @@ st_optionneg(g) /* no flags? set to default value for V1 actions */ if (v == 0) v = SMFI_V1_ACTS; - i = g->a_ctx->ctx_smfi->xxfi_flags; - if ((v & i) != i) - { - smi_log(SMI_LOG_ERR, - "%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x", - g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, v, i); - return _SMFIS_ABORT; - } + ctx->ctx_mta_aflags = v; /* MTA action flags */ (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]), MILTER_LEN_BYTES); @@ -579,18 +833,185 @@ st_optionneg(g) /* 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) + 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 && + (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[%d]: 0x%x does not fulfill protocol requirements 0x%x", - g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, v, i); + "%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 ** @@ -636,9 +1057,9 @@ st_connectinfo(g) if (i + sizeof port >= l) { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: wrong len %d >= %d", + "%s: connect[%ld]: wrong len %d >= %d", g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, (int) i, (int) l); + (long) g->a_ctx->ctx_id, (int) i, (int) l); return _SMFIS_ABORT; } (void) memcpy((void *) &port, (void *) (s + i), @@ -655,9 +1076,9 @@ st_connectinfo(g) != 1) { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: inet_aton failed", + "%s: connect[%ld]: inet_aton failed", g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id); + (long) g->a_ctx->ctx_id); return _SMFIS_ABORT; } sockaddr.sa.sa_family = AF_INET; @@ -673,9 +1094,9 @@ st_connectinfo(g) &sockaddr.sin6.sin6_addr) != 1) { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: mi_inet_pton failed", + "%s: connect[%ld]: mi_inet_pton failed", g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id); + (long) g->a_ctx->ctx_id); return _SMFIS_ABORT; } sockaddr.sa.sa_family = AF_INET6; @@ -692,9 +1113,9 @@ st_connectinfo(g) sizeof sockaddr.sunix.sun_path) { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: path too long", + "%s: connect[%ld]: path too long", g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id); + (long) g->a_ctx->ctx_id); return _SMFIS_ABORT; } sockaddr.sunix.sun_family = AF_UNIX; @@ -703,9 +1124,9 @@ st_connectinfo(g) # endif /* NETUNIX */ { smi_log(SMI_LOG_ERR, - "%s: connect[%d]: unknown family %d", + "%s: connect[%ld]: unknown family %d", g->a_ctx->ctx_smfi->xxfi_name, - (int) g->a_ctx->ctx_id, family); + (long) g->a_ctx->ctx_id, family); return _SMFIS_ABORT; } } @@ -737,7 +1158,6 @@ st_eoh(g) return SMFIS_CONTINUE; } -#if SMFI_VERSION > 3 /* ** ST_DATA -- DATA command ** @@ -761,7 +1181,6 @@ st_data(g) return (*fi_data)(g->a_ctx); return SMFIS_CONTINUE; } -#endif /* SMFI_VERSION > 3 */ /* ** ST_HELO -- helo/ehlo command @@ -772,6 +1191,7 @@ st_data(g) ** Returns: ** continue or filter-specified value */ + static int st_helo(g) genarg *g; @@ -791,6 +1211,7 @@ st_helo(g) } return SMFIS_CONTINUE; } + /* ** ST_HEADER -- header line ** @@ -852,6 +1273,7 @@ st_sender(g) { ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL) } + /* ** ST_RCPT -- RCPT TO command ** @@ -869,7 +1291,6 @@ st_rcpt(g) ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT) } -#if SMFI_VERSION > 2 /* ** ST_UNKNOWN -- unrecognized or unimplemented command ** @@ -884,17 +1305,15 @@ static int st_unknown(g) genarg *g; { - sfsistat (*fi_unknown) __P((SMFICTX *, char *)); + sfsistat (*fi_unknown) __P((SMFICTX *, const 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_unknown = g->a_ctx->ctx_smfi->xxfi_unknown) != NULL) - return (*fi_unknown)(g->a_ctx, g->a_buf); + return (*fi_unknown)(g->a_ctx, (const char *) g->a_buf); return SMFIS_CONTINUE; } -#endif /* SMFI_VERSION > 2 */ /* ** ST_MACROS -- deal with macros received from the MTA @@ -934,9 +1353,15 @@ st_macros(g) 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; @@ -949,6 +1374,7 @@ st_macros(g) g->a_ctx->ctx_mac_buf[i] = g->a_buf; return _SMFIS_KEEP; } + /* ** ST_QUIT -- quit command ** @@ -964,8 +1390,17 @@ 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 ** @@ -990,6 +1425,7 @@ st_bodychunk(g) g->a_len); return SMFIS_CONTINUE; } + /* ** ST_BODYEND -- deal with the last piece of the mail body ** @@ -1037,6 +1473,7 @@ st_bodyend(g) return (*fi_eom)(g->a_ctx); return r; } + /* ** ST_ABORTFCT -- deal with aborts ** @@ -1060,6 +1497,7 @@ st_abortfct(g) (void) (*fi_abort)(g->a_ctx); return _SMFIS_NOREPLY; } + /* ** TRANS_OK -- is the state transition ok? ** @@ -1109,6 +1547,7 @@ trans_ok(old, new) } while (s < SIZE_NEXT_STATES); return false; } + /* ** FIX_STM -- add "skip" bits to the state transition table ** @@ -1145,7 +1584,12 @@ fix_stm(ctx) 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 ** @@ -1196,6 +1640,7 @@ dec_argv(buf, len) s[elem] = NULL; return s; } + /* ** DEC_ARG2 -- split a buffer into two strings ** @@ -1228,6 +1673,7 @@ dec_arg2(buf, len, s1, s2) *s2 = buf + i + 1; return MI_SUCCESS; } + /* ** SENDOK -- is it ok for the filter to send stuff to the MTA? ** @@ -1248,9 +1694,81 @@ mi_sendok(ctx, flag) return false; /* did the milter request this operation? */ - if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags)) + 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 */ |