engine.c revision 141858
1219820Sjeff/* 2219820Sjeff * Copyright (c) 1999-2003 Sendmail, Inc. and its suppliers. 3219820Sjeff * All rights reserved. 4219820Sjeff * 5219820Sjeff * By using this file, you agree to the terms and conditions set 6219820Sjeff * forth in the LICENSE file which can be found at the top level of 7219820Sjeff * the sendmail distribution. 8219820Sjeff * 9219820Sjeff */ 10219820Sjeff 11219820Sjeff#include <sm/gen.h> 12219820SjeffSM_RCSID("@(#)$Id: engine.c,v 8.120 2004/10/20 21:09:00 ca Exp $") 13219820Sjeff 14219820Sjeff#include "libmilter.h" 15219820Sjeff 16219820Sjeff#if NETINET || NETINET6 17219820Sjeff# include <arpa/inet.h> 18219820Sjeff#endif /* NETINET || NETINET6 */ 19219820Sjeff 20219820Sjeff/* generic argument for functions in the command table */ 21219820Sjeffstruct arg_struct 22219820Sjeff{ 23219820Sjeff size_t a_len; /* length of buffer */ 24219820Sjeff char *a_buf; /* argument string */ 25219820Sjeff int a_idx; /* index for macro array */ 26219820Sjeff SMFICTX_PTR a_ctx; /* context */ 27219820Sjeff}; 28219820Sjeff 29219820Sjefftypedef struct arg_struct genarg; 30219820Sjeff 31219820Sjeff/* structure for commands received from MTA */ 32219820Sjeffstruct cmdfct_t 33219820Sjeff{ 34219820Sjeff char cm_cmd; /* command */ 35219820Sjeff int cm_argt; /* type of arguments expected */ 36219820Sjeff int cm_next; /* next state */ 37219820Sjeff int cm_todo; /* what to do next */ 38219820Sjeff int cm_macros; /* index for macros */ 39219820Sjeff int (*cm_fct) __P((genarg *)); /* function to execute */ 40219820Sjeff}; 41219820Sjeff 42219820Sjefftypedef struct cmdfct_t cmdfct; 43219820Sjeff 44219820Sjeff/* possible values for cm_argt */ 45219820Sjeff#define CM_ARG0 0 /* no args */ 46219820Sjeff#define CM_ARG1 1 /* one arg (string) */ 47219820Sjeff#define CM_ARG2 2 /* two args (strings) */ 48219820Sjeff#define CM_ARGA 4 /* one string and _SOCK_ADDR */ 49219820Sjeff#define CM_ARGO 5 /* two integers */ 50219820Sjeff#define CM_ARGV 8 /* \0 separated list of args, NULL-terminated */ 51219820Sjeff#define CM_ARGN 9 /* \0 separated list of args (strings) */ 52219820Sjeff 53219820Sjeff/* possible values for cm_todo */ 54219820Sjeff#define CT_CONT 0x0000 /* continue reading commands */ 55219820Sjeff#define CT_IGNO 0x0001 /* continue even when error */ 56219820Sjeff 57219820Sjeff/* not needed right now, done via return code instead */ 58219820Sjeff#define CT_KEEP 0x0004 /* keep buffer (contains symbols) */ 59219820Sjeff#define CT_END 0x0008 /* start replying */ 60219820Sjeff 61219820Sjeff/* index in macro array: macros only for these commands */ 62219820Sjeff#define CI_NONE (-1) 63219820Sjeff#define CI_CONN 0 64219820Sjeff#define CI_HELO 1 65219820Sjeff#define CI_MAIL 2 66219820Sjeff#define CI_RCPT 3 67219820Sjeff#define CI_EOM 4 68219820Sjeff#if CI_EOM >= MAX_MACROS_ENTRIES 69219820SjeffERROR: do not compile with CI_EOM >= MAX_MACROS_ENTRIES 70219820Sjeff#endif 71219820Sjeff 72219820Sjeff/* function prototypes */ 73219820Sjeffstatic int st_abortfct __P((genarg *)); 74219820Sjeffstatic int st_macros __P((genarg *)); 75219820Sjeffstatic int st_optionneg __P((genarg *)); 76219820Sjeffstatic int st_bodychunk __P((genarg *)); 77219820Sjeffstatic int st_connectinfo __P((genarg *)); 78219820Sjeffstatic int st_bodyend __P((genarg *)); 79219820Sjeffstatic int st_helo __P((genarg *)); 80219820Sjeffstatic int st_header __P((genarg *)); 81219820Sjeffstatic int st_sender __P((genarg *)); 82219820Sjeffstatic int st_rcpt __P((genarg *)); 83219820Sjeff#if SMFI_VERSION > 2 84219820Sjeffstatic int st_unknown __P((genarg *)); 85219820Sjeff#endif /* SMFI_VERSION > 2 */ 86219820Sjeff#if SMFI_VERSION > 3 87219820Sjeffstatic int st_data __P((genarg *)); 88219820Sjeff#endif /* SMFI_VERSION > 3 */ 89219820Sjeffstatic int st_eoh __P((genarg *)); 90219820Sjeffstatic int st_quit __P((genarg *)); 91219820Sjeffstatic int sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR)); 92219820Sjeffstatic void fix_stm __P((SMFICTX_PTR)); 93219820Sjeffstatic bool trans_ok __P((int, int)); 94219820Sjeffstatic char **dec_argv __P((char *, size_t)); 95219820Sjeffstatic int dec_arg2 __P((char *, size_t, char **, char **)); 96219820Sjeff 97219820Sjeff/* states */ 98219820Sjeff#define ST_NONE (-1) 99219820Sjeff#define ST_INIT 0 /* initial state */ 100219820Sjeff#define ST_OPTS 1 /* option negotiation */ 101219820Sjeff#define ST_CONN 2 /* connection info */ 102219820Sjeff#define ST_HELO 3 /* helo */ 103219820Sjeff#define ST_MAIL 4 /* mail from */ 104219820Sjeff#define ST_RCPT 5 /* rcpt to */ 105219820Sjeff#define ST_DATA 6 /* data */ 106219820Sjeff#define ST_HDRS 7 /* headers */ 107219820Sjeff#define ST_EOHS 8 /* end of headers */ 108219820Sjeff#define ST_BODY 9 /* body */ 109219820Sjeff#define ST_ENDM 10 /* end of message */ 110219820Sjeff#define ST_QUIT 11 /* quit */ 111219820Sjeff#define ST_ABRT 12 /* abort */ 112219820Sjeff#define ST_UNKN 13 /* unknown SMTP command */ 113219820Sjeff#define ST_LAST ST_UNKN /* last valid state */ 114219820Sjeff#define ST_SKIP 15 /* not a state but required for the state table */ 115219820Sjeff 116219820Sjeff/* in a mail transaction? must be before eom according to spec. */ 117219820Sjeff#define ST_IN_MAIL(st) ((st) >= ST_MAIL && (st) < ST_ENDM) 118219820Sjeff 119219820Sjeff/* 120219820Sjeff** set of next states 121219820Sjeff** each state (ST_*) corresponds to bit in an int value (1 << state) 122219820Sjeff** each state has a set of allowed transitions ('or' of bits of states) 123219820Sjeff** so a state transition is valid if the mask of the next state 124219820Sjeff** is set in the NX_* value 125219820Sjeff** this function is coded in trans_ok(), see below. 126219820Sjeff*/ 127219820Sjeff 128219820Sjeff#define MI_MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */ 129219820Sjeff#define NX_INIT (MI_MASK(ST_OPTS)) 130219820Sjeff#define NX_OPTS (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN)) 131219820Sjeff#define NX_CONN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN)) 132219820Sjeff#define NX_HELO (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN)) 133219820Sjeff#define NX_MAIL (MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN)) 134219820Sjeff#define NX_RCPT (MI_MASK(ST_HDRS) | MI_MASK(ST_EOHS) | MI_MASK(ST_DATA) | \ 135219820Sjeff MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | \ 136219820Sjeff MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN)) 137219820Sjeff#define NX_DATA (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT)) 138219820Sjeff#define NX_HDRS (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT)) 139219820Sjeff#define NX_EOHS (MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | MI_MASK(ST_ABRT)) 140219820Sjeff#define NX_BODY (MI_MASK(ST_ENDM) | MI_MASK(ST_BODY) | MI_MASK(ST_ABRT)) 141219820Sjeff#define NX_ENDM (MI_MASK(ST_QUIT) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN)) 142219820Sjeff#define NX_QUIT 0 143219820Sjeff#define NX_ABRT 0 144219820Sjeff#define NX_UNKN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | \ 145219820Sjeff MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | \ 146219820Sjeff MI_MASK(ST_DATA) | \ 147219820Sjeff MI_MASK(ST_BODY) | MI_MASK(ST_UNKN) | \ 148219820Sjeff MI_MASK(ST_ABRT) | MI_MASK(ST_QUIT)) 149219820Sjeff#define NX_SKIP MI_MASK(ST_SKIP) 150219820Sjeff 151219820Sjeffstatic int next_states[] = 152219820Sjeff{ 153219820Sjeff NX_INIT, 154219820Sjeff NX_OPTS, 155219820Sjeff NX_CONN, 156219820Sjeff NX_HELO, 157219820Sjeff NX_MAIL, 158219820Sjeff NX_RCPT, 159219820Sjeff NX_DATA, 160219820Sjeff NX_HDRS, 161219820Sjeff NX_EOHS, 162219820Sjeff NX_BODY, 163219820Sjeff NX_ENDM, 164219820Sjeff NX_QUIT, 165219820Sjeff NX_ABRT, 166219820Sjeff NX_UNKN 167219820Sjeff}; 168219820Sjeff 169219820Sjeff/* commands received by milter */ 170219820Sjeffstatic cmdfct cmds[] = 171219820Sjeff{ 172219820Sjeff{SMFIC_ABORT, CM_ARG0, ST_ABRT, CT_CONT, CI_NONE, st_abortfct }, 173219820Sjeff{SMFIC_MACRO, CM_ARGV, ST_NONE, CT_KEEP, CI_NONE, st_macros }, 174219820Sjeff{SMFIC_BODY, CM_ARG1, ST_BODY, CT_CONT, CI_NONE, st_bodychunk }, 175219820Sjeff{SMFIC_CONNECT, CM_ARG2, ST_CONN, CT_CONT, CI_CONN, st_connectinfo }, 176219820Sjeff{SMFIC_BODYEOB, CM_ARG1, ST_ENDM, CT_CONT, CI_EOM, st_bodyend }, 177219820Sjeff{SMFIC_HELO, CM_ARG1, ST_HELO, CT_CONT, CI_HELO, st_helo }, 178219820Sjeff{SMFIC_HEADER, CM_ARG2, ST_HDRS, CT_CONT, CI_NONE, st_header }, 179219820Sjeff{SMFIC_MAIL, CM_ARGV, ST_MAIL, CT_CONT, CI_MAIL, st_sender }, 180219820Sjeff{SMFIC_OPTNEG, CM_ARGO, ST_OPTS, CT_CONT, CI_NONE, st_optionneg }, 181219820Sjeff{SMFIC_EOH, CM_ARG0, ST_EOHS, CT_CONT, CI_NONE, st_eoh }, 182219820Sjeff{SMFIC_QUIT, CM_ARG0, ST_QUIT, CT_END, CI_NONE, st_quit }, 183219820Sjeff#if SMFI_VERSION > 3 184219820Sjeff{SMFIC_DATA, CM_ARG0, ST_DATA, CT_CONT, CI_NONE, st_data }, 185219820Sjeff#endif /* SMFI_VERSION > 3 */ 186219820Sjeff{SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt } 187219820Sjeff#if SMFI_VERSION > 2 188219820Sjeff,{SMFIC_UNKNOWN,CM_ARG1, ST_UNKN, CT_IGNO, CI_NONE, st_unknown } 189219820Sjeff#endif /* SMFI_VERSION > 2 */ 190219820Sjeff}; 191219820Sjeff 192219820Sjeff/* additional (internal) reply codes */ 193219820Sjeff#define _SMFIS_KEEP 20 194219820Sjeff#define _SMFIS_ABORT 21 195219820Sjeff#define _SMFIS_OPTIONS 22 196219820Sjeff#define _SMFIS_NOREPLY 23 197219820Sjeff#define _SMFIS_FAIL (-1) 198219820Sjeff#define _SMFIS_NONE (-2) 199219820Sjeff 200219820Sjeff/* 201219820Sjeff** MI_ENGINE -- receive commands and process them 202219820Sjeff** 203219820Sjeff** Parameters: 204219820Sjeff** ctx -- context structure 205219820Sjeff** 206219820Sjeff** Returns: 207219820Sjeff** MI_FAILURE/MI_SUCCESS 208219820Sjeff*/ 209219820Sjeffint 210219820Sjeffmi_engine(ctx) 211219820Sjeff SMFICTX_PTR ctx; 212219820Sjeff{ 213219820Sjeff size_t len; 214219820Sjeff int i; 215219820Sjeff socket_t sd; 216219820Sjeff int ret = MI_SUCCESS; 217219820Sjeff int ncmds = sizeof(cmds) / sizeof(cmdfct); 218219820Sjeff int curstate = ST_INIT; 219219820Sjeff int newstate; 220219820Sjeff bool call_abort; 221219820Sjeff sfsistat r; 222219820Sjeff char cmd; 223219820Sjeff char *buf = NULL; 224219820Sjeff genarg arg; 225219820Sjeff struct timeval timeout; 226219820Sjeff int (*f) __P((genarg *)); 227219820Sjeff sfsistat (*fi_abort) __P((SMFICTX *)); 228219820Sjeff sfsistat (*fi_close) __P((SMFICTX *)); 229219820Sjeff 230219820Sjeff arg.a_ctx = ctx; 231219820Sjeff sd = ctx->ctx_sd; 232219820Sjeff fi_abort = ctx->ctx_smfi->xxfi_abort; 233219820Sjeff mi_clr_macros(ctx, 0); 234219820Sjeff fix_stm(ctx); 235219820Sjeff r = _SMFIS_NONE; 236219820Sjeff do 237219820Sjeff { 238219820Sjeff /* call abort only if in a mail transaction */ 239219820Sjeff call_abort = ST_IN_MAIL(curstate); 240219820Sjeff timeout.tv_sec = ctx->ctx_timeout; 241219820Sjeff timeout.tv_usec = 0; 242219820Sjeff if (mi_stop() == MILTER_ABRT) 243219820Sjeff { 244219820Sjeff if (ctx->ctx_dbg > 3) 245219820Sjeff sm_dprintf("[%d] milter_abort\n", 246219820Sjeff (int) ctx->ctx_id); 247219820Sjeff ret = MI_FAILURE; 248219820Sjeff break; 249219820Sjeff } 250219820Sjeff 251219820Sjeff /* 252219820Sjeff ** Notice: buf is allocated by mi_rd_cmd() and it will 253219820Sjeff ** usually be free()d after it has been used in f(). 254219820Sjeff ** However, if the function returns _SMFIS_KEEP then buf 255219820Sjeff ** contains macros and will not be free()d. 256219820Sjeff ** Hence r must be set to _SMFIS_NONE if a new buf is 257219820Sjeff ** allocated to avoid problem with housekeeping, esp. 258219820Sjeff ** if the code "break"s out of the loop. 259219820Sjeff */ 260219820Sjeff 261219820Sjeff r = _SMFIS_NONE; 262219820Sjeff if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len, 263219820Sjeff ctx->ctx_smfi->xxfi_name)) == NULL && 264219820Sjeff cmd < SMFIC_VALIDCMD) 265219820Sjeff { 266219820Sjeff if (ctx->ctx_dbg > 5) 267219820Sjeff sm_dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n", 268219820Sjeff (int) ctx->ctx_id, (int) cmd); 269219820Sjeff 270219820Sjeff /* 271219820Sjeff ** eof is currently treated as failure -> 272219820Sjeff ** abort() instead of close(), otherwise use: 273219820Sjeff ** if (cmd != SMFIC_EOF) 274219820Sjeff */ 275219820Sjeff 276219820Sjeff ret = MI_FAILURE; 277219820Sjeff break; 278219820Sjeff } 279219820Sjeff if (ctx->ctx_dbg > 4) 280219820Sjeff sm_dprintf("[%d] got cmd '%c' len %d\n", 281219820Sjeff (int) ctx->ctx_id, cmd, (int) len); 282219820Sjeff for (i = 0; i < ncmds; i++) 283219820Sjeff { 284219820Sjeff if (cmd == cmds[i].cm_cmd) 285219820Sjeff break; 286219820Sjeff } 287219820Sjeff if (i >= ncmds) 288219820Sjeff { 289219820Sjeff /* unknown command */ 290219820Sjeff if (ctx->ctx_dbg > 1) 291219820Sjeff sm_dprintf("[%d] cmd '%c' unknown\n", 292219820Sjeff (int) ctx->ctx_id, cmd); 293219820Sjeff ret = MI_FAILURE; 294219820Sjeff break; 295219820Sjeff } 296219820Sjeff if ((f = cmds[i].cm_fct) == NULL) 297219820Sjeff { 298219820Sjeff /* stop for now */ 299219820Sjeff if (ctx->ctx_dbg > 1) 300219820Sjeff sm_dprintf("[%d] cmd '%c' not impl\n", 301219820Sjeff (int) ctx->ctx_id, cmd); 302219820Sjeff ret = MI_FAILURE; 303219820Sjeff break; 304219820Sjeff } 305219820Sjeff 306219820Sjeff /* is new state ok? */ 307219820Sjeff newstate = cmds[i].cm_next; 308219820Sjeff if (ctx->ctx_dbg > 5) 309219820Sjeff sm_dprintf("[%d] cur %x new %x nextmask %x\n", 310219820Sjeff (int) ctx->ctx_id, 311219820Sjeff curstate, newstate, next_states[curstate]); 312219820Sjeff 313219820Sjeff if (newstate != ST_NONE && !trans_ok(curstate, newstate)) 314219820Sjeff { 315219820Sjeff if (ctx->ctx_dbg > 1) 316219820Sjeff sm_dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n", 317219820Sjeff (int) ctx->ctx_id, 318219820Sjeff curstate, MI_MASK(curstate), 319219820Sjeff newstate, MI_MASK(newstate), 320219820Sjeff next_states[curstate]); 321219820Sjeff 322219820Sjeff /* call abort only if in a mail transaction */ 323219820Sjeff if (fi_abort != NULL && call_abort) 324219820Sjeff (void) (*fi_abort)(ctx); 325219820Sjeff 326219820Sjeff /* 327219820Sjeff ** try to reach the new state from HELO 328219820Sjeff ** if it can't be reached, ignore the command. 329219820Sjeff */ 330219820Sjeff 331219820Sjeff curstate = ST_HELO; 332219820Sjeff if (!trans_ok(curstate, newstate)) 333219820Sjeff { 334219820Sjeff if (buf != NULL) 335219820Sjeff { 336219820Sjeff free(buf); 337219820Sjeff buf = NULL; 338219820Sjeff } 339219820Sjeff continue; 340219820Sjeff } 341219820Sjeff } 342219820Sjeff arg.a_len = len; 343219820Sjeff arg.a_buf = buf; 344219820Sjeff if (newstate != ST_NONE) 345219820Sjeff { 346219820Sjeff curstate = newstate; 347219820Sjeff ctx->ctx_state = curstate; 348219820Sjeff } 349219820Sjeff arg.a_idx = cmds[i].cm_macros; 350219820Sjeff call_abort = ST_IN_MAIL(curstate); 351219820Sjeff 352219820Sjeff /* call function to deal with command */ 353219820Sjeff r = (*f)(&arg); 354219820Sjeff if (r != _SMFIS_KEEP && buf != NULL) 355219820Sjeff { 356219820Sjeff free(buf); 357219820Sjeff buf = NULL; 358219820Sjeff } 359219820Sjeff if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS) 360219820Sjeff { 361219820Sjeff ret = MI_FAILURE; 362219820Sjeff break; 363219820Sjeff } 364219820Sjeff 365219820Sjeff if (r == SMFIS_ACCEPT) 366219820Sjeff { 367219820Sjeff /* accept mail, no further actions taken */ 368219820Sjeff curstate = ST_HELO; 369219820Sjeff } 370219820Sjeff else if (r == SMFIS_REJECT || r == SMFIS_DISCARD || 371219820Sjeff r == SMFIS_TEMPFAIL) 372219820Sjeff { 373219820Sjeff /* 374219820Sjeff ** further actions depend on current state 375219820Sjeff ** if the IGNO bit is set: "ignore" the error, 376219820Sjeff ** i.e., stay in the current state 377219820Sjeff */ 378219820Sjeff if (!bitset(CT_IGNO, cmds[i].cm_todo)) 379219820Sjeff curstate = ST_HELO; 380219820Sjeff } 381219820Sjeff else if (r == _SMFIS_ABORT) 382219820Sjeff { 383219820Sjeff if (ctx->ctx_dbg > 5) 384219820Sjeff sm_dprintf("[%d] function returned abort\n", 385219820Sjeff (int) ctx->ctx_id); 386219820Sjeff ret = MI_FAILURE; 387219820Sjeff break; 388219820Sjeff } 389219820Sjeff } while (!bitset(CT_END, cmds[i].cm_todo)); 390219820Sjeff 391219820Sjeff if (ret != MI_SUCCESS) 392219820Sjeff { 393219820Sjeff /* call abort only if in a mail transaction */ 394219820Sjeff if (fi_abort != NULL && call_abort) 395219820Sjeff (void) (*fi_abort)(ctx); 396219820Sjeff } 397219820Sjeff 398219820Sjeff /* close must always be called */ 399219820Sjeff if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL) 400219820Sjeff (void) (*fi_close)(ctx); 401219820Sjeff if (r != _SMFIS_KEEP && buf != NULL) 402219820Sjeff free(buf); 403219820Sjeff mi_clr_macros(ctx, 0); 404219820Sjeff return ret; 405219820Sjeff} 406219820Sjeff/* 407219820Sjeff** SENDREPLY -- send a reply to the MTA 408219820Sjeff** 409219820Sjeff** Parameters: 410219820Sjeff** r -- reply code 411219820Sjeff** sd -- socket descriptor 412219820Sjeff** timeout_ptr -- (ptr to) timeout to use for sending 413219820Sjeff** ctx -- context structure 414219820Sjeff** 415219820Sjeff** Returns: 416219820Sjeff** MI_SUCCESS/MI_FAILURE 417219820Sjeff*/ 418219820Sjeff 419219820Sjeffstatic int 420219820Sjeffsendreply(r, sd, timeout_ptr, ctx) 421219820Sjeff sfsistat r; 422219820Sjeff socket_t sd; 423219820Sjeff struct timeval *timeout_ptr; 424219820Sjeff SMFICTX_PTR ctx; 425219820Sjeff{ 426219820Sjeff int ret = MI_SUCCESS; 427219820Sjeff 428219820Sjeff switch (r) 429219820Sjeff { 430219820Sjeff case SMFIS_CONTINUE: 431219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0); 432219820Sjeff break; 433219820Sjeff case SMFIS_TEMPFAIL: 434219820Sjeff case SMFIS_REJECT: 435219820Sjeff if (ctx->ctx_reply != NULL && 436219820Sjeff ((r == SMFIS_TEMPFAIL && *ctx->ctx_reply == '4') || 437219820Sjeff (r == SMFIS_REJECT && *ctx->ctx_reply == '5'))) 438219820Sjeff { 439219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE, 440219820Sjeff ctx->ctx_reply, 441219820Sjeff strlen(ctx->ctx_reply) + 1); 442219820Sjeff free(ctx->ctx_reply); 443219820Sjeff ctx->ctx_reply = NULL; 444219820Sjeff } 445219820Sjeff else 446219820Sjeff { 447219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ? 448219820Sjeff SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0); 449219820Sjeff } 450219820Sjeff break; 451219820Sjeff case SMFIS_DISCARD: 452219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0); 453219820Sjeff break; 454219820Sjeff case SMFIS_ACCEPT: 455219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0); 456219820Sjeff break; 457219820Sjeff case _SMFIS_OPTIONS: 458219820Sjeff { 459219820Sjeff char buf[MILTER_OPTLEN]; 460219820Sjeff mi_int32 v; 461219820Sjeff 462219820Sjeff v = htonl(ctx->ctx_smfi->xxfi_version); 463219820Sjeff (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); 464219820Sjeff v = htonl(ctx->ctx_smfi->xxfi_flags); 465219820Sjeff (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v, 466219820Sjeff MILTER_LEN_BYTES); 467219820Sjeff v = htonl(ctx->ctx_pflags); 468219820Sjeff (void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), (void *) &v, 469219820Sjeff MILTER_LEN_BYTES); 470219820Sjeff ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, buf, 471219820Sjeff MILTER_OPTLEN); 472219820Sjeff } 473219820Sjeff break; 474219820Sjeff default: /* don't send a reply */ 475219820Sjeff break; 476219820Sjeff } 477219820Sjeff return ret; 478219820Sjeff} 479219820Sjeff 480219820Sjeff/* 481219820Sjeff** CLR_MACROS -- clear set of macros starting from a given index 482219820Sjeff** 483219820Sjeff** Parameters: 484219820Sjeff** ctx -- context structure 485219820Sjeff** m -- index from which to clear all macros 486219820Sjeff** 487219820Sjeff** Returns: 488219820Sjeff** None. 489219820Sjeff*/ 490219820Sjeffvoid 491219820Sjeffmi_clr_macros(ctx, m) 492219820Sjeff SMFICTX_PTR ctx; 493219820Sjeff int m; 494219820Sjeff{ 495219820Sjeff int i; 496219820Sjeff 497219820Sjeff for (i = m; i < MAX_MACROS_ENTRIES; i++) 498219820Sjeff { 499219820Sjeff if (ctx->ctx_mac_ptr[i] != NULL) 500219820Sjeff { 501219820Sjeff free(ctx->ctx_mac_ptr[i]); 502219820Sjeff ctx->ctx_mac_ptr[i] = NULL; 503219820Sjeff } 504219820Sjeff if (ctx->ctx_mac_buf[i] != NULL) 505219820Sjeff { 506219820Sjeff free(ctx->ctx_mac_buf[i]); 507219820Sjeff ctx->ctx_mac_buf[i] = NULL; 508219820Sjeff } 509219820Sjeff } 510219820Sjeff} 511219820Sjeff/* 512219820Sjeff** ST_OPTIONNEG -- negotiate options 513219820Sjeff** 514219820Sjeff** Parameters: 515219820Sjeff** g -- generic argument structure 516219820Sjeff** 517219820Sjeff** Returns: 518219820Sjeff** abort/send options/continue 519219820Sjeff*/ 520219820Sjeff 521219820Sjeffstatic int 522219820Sjeffst_optionneg(g) 523219820Sjeff genarg *g; 524219820Sjeff{ 525219820Sjeff mi_int32 i, v; 526219820Sjeff 527219820Sjeff if (g == NULL || g->a_ctx->ctx_smfi == NULL) 528219820Sjeff return SMFIS_CONTINUE; 529219820Sjeff mi_clr_macros(g->a_ctx, g->a_idx + 1); 530219820Sjeff 531219820Sjeff /* check for minimum length */ 532219820Sjeff if (g->a_len < MILTER_OPTLEN) 533219820Sjeff { 534219820Sjeff smi_log(SMI_LOG_ERR, 535219820Sjeff "%s: st_optionneg[%d]: len too short %d < %d", 536219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 537219820Sjeff (int) g->a_ctx->ctx_id, (int) g->a_len, 538219820Sjeff MILTER_OPTLEN); 539219820Sjeff return _SMFIS_ABORT; 540219820Sjeff } 541219820Sjeff 542219820Sjeff (void) memcpy((void *) &i, (void *) &(g->a_buf[0]), 543219820Sjeff MILTER_LEN_BYTES); 544219820Sjeff v = ntohl(i); 545219820Sjeff if (v < g->a_ctx->ctx_smfi->xxfi_version) 546219820Sjeff { 547219820Sjeff /* hard failure for now! */ 548219820Sjeff smi_log(SMI_LOG_ERR, 549219820Sjeff "%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d", 550219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 551219820Sjeff (int) g->a_ctx->ctx_id, (int) v, 552219820Sjeff g->a_ctx->ctx_smfi->xxfi_version); 553219820Sjeff return _SMFIS_ABORT; 554219820Sjeff } 555219820Sjeff 556219820Sjeff (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]), 557219820Sjeff MILTER_LEN_BYTES); 558219820Sjeff v = ntohl(i); 559219820Sjeff 560219820Sjeff /* no flags? set to default value for V1 actions */ 561219820Sjeff if (v == 0) 562219820Sjeff v = SMFI_V1_ACTS; 563219820Sjeff i = g->a_ctx->ctx_smfi->xxfi_flags; 564219820Sjeff if ((v & i) != i) 565219820Sjeff { 566219820Sjeff smi_log(SMI_LOG_ERR, 567219820Sjeff "%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x", 568219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 569219820Sjeff (int) g->a_ctx->ctx_id, v, i); 570219820Sjeff return _SMFIS_ABORT; 571219820Sjeff } 572219820Sjeff 573219820Sjeff (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]), 574219820Sjeff MILTER_LEN_BYTES); 575219820Sjeff v = ntohl(i); 576219820Sjeff 577219820Sjeff /* no flags? set to default value for V1 protocol */ 578219820Sjeff if (v == 0) 579219820Sjeff v = SMFI_V1_PROT; 580219820Sjeff i = g->a_ctx->ctx_pflags; 581219820Sjeff if ((v & i) != i) 582219820Sjeff { 583219820Sjeff smi_log(SMI_LOG_ERR, 584219820Sjeff "%s: st_optionneg[%d]: 0x%x does not fulfill protocol requirements 0x%x", 585219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 586219820Sjeff (int) g->a_ctx->ctx_id, v, i); 587219820Sjeff return _SMFIS_ABORT; 588219820Sjeff } 589219820Sjeff 590219820Sjeff return _SMFIS_OPTIONS; 591219820Sjeff} 592219820Sjeff/* 593219820Sjeff** ST_CONNECTINFO -- receive connection information 594219820Sjeff** 595219820Sjeff** Parameters: 596219820Sjeff** g -- generic argument structure 597219820Sjeff** 598219820Sjeff** Returns: 599219820Sjeff** continue or filter-specified value 600219820Sjeff*/ 601219820Sjeff 602219820Sjeffstatic int 603219820Sjeffst_connectinfo(g) 604219820Sjeff genarg *g; 605219820Sjeff{ 606219820Sjeff size_t l; 607219820Sjeff size_t i; 608219820Sjeff char *s, family; 609219820Sjeff unsigned short port = 0; 610219820Sjeff _SOCK_ADDR sockaddr; 611219820Sjeff sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *)); 612219820Sjeff 613219820Sjeff if (g == NULL) 614219820Sjeff return _SMFIS_ABORT; 615219820Sjeff mi_clr_macros(g->a_ctx, g->a_idx + 1); 616219820Sjeff if (g->a_ctx->ctx_smfi == NULL || 617219820Sjeff (fi_connect = g->a_ctx->ctx_smfi->xxfi_connect) == NULL) 618219820Sjeff return SMFIS_CONTINUE; 619219820Sjeff 620219820Sjeff s = g->a_buf; 621219820Sjeff i = 0; 622219820Sjeff l = g->a_len; 623219820Sjeff while (s[i] != '\0' && i <= l) 624219820Sjeff ++i; 625219820Sjeff if (i + 1 >= l) 626219820Sjeff return _SMFIS_ABORT; 627219820Sjeff 628219820Sjeff /* Move past trailing \0 in host string */ 629219820Sjeff i++; 630219820Sjeff family = s[i++]; 631219820Sjeff (void) memset(&sockaddr, '\0', sizeof sockaddr); 632219820Sjeff if (family != SMFIA_UNKNOWN) 633219820Sjeff { 634219820Sjeff if (i + sizeof port >= l) 635219820Sjeff { 636219820Sjeff smi_log(SMI_LOG_ERR, 637219820Sjeff "%s: connect[%d]: wrong len %d >= %d", 638219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 639219820Sjeff (int) g->a_ctx->ctx_id, (int) i, (int) l); 640219820Sjeff return _SMFIS_ABORT; 641219820Sjeff } 642219820Sjeff (void) memcpy((void *) &port, (void *) (s + i), 643219820Sjeff sizeof port); 644219820Sjeff i += sizeof port; 645219820Sjeff 646219820Sjeff /* make sure string is terminated */ 647219820Sjeff if (s[l - 1] != '\0') 648219820Sjeff return _SMFIS_ABORT; 649219820Sjeff# if NETINET 650219820Sjeff if (family == SMFIA_INET) 651219820Sjeff { 652219820Sjeff if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr) 653219820Sjeff != 1) 654219820Sjeff { 655219820Sjeff smi_log(SMI_LOG_ERR, 656219820Sjeff "%s: connect[%d]: inet_aton failed", 657219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 658219820Sjeff (int) g->a_ctx->ctx_id); 659219820Sjeff return _SMFIS_ABORT; 660219820Sjeff } 661219820Sjeff sockaddr.sa.sa_family = AF_INET; 662219820Sjeff if (port > 0) 663219820Sjeff sockaddr.sin.sin_port = port; 664219820Sjeff } 665219820Sjeff else 666219820Sjeff# endif /* NETINET */ 667219820Sjeff# if NETINET6 668219820Sjeff if (family == SMFIA_INET6) 669219820Sjeff { 670219820Sjeff if (mi_inet_pton(AF_INET6, s + i, 671219820Sjeff &sockaddr.sin6.sin6_addr) != 1) 672219820Sjeff { 673219820Sjeff smi_log(SMI_LOG_ERR, 674219820Sjeff "%s: connect[%d]: mi_inet_pton failed", 675219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 676219820Sjeff (int) g->a_ctx->ctx_id); 677219820Sjeff return _SMFIS_ABORT; 678219820Sjeff } 679219820Sjeff sockaddr.sa.sa_family = AF_INET6; 680219820Sjeff if (port > 0) 681219820Sjeff sockaddr.sin6.sin6_port = port; 682219820Sjeff } 683219820Sjeff else 684219820Sjeff# endif /* NETINET6 */ 685219820Sjeff# if NETUNIX 686219820Sjeff if (family == SMFIA_UNIX) 687219820Sjeff { 688219820Sjeff if (sm_strlcpy(sockaddr.sunix.sun_path, s + i, 689219820Sjeff sizeof sockaddr.sunix.sun_path) >= 690219820Sjeff sizeof sockaddr.sunix.sun_path) 691219820Sjeff { 692219820Sjeff smi_log(SMI_LOG_ERR, 693219820Sjeff "%s: connect[%d]: path too long", 694219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 695219820Sjeff (int) g->a_ctx->ctx_id); 696219820Sjeff return _SMFIS_ABORT; 697219820Sjeff } 698219820Sjeff sockaddr.sunix.sun_family = AF_UNIX; 699219820Sjeff } 700219820Sjeff else 701219820Sjeff# endif /* NETUNIX */ 702219820Sjeff { 703219820Sjeff smi_log(SMI_LOG_ERR, 704219820Sjeff "%s: connect[%d]: unknown family %d", 705219820Sjeff g->a_ctx->ctx_smfi->xxfi_name, 706219820Sjeff (int) g->a_ctx->ctx_id, family); 707219820Sjeff return _SMFIS_ABORT; 708219820Sjeff } 709219820Sjeff } 710219820Sjeff return (*fi_connect)(g->a_ctx, g->a_buf, 711219820Sjeff family != SMFIA_UNKNOWN ? &sockaddr : NULL); 712219820Sjeff} 713219820Sjeff 714219820Sjeff/* 715219820Sjeff** ST_EOH -- end of headers 716219820Sjeff** 717219820Sjeff** Parameters: 718219820Sjeff** g -- generic argument structure 719219820Sjeff** 720219820Sjeff** Returns: 721219820Sjeff** continue or filter-specified value 722219820Sjeff*/ 723219820Sjeff 724219820Sjeffstatic int 725219820Sjeffst_eoh(g) 726219820Sjeff genarg *g; 727219820Sjeff{ 728219820Sjeff sfsistat (*fi_eoh) __P((SMFICTX *)); 729219820Sjeff 730219820Sjeff if (g == NULL) 731219820Sjeff return _SMFIS_ABORT; 732219820Sjeff if (g->a_ctx->ctx_smfi != NULL && 733219820Sjeff (fi_eoh = g->a_ctx->ctx_smfi->xxfi_eoh) != NULL) 734219820Sjeff return (*fi_eoh)(g->a_ctx); 735219820Sjeff return SMFIS_CONTINUE; 736219820Sjeff} 737219820Sjeff 738219820Sjeff#if SMFI_VERSION > 3 739219820Sjeff/* 740219820Sjeff** ST_DATA -- DATA command 741219820Sjeff** 742219820Sjeff** Parameters: 743219820Sjeff** g -- generic argument structure 744219820Sjeff** 745219820Sjeff** Returns: 746219820Sjeff** continue or filter-specified value 747219820Sjeff*/ 748219820Sjeff 749219820Sjeffstatic int 750219820Sjeffst_data(g) 751219820Sjeff genarg *g; 752219820Sjeff{ 753219820Sjeff sfsistat (*fi_data) __P((SMFICTX *)); 754219820Sjeff 755219820Sjeff if (g == NULL) 756219820Sjeff return _SMFIS_ABORT; 757219820Sjeff if (g->a_ctx->ctx_smfi != NULL && 758219820Sjeff (fi_data = g->a_ctx->ctx_smfi->xxfi_data) != NULL) 759219820Sjeff return (*fi_data)(g->a_ctx); 760219820Sjeff return SMFIS_CONTINUE; 761219820Sjeff} 762219820Sjeff#endif /* SMFI_VERSION > 3 */ 763219820Sjeff 764219820Sjeff/* 765219820Sjeff** ST_HELO -- helo/ehlo command 766219820Sjeff** 767219820Sjeff** Parameters: 768219820Sjeff** g -- generic argument structure 769219820Sjeff** 770219820Sjeff** Returns: 771219820Sjeff** continue or filter-specified value 772219820Sjeff*/ 773219820Sjeffstatic int 774219820Sjeffst_helo(g) 775219820Sjeff genarg *g; 776219820Sjeff{ 777219820Sjeff sfsistat (*fi_helo) __P((SMFICTX *, char *)); 778219820Sjeff 779219820Sjeff if (g == NULL) 780219820Sjeff return _SMFIS_ABORT; 781219820Sjeff mi_clr_macros(g->a_ctx, g->a_idx + 1); 782219820Sjeff if (g->a_ctx->ctx_smfi != NULL && 783219820Sjeff (fi_helo = g->a_ctx->ctx_smfi->xxfi_helo) != NULL) 784219820Sjeff { 785219820Sjeff /* paranoia: check for terminating '\0' */ 786219820Sjeff if (g->a_len == 0 || g->a_buf[g->a_len - 1] != '\0') 787219820Sjeff return MI_FAILURE; 788219820Sjeff return (*fi_helo)(g->a_ctx, g->a_buf); 789219820Sjeff } 790219820Sjeff return SMFIS_CONTINUE; 791219820Sjeff} 792219820Sjeff/* 793219820Sjeff** ST_HEADER -- header line 794219820Sjeff** 795219820Sjeff** Parameters: 796219820Sjeff** g -- generic argument structure 797219820Sjeff** 798219820Sjeff** Returns: 799219820Sjeff** continue or filter-specified value 800219820Sjeff*/ 801219820Sjeff 802219820Sjeffstatic int 803219820Sjeffst_header(g) 804219820Sjeff genarg *g; 805219820Sjeff{ 806219820Sjeff char *hf, *hv; 807219820Sjeff sfsistat (*fi_header) __P((SMFICTX *, char *, char *)); 808219820Sjeff 809219820Sjeff if (g == NULL) 810219820Sjeff return _SMFIS_ABORT; 811219820Sjeff if (g->a_ctx->ctx_smfi == NULL || 812219820Sjeff (fi_header = g->a_ctx->ctx_smfi->xxfi_header) == NULL) 813219820Sjeff return SMFIS_CONTINUE; 814219820Sjeff if (dec_arg2(g->a_buf, g->a_len, &hf, &hv) == MI_SUCCESS) 815219820Sjeff return (*fi_header)(g->a_ctx, hf, hv); 816219820Sjeff else 817219820Sjeff return _SMFIS_ABORT; 818219820Sjeff} 819219820Sjeff 820219820Sjeff#define ARGV_FCT(lf, rf, idx) \ 821219820Sjeff char **argv; \ 822219820Sjeff sfsistat (*lf) __P((SMFICTX *, char **)); \ 823219820Sjeff int r; \ 824219820Sjeff \ 825219820Sjeff if (g == NULL) \ 826219820Sjeff return _SMFIS_ABORT; \ 827219820Sjeff mi_clr_macros(g->a_ctx, g->a_idx + 1); \ 828219820Sjeff if (g->a_ctx->ctx_smfi == NULL || \ 829219820Sjeff (lf = g->a_ctx->ctx_smfi->rf) == NULL) \ 830219820Sjeff return SMFIS_CONTINUE; \ 831219820Sjeff if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL) \ 832219820Sjeff return _SMFIS_ABORT; \ 833219820Sjeff r = (*lf)(g->a_ctx, argv); \ 834219820Sjeff free(argv); \ 835219820Sjeff return r; 836219820Sjeff 837219820Sjeff/* 838219820Sjeff** ST_SENDER -- MAIL FROM command 839219820Sjeff** 840219820Sjeff** Parameters: 841219820Sjeff** g -- generic argument structure 842219820Sjeff** 843219820Sjeff** Returns: 844219820Sjeff** continue or filter-specified value 845219820Sjeff*/ 846219820Sjeff 847219820Sjeffstatic int 848219820Sjeffst_sender(g) 849219820Sjeff genarg *g; 850219820Sjeff{ 851219820Sjeff ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL) 852219820Sjeff} 853219820Sjeff/* 854219820Sjeff** ST_RCPT -- RCPT TO command 855219820Sjeff** 856219820Sjeff** Parameters: 857219820Sjeff** g -- generic argument structure 858219820Sjeff** 859219820Sjeff** Returns: 860219820Sjeff** continue or filter-specified value 861219820Sjeff*/ 862219820Sjeff 863219820Sjeffstatic int 864219820Sjeffst_rcpt(g) 865219820Sjeff genarg *g; 866219820Sjeff{ 867219820Sjeff ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT) 868219820Sjeff} 869219820Sjeff 870219820Sjeff#if SMFI_VERSION > 2 871219820Sjeff/* 872219820Sjeff** ST_UNKNOWN -- unrecognized or unimplemented command 873219820Sjeff** 874219820Sjeff** Parameters: 875219820Sjeff** g -- generic argument structure 876219820Sjeff** 877219820Sjeff** Returns: 878219820Sjeff** continue or filter-specified value 879219820Sjeff*/ 880219820Sjeff 881219820Sjeffstatic int 882219820Sjeffst_unknown(g) 883219820Sjeff genarg *g; 884219820Sjeff{ 885219820Sjeff sfsistat (*fi_unknown) __P((SMFICTX *, char *)); 886219820Sjeff 887219820Sjeff if (g == NULL) 888219820Sjeff return _SMFIS_ABORT; 889219820Sjeff mi_clr_macros(g->a_ctx, g->a_idx + 1); 890219820Sjeff if (g->a_ctx->ctx_smfi != NULL && 891219820Sjeff (fi_unknown = g->a_ctx->ctx_smfi->xxfi_unknown) != NULL) 892219820Sjeff return (*fi_unknown)(g->a_ctx, g->a_buf); 893219820Sjeff return SMFIS_CONTINUE; 894219820Sjeff} 895219820Sjeff#endif /* SMFI_VERSION > 2 */ 896219820Sjeff 897219820Sjeff/* 898219820Sjeff** ST_MACROS -- deal with macros received from the MTA 899219820Sjeff** 900219820Sjeff** Parameters: 901219820Sjeff** g -- generic argument structure 902219820Sjeff** 903219820Sjeff** Returns: 904219820Sjeff** continue/keep 905219820Sjeff** 906219820Sjeff** Side effects: 907219820Sjeff** set pointer in macro array to current values. 908219820Sjeff*/ 909219820Sjeff 910219820Sjeffstatic int 911219820Sjeffst_macros(g) 912219820Sjeff genarg *g; 913219820Sjeff{ 914219820Sjeff int i; 915219820Sjeff char **argv; 916219820Sjeff 917219820Sjeff if (g == NULL || g->a_len < 1) 918219820Sjeff return _SMFIS_FAIL; 919219820Sjeff if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL) 920219820Sjeff return _SMFIS_FAIL; 921219820Sjeff switch (g->a_buf[0]) 922219820Sjeff { 923219820Sjeff case SMFIC_CONNECT: 924219820Sjeff i = CI_CONN; 925219820Sjeff break; 926219820Sjeff case SMFIC_HELO: 927219820Sjeff i = CI_HELO; 928219820Sjeff break; 929219820Sjeff case SMFIC_MAIL: 930219820Sjeff i = CI_MAIL; 931219820Sjeff break; 932219820Sjeff case SMFIC_RCPT: 933219820Sjeff i = CI_RCPT; 934219820Sjeff break; 935219820Sjeff case SMFIC_BODYEOB: 936219820Sjeff i = CI_EOM; 937219820Sjeff break; 938219820Sjeff default: 939219820Sjeff free(argv); 940219820Sjeff return _SMFIS_FAIL; 941219820Sjeff } 942219820Sjeff if (g->a_ctx->ctx_mac_ptr[i] != NULL) 943219820Sjeff free(g->a_ctx->ctx_mac_ptr[i]); 944219820Sjeff if (g->a_ctx->ctx_mac_buf[i] != NULL) 945219820Sjeff free(g->a_ctx->ctx_mac_buf[i]); 946219820Sjeff g->a_ctx->ctx_mac_ptr[i] = argv; 947219820Sjeff g->a_ctx->ctx_mac_buf[i] = g->a_buf; 948219820Sjeff return _SMFIS_KEEP; 949219820Sjeff} 950219820Sjeff/* 951219820Sjeff** ST_QUIT -- quit command 952219820Sjeff** 953219820Sjeff** Parameters: 954219820Sjeff** g -- generic argument structure 955219820Sjeff** 956219820Sjeff** Returns: 957219820Sjeff** noreply 958219820Sjeff*/ 959219820Sjeff 960219820Sjeff/* ARGSUSED */ 961219820Sjeffstatic int 962219820Sjeffst_quit(g) 963219820Sjeff genarg *g; 964{ 965 return _SMFIS_NOREPLY; 966} 967/* 968** ST_BODYCHUNK -- deal with a piece of the mail body 969** 970** Parameters: 971** g -- generic argument structure 972** 973** Returns: 974** continue or filter-specified value 975*/ 976 977static int 978st_bodychunk(g) 979 genarg *g; 980{ 981 sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); 982 983 if (g == NULL) 984 return _SMFIS_ABORT; 985 if (g->a_ctx->ctx_smfi != NULL && 986 (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL) 987 return (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, 988 g->a_len); 989 return SMFIS_CONTINUE; 990} 991/* 992** ST_BODYEND -- deal with the last piece of the mail body 993** 994** Parameters: 995** g -- generic argument structure 996** 997** Returns: 998** continue or filter-specified value 999** 1000** Side effects: 1001** sends a reply for the body part (if non-empty). 1002*/ 1003 1004static int 1005st_bodyend(g) 1006 genarg *g; 1007{ 1008 sfsistat r; 1009 sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t)); 1010 sfsistat (*fi_eom) __P((SMFICTX *)); 1011 1012 if (g == NULL) 1013 return _SMFIS_ABORT; 1014 r = SMFIS_CONTINUE; 1015 if (g->a_ctx->ctx_smfi != NULL) 1016 { 1017 if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL && 1018 g->a_len > 0) 1019 { 1020 socket_t sd; 1021 struct timeval timeout; 1022 1023 timeout.tv_sec = g->a_ctx->ctx_timeout; 1024 timeout.tv_usec = 0; 1025 sd = g->a_ctx->ctx_sd; 1026 r = (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf, 1027 g->a_len); 1028 if (r != SMFIS_CONTINUE && 1029 sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS) 1030 return _SMFIS_ABORT; 1031 } 1032 } 1033 if (r == SMFIS_CONTINUE && 1034 (fi_eom = g->a_ctx->ctx_smfi->xxfi_eom) != NULL) 1035 return (*fi_eom)(g->a_ctx); 1036 return r; 1037} 1038/* 1039** ST_ABORTFCT -- deal with aborts 1040** 1041** Parameters: 1042** g -- generic argument structure 1043** 1044** Returns: 1045** abort or filter-specified value 1046*/ 1047 1048static int 1049st_abortfct(g) 1050 genarg *g; 1051{ 1052 sfsistat (*fi_abort) __P((SMFICTX *)); 1053 1054 if (g == NULL) 1055 return _SMFIS_ABORT; 1056 if (g != NULL && g->a_ctx->ctx_smfi != NULL && 1057 (fi_abort = g->a_ctx->ctx_smfi->xxfi_abort) != NULL) 1058 (void) (*fi_abort)(g->a_ctx); 1059 return _SMFIS_NOREPLY; 1060} 1061/* 1062** TRANS_OK -- is the state transition ok? 1063** 1064** Parameters: 1065** old -- old state 1066** new -- new state 1067** 1068** Returns: 1069** state transition ok 1070*/ 1071 1072static bool 1073trans_ok(old, new) 1074 int old, new; 1075{ 1076 int s, n; 1077 1078 s = old; 1079 do 1080 { 1081 /* is this state transition allowed? */ 1082 if ((MI_MASK(new) & next_states[s]) != 0) 1083 return true; 1084 1085 /* 1086 ** no: try next state; 1087 ** this works since the relevant states are ordered 1088 ** strict sequentially 1089 */ 1090 1091 n = s + 1; 1092 1093 /* 1094 ** can we actually "skip" this state? 1095 ** see fix_stm() which sets this bit for those 1096 ** states which the filter program is not interested in 1097 */ 1098 1099 if (bitset(NX_SKIP, next_states[n])) 1100 s = n; 1101 else 1102 return false; 1103 } while (s <= ST_LAST); 1104 return false; 1105} 1106/* 1107** FIX_STM -- add "skip" bits to the state transition table 1108** 1109** Parameters: 1110** ctx -- context structure 1111** 1112** Returns: 1113** None. 1114** 1115** Side effects: 1116** may change state transition table. 1117*/ 1118 1119static void 1120fix_stm(ctx) 1121 SMFICTX_PTR ctx; 1122{ 1123 unsigned long fl; 1124 1125 if (ctx == NULL || ctx->ctx_smfi == NULL) 1126 return; 1127 fl = ctx->ctx_pflags; 1128 if (bitset(SMFIP_NOCONNECT, fl)) 1129 next_states[ST_CONN] |= NX_SKIP; 1130 if (bitset(SMFIP_NOHELO, fl)) 1131 next_states[ST_HELO] |= NX_SKIP; 1132 if (bitset(SMFIP_NOMAIL, fl)) 1133 next_states[ST_MAIL] |= NX_SKIP; 1134 if (bitset(SMFIP_NORCPT, fl)) 1135 next_states[ST_RCPT] |= NX_SKIP; 1136 if (bitset(SMFIP_NOHDRS, fl)) 1137 next_states[ST_HDRS] |= NX_SKIP; 1138 if (bitset(SMFIP_NOEOH, fl)) 1139 next_states[ST_EOHS] |= NX_SKIP; 1140 if (bitset(SMFIP_NOBODY, fl)) 1141 next_states[ST_BODY] |= NX_SKIP; 1142} 1143/* 1144** DEC_ARGV -- split a buffer into a list of strings, NULL terminated 1145** 1146** Parameters: 1147** buf -- buffer with several strings 1148** len -- length of buffer 1149** 1150** Returns: 1151** array of pointers to the individual strings 1152*/ 1153 1154static char ** 1155dec_argv(buf, len) 1156 char *buf; 1157 size_t len; 1158{ 1159 char **s; 1160 size_t i; 1161 int elem, nelem; 1162 1163 nelem = 0; 1164 for (i = 0; i < len; i++) 1165 { 1166 if (buf[i] == '\0') 1167 ++nelem; 1168 } 1169 if (nelem == 0) 1170 return NULL; 1171 1172 /* last entry is only for the name */ 1173 s = (char **)malloc((nelem + 1) * (sizeof *s)); 1174 if (s == NULL) 1175 return NULL; 1176 s[0] = buf; 1177 for (i = 0, elem = 0; i < len && elem < nelem; i++) 1178 { 1179 if (buf[i] == '\0') 1180 { 1181 ++elem; 1182 if (i + 1 >= len) 1183 s[elem] = NULL; 1184 else 1185 s[elem] = &(buf[i + 1]); 1186 } 1187 } 1188 1189 /* overwrite last entry (already done above, just paranoia) */ 1190 s[elem] = NULL; 1191 return s; 1192} 1193/* 1194** DEC_ARG2 -- split a buffer into two strings 1195** 1196** Parameters: 1197** buf -- buffer with two strings 1198** len -- length of buffer 1199** s1,s2 -- pointer to result strings 1200** 1201** Returns: 1202** MI_FAILURE/MI_SUCCESS 1203*/ 1204 1205static int 1206dec_arg2(buf, len, s1, s2) 1207 char *buf; 1208 size_t len; 1209 char **s1; 1210 char **s2; 1211{ 1212 size_t i; 1213 1214 /* paranoia: check for terminating '\0' */ 1215 if (len == 0 || buf[len - 1] != '\0') 1216 return MI_FAILURE; 1217 *s1 = buf; 1218 for (i = 1; i < len && buf[i] != '\0'; i++) 1219 continue; 1220 if (i >= len - 1) 1221 return MI_FAILURE; 1222 *s2 = buf + i + 1; 1223 return MI_SUCCESS; 1224} 1225/* 1226** SENDOK -- is it ok for the filter to send stuff to the MTA? 1227** 1228** Parameters: 1229** ctx -- context structure 1230** flag -- flag to check 1231** 1232** Returns: 1233** sending allowed (in current state) 1234*/ 1235 1236bool 1237mi_sendok(ctx, flag) 1238 SMFICTX_PTR ctx; 1239 int flag; 1240{ 1241 if (ctx == NULL || ctx->ctx_smfi == NULL) 1242 return false; 1243 1244 /* did the milter request this operation? */ 1245 if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags)) 1246 return false; 1247 1248 /* are we in the correct state? It must be "End of Message". */ 1249 return ctx->ctx_state == ST_ENDM; 1250} 1251