engine.c revision 66494
1/*
2 *  Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#ifndef lint
12static char id[] = "@(#)$Id: engine.c,v 8.67.4.14 2000/08/14 08:27:30 gshapiro Exp $";
13#endif /* ! lint */
14
15#if _FFR_MILTER
16#include "libmilter.h"
17#include "sendmail/useful.h"
18
19#if NETINET || NETINET6
20# include <arpa/inet.h>
21#endif /* NETINET || NETINET6 */
22
23/* generic argument for functions in the command table */
24struct arg_struct
25{
26	size_t		a_len;		/* length of buffer */
27	char		*a_buf;		/* argument string */
28	int		a_idx;		/* index for macro array */
29	SMFICTX_PTR	a_ctx;		/* context */
30};
31
32typedef struct arg_struct genarg;
33
34/* structure for commands received from MTA */
35struct cmdfct_t
36{
37	char	cm_cmd;				/* command */
38	int	cm_argt;			/* type of arguments expected */
39	int	cm_next;			/* next state */
40	int	cm_todo;			/* what to do next */
41	int	cm_macros;			/* index for macros */
42	int	(*cm_fct) __P((genarg *));	/* function to execute */
43};
44
45typedef struct cmdfct_t cmdfct;
46
47/* possible values for cm_argt */
48#define	CM_ARG0	0	/* no args */
49#define	CM_ARG1	1	/* one arg (string) */
50#define	CM_ARG2	2	/* two args (strings) */
51#define	CM_ARGA	4	/* one string and _SOCK_ADDR */
52#define	CM_ARGO	5	/* two integers */
53#define	CM_ARGV	8	/* \0 separated list of args, NULL-terminated */
54#define	CM_ARGN	9	/* \0 separated list of args (strings) */
55
56/* possible values for cm_todo */
57#define	CT_CONT		0x0000	/* continue reading commands */
58#define	CT_IGNO		0x0001	/* continue even when error  */
59
60/* not needed right now, done via return code instead */
61#define	CT_KEEP		0x0004	/* keep buffer (contains symbols) */
62#define	CT_END		0x0008	/* start replying */
63
64/* index in macro array: macros only for these commands */
65#define	CI_NONE	(-1)
66#define	CI_CONN	0
67#define	CI_HELO	1
68#define	CI_MAIL	2
69#define	CI_RCPT	3
70#if CI_RCPT >= MAX_MACROS_ENTRIES
71ERROR: do not compile with CI_RCPT >= MAX_MACROS_ENTRIES
72#endif
73
74/* function prototypes */
75static int	st_abortfct __P((genarg *));
76static int	st_macros __P((genarg *));
77static int	st_optionneg __P((genarg *));
78static int	st_bodychunk __P((genarg *));
79static int	st_connectinfo __P((genarg *));
80static int	st_bodyend __P((genarg *));
81static int	st_helo __P((genarg *));
82static int	st_header __P((genarg *));
83static int	st_sender __P((genarg *));
84static int	st_rcpt __P((genarg *));
85static int	st_eoh __P((genarg *));
86static int	st_quit __P((genarg *));
87static int	sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR));
88static void	fix_stm __P((SMFICTX_PTR));
89static bool	trans_ok __P((int, int));
90static char	**dec_argv __P((char *, size_t));
91static int	dec_arg2 __P((char *, size_t, char **, char **));
92
93/* states */
94#define ST_NONE	(-1)
95#define ST_INIT	0	/* initial state */
96#define ST_OPTS	1	/* option negotiation */
97#define ST_CONN	2	/* connection info */
98#define ST_HELO	3	/* helo */
99#define ST_MAIL	4	/* mail from */
100#define ST_RCPT	5	/* rcpt to */
101#define ST_HDRS	6	/* headers */
102#define ST_EOHS	7	/* end of headers */
103#define ST_BODY	8	/* body */
104#define ST_ENDM	9	/* end of message */
105#define ST_QUIT	10	/* quit */
106#define ST_ABRT	11	/* abort */
107#define ST_LAST	ST_ABRT
108#define ST_SKIP	15	/* not a state but required for the state table */
109
110/* in a mail transaction? must be before eom according to spec. */
111#define ST_IN_MAIL(st)	((st) >= ST_MAIL && (st) < ST_ENDM)
112
113/*
114**  set of next states
115**  each state (ST_*) corresponds to bit in an int value (1 << state)
116**  each state has a set of allowed transitions ('or' of bits of states)
117**  so a state transition is valid if the mask of the next state
118**  is set in the NX_* value
119**  this function is coded in trans_ok(), see below.
120*/
121#define MASK(x)	(0x0001 << (x))	/* generate a bit "mask" for a state */
122#define NX_INIT	(MASK(ST_OPTS))
123#define NX_OPTS	(MASK(ST_CONN))
124#define NX_CONN	(MASK(ST_HELO) | MASK(ST_MAIL))
125#define NX_HELO	(MASK(ST_MAIL))
126#define NX_MAIL	(MASK(ST_RCPT) | MASK(ST_ABRT))
127#define NX_RCPT	(MASK(ST_HDRS) | MASK(ST_EOHS) | MASK(ST_RCPT) | MASK(ST_ABRT))
128#define NX_HDRS	(MASK(ST_EOHS) | MASK(ST_HDRS) | MASK(ST_ABRT))
129#define NX_EOHS	(MASK(ST_BODY) | MASK(ST_ENDM) | MASK(ST_ABRT))
130#define NX_BODY	(MASK(ST_ENDM) | MASK(ST_BODY) | MASK(ST_ABRT))
131#define NX_ENDM	(MASK(ST_QUIT) | MASK(ST_MAIL))
132#define NX_QUIT	0
133#define NX_ABRT	0
134#define NX_SKIP MASK(ST_SKIP)
135
136static int next_states[] =
137{
138	NX_INIT,
139	NX_OPTS,
140	NX_CONN,
141	NX_HELO,
142	NX_MAIL,
143	NX_RCPT,
144	NX_HDRS,
145	NX_EOHS,
146	NX_BODY,
147	NX_ENDM,
148	NX_QUIT,
149	NX_ABRT
150};
151
152/* commands received by milter */
153static cmdfct cmds[] =
154{
155{SMFIC_ABORT,	CM_ARG0, ST_ABRT,  CT_CONT,	CI_NONE, st_abortfct	},
156{SMFIC_MACRO,	CM_ARGV, ST_NONE,  CT_KEEP,	CI_NONE, st_macros	},
157{SMFIC_BODY,	CM_ARG1, ST_BODY,  CT_CONT,	CI_NONE, st_bodychunk	},
158{SMFIC_CONNECT,	CM_ARG2, ST_CONN,  CT_CONT,	CI_CONN, st_connectinfo	},
159{SMFIC_BODYEOB,	CM_ARG1, ST_ENDM,  CT_CONT,	CI_NONE, st_bodyend	},
160{SMFIC_HELO,	CM_ARG1, ST_HELO,  CT_CONT,	CI_HELO, st_helo	},
161{SMFIC_HEADER,	CM_ARG2, ST_HDRS,  CT_CONT,	CI_NONE, st_header	},
162{SMFIC_MAIL,	CM_ARGV, ST_MAIL,  CT_CONT,	CI_MAIL, st_sender	},
163{SMFIC_OPTNEG,	CM_ARGO, ST_OPTS,  CT_CONT,	CI_NONE, st_optionneg	},
164{SMFIC_EOH,	CM_ARG0, ST_EOHS,  CT_CONT,	CI_NONE, st_eoh		},
165{SMFIC_QUIT,	CM_ARG0, ST_QUIT,  CT_END,	CI_NONE, st_quit	},
166{SMFIC_RCPT,	CM_ARGV, ST_RCPT,  CT_IGNO,	CI_RCPT, st_rcpt	}
167};
168
169/* additional (internal) reply codes */
170#define _SMFIS_KEEP	20
171#define _SMFIS_ABORT	21
172#define _SMFIS_OPTIONS	22
173#define _SMFIS_NOREPLY	23
174#define _SMFIS_FAIL	(-1)
175
176/*
177**  MI_ENGINE -- receive commands and process them
178**
179**	Parameters:
180**		ctx -- context structure
181**
182**	Returns:
183**		MI_FAILURE/MI_SUCCESS
184*/
185int
186mi_engine(ctx)
187	SMFICTX_PTR ctx;
188{
189	size_t len;
190	int i;
191	socket_t sd;
192	int ret = MI_SUCCESS;
193	int ncmds = sizeof(cmds) / sizeof(cmdfct);
194	int curstate = ST_INIT;
195	int newstate;
196	bool call_abort;
197	sfsistat r;
198	char cmd;
199	char *buf = NULL;
200	genarg arg;
201	struct timeval timeout;
202	int (*f) __P((genarg *));
203	sfsistat (*fi_abort) __P((SMFICTX *));
204	sfsistat (*fi_close) __P((SMFICTX *));
205
206	arg.a_ctx = ctx;
207	sd = ctx->ctx_sd;
208	fi_abort = ctx->ctx_smfi->xxfi_abort;
209	mi_clr_macros(ctx, 0);
210	fix_stm(ctx);
211	do
212	{
213		/* call abort only if in a mail transaction */
214		call_abort = ST_IN_MAIL(curstate);
215		timeout.tv_sec = ctx->ctx_timeout;
216		timeout.tv_usec = 0;
217		if (mi_stop() == MILTER_ABRT)
218		{
219			if (ctx->ctx_dbg > 3)
220				dprintf("[%d] milter_abort\n",
221					(int) ctx->ctx_id);
222			ret = MI_FAILURE;
223			break;
224		}
225		if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len,
226				     ctx->ctx_smfi->xxfi_name)) == NULL &&
227		    cmd < SMFIC_VALIDCMD)
228		{
229			if (ctx->ctx_dbg > 5)
230				dprintf("[%d] mi_engine: mi_rd_cmd error (%x)\n",
231					(int) ctx->ctx_id, (int) cmd);
232
233			/*
234			**  eof is currently treated as failure ->
235			**  abort() instead of close(), otherwise use:
236			**  if (cmd != SMFIC_EOF)
237			*/
238
239			ret = MI_FAILURE;
240			break;
241		}
242		if (ctx->ctx_dbg > 4)
243			dprintf("[%d] got cmd '%c' len %d\n",
244				(int) ctx->ctx_id, cmd, len);
245		for (i = 0; i < ncmds; i++)
246		{
247			if (cmd == cmds[i].cm_cmd)
248				break;
249		}
250		if (i >= ncmds)
251		{
252			/* unknown command */
253			if (ctx->ctx_dbg > 1)
254				dprintf("[%d] cmd '%c' unknown\n",
255					(int) ctx->ctx_id, cmd);
256			ret = MI_FAILURE;
257			break;
258		}
259		if ((f = cmds[i].cm_fct) == NULL)
260		{
261			/* stop for now */
262			if (ctx->ctx_dbg > 1)
263				dprintf("[%d] cmd '%c' not impl\n",
264					(int) ctx->ctx_id, cmd);
265			ret = MI_FAILURE;
266			break;
267		}
268
269		/* is new state ok? */
270		newstate = cmds[i].cm_next;
271		if (ctx->ctx_dbg > 5)
272			dprintf("[%d] cur %x new %x nextmask %x\n",
273				(int) ctx->ctx_id,
274				curstate, newstate, next_states[curstate]);
275
276		if (newstate != ST_NONE && !trans_ok(curstate, newstate))
277		{
278			if (ctx->ctx_dbg > 1)
279				dprintf("[%d] abort: cur %d (%x) new %d (%x) next %x\n",
280					(int) ctx->ctx_id,
281					curstate, MASK(curstate),
282					newstate, MASK(newstate),
283					next_states[curstate]);
284
285			/* call abort only if in a mail transaction */
286			if (fi_abort != NULL && call_abort)
287				(void) (*fi_abort)(ctx);
288
289			/*
290			**  try to reach the new state from HELO
291			**  if it can't be reached, ignore the command.
292			*/
293
294			curstate = ST_HELO;
295			if (!trans_ok(curstate, newstate))
296				continue;
297		}
298		arg.a_len = len;
299		arg.a_buf = buf;
300		if (newstate != ST_NONE)
301		{
302			curstate = newstate;
303			ctx->ctx_state = curstate;
304		}
305		arg.a_idx = cmds[i].cm_macros;
306
307		/* call function to deal with command */
308		r = (*f)(&arg);
309		if (r != _SMFIS_KEEP && buf != NULL)
310		{
311			free(buf);
312			buf = NULL;
313		}
314		if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS)
315		{
316			ret = MI_FAILURE;
317			break;
318		}
319
320		call_abort = ST_IN_MAIL(curstate);
321		if (r == SMFIS_ACCEPT)
322		{
323			/* accept mail, no further actions taken */
324			curstate = ST_HELO;
325		}
326		else if (r == SMFIS_REJECT || r == SMFIS_DISCARD ||
327			 r ==  SMFIS_TEMPFAIL)
328		{
329			/*
330			**  further actions depend on current state
331			**  if the IGNO bit is set: "ignore" the error,
332			**  i.e., stay in the current state
333			*/
334			if (!bitset(CT_IGNO, cmds[i].cm_todo))
335				curstate = ST_HELO;
336		}
337		else if (r == _SMFIS_ABORT)
338		{
339			if (ctx->ctx_dbg > 5)
340				dprintf("[%d] function returned abort\n",
341					(int) ctx->ctx_id);
342			ret = MI_FAILURE;
343			break;
344		}
345	} while (!bitset(CT_END, cmds[i].cm_todo));
346
347	if (ret != MI_SUCCESS)
348	{
349		/* call abort only if in a mail transaction */
350		if (fi_abort != NULL && call_abort)
351			(void) (*fi_abort)(ctx);
352	}
353
354	/* close must always be called */
355	if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
356		(void) (*fi_close)(ctx);
357	if (buf != NULL)
358		free(buf);
359	mi_clr_macros(ctx, 0);
360	return ret;
361}
362/*
363**  SENDREPLY -- send a reply to the MTA
364**
365**	Parameters:
366**		r -- reply code
367**		sd -- socket descriptor
368**		timeout_ptr -- (ptr to) timeout to use for sending
369**		ctx -- context structure
370**
371**	Returns:
372**		MI_SUCCESS/MI_FAILURE
373*/
374
375static int
376sendreply(r, sd, timeout_ptr, ctx)
377	sfsistat r;
378	socket_t sd;
379	struct timeval *timeout_ptr;
380	SMFICTX_PTR ctx;
381{
382	int ret = MI_SUCCESS;
383
384	switch(r)
385	{
386	  case SMFIS_CONTINUE:
387		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0);
388		break;
389	  case SMFIS_TEMPFAIL:
390	  case SMFIS_REJECT:
391		if (ctx->ctx_reply != NULL)
392		{
393			ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE,
394					ctx->ctx_reply,
395					strlen(ctx->ctx_reply) + 1);
396			free(ctx->ctx_reply);
397			ctx->ctx_reply = NULL;
398		}
399		else
400		{
401			ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ?
402					SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0);
403		}
404		break;
405	  case SMFIS_DISCARD:
406		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0);
407		break;
408	  case SMFIS_ACCEPT:
409		ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0);
410		break;
411	  case _SMFIS_OPTIONS:
412		{
413			char buf[MILTER_OPTLEN];
414			mi_int32 v;
415
416			v = htonl(ctx->ctx_smfi->xxfi_version);
417			(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
418			v = htonl(ctx->ctx_smfi->xxfi_flags);
419			(void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v,
420				      MILTER_LEN_BYTES);
421			v = htonl(ctx->ctx_pflags);
422			(void) memcpy(&(buf[MILTER_LEN_BYTES * 2]), (void *) &v,
423				      MILTER_LEN_BYTES);
424			ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG, buf,
425				       MILTER_OPTLEN);
426		}
427		break;
428	  default:	/* don't send a reply */
429		break;
430	}
431	return ret;
432}
433
434/*
435**  CLR_MACROS -- clear set of macros starting from a given index
436**
437**	Parameters:
438**		ctx -- context structure
439**		m -- index from which to clear all macros
440**
441**	Returns:
442**		None.
443*/
444void
445mi_clr_macros(ctx, m)
446	SMFICTX_PTR ctx;
447	int m;
448{
449	int i;
450
451	for (i = m; i < MAX_MACROS_ENTRIES; i++)
452	{
453		if (ctx->ctx_mac_ptr[i] != NULL)
454		{
455			free(ctx->ctx_mac_ptr[i]);
456			ctx->ctx_mac_ptr[i] = NULL;
457		}
458		if (ctx->ctx_mac_buf[i] != NULL)
459		{
460			free(ctx->ctx_mac_buf[i]);
461			ctx->ctx_mac_buf[i] = NULL;
462		}
463	}
464}
465/*
466**  ST_OPTIONNEG -- negotiate options
467**
468**	Parameters:
469**		g -- generic argument structure
470**
471**	Returns:
472**		abort/send options/continue
473*/
474
475static int
476st_optionneg(g)
477	genarg *g;
478{
479	mi_int32 i, v;
480
481	if (g == NULL || g->a_ctx->ctx_smfi == NULL)
482		return SMFIS_CONTINUE;
483	mi_clr_macros(g->a_ctx, g->a_idx + 1);
484
485	/* check for minimum length */
486	if (g->a_len < MILTER_OPTLEN)
487	{
488		smi_log(SMI_LOG_ERR,
489			"%s: st_optionneg[%d]: len too short %d < %d",
490			g->a_ctx->ctx_smfi->xxfi_name,
491			(int) g->a_ctx->ctx_id, g->a_len,
492			MILTER_OPTLEN);
493		return _SMFIS_ABORT;
494	}
495
496	(void) memcpy((void *) &i, (void *) &(g->a_buf[0]),
497		      MILTER_LEN_BYTES);
498	v = ntohl(i);
499	if (v < g->a_ctx->ctx_smfi->xxfi_version)
500	{
501		/* hard failure for now! */
502		smi_log(SMI_LOG_ERR,
503			"%s: st_optionneg[%d]: version mismatch MTA: %d < milter: %d",
504			g->a_ctx->ctx_smfi->xxfi_name,
505			(int) g->a_ctx->ctx_id, (int) v,
506			g->a_ctx->ctx_smfi->xxfi_version);
507		return _SMFIS_ABORT;
508	}
509
510	(void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]),
511		      MILTER_LEN_BYTES);
512	v = ntohl(i);
513
514	/* no flags? set to default value for V1 actions */
515	if (v == 0)
516		v = SMFI_V1_ACTS;
517	i = g->a_ctx->ctx_smfi->xxfi_flags;
518	if ((v & i) != i)
519	{
520		smi_log(SMI_LOG_ERR,
521			"%s: st_optionneg[%d]: 0x%x does not fulfill action requirements 0x%x",
522			g->a_ctx->ctx_smfi->xxfi_name,
523			(int) g->a_ctx->ctx_id, v, i);
524		return _SMFIS_ABORT;
525	}
526
527	(void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]),
528		      MILTER_LEN_BYTES);
529	v = ntohl(i);
530
531	/* no flags? set to default value for V1 protocol */
532	if (v == 0)
533		v = SMFI_V1_PROT;
534	i = g->a_ctx->ctx_pflags;
535	if ((v & i) != i)
536	{
537		smi_log(SMI_LOG_ERR,
538			"%s: st_optionneg[%d]: 0x%x does not fulfill protocol requirements 0x%x",
539			g->a_ctx->ctx_smfi->xxfi_name,
540			(int) g->a_ctx->ctx_id, v, i);
541		return _SMFIS_ABORT;
542	}
543
544	return _SMFIS_OPTIONS;
545}
546/*
547**  ST_CONNECTINFO -- receive connection information
548**
549**	Parameters:
550**		g -- generic argument structure
551**
552**	Returns:
553**		continue or filter-specified value
554*/
555
556static int
557st_connectinfo(g)
558	genarg *g;
559{
560	size_t l;
561	size_t i;
562	char *s, family;
563	u_short port = 0;
564	_SOCK_ADDR sockaddr;
565	sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *));
566
567	if (g == NULL)
568		return _SMFIS_ABORT;
569	mi_clr_macros(g->a_ctx, g->a_idx + 1);
570	if (g->a_ctx->ctx_smfi == NULL ||
571	    (fi_connect = g->a_ctx->ctx_smfi->xxfi_connect) == NULL)
572		return SMFIS_CONTINUE;
573
574	s = g->a_buf;
575	i = 0;
576	l = g->a_len;
577	while (s[i] != '\0' && i <= l)
578		++i;
579	if (i >= l)
580		return _SMFIS_ABORT;
581
582	/* Move past trailing \0 in host string */
583	i++;
584	family = s[i++];
585	memset(&sockaddr, '\0', sizeof sockaddr);
586	if (family != SMFIA_UNKNOWN)
587	{
588		(void) memcpy((void *) &port, (void *) (s + i),
589			      sizeof port);
590		port = ntohs(port);
591		if ((i += sizeof port) >= l)
592		{
593			smi_log(SMI_LOG_ERR,
594				"%s: connect[%d]: wrong len %d >= %d",
595				g->a_ctx->ctx_smfi->xxfi_name,
596				(int) g->a_ctx->ctx_id, i, l);
597			return _SMFIS_ABORT;
598		}
599# if NETINET
600		if (family == SMFIA_INET)
601		{
602			if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr)
603			    == INADDR_NONE)
604			{
605				smi_log(SMI_LOG_ERR,
606					"%s: connect[%d]: inet_aton failed",
607					g->a_ctx->ctx_smfi->xxfi_name,
608					(int) g->a_ctx->ctx_id);
609				return _SMFIS_ABORT;
610			}
611			sockaddr.sa.sa_family = AF_INET;
612			if (port > 0)
613				sockaddr.sin.sin_port = port;
614		}
615		else
616# endif /* NETINET */
617# if NETINET6
618		if (family == SMFIA_INET6)
619		{
620			if (inet_pton(AF_INET6, s + i,
621				      &sockaddr.sin6.sin6_addr) != 1)
622			{
623				smi_log(SMI_LOG_ERR,
624					"%s: connect[%d]: inet_pton failed",
625					g->a_ctx->ctx_smfi->xxfi_name,
626					(int) g->a_ctx->ctx_id);
627				return _SMFIS_ABORT;
628			}
629			sockaddr.sa.sa_family = AF_INET6;
630			if (port > 0)
631				sockaddr.sin6.sin6_port = port;
632		}
633		else
634# endif /* NETINET6 */
635# if NETUNIX
636		if (family == SMFIA_UNIX)
637		{
638			if (strlcpy(sockaddr.sunix.sun_path, s + i,
639			    sizeof sockaddr.sunix.sun_path) >=
640			    sizeof sockaddr.sunix.sun_path)
641			{
642				smi_log(SMI_LOG_ERR,
643					"%s: connect[%d]: path too long",
644					g->a_ctx->ctx_smfi->xxfi_name,
645					(int) g->a_ctx->ctx_id);
646				return _SMFIS_ABORT;
647			}
648			sockaddr.sunix.sun_family = AF_UNIX;
649		}
650		else
651# endif /* NETUNIX */
652		{
653			smi_log(SMI_LOG_ERR,
654				"%s: connect[%d]: unknown family %d",
655				g->a_ctx->ctx_smfi->xxfi_name,
656				(int) g->a_ctx->ctx_id, family);
657			return _SMFIS_ABORT;
658		}
659	}
660	return (*fi_connect)(g->a_ctx, g->a_buf,
661			     family != SMFIA_UNKNOWN ? &sockaddr : NULL);
662}
663/*
664**  ST_EOH -- end of headers
665**
666**	Parameters:
667**		g -- generic argument structure
668**
669**	Returns:
670**		continue or filter-specified value
671*/
672
673static int
674st_eoh(g)
675	genarg *g;
676{
677	sfsistat (*fi_eoh) __P((SMFICTX *));
678
679	if (g == NULL)
680		return _SMFIS_ABORT;
681	if (g->a_ctx->ctx_smfi != NULL &&
682	    (fi_eoh = g->a_ctx->ctx_smfi->xxfi_eoh) != NULL)
683		return (*fi_eoh)(g->a_ctx);
684	return SMFIS_CONTINUE;
685}
686/*
687**  ST_HELO -- helo/ehlo command
688**
689**	Parameters:
690**		g -- generic argument structure
691**
692**	Returns:
693**		continue or filter-specified value
694*/
695static int
696st_helo(g)
697	genarg *g;
698{
699	sfsistat (*fi_helo) __P((SMFICTX *, char *));
700
701	if (g == NULL)
702		return _SMFIS_ABORT;
703	mi_clr_macros(g->a_ctx, g->a_idx + 1);
704	if (g->a_ctx->ctx_smfi != NULL &&
705	    (fi_helo = g->a_ctx->ctx_smfi->xxfi_helo) != NULL)
706		return (*fi_helo)(g->a_ctx, g->a_buf);
707	return SMFIS_CONTINUE;
708}
709/*
710**  ST_HEADER -- header line
711**
712**	Parameters:
713**		g -- generic argument structure
714**
715**	Returns:
716**		continue or filter-specified value
717*/
718
719static int
720st_header(g)
721	genarg *g;
722{
723	char *hf, *hv;
724	sfsistat (*fi_header) __P((SMFICTX *, char *, char *));
725
726	if (g == NULL)
727		return _SMFIS_ABORT;
728	if (g->a_ctx->ctx_smfi == NULL ||
729	    (fi_header = g->a_ctx->ctx_smfi->xxfi_header) == NULL)
730		return SMFIS_CONTINUE;
731	if (dec_arg2(g->a_buf, g->a_len, &hf, &hv) == MI_SUCCESS)
732		return (*fi_header)(g->a_ctx, hf, hv);
733	else
734		return _SMFIS_ABORT;
735}
736
737#define ARGV_FCT(lf, rf, idx) \
738	char **argv;	\
739	sfsistat (*lf) __P((SMFICTX *, char **));	\
740	int r;	\
741	\
742	if (g == NULL)	\
743		return _SMFIS_ABORT;	\
744	mi_clr_macros(g->a_ctx, g->a_idx + 1);	\
745	if (g->a_ctx->ctx_smfi == NULL ||	\
746	    (lf = g->a_ctx->ctx_smfi->rf) == NULL)	\
747		return SMFIS_CONTINUE;	\
748	if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL)	\
749		return _SMFIS_ABORT;	\
750	r = (*lf)(g->a_ctx, argv);	\
751	free(argv);	\
752	return r;
753
754/*
755**  ST_SENDER -- MAIL FROM command
756**
757**	Parameters:
758**		g -- generic argument structure
759**
760**	Returns:
761**		continue or filter-specified value
762*/
763
764static int
765st_sender(g)
766	genarg *g;
767{
768	ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL)
769}
770/*
771**  ST_RCPT -- RCPT TO command
772**
773**	Parameters:
774**		g -- generic argument structure
775**
776**	Returns:
777**		continue or filter-specified value
778*/
779
780static int
781st_rcpt(g)
782	genarg *g;
783{
784	ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT)
785}
786/*
787**  ST_MACROS -- deal with macros received from the MTA
788**
789**	Parameters:
790**		g -- generic argument structure
791**
792**	Returns:
793**		continue/keep
794**
795**	Side effects:
796**		set pointer in macro array to current values.
797*/
798
799static int
800st_macros(g)
801	genarg *g;
802{
803	int i;
804	char **argv;
805
806	if (g == NULL || g->a_len < 1)
807		return _SMFIS_FAIL;
808	if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL)
809		return _SMFIS_FAIL;
810	switch(g->a_buf[0])
811	{
812	  case SMFIC_CONNECT:
813		i = CI_CONN;
814		break;
815	  case SMFIC_HELO:
816		i = CI_HELO;
817		break;
818	  case SMFIC_MAIL:
819		i = CI_MAIL;
820		break;
821	  case SMFIC_RCPT:
822		i = CI_RCPT;
823		break;
824	  default:
825		free(argv);
826		return _SMFIS_FAIL;
827	}
828	if (g->a_ctx->ctx_mac_ptr[i] != NULL)
829		free(g->a_ctx->ctx_mac_ptr[i]);
830	if (g->a_ctx->ctx_mac_buf[i] != NULL)
831		free(g->a_ctx->ctx_mac_buf[i]);
832	g->a_ctx->ctx_mac_ptr[i] = argv;
833	g->a_ctx->ctx_mac_buf[i] = g->a_buf;
834	return _SMFIS_KEEP;
835}
836/*
837**  ST_QUIT -- quit command
838**
839**	Parameters:
840**		g -- generic argument structure
841**
842**	Returns:
843**		noreply
844*/
845
846static int
847st_quit(g)
848	genarg *g;
849{
850	return _SMFIS_NOREPLY;
851}
852/*
853**  ST_BODYCHUNK -- deal with a piece of the mail body
854**
855**	Parameters:
856**		g -- generic argument structure
857**
858**	Returns:
859**		continue or filter-specified value
860*/
861
862static int
863st_bodychunk(g)
864	genarg *g;
865{
866	sfsistat (*fi_body) __P((SMFICTX *, u_char *, size_t));
867
868	if (g == NULL)
869		return _SMFIS_ABORT;
870	if (g->a_ctx->ctx_smfi != NULL &&
871	    (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL)
872		return (*fi_body)(g->a_ctx, (u_char *)g->a_buf, g->a_len);
873	return SMFIS_CONTINUE;
874}
875/*
876**  ST_BODYEND -- deal with the last piece of the mail body
877**
878**	Parameters:
879**		g -- generic argument structure
880**
881**	Returns:
882**		continue or filter-specified value
883**
884**	Side effects:
885**		sends a reply for the body part (if non-empty).
886*/
887
888static int
889st_bodyend(g)
890	genarg *g;
891{
892	sfsistat r;
893	sfsistat (*fi_body) __P((SMFICTX *, u_char *, size_t));
894	sfsistat (*fi_eom) __P((SMFICTX *));
895
896	if (g == NULL)
897		return _SMFIS_ABORT;
898	r = SMFIS_CONTINUE;
899	if (g->a_ctx->ctx_smfi != NULL)
900	{
901		if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL &&
902		    g->a_len > 0)
903		{
904			socket_t sd;
905			struct timeval timeout;
906
907			timeout.tv_sec = g->a_ctx->ctx_timeout;
908			timeout.tv_usec = 0;
909			sd = g->a_ctx->ctx_sd;
910			r = (*fi_body)(g->a_ctx, (u_char *)g->a_buf, g->a_len);
911			if (r != SMFIS_CONTINUE &&
912			    sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS)
913				return _SMFIS_ABORT;
914		}
915	}
916	if (r == SMFIS_CONTINUE &&
917	    (fi_eom = g->a_ctx->ctx_smfi->xxfi_eom) != NULL)
918		return (*fi_eom)(g->a_ctx);
919	return r;
920}
921/*
922**  ST_ABORTFCT -- deal with aborts
923**
924**	Parameters:
925**		g -- generic argument structure
926**
927**	Returns:
928**		abort or filter-specified value
929*/
930
931static int
932st_abortfct(g)
933	genarg *g;
934{
935	sfsistat (*fi_abort) __P((SMFICTX *));
936
937	if (g == NULL)
938		return _SMFIS_ABORT;
939	if (g != NULL && g->a_ctx->ctx_smfi != NULL &&
940	    (fi_abort = g->a_ctx->ctx_smfi->xxfi_abort) != NULL)
941		(void) (*fi_abort)(g->a_ctx);
942	return _SMFIS_NOREPLY;
943}
944/*
945**  TRANS_OK -- is the state transition ok?
946**
947**	Parameters:
948**		old -- old state
949**		new -- new state
950**
951**	Returns:
952**		state transition ok
953*/
954
955static bool
956trans_ok(old, new)
957	int old, new;
958{
959	int s, n;
960
961	s = old;
962	do
963	{
964		/* is this state transition allowed? */
965		if ((MASK(new) & next_states[s]) != 0)
966			return TRUE;
967
968		/*
969		**  no: try next state;
970		**  this works since the relevant states are ordered
971		**  strict sequentially
972		*/
973		n = s + 1;
974
975		/*
976		**  can we actually "skip" this state?
977		**  see fix_stm() which sets this bit for those
978		**  states which the filter program is not interested in
979		*/
980		if (bitset(NX_SKIP, next_states[n]))
981			s = n;
982		else
983			return FALSE;
984	} while (s <= ST_LAST);
985	return FALSE;
986}
987/*
988**  FIX_STM -- add "skip" bits to the state transition table
989**
990**	Parameters:
991**		ctx -- context structure
992**
993**	Returns:
994**		None.
995**
996**	Side effects:
997**		may change state transition table.
998*/
999
1000static void
1001fix_stm(ctx)
1002	SMFICTX_PTR ctx;
1003{
1004	u_long fl;
1005
1006	if (ctx == NULL || ctx->ctx_smfi == NULL)
1007		return;
1008	fl = ctx->ctx_pflags;
1009	if (bitset(SMFIP_NOCONNECT, fl))
1010		next_states[ST_CONN] |= NX_SKIP;
1011	if (bitset(SMFIP_NOHELO, fl))
1012		next_states[ST_HELO] |= NX_SKIP;
1013	if (bitset(SMFIP_NOMAIL, fl))
1014		next_states[ST_MAIL] |= NX_SKIP;
1015	if (bitset(SMFIP_NORCPT, fl))
1016		next_states[ST_RCPT] |= NX_SKIP;
1017	if (bitset(SMFIP_NOHDRS, fl))
1018		next_states[ST_HDRS] |= NX_SKIP;
1019	if (bitset(SMFIP_NOEOH, fl))
1020		next_states[ST_EOHS] |= NX_SKIP;
1021	if (bitset(SMFIP_NOBODY, fl))
1022		next_states[ST_BODY] |= NX_SKIP;
1023}
1024/*
1025**  DEC_ARGV -- split a buffer into a list of strings, NULL terminated
1026**
1027**	Parameters:
1028**		buf -- buffer with several strings
1029**		len -- length of buffer
1030**
1031**	Returns:
1032**		array of pointers to the individual strings
1033*/
1034
1035static char **
1036dec_argv(buf, len)
1037	char *buf;
1038	size_t len;
1039{
1040	char **s;
1041	size_t i;
1042	int elem, nelem;
1043
1044	nelem = 0;
1045	for (i = 0; i < len; i++)
1046	{
1047		if (buf[i] == '\0')
1048			++nelem;
1049	}
1050	if (nelem == 0)
1051		return NULL;
1052
1053	/* last entry is only for the name */
1054	s = (char **)malloc((nelem + 1) * (sizeof *s));
1055	if (s == NULL)
1056		return NULL;
1057	s[0] = buf;
1058	for (i = 0, elem = 0; i < len && elem < nelem; i++)
1059	{
1060		if (buf[i] == '\0')
1061			s[++elem] = &(buf[i + 1]);
1062	}
1063
1064	/* overwrite last entry */
1065	s[elem] = NULL;
1066	return s;
1067}
1068/*
1069**  DEC_ARG2 -- split a buffer into two strings
1070**
1071**	Parameters:
1072**		buf -- buffer with two strings
1073**		len -- length of buffer
1074**		s1,s2 -- pointer to result strings
1075**
1076**	Returns:
1077**		MI_FAILURE/MI_SUCCESS
1078*/
1079
1080static int
1081dec_arg2(buf, len, s1, s2)
1082	char *buf;
1083	size_t len;
1084	char **s1;
1085	char **s2;
1086{
1087	size_t i;
1088
1089	*s1 = buf;
1090	for (i = 1; i < len && buf[i] != '\0'; i++)
1091		continue;
1092	if (i >= len - 1)
1093		return MI_FAILURE;
1094	*s2 = buf + i + 1;
1095	return MI_SUCCESS;
1096}
1097/*
1098**  SENDOK -- is it ok for the filter to send stuff to the MTA?
1099**
1100**	Parameters:
1101**		ctx -- context structure
1102**		flag -- flag to check
1103**
1104**	Returns:
1105**		sending allowed (in current state)
1106*/
1107
1108bool
1109mi_sendok(ctx, flag)
1110	SMFICTX_PTR ctx;
1111	int flag;
1112{
1113	if (ctx == NULL || ctx->ctx_smfi == NULL)
1114		return FALSE;
1115	if (flag != 0 && !bitset(flag, ctx->ctx_smfi->xxfi_flags))
1116		return FALSE;
1117	return ctx->ctx_state == ST_ENDM;
1118}
1119#endif /* _FFR_MILTER */
1120