xref: /onnv-gate/usr/src/cmd/sendmail/src/milter.c (revision 3544:8dfb1c11a5d7)
10Sstevel@tonic-gate /*
2*3544Sjbeck  * Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers.
30Sstevel@tonic-gate  *	All rights reserved.
40Sstevel@tonic-gate  *
50Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
60Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
70Sstevel@tonic-gate  * the sendmail distribution.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  */
100Sstevel@tonic-gate 
110Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
120Sstevel@tonic-gate 
130Sstevel@tonic-gate #include <sendmail.h>
140Sstevel@tonic-gate 
15*3544Sjbeck SM_RCSID("@(#)$Id: milter.c,v 8.266 2006/11/29 00:20:41 ca Exp $")
160Sstevel@tonic-gate 
170Sstevel@tonic-gate #if MILTER
18*3544Sjbeck # include <sm/sendmail.h>
190Sstevel@tonic-gate # include <libmilter/mfapi.h>
200Sstevel@tonic-gate # include <libmilter/mfdef.h>
210Sstevel@tonic-gate 
220Sstevel@tonic-gate # include <errno.h>
23616Sjbeck # include <sm/time.h>
240Sstevel@tonic-gate # include <sys/uio.h>
250Sstevel@tonic-gate 
260Sstevel@tonic-gate # if NETINET || NETINET6
270Sstevel@tonic-gate #  include <arpa/inet.h>
28*3544Sjbeck #  if MILTER_NO_NAGLE
290Sstevel@tonic-gate #   include <netinet/tcp.h>
30*3544Sjbeck #  endif /* MILTER_NO_NAGLE */
310Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate # include <sm/fdset.h>
340Sstevel@tonic-gate 
350Sstevel@tonic-gate static void	milter_connect_timeout __P((int));
360Sstevel@tonic-gate static void	milter_error __P((struct milter *, ENVELOPE *));
370Sstevel@tonic-gate static int	milter_open __P((struct milter *, bool, ENVELOPE *));
380Sstevel@tonic-gate static void	milter_parse_timeouts __P((char *, struct milter *));
39*3544Sjbeck static char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
40*3544Sjbeck 			ENVELOPE *, const char *));
41*3544Sjbeck static char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
42*3544Sjbeck 			ENVELOPE *, const char *));
43*3544Sjbeck static char	*milter_write __P((struct milter *, int, char *, ssize_t,
44*3544Sjbeck 			time_t, ENVELOPE *, const char *));
45*3544Sjbeck static char	*milter_send_command __P((struct milter *, int, void *,
46*3544Sjbeck 			ssize_t, ENVELOPE *, char *, const char *));
47*3544Sjbeck static char	*milter_command __P((int, void *, ssize_t, char **,
48*3544Sjbeck 			ENVELOPE *, char *, const char *, bool));
49*3544Sjbeck static char	*milter_body __P((struct milter *, ENVELOPE *, char *));
50*3544Sjbeck static int	milter_reopen_df __P((ENVELOPE *));
51*3544Sjbeck static int	milter_reset_df __P((ENVELOPE *));
52*3544Sjbeck static void	milter_quit_filter __P((struct milter *, ENVELOPE *));
53*3544Sjbeck static void	milter_abort_filter __P((struct milter *, ENVELOPE *));
54*3544Sjbeck static void	milter_send_macros __P((struct milter *, char **, int,
55*3544Sjbeck 			ENVELOPE *));
56*3544Sjbeck static int	milter_negotiate __P((struct milter *, ENVELOPE *));
57*3544Sjbeck static void	milter_per_connection_check __P((ENVELOPE *));
58*3544Sjbeck static char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
59*3544Sjbeck static void	milter_addheader __P((struct milter *, char *, ssize_t,
60*3544Sjbeck 			ENVELOPE *));
61*3544Sjbeck static void	milter_insheader __P((struct milter *, char *, ssize_t,
62*3544Sjbeck 			ENVELOPE *));
63*3544Sjbeck static void	milter_changeheader __P((struct milter *, char *, ssize_t,
64*3544Sjbeck 			ENVELOPE *));
65*3544Sjbeck static void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
66*3544Sjbeck static void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
67*3544Sjbeck static void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
68*3544Sjbeck static void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
69*3544Sjbeck static int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
70*3544Sjbeck static int	milter_set_macros __P((char *, char **, char *, int));
71*3544Sjbeck 
72*3544Sjbeck 
73*3544Sjbeck /* milter states */
74*3544Sjbeck # define SMFS_CLOSED		'C'	/* closed for all further actions */
75*3544Sjbeck # define SMFS_OPEN		'O'	/* connected to remote milter filter */
76*3544Sjbeck # define SMFS_INMSG		'M'	/* currently servicing a message */
77*3544Sjbeck # define SMFS_DONE		'D'	/* done with current message */
78*3544Sjbeck # define SMFS_CLOSABLE		'Q'	/* done with current connection */
79*3544Sjbeck # define SMFS_ERROR		'E'	/* error state */
80*3544Sjbeck # define SMFS_READY		'R'	/* ready for action */
81*3544Sjbeck # define SMFS_SKIP		'S'	/* skip body */
820Sstevel@tonic-gate 
830Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
840Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
850Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
860Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
870Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
880Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
89*3544Sjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1];
900Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
910Sstevel@tonic-gate 
920Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
930Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
940Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
950Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
960Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
970Sstevel@tonic-gate 	{ \
980Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
990Sstevel@tonic-gate 		milter_abort(e); \
1000Sstevel@tonic-gate 	}
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
1030Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
1040Sstevel@tonic-gate 	{ \
1050Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1060Sstevel@tonic-gate 		{ \
1070Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1080Sstevel@tonic-gate 							 "filter failure"); \
1090Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1100Sstevel@tonic-gate 				  e->e_quarmsg); \
1110Sstevel@tonic-gate 		} \
1120Sstevel@tonic-gate 	} \
1130Sstevel@tonic-gate 	else if (tTd(71, 101)) \
1140Sstevel@tonic-gate 	{ \
1150Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1160Sstevel@tonic-gate 		{ \
1170Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1180Sstevel@tonic-gate 							 "filter failure"); \
1190Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1200Sstevel@tonic-gate 				  e->e_quarmsg); \
1210Sstevel@tonic-gate 		} \
1220Sstevel@tonic-gate 	} \
1230Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
1240Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
1250Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
1260Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
1270Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
1280Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
1290Sstevel@tonic-gate 	else \
1300Sstevel@tonic-gate 		action;
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
1330Sstevel@tonic-gate 	if (response == NULL || \
1340Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
1350Sstevel@tonic-gate 	    rlen < 3 || \
1360Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
1370Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
1380Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
1390Sstevel@tonic-gate 	{ \
1400Sstevel@tonic-gate 		if (response != NULL) \
1410Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
1420Sstevel@tonic-gate 		response = newstr(default); \
1430Sstevel@tonic-gate 	} \
1440Sstevel@tonic-gate 	else \
1450Sstevel@tonic-gate 	{ \
1460Sstevel@tonic-gate 		char *ptr = response; \
1470Sstevel@tonic-gate  \
1480Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
1490Sstevel@tonic-gate 		while (*ptr != '\0') \
1500Sstevel@tonic-gate 		{ \
1510Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
1520Sstevel@tonic-gate 			{ \
1530Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
1540Sstevel@tonic-gate 				response = newstr(default); \
1550Sstevel@tonic-gate 				break; \
1560Sstevel@tonic-gate 			} \
1570Sstevel@tonic-gate 			ptr++; \
1580Sstevel@tonic-gate 		} \
1590Sstevel@tonic-gate 	}
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
1620Sstevel@tonic-gate { \
1630Sstevel@tonic-gate 	int save_errno = errno; \
1640Sstevel@tonic-gate  \
1650Sstevel@tonic-gate 	if (tTd(64, 5)) \
1660Sstevel@tonic-gate 	{ \
1670Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
1680Sstevel@tonic-gate 		sm_dprintf("\n"); \
1690Sstevel@tonic-gate 	} \
1700Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
1710Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
1720Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
1730Sstevel@tonic-gate 	{ \
1740Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
1750Sstevel@tonic-gate 		{ \
1760Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
1770Sstevel@tonic-gate 			e->e_dfp = NULL; \
1780Sstevel@tonic-gate 		} \
1790Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
1800Sstevel@tonic-gate 	} \
1810Sstevel@tonic-gate 	errno = save_errno; \
1820Sstevel@tonic-gate }
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate /*
1850Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
1860Sstevel@tonic-gate **
1870Sstevel@tonic-gate **	Parameters:
1880Sstevel@tonic-gate **		routine -- routine name for debug/logging
1890Sstevel@tonic-gate **		secs -- number of seconds in timeout
1900Sstevel@tonic-gate **		write -- waiting to read or write?
1910Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
1920Sstevel@tonic-gate **
1930Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
1940Sstevel@tonic-gate */
1950Sstevel@tonic-gate 
196*3544Sjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \
1970Sstevel@tonic-gate { \
1980Sstevel@tonic-gate 	int ret; \
1990Sstevel@tonic-gate 	int save_errno; \
2000Sstevel@tonic-gate 	fd_set fds; \
2010Sstevel@tonic-gate 	struct timeval tv; \
2020Sstevel@tonic-gate  \
2030Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
2040Sstevel@tonic-gate 	{ \
2050Sstevel@tonic-gate 		if (tTd(64, 5)) \
2060Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
2070Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
2080Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
2090Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2100Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2110Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
2120Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
2130Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
2140Sstevel@tonic-gate 		milter_error(m, e); \
2150Sstevel@tonic-gate 		return NULL; \
2160Sstevel@tonic-gate 	} \
2170Sstevel@tonic-gate  \
2180Sstevel@tonic-gate 	do \
2190Sstevel@tonic-gate 	{ \
2200Sstevel@tonic-gate 		FD_ZERO(&fds); \
2210Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
2220Sstevel@tonic-gate 		tv.tv_sec = (secs); \
2230Sstevel@tonic-gate 		tv.tv_usec = 0; \
2240Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
2250Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
2260Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
2270Sstevel@tonic-gate 			     NULL, &tv); \
2280Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
2290Sstevel@tonic-gate  \
2300Sstevel@tonic-gate 	switch (ret) \
2310Sstevel@tonic-gate 	{ \
2320Sstevel@tonic-gate 	  case 0: \
2330Sstevel@tonic-gate 		if (tTd(64, 5)) \
234*3544Sjbeck 			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
235*3544Sjbeck 				(routine), m->mf_name, (function)); \
2360Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2370Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
238*3544Sjbeck 				  "Milter (%s): timeout %s data %s, where=%s", \
239*3544Sjbeck 				  m->mf_name, \
2400Sstevel@tonic-gate 				  started ? "during" : "before", \
241*3544Sjbeck 				  (routine), (function)); \
2420Sstevel@tonic-gate 		milter_error(m, e); \
2430Sstevel@tonic-gate 		return NULL; \
2440Sstevel@tonic-gate  \
2450Sstevel@tonic-gate 	  case -1: \
2460Sstevel@tonic-gate 		save_errno = errno; \
2470Sstevel@tonic-gate 		if (tTd(64, 5)) \
2480Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
2490Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
2500Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2510Sstevel@tonic-gate 		{ \
2520Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2530Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
2540Sstevel@tonic-gate 				  m->mf_name, (routine), \
2550Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
2560Sstevel@tonic-gate 		} \
2570Sstevel@tonic-gate 		milter_error(m, e); \
2580Sstevel@tonic-gate 		return NULL; \
2590Sstevel@tonic-gate  \
2600Sstevel@tonic-gate 	  default: \
2610Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
2620Sstevel@tonic-gate 			break; \
2630Sstevel@tonic-gate 		if (tTd(64, 5)) \
2640Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
2650Sstevel@tonic-gate 				(routine), m->mf_name); \
2660Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2670Sstevel@tonic-gate 		{ \
2680Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2690Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
2700Sstevel@tonic-gate 				  m->mf_name, (routine)); \
2710Sstevel@tonic-gate 		} \
2720Sstevel@tonic-gate 		milter_error(m, e); \
2730Sstevel@tonic-gate 		return NULL; \
2740Sstevel@tonic-gate 	} \
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate /*
2780Sstevel@tonic-gate **  Low level functions
2790Sstevel@tonic-gate */
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
2830Sstevel@tonic-gate **
2840Sstevel@tonic-gate **	Parameters:
2850Sstevel@tonic-gate **		m -- milter to read from.
2860Sstevel@tonic-gate **		cmd -- return param for command read.
2870Sstevel@tonic-gate **		rlen -- return length of response string.
2880Sstevel@tonic-gate **		to -- timeout in seconds.
2890Sstevel@tonic-gate **		e -- current envelope.
2900Sstevel@tonic-gate **
2910Sstevel@tonic-gate **	Returns:
2920Sstevel@tonic-gate **		response string (may be NULL)
2930Sstevel@tonic-gate */
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate static char *
296*3544Sjbeck milter_sysread(m, buf, sz, to, e, where)
2970Sstevel@tonic-gate 	struct milter *m;
2980Sstevel@tonic-gate 	char *buf;
2990Sstevel@tonic-gate 	ssize_t sz;
3000Sstevel@tonic-gate 	time_t to;
3010Sstevel@tonic-gate 	ENVELOPE *e;
302*3544Sjbeck 	const char *where;
3030Sstevel@tonic-gate {
3040Sstevel@tonic-gate 	time_t readstart = 0;
3050Sstevel@tonic-gate 	ssize_t len, curl;
3060Sstevel@tonic-gate 	bool started = false;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	curl = 0;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	if (to > 0)
3110Sstevel@tonic-gate 		readstart = curtime();
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	for (;;)
3140Sstevel@tonic-gate 	{
3150Sstevel@tonic-gate 		if (to > 0)
3160Sstevel@tonic-gate 		{
3170Sstevel@tonic-gate 			time_t now;
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 			now = curtime();
3200Sstevel@tonic-gate 			if (now - readstart >= to)
3210Sstevel@tonic-gate 			{
3220Sstevel@tonic-gate 				if (tTd(64, 5))
323*3544Sjbeck 					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
324*3544Sjbeck 						  m->mf_name,
3250Sstevel@tonic-gate 						  started ? "during" : "before",
326*3544Sjbeck 						  where);
3270Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3280Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
329*3544Sjbeck 						  "Milter (%s): timeout %s data read in %s",
330*3544Sjbeck 						  m->mf_name,
3310Sstevel@tonic-gate 						  started ? "during" : "before",
332*3544Sjbeck 						  where);
3330Sstevel@tonic-gate 				milter_error(m, e);
3340Sstevel@tonic-gate 				return NULL;
3350Sstevel@tonic-gate 			}
3360Sstevel@tonic-gate 			to -= now - readstart;
3370Sstevel@tonic-gate 			readstart = now;
338*3544Sjbeck 			MILTER_TIMEOUT("read", to, false, started, where);
3390Sstevel@tonic-gate 		}
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 		if (len < 0)
3440Sstevel@tonic-gate 		{
3450Sstevel@tonic-gate 			int save_errno = errno;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 			if (tTd(64, 5))
348*3544Sjbeck 				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
3490Sstevel@tonic-gate 					m->mf_name, (long) len,
3500Sstevel@tonic-gate 					sm_errstring(save_errno));
3510Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3520Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3530Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
3540Sstevel@tonic-gate 					  m->mf_name, (long) len,
3550Sstevel@tonic-gate 					  sm_errstring(save_errno));
3560Sstevel@tonic-gate 			milter_error(m, e);
3570Sstevel@tonic-gate 			return NULL;
3580Sstevel@tonic-gate 		}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 		started = true;
3610Sstevel@tonic-gate 		curl += len;
3620Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
3630Sstevel@tonic-gate 			break;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	}
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	if (curl != sz)
3680Sstevel@tonic-gate 	{
3690Sstevel@tonic-gate 		if (tTd(64, 5))
370*3544Sjbeck 			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
3710Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
3720Sstevel@tonic-gate 		if (MilterLogLevel > 0)
3730Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
374*3544Sjbeck 				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
3750Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
3760Sstevel@tonic-gate 		milter_error(m, e);
3770Sstevel@tonic-gate 		return NULL;
3780Sstevel@tonic-gate 	}
3790Sstevel@tonic-gate 	return buf;
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate static char *
383*3544Sjbeck milter_read(m, cmd, rlen, to, e, where)
3840Sstevel@tonic-gate 	struct milter *m;
3850Sstevel@tonic-gate 	char *cmd;
3860Sstevel@tonic-gate 	ssize_t *rlen;
3870Sstevel@tonic-gate 	time_t to;
3880Sstevel@tonic-gate 	ENVELOPE *e;
389*3544Sjbeck 	const char *where;
3900Sstevel@tonic-gate {
3910Sstevel@tonic-gate 	time_t readstart = 0;
3920Sstevel@tonic-gate 	ssize_t expl;
3930Sstevel@tonic-gate 	mi_int32 i;
394*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
3950Sstevel@tonic-gate 	int cork = 0;
396*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
3970Sstevel@tonic-gate 	char *buf;
3980Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	if (m->mf_sock < 0)
4010Sstevel@tonic-gate 	{
4020Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4030Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
404*3544Sjbeck 				  "milter_read(%s): socket closed, where=%s",
405*3544Sjbeck 				  m->mf_name, where);
4060Sstevel@tonic-gate 		milter_error(m, e);
4070Sstevel@tonic-gate 		return NULL;
4080Sstevel@tonic-gate 	}
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	*rlen = 0;
4110Sstevel@tonic-gate 	*cmd = '\0';
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	if (to > 0)
4140Sstevel@tonic-gate 		readstart = curtime();
4150Sstevel@tonic-gate 
416*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4170Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4180Sstevel@tonic-gate 		   sizeof(cork));
419*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
420*3544Sjbeck 
421*3544Sjbeck 	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
4220Sstevel@tonic-gate 		return NULL;
4230Sstevel@tonic-gate 
424*3544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4250Sstevel@tonic-gate 	cork = 1;
4260Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4270Sstevel@tonic-gate 		   sizeof(cork));
428*3544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	/* reset timeout */
4310Sstevel@tonic-gate 	if (to > 0)
4320Sstevel@tonic-gate 	{
4330Sstevel@tonic-gate 		time_t now;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 		now = curtime();
4360Sstevel@tonic-gate 		if (now - readstart >= to)
4370Sstevel@tonic-gate 		{
4380Sstevel@tonic-gate 			if (tTd(64, 5))
439*3544Sjbeck 				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
440*3544Sjbeck 					m->mf_name, where);
4410Sstevel@tonic-gate 			if (MilterLogLevel > 0)
4420Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
443*3544Sjbeck 					  "Milter read(%s): timeout before data read, where=%s",
444*3544Sjbeck 					  m->mf_name, where);
4450Sstevel@tonic-gate 			milter_error(m, e);
4460Sstevel@tonic-gate 			return NULL;
4470Sstevel@tonic-gate 		}
4480Sstevel@tonic-gate 		to -= now - readstart;
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
4520Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
4530Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
4540Sstevel@tonic-gate 	expl = ntohl(i) - 1;
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	if (tTd(64, 25))
4570Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
4580Sstevel@tonic-gate 			m->mf_name, (long) expl);
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	if (expl < 0)
4610Sstevel@tonic-gate 	{
4620Sstevel@tonic-gate 		if (tTd(64, 5))
463*3544Sjbeck 			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
464*3544Sjbeck 				m->mf_name, (long) expl, where);
4650Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4660Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
467*3544Sjbeck 				  "milter_read(%s): read size %ld out of range, where=%s",
468*3544Sjbeck 				  m->mf_name, (long) expl, where);
4690Sstevel@tonic-gate 		milter_error(m, e);
4700Sstevel@tonic-gate 		return NULL;
4710Sstevel@tonic-gate 	}
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	if (expl == 0)
4740Sstevel@tonic-gate 		return NULL;
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
4770Sstevel@tonic-gate 
478*3544Sjbeck 	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
4790Sstevel@tonic-gate 	{
4800Sstevel@tonic-gate 		sm_free(buf); /* XXX */
4810Sstevel@tonic-gate 		return NULL;
4820Sstevel@tonic-gate 	}
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 	if (tTd(64, 50))
4850Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
4860Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
4870Sstevel@tonic-gate 	*rlen = expl;
4880Sstevel@tonic-gate 	return buf;
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate /*
4920Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
4930Sstevel@tonic-gate **
4940Sstevel@tonic-gate **	Parameters:
4950Sstevel@tonic-gate **		m -- milter to read from.
4960Sstevel@tonic-gate **		cmd -- command to send.
4970Sstevel@tonic-gate **		buf -- optional command data.
4980Sstevel@tonic-gate **		len -- length of buf.
4990Sstevel@tonic-gate **		to -- timeout in seconds.
5000Sstevel@tonic-gate **		e -- current envelope.
5010Sstevel@tonic-gate **
5020Sstevel@tonic-gate **	Returns:
5030Sstevel@tonic-gate **		buf if successful, NULL otherwise
5040Sstevel@tonic-gate **		Not actually used anywhere but function prototype
5050Sstevel@tonic-gate **			must match milter_read()
5060Sstevel@tonic-gate */
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate static char *
509*3544Sjbeck milter_write(m, cmd, buf, len, to, e, where)
5100Sstevel@tonic-gate 	struct milter *m;
511*3544Sjbeck 	int cmd;
5120Sstevel@tonic-gate 	char *buf;
5130Sstevel@tonic-gate 	ssize_t len;
5140Sstevel@tonic-gate 	time_t to;
5150Sstevel@tonic-gate 	ENVELOPE *e;
516*3544Sjbeck 	const char *where;
5170Sstevel@tonic-gate {
5180Sstevel@tonic-gate 	time_t writestart = (time_t) 0;
5190Sstevel@tonic-gate 	ssize_t sl, i;
5200Sstevel@tonic-gate 	int num_vectors;
5210Sstevel@tonic-gate 	mi_int32 nl;
522*3544Sjbeck 	char command = (char) cmd;
5230Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
5240Sstevel@tonic-gate 	bool started = false;
5250Sstevel@tonic-gate 	struct iovec vector[2];
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	/*
5280Sstevel@tonic-gate 	**  At most two buffers will be written, though
5290Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
5300Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
5310Sstevel@tonic-gate 	*/
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
5340Sstevel@tonic-gate 	{
5350Sstevel@tonic-gate 		if (tTd(64, 5))
5360Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): length %ld out of range\n",
5370Sstevel@tonic-gate 				m->mf_name, (long) len);
5380Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5390Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5400Sstevel@tonic-gate 				  "milter_write(%s): length %ld out of range",
5410Sstevel@tonic-gate 				  m->mf_name, (long) len);
5420Sstevel@tonic-gate 		milter_error(m, e);
5430Sstevel@tonic-gate 		return NULL;
5440Sstevel@tonic-gate 	}
5450Sstevel@tonic-gate 	if (m->mf_sock < 0)
5460Sstevel@tonic-gate 	{
5470Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5480Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5490Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
5500Sstevel@tonic-gate 				  m->mf_name);
5510Sstevel@tonic-gate 		milter_error(m, e);
5520Sstevel@tonic-gate 		return NULL;
5530Sstevel@tonic-gate 	}
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	if (tTd(64, 20))
5560Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
557*3544Sjbeck 			   m->mf_name, command, (long) len);
558*3544Sjbeck 
559*3544Sjbeck 	nl = htonl(len + 1);	/* add 1 for the command char */
5600Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
561*3544Sjbeck 	data[MILTER_LEN_BYTES] = command;
5620Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	/* set up the vector for the size / command */
5650Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
5660Sstevel@tonic-gate 	vector[0].iov_len  = sl;
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	/*
5690Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
5700Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
5710Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
5720Sstevel@tonic-gate 	*/
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
5750Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
5760Sstevel@tonic-gate 	{
5770Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
5780Sstevel@tonic-gate 		num_vectors = 1;
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 	else
5810Sstevel@tonic-gate 	{
5820Sstevel@tonic-gate 		/*
5830Sstevel@tonic-gate 		**  There is both size / command and command data.
5840Sstevel@tonic-gate 		**  Set up the vector for the command data.
5850Sstevel@tonic-gate 		*/
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		num_vectors = 2;
5880Sstevel@tonic-gate 		sl += len;
5890Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
5900Sstevel@tonic-gate 		vector[1].iov_len  = len;
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 		if (tTd(64, 50))
5930Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
5940Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if (to > 0)
5980Sstevel@tonic-gate 	{
5990Sstevel@tonic-gate 		writestart = curtime();
600*3544Sjbeck 		MILTER_TIMEOUT("write", to, true, started, where);
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	/* write the vector(s) */
6040Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
6050Sstevel@tonic-gate 	if (i != sl)
6060Sstevel@tonic-gate 	{
6070Sstevel@tonic-gate 		int save_errno = errno;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 		if (tTd(64, 5))
6100Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
611*3544Sjbeck 				   m->mf_name, command, (long) i, (long) sl,
6120Sstevel@tonic-gate 				   sm_errstring(save_errno));
6130Sstevel@tonic-gate 		if (MilterLogLevel > 0)
6140Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6150Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
616*3544Sjbeck 				  m->mf_name, command, (long) i, (long) sl,
6170Sstevel@tonic-gate 				  sm_errstring(save_errno));
6180Sstevel@tonic-gate 		milter_error(m, e);
6190Sstevel@tonic-gate 		return NULL;
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 	return buf;
6220Sstevel@tonic-gate }
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate **  Utility functions
6260Sstevel@tonic-gate */
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate /*
6290Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
6300Sstevel@tonic-gate **
6310Sstevel@tonic-gate **	Parameters:
6320Sstevel@tonic-gate **		m -- milter to connect to.
6330Sstevel@tonic-gate **		parseonly -- parse but don't connect.
6340Sstevel@tonic-gate **		e -- current envelope.
6350Sstevel@tonic-gate **
6360Sstevel@tonic-gate **	Returns:
6370Sstevel@tonic-gate **		connected socket if successful && !parseonly,
6380Sstevel@tonic-gate **		0 upon parse success if parseonly,
6390Sstevel@tonic-gate **		-1 otherwise.
6400Sstevel@tonic-gate */
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate static int
6450Sstevel@tonic-gate milter_open(m, parseonly, e)
6460Sstevel@tonic-gate 	struct milter *m;
6470Sstevel@tonic-gate 	bool parseonly;
6480Sstevel@tonic-gate 	ENVELOPE *e;
6490Sstevel@tonic-gate {
6500Sstevel@tonic-gate 	int sock = 0;
6510Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
6520Sstevel@tonic-gate 	int addrno = 0;
6530Sstevel@tonic-gate 	int save_errno;
6540Sstevel@tonic-gate 	char *p;
6550Sstevel@tonic-gate 	char *colon;
6560Sstevel@tonic-gate 	char *at;
6570Sstevel@tonic-gate 	struct hostent *hp = NULL;
6580Sstevel@tonic-gate 	SOCKADDR addr;
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
6610Sstevel@tonic-gate 	{
6620Sstevel@tonic-gate 		if (tTd(64, 5))
6630Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
6640Sstevel@tonic-gate 				   m->mf_name);
6650Sstevel@tonic-gate 		if (parseonly)
6660Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
6670Sstevel@tonic-gate 			       m->mf_name);
6680Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
6690Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6700Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
6710Sstevel@tonic-gate 				  m->mf_name);
6720Sstevel@tonic-gate 		milter_error(m, e);
6730Sstevel@tonic-gate 		return -1;
6740Sstevel@tonic-gate 	}
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
677*3544Sjbeck 	memset(&addr, '\0', sizeof(addr));
6780Sstevel@tonic-gate 	p = m->mf_conn;
6790Sstevel@tonic-gate 	colon = strchr(p, ':');
6800Sstevel@tonic-gate 	if (colon != NULL)
6810Sstevel@tonic-gate 	{
6820Sstevel@tonic-gate 		*colon = '\0';
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 		if (*p == '\0')
6850Sstevel@tonic-gate 		{
6860Sstevel@tonic-gate # if NETUNIX
6870Sstevel@tonic-gate 			/* default to AF_UNIX */
6880Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
6890Sstevel@tonic-gate # else /* NETUNIX */
6900Sstevel@tonic-gate #  if NETINET
6910Sstevel@tonic-gate 			/* default to AF_INET */
6920Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
6930Sstevel@tonic-gate #  else /* NETINET */
6940Sstevel@tonic-gate #   if NETINET6
6950Sstevel@tonic-gate 			/* default to AF_INET6 */
6960Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
6970Sstevel@tonic-gate #   else /* NETINET6 */
6980Sstevel@tonic-gate 			/* no protocols available */
6990Sstevel@tonic-gate 			if (MilterLogLevel > 0)
7000Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7010Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
7020Sstevel@tonic-gate 					  m->mf_name);
7030Sstevel@tonic-gate 			milter_error(m, e);
7040Sstevel@tonic-gate 			return -1;
7050Sstevel@tonic-gate #   endif /* NETINET6 */
7060Sstevel@tonic-gate #  endif /* NETINET */
7070Sstevel@tonic-gate # endif /* NETUNIX */
7080Sstevel@tonic-gate 		}
7090Sstevel@tonic-gate # if NETUNIX
7100Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
7110Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
7120Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
7130Sstevel@tonic-gate # endif /* NETUNIX */
7140Sstevel@tonic-gate # if NETINET
7150Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
7160Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
7170Sstevel@tonic-gate # endif /* NETINET */
7180Sstevel@tonic-gate # if NETINET6
7190Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
7200Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
7210Sstevel@tonic-gate # endif /* NETINET6 */
7220Sstevel@tonic-gate 		else
7230Sstevel@tonic-gate 		{
7240Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
7250Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
7260Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
7270Sstevel@tonic-gate 			errno = EINVAL;
7280Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
7290Sstevel@tonic-gate 			if (tTd(64, 5))
7300Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
7310Sstevel@tonic-gate 					m->mf_name, p);
7320Sstevel@tonic-gate 			if (parseonly)
7330Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
7340Sstevel@tonic-gate 				       m->mf_name, p);
7350Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7360Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7370Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
7380Sstevel@tonic-gate 					  m->mf_name, p);
7390Sstevel@tonic-gate 			milter_error(m, e);
7400Sstevel@tonic-gate 			return -1;
7410Sstevel@tonic-gate 		}
7420Sstevel@tonic-gate 		*colon++ = ':';
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 	else
7450Sstevel@tonic-gate 	{
7460Sstevel@tonic-gate 		/* default to AF_UNIX */
7470Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
7480Sstevel@tonic-gate 		colon = p;
7490Sstevel@tonic-gate 	}
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate # if NETUNIX
7520Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
7530Sstevel@tonic-gate 	{
7540Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 		at = colon;
757*3544Sjbeck 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7580Sstevel@tonic-gate 		{
7590Sstevel@tonic-gate 			if (tTd(64, 5))
7600Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
7610Sstevel@tonic-gate 					m->mf_name, colon);
7620Sstevel@tonic-gate 			errno = EINVAL;
7630Sstevel@tonic-gate 			if (parseonly)
7640Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
7650Sstevel@tonic-gate 				       m->mf_name, colon);
7660Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7670Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7680Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
7690Sstevel@tonic-gate 					  m->mf_name, colon);
7700Sstevel@tonic-gate 			milter_error(m, e);
7710Sstevel@tonic-gate 			return -1;
7720Sstevel@tonic-gate 		}
7730Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7740Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
7770Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
7780Sstevel@tonic-gate 		{
7790Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
7800Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
7810Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7820Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
7830Sstevel@tonic-gate 						     m->mf_name, colon);
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 		else if (errno != 0)
7860Sstevel@tonic-gate 		{
7870Sstevel@tonic-gate 			/* if not safe, don't create */
7880Sstevel@tonic-gate 			save_errno = errno;
7890Sstevel@tonic-gate 			if (tTd(64, 5))
7900Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
7910Sstevel@tonic-gate 					m->mf_name, colon);
7920Sstevel@tonic-gate 			errno = save_errno;
7930Sstevel@tonic-gate 			if (parseonly)
7940Sstevel@tonic-gate 			{
7950Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
7960Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
7970Sstevel@tonic-gate 				    OpMode == MD_SMTP)
7980Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
7990Sstevel@tonic-gate 					       m->mf_name, colon);
8000Sstevel@tonic-gate 			}
8010Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8020Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8030Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
8040Sstevel@tonic-gate 					  m->mf_name, colon);
8050Sstevel@tonic-gate 			milter_error(m, e);
8060Sstevel@tonic-gate 			return -1;
8070Sstevel@tonic-gate 		}
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
810*3544Sjbeck 			       sizeof(addr.sunix.sun_path));
811*3544Sjbeck 		addrlen = sizeof(struct sockaddr_un);
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 	else
8140Sstevel@tonic-gate # endif /* NETUNIX */
8150Sstevel@tonic-gate # if NETINET || NETINET6
8160Sstevel@tonic-gate 	if (false
8170Sstevel@tonic-gate #  if NETINET
8180Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
8190Sstevel@tonic-gate #  endif /* NETINET */
8200Sstevel@tonic-gate #  if NETINET6
8210Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
8220Sstevel@tonic-gate #  endif /* NETINET6 */
8230Sstevel@tonic-gate 		 )
8240Sstevel@tonic-gate 	{
8250Sstevel@tonic-gate 		unsigned short port;
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 		/* Parse port@host */
8280Sstevel@tonic-gate 		at = strchr(colon, '@');
8290Sstevel@tonic-gate 		if (at == NULL)
8300Sstevel@tonic-gate 		{
8310Sstevel@tonic-gate 			if (tTd(64, 5))
8320Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
8330Sstevel@tonic-gate 					m->mf_name, colon);
8340Sstevel@tonic-gate 			if (parseonly)
8350Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
8360Sstevel@tonic-gate 				       m->mf_name, colon);
8370Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8380Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8390Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
8400Sstevel@tonic-gate 					  m->mf_name, colon);
8410Sstevel@tonic-gate 			milter_error(m, e);
8420Sstevel@tonic-gate 			return -1;
8430Sstevel@tonic-gate 		}
8440Sstevel@tonic-gate 		*at = '\0';
8450Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
8460Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
8470Sstevel@tonic-gate 		else
8480Sstevel@tonic-gate 		{
8490Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
8500Sstevel@tonic-gate 			if (tTd(64, 5))
8510Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
8520Sstevel@tonic-gate 					m->mf_name, colon);
8530Sstevel@tonic-gate 			if (parseonly)
8540Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
8550Sstevel@tonic-gate 				       m->mf_name, colon);
8560Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8570Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8580Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
8590Sstevel@tonic-gate 					  m->mf_name, colon);
8600Sstevel@tonic-gate 			milter_error(m, e);
8610Sstevel@tonic-gate 			return -1;
8620Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
863*3544Sjbeck 			struct servent *sp;
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
8660Sstevel@tonic-gate 			if (sp == NULL)
8670Sstevel@tonic-gate 			{
8680Sstevel@tonic-gate 				save_errno = errno;
8690Sstevel@tonic-gate 				if (tTd(64, 5))
8700Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
8710Sstevel@tonic-gate 						m->mf_name, colon);
8720Sstevel@tonic-gate 				errno = save_errno;
8730Sstevel@tonic-gate 				if (parseonly)
8740Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
8750Sstevel@tonic-gate 					       m->mf_name, colon);
8760Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
8770Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
8780Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
8790Sstevel@tonic-gate 						  m->mf_name, colon);
8800Sstevel@tonic-gate 				milter_error(m, e);
8810Sstevel@tonic-gate 				return -1;
8820Sstevel@tonic-gate 			}
8830Sstevel@tonic-gate 			port = sp->s_port;
8840Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
8850Sstevel@tonic-gate 		}
8860Sstevel@tonic-gate 		*at++ = '@';
8870Sstevel@tonic-gate 		if (*at == '[')
8880Sstevel@tonic-gate 		{
8890Sstevel@tonic-gate 			char *end;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 			end = strchr(at, ']');
8920Sstevel@tonic-gate 			if (end != NULL)
8930Sstevel@tonic-gate 			{
8940Sstevel@tonic-gate 				bool found = false;
8950Sstevel@tonic-gate #  if NETINET
8960Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
8970Sstevel@tonic-gate #  endif /* NETINET */
8980Sstevel@tonic-gate #  if NETINET6
8990Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
9000Sstevel@tonic-gate #  endif /* NETINET6 */
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 				*end = '\0';
9030Sstevel@tonic-gate #  if NETINET
9040Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
9050Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
9060Sstevel@tonic-gate 				{
9070Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
9080Sstevel@tonic-gate 					addr.sin.sin_port = port;
9090Sstevel@tonic-gate 					found = true;
9100Sstevel@tonic-gate 				}
9110Sstevel@tonic-gate #  endif /* NETINET */
9120Sstevel@tonic-gate #  if NETINET6
913*3544Sjbeck 				(void) memset(&hid6, '\0', sizeof(hid6));
9140Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
9150Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
9160Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
9170Sstevel@tonic-gate 				{
9180Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
9190Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
9200Sstevel@tonic-gate 					found = true;
9210Sstevel@tonic-gate 				}
9220Sstevel@tonic-gate #  endif /* NETINET6 */
9230Sstevel@tonic-gate 				*end = ']';
9240Sstevel@tonic-gate 				if (!found)
9250Sstevel@tonic-gate 				{
9260Sstevel@tonic-gate 					if (tTd(64, 5))
9270Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9280Sstevel@tonic-gate 							m->mf_name, at);
9290Sstevel@tonic-gate 					if (parseonly)
9300Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
9310Sstevel@tonic-gate 						       m->mf_name, at);
9320Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
9330Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9340Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
9350Sstevel@tonic-gate 							  m->mf_name, at);
9360Sstevel@tonic-gate 					milter_error(m, e);
9370Sstevel@tonic-gate 					return -1;
9380Sstevel@tonic-gate 				}
9390Sstevel@tonic-gate 			}
9400Sstevel@tonic-gate 			else
9410Sstevel@tonic-gate 			{
9420Sstevel@tonic-gate 				if (tTd(64, 5))
9430Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9440Sstevel@tonic-gate 						m->mf_name, at);
9450Sstevel@tonic-gate 				if (parseonly)
9460Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
9470Sstevel@tonic-gate 					       m->mf_name, at);
9480Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9490Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9500Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
9510Sstevel@tonic-gate 						  m->mf_name, at);
9520Sstevel@tonic-gate 				milter_error(m, e);
9530Sstevel@tonic-gate 				return -1;
9540Sstevel@tonic-gate 			}
9550Sstevel@tonic-gate 		}
9560Sstevel@tonic-gate 		else
9570Sstevel@tonic-gate 		{
9580Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
9590Sstevel@tonic-gate 			if (hp == NULL)
9600Sstevel@tonic-gate 			{
9610Sstevel@tonic-gate 				save_errno = errno;
9620Sstevel@tonic-gate 				if (tTd(64, 5))
9630Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
9640Sstevel@tonic-gate 						   m->mf_name, at);
9650Sstevel@tonic-gate 				errno = save_errno;
9660Sstevel@tonic-gate 				if (parseonly)
9670Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
9680Sstevel@tonic-gate 					       m->mf_name, at);
9690Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9700Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9710Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
9720Sstevel@tonic-gate 						  m->mf_name, at);
9730Sstevel@tonic-gate 				milter_error(m, e);
9740Sstevel@tonic-gate 				return -1;
9750Sstevel@tonic-gate 			}
9760Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
9770Sstevel@tonic-gate 			switch (hp->h_addrtype)
9780Sstevel@tonic-gate 			{
9790Sstevel@tonic-gate #  if NETINET
9800Sstevel@tonic-gate 			  case AF_INET:
9810Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
9820Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
9830Sstevel@tonic-gate 				addr.sin.sin_port = port;
984*3544Sjbeck 				addrlen = sizeof(struct sockaddr_in);
9850Sstevel@tonic-gate 				addrno = 1;
9860Sstevel@tonic-gate 				break;
9870Sstevel@tonic-gate #  endif /* NETINET */
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate #  if NETINET6
9900Sstevel@tonic-gate 			  case AF_INET6:
9910Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
9920Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
9930Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
994*3544Sjbeck 				addrlen = sizeof(struct sockaddr_in6);
9950Sstevel@tonic-gate 				addrno = 1;
9960Sstevel@tonic-gate 				break;
9970Sstevel@tonic-gate #  endif /* NETINET6 */
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 			  default:
10000Sstevel@tonic-gate 				if (tTd(64, 5))
10010Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
10020Sstevel@tonic-gate 						   m->mf_name, at,
10030Sstevel@tonic-gate 						   hp->h_addrtype);
10040Sstevel@tonic-gate 				if (parseonly)
10050Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
10060Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
10070Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
10080Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
10090Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
10100Sstevel@tonic-gate 						  m->mf_name, at,
10110Sstevel@tonic-gate 						  hp->h_addrtype);
10120Sstevel@tonic-gate 				milter_error(m, e);
10130Sstevel@tonic-gate #  if NETINET6
10140Sstevel@tonic-gate 				freehostent(hp);
10150Sstevel@tonic-gate #  endif /* NETINET6 */
10160Sstevel@tonic-gate 				return -1;
10170Sstevel@tonic-gate 			}
10180Sstevel@tonic-gate 		}
10190Sstevel@tonic-gate 	}
10200Sstevel@tonic-gate 	else
10210Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
10220Sstevel@tonic-gate 	{
10230Sstevel@tonic-gate 		if (tTd(64, 5))
10240Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
10250Sstevel@tonic-gate 				   m->mf_name);
10260Sstevel@tonic-gate 		if (parseonly)
10270Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
10280Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
10290Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
10300Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
10310Sstevel@tonic-gate 				  m->mf_name);
10320Sstevel@tonic-gate 		milter_error(m, e);
10330Sstevel@tonic-gate 		return -1;
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/* just parsing through? */
10370Sstevel@tonic-gate 	if (parseonly)
10380Sstevel@tonic-gate 	{
10390Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
10400Sstevel@tonic-gate # if NETINET6
10410Sstevel@tonic-gate 		if (hp != NULL)
10420Sstevel@tonic-gate 			freehostent(hp);
10430Sstevel@tonic-gate # endif /* NETINET6 */
10440Sstevel@tonic-gate 		return 0;
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	/* sanity check */
10480Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
10490Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
10500Sstevel@tonic-gate 	{
10510Sstevel@tonic-gate 		/* shouldn't happen */
10520Sstevel@tonic-gate 		if (tTd(64, 1))
10530Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
10540Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
10550Sstevel@tonic-gate 		milter_error(m, e);
10560Sstevel@tonic-gate # if NETINET6
10570Sstevel@tonic-gate 		if (hp != NULL)
10580Sstevel@tonic-gate 			freehostent(hp);
10590Sstevel@tonic-gate # endif /* NETINET6 */
10600Sstevel@tonic-gate 		return -1;
10610Sstevel@tonic-gate 	}
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	/* nope, actually connecting */
10640Sstevel@tonic-gate 	for (;;)
10650Sstevel@tonic-gate 	{
10660Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
10670Sstevel@tonic-gate 		if (sock < 0)
10680Sstevel@tonic-gate 		{
10690Sstevel@tonic-gate 			save_errno = errno;
10700Sstevel@tonic-gate 			if (tTd(64, 5))
10710Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
10720Sstevel@tonic-gate 					   m->mf_name,
10730Sstevel@tonic-gate 					   sm_errstring(save_errno));
10740Sstevel@tonic-gate 			if (MilterLogLevel > 0)
10750Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
10760Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
10770Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
10780Sstevel@tonic-gate 			milter_error(m, e);
10790Sstevel@tonic-gate # if NETINET6
10800Sstevel@tonic-gate 			if (hp != NULL)
10810Sstevel@tonic-gate 				freehostent(hp);
10820Sstevel@tonic-gate # endif /* NETINET6 */
10830Sstevel@tonic-gate 			return -1;
10840Sstevel@tonic-gate 		}
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
10870Sstevel@tonic-gate 		{
10880Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
10890Sstevel@tonic-gate 			int i;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
10920Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
10930Sstevel@tonic-gate 						 milter_connect_timeout, 0);
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
10960Sstevel@tonic-gate 			save_errno = errno;
10970Sstevel@tonic-gate 			if (ev != NULL)
10980Sstevel@tonic-gate 				sm_clrevent(ev);
10990Sstevel@tonic-gate 			errno = save_errno;
11000Sstevel@tonic-gate 			if (i >= 0)
11010Sstevel@tonic-gate 				break;
11020Sstevel@tonic-gate 		}
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate 		/* couldn't connect.... try next address */
11050Sstevel@tonic-gate 		save_errno = errno;
11060Sstevel@tonic-gate 		p = CurHostName;
11070Sstevel@tonic-gate 		CurHostName = at;
11080Sstevel@tonic-gate 		if (tTd(64, 5))
11090Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
11100Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
11110Sstevel@tonic-gate 		if (MilterLogLevel > 13)
11120Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
11130Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
11140Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
11150Sstevel@tonic-gate 		CurHostName = p;
11160Sstevel@tonic-gate 		(void) close(sock);
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate 		/* try next address */
11190Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
11200Sstevel@tonic-gate 		{
11210Sstevel@tonic-gate 			switch (addr.sa.sa_family)
11220Sstevel@tonic-gate 			{
11230Sstevel@tonic-gate # if NETINET
11240Sstevel@tonic-gate 			  case AF_INET:
11250Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
11260Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11270Sstevel@tonic-gate 					INADDRSZ);
11280Sstevel@tonic-gate 				break;
11290Sstevel@tonic-gate # endif /* NETINET */
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate # if NETINET6
11320Sstevel@tonic-gate 			  case AF_INET6:
11330Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
11340Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11350Sstevel@tonic-gate 					IN6ADDRSZ);
11360Sstevel@tonic-gate 				break;
11370Sstevel@tonic-gate # endif /* NETINET6 */
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate 			  default:
11400Sstevel@tonic-gate 				if (tTd(64, 5))
11410Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
11420Sstevel@tonic-gate 						   m->mf_name, at,
11430Sstevel@tonic-gate 						   hp->h_addrtype);
11440Sstevel@tonic-gate 				if (MilterLogLevel > 0)
11450Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
11460Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
11470Sstevel@tonic-gate 						  m->mf_name, at,
11480Sstevel@tonic-gate 						  hp->h_addrtype);
11490Sstevel@tonic-gate 				milter_error(m, e);
11500Sstevel@tonic-gate # if NETINET6
11510Sstevel@tonic-gate 				freehostent(hp);
11520Sstevel@tonic-gate # endif /* NETINET6 */
11530Sstevel@tonic-gate 				return -1;
11540Sstevel@tonic-gate 			}
11550Sstevel@tonic-gate 			continue;
11560Sstevel@tonic-gate 		}
11570Sstevel@tonic-gate 		p = CurHostName;
11580Sstevel@tonic-gate 		CurHostName = at;
11590Sstevel@tonic-gate 		if (tTd(64, 5))
11600Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
11610Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno));
11620Sstevel@tonic-gate 		if (MilterLogLevel > 0)
11630Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
11640Sstevel@tonic-gate 				  "Milter (%s): error connecting to filter: %s",
11650Sstevel@tonic-gate 				  m->mf_name, sm_errstring(save_errno));
11660Sstevel@tonic-gate 		CurHostName = p;
11670Sstevel@tonic-gate 		milter_error(m, e);
11680Sstevel@tonic-gate # if NETINET6
11690Sstevel@tonic-gate 		if (hp != NULL)
11700Sstevel@tonic-gate 			freehostent(hp);
11710Sstevel@tonic-gate # endif /* NETINET6 */
11720Sstevel@tonic-gate 		return -1;
11730Sstevel@tonic-gate 	}
11740Sstevel@tonic-gate 	m->mf_state = SMFS_OPEN;
11750Sstevel@tonic-gate # if NETINET6
11760Sstevel@tonic-gate 	if (hp != NULL)
11770Sstevel@tonic-gate 	{
11780Sstevel@tonic-gate 		freehostent(hp);
11790Sstevel@tonic-gate 		hp = NULL;
11800Sstevel@tonic-gate 	}
11810Sstevel@tonic-gate # endif /* NETINET6 */
1182*3544Sjbeck # if MILTER_NO_NAGLE && !defined(TCP_CORK)
11830Sstevel@tonic-gate 	{
11840Sstevel@tonic-gate 		int nodelay = 1;
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
11870Sstevel@tonic-gate 			   (char *)&nodelay, sizeof(nodelay));
11880Sstevel@tonic-gate 	}
1189*3544Sjbeck # endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
11900Sstevel@tonic-gate 	return sock;
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate static void
11940Sstevel@tonic-gate milter_connect_timeout(ignore)
11950Sstevel@tonic-gate 	int ignore;
11960Sstevel@tonic-gate {
11970Sstevel@tonic-gate 	/*
11980Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11990Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
12000Sstevel@tonic-gate 	**	DOING.
12010Sstevel@tonic-gate 	*/
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 	errno = ETIMEDOUT;
12040Sstevel@tonic-gate 	longjmp(MilterConnectTimeout, 1);
12050Sstevel@tonic-gate }
1206*3544Sjbeck 
12070Sstevel@tonic-gate /*
12080Sstevel@tonic-gate **  MILTER_SETUP -- setup structure for a mail filter
12090Sstevel@tonic-gate **
12100Sstevel@tonic-gate **	Parameters:
12110Sstevel@tonic-gate **		line -- the options line.
12120Sstevel@tonic-gate **
12130Sstevel@tonic-gate **	Returns:
12140Sstevel@tonic-gate **		none
12150Sstevel@tonic-gate */
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate void
12180Sstevel@tonic-gate milter_setup(line)
12190Sstevel@tonic-gate 	char *line;
12200Sstevel@tonic-gate {
12210Sstevel@tonic-gate 	char fcode;
1222*3544Sjbeck 	char *p;
1223*3544Sjbeck 	struct milter *m;
12240Sstevel@tonic-gate 	STAB *s;
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	/* collect the filter name */
12270Sstevel@tonic-gate 	for (p = line;
12280Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
12290Sstevel@tonic-gate 	     p++)
12300Sstevel@tonic-gate 		continue;
12310Sstevel@tonic-gate 	if (*p != '\0')
12320Sstevel@tonic-gate 		*p++ = '\0';
12330Sstevel@tonic-gate 	if (line[0] == '\0')
12340Sstevel@tonic-gate 	{
12350Sstevel@tonic-gate 		syserr("name required for mail filter");
12360Sstevel@tonic-gate 		return;
12370Sstevel@tonic-gate 	}
1238*3544Sjbeck 	m = (struct milter *) xalloc(sizeof(*m));
1239*3544Sjbeck 	memset((char *) m, '\0', sizeof(*m));
12400Sstevel@tonic-gate 	m->mf_name = newstr(line);
12410Sstevel@tonic-gate 	m->mf_state = SMFS_READY;
12420Sstevel@tonic-gate 	m->mf_sock = -1;
12430Sstevel@tonic-gate 	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
12440Sstevel@tonic-gate 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
12450Sstevel@tonic-gate 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
12460Sstevel@tonic-gate 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
1247*3544Sjbeck #if MILTER_CHECK
1248*3544Sjbeck 	m->mf_mta_prot_version = SMFI_PROT_VERSION;
1249*3544Sjbeck 	m->mf_mta_prot_flags = SMFI_CURR_PROT;
1250*3544Sjbeck 	m->mf_mta_actions = SMFI_CURR_ACTS;
1251*3544Sjbeck #endif /* MILTER_CHECK */
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
12540Sstevel@tonic-gate 	while (*p != '\0')
12550Sstevel@tonic-gate 	{
12560Sstevel@tonic-gate 		char *delimptr;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 		while (*p != '\0' &&
12590Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
12600Sstevel@tonic-gate 			p++;
12610Sstevel@tonic-gate 
12620Sstevel@tonic-gate 		/* p now points to field code */
12630Sstevel@tonic-gate 		fcode = *p;
12640Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
12650Sstevel@tonic-gate 			p++;
12660Sstevel@tonic-gate 		if (*p++ != '=')
12670Sstevel@tonic-gate 		{
12680Sstevel@tonic-gate 			syserr("X%s: `=' expected", m->mf_name);
12690Sstevel@tonic-gate 			return;
12700Sstevel@tonic-gate 		}
12710Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
12720Sstevel@tonic-gate 			p++;
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 		/* p now points to the field body */
12750Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 		/* install the field into the filter struct */
12780Sstevel@tonic-gate 		switch (fcode)
12790Sstevel@tonic-gate 		{
12800Sstevel@tonic-gate 		  case 'S':		/* socket */
12810Sstevel@tonic-gate 			if (p == NULL)
12820Sstevel@tonic-gate 				m->mf_conn = NULL;
12830Sstevel@tonic-gate 			else
12840Sstevel@tonic-gate 				m->mf_conn = newstr(p);
12850Sstevel@tonic-gate 			break;
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 		  case 'F':		/* Milter flags configured on MTA */
12880Sstevel@tonic-gate 			for (; *p != '\0'; p++)
12890Sstevel@tonic-gate 			{
12900Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
12910Sstevel@tonic-gate 					setbitn(bitidx(*p), m->mf_flags);
12920Sstevel@tonic-gate 			}
12930Sstevel@tonic-gate 			break;
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 		  case 'T':		/* timeouts */
12960Sstevel@tonic-gate 			milter_parse_timeouts(p, m);
12970Sstevel@tonic-gate 			break;
12980Sstevel@tonic-gate 
1299*3544Sjbeck #if MILTER_CHECK
1300*3544Sjbeck 		  case 'a':
1301*3544Sjbeck 			m->mf_mta_actions = strtoul(p, NULL, 0);
1302*3544Sjbeck 			break;
1303*3544Sjbeck 		  case 'f':
1304*3544Sjbeck 			m->mf_mta_prot_flags = strtoul(p, NULL, 0);
1305*3544Sjbeck 			break;
1306*3544Sjbeck 		  case 'v':
1307*3544Sjbeck 			m->mf_mta_prot_version = strtoul(p, NULL, 0);
1308*3544Sjbeck 			break;
1309*3544Sjbeck #endif /* MILTER_CHECK */
1310*3544Sjbeck 
13110Sstevel@tonic-gate 		  default:
13120Sstevel@tonic-gate 			syserr("X%s: unknown filter equate %c=",
13130Sstevel@tonic-gate 			       m->mf_name, fcode);
13140Sstevel@tonic-gate 			break;
13150Sstevel@tonic-gate 		}
13160Sstevel@tonic-gate 		p = delimptr;
13170Sstevel@tonic-gate 	}
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 	/* early check for errors */
13200Sstevel@tonic-gate 	(void) milter_open(m, true, CurEnv);
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	/* enter the filter into the symbol table */
13230Sstevel@tonic-gate 	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
13240Sstevel@tonic-gate 	if (s->s_milter != NULL)
13250Sstevel@tonic-gate 		syserr("X%s: duplicate filter definition", m->mf_name);
13260Sstevel@tonic-gate 	else
13270Sstevel@tonic-gate 		s->s_milter = m;
13280Sstevel@tonic-gate }
1329*3544Sjbeck 
13300Sstevel@tonic-gate /*
13310Sstevel@tonic-gate **  MILTER_CONFIG -- parse option list into an array and check config
13320Sstevel@tonic-gate **
13330Sstevel@tonic-gate **	Called when reading configuration file.
13340Sstevel@tonic-gate **
13350Sstevel@tonic-gate **	Parameters:
13360Sstevel@tonic-gate **		spec -- the filter list.
13370Sstevel@tonic-gate **		list -- the array to fill in.
13380Sstevel@tonic-gate **		max -- the maximum number of entries in list.
13390Sstevel@tonic-gate **
13400Sstevel@tonic-gate **	Returns:
13410Sstevel@tonic-gate **		none
13420Sstevel@tonic-gate */
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate void
13450Sstevel@tonic-gate milter_config(spec, list, max)
13460Sstevel@tonic-gate 	char *spec;
13470Sstevel@tonic-gate 	struct milter **list;
13480Sstevel@tonic-gate 	int max;
13490Sstevel@tonic-gate {
13500Sstevel@tonic-gate 	int numitems = 0;
1351*3544Sjbeck 	char *p;
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 	/* leave one for the NULL signifying the end of the list */
13540Sstevel@tonic-gate 	max--;
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 	for (p = spec; p != NULL; )
13570Sstevel@tonic-gate 	{
13580Sstevel@tonic-gate 		STAB *s;
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
13610Sstevel@tonic-gate 			p++;
13620Sstevel@tonic-gate 		if (*p == '\0')
13630Sstevel@tonic-gate 			break;
13640Sstevel@tonic-gate 		spec = p;
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate 		if (numitems >= max)
13670Sstevel@tonic-gate 		{
13680Sstevel@tonic-gate 			syserr("Too many filters defined, %d max", max);
13690Sstevel@tonic-gate 			if (max > 0)
13700Sstevel@tonic-gate 				list[0] = NULL;
13710Sstevel@tonic-gate 			return;
13720Sstevel@tonic-gate 		}
13730Sstevel@tonic-gate 		p = strpbrk(p, ";,");
13740Sstevel@tonic-gate 		if (p != NULL)
13750Sstevel@tonic-gate 			*p++ = '\0';
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 		s = stab(spec, ST_MILTER, ST_FIND);
13780Sstevel@tonic-gate 		if (s == NULL)
13790Sstevel@tonic-gate 		{
13800Sstevel@tonic-gate 			syserr("InputFilter %s not defined", spec);
13810Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
13820Sstevel@tonic-gate 			return;
13830Sstevel@tonic-gate 		}
13840Sstevel@tonic-gate 		list[numitems++] = s->s_milter;
13850Sstevel@tonic-gate 	}
13860Sstevel@tonic-gate 	list[numitems] = NULL;
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 	/* if not set, set to LogLevel */
13890Sstevel@tonic-gate 	if (MilterLogLevel == -1)
13900Sstevel@tonic-gate 		MilterLogLevel = LogLevel;
13910Sstevel@tonic-gate }
1392*3544Sjbeck 
13930Sstevel@tonic-gate /*
13940Sstevel@tonic-gate **  MILTER_PARSE_TIMEOUTS -- parse timeout list
13950Sstevel@tonic-gate **
13960Sstevel@tonic-gate **	Called when reading configuration file.
13970Sstevel@tonic-gate **
13980Sstevel@tonic-gate **	Parameters:
13990Sstevel@tonic-gate **		spec -- the timeout list.
14000Sstevel@tonic-gate **		m -- milter to set.
14010Sstevel@tonic-gate **
14020Sstevel@tonic-gate **	Returns:
14030Sstevel@tonic-gate **		none
14040Sstevel@tonic-gate */
14050Sstevel@tonic-gate 
14060Sstevel@tonic-gate static void
14070Sstevel@tonic-gate milter_parse_timeouts(spec, m)
14080Sstevel@tonic-gate 	char *spec;
14090Sstevel@tonic-gate 	struct milter *m;
14100Sstevel@tonic-gate {
14110Sstevel@tonic-gate 	char fcode;
14120Sstevel@tonic-gate 	int tcode;
1413*3544Sjbeck 	char *p;
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 	p = spec;
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
14180Sstevel@tonic-gate 	while (*p != '\0')
14190Sstevel@tonic-gate 	{
14200Sstevel@tonic-gate 		char *delimptr;
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate 		while (*p != '\0' &&
14230Sstevel@tonic-gate 		       (*p == ';' || (isascii(*p) && isspace(*p))))
14240Sstevel@tonic-gate 			p++;
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 		/* p now points to field code */
14270Sstevel@tonic-gate 		fcode = *p;
14280Sstevel@tonic-gate 		while (*p != '\0' && *p != ':')
14290Sstevel@tonic-gate 			p++;
14300Sstevel@tonic-gate 		if (*p++ != ':')
14310Sstevel@tonic-gate 		{
14320Sstevel@tonic-gate 			syserr("X%s, T=: `:' expected", m->mf_name);
14330Sstevel@tonic-gate 			return;
14340Sstevel@tonic-gate 		}
14350Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
14360Sstevel@tonic-gate 			p++;
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 		/* p now points to the field body */
14390Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ';');
14400Sstevel@tonic-gate 		tcode = -1;
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 		/* install the field into the filter struct */
14430Sstevel@tonic-gate 		switch (fcode)
14440Sstevel@tonic-gate 		{
14450Sstevel@tonic-gate 		  case 'C':
14460Sstevel@tonic-gate 			tcode = SMFTO_CONNECT;
14470Sstevel@tonic-gate 			break;
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 		  case 'S':
14500Sstevel@tonic-gate 			tcode = SMFTO_WRITE;
14510Sstevel@tonic-gate 			break;
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 		  case 'R':
14540Sstevel@tonic-gate 			tcode = SMFTO_READ;
14550Sstevel@tonic-gate 			break;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 		  case 'E':
14580Sstevel@tonic-gate 			tcode = SMFTO_EOM;
14590Sstevel@tonic-gate 			break;
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 		  default:
14620Sstevel@tonic-gate 			if (tTd(64, 5))
14630Sstevel@tonic-gate 				sm_dprintf("X%s: %c unknown\n",
14640Sstevel@tonic-gate 					   m->mf_name, fcode);
14650Sstevel@tonic-gate 			syserr("X%s: unknown filter timeout %c",
14660Sstevel@tonic-gate 			       m->mf_name, fcode);
14670Sstevel@tonic-gate 			break;
14680Sstevel@tonic-gate 		}
14690Sstevel@tonic-gate 		if (tcode >= 0)
14700Sstevel@tonic-gate 		{
14710Sstevel@tonic-gate 			m->mf_timeout[tcode] = convtime(p, 's');
14720Sstevel@tonic-gate 			if (tTd(64, 5))
14730Sstevel@tonic-gate 				sm_dprintf("X%s: %c=%ld\n",
14740Sstevel@tonic-gate 					   m->mf_name, fcode,
14750Sstevel@tonic-gate 					   (u_long) m->mf_timeout[tcode]);
14760Sstevel@tonic-gate 		}
14770Sstevel@tonic-gate 		p = delimptr;
14780Sstevel@tonic-gate 	}
14790Sstevel@tonic-gate }
1480*3544Sjbeck 
1481*3544Sjbeck /*
1482*3544Sjbeck **  MILTER_SET_MACROS -- set milter macros
1483*3544Sjbeck **
1484*3544Sjbeck **	Parameters:
1485*3544Sjbeck **		name -- name of milter.
1486*3544Sjbeck **		macros -- where to store macros.
1487*3544Sjbeck **		val -- the value of the option.
1488*3544Sjbeck **		nummac -- current number of macros
1489*3544Sjbeck **
1490*3544Sjbeck **	Returns:
1491*3544Sjbeck **		new number of macros
1492*3544Sjbeck */
1493*3544Sjbeck 
1494*3544Sjbeck static int
1495*3544Sjbeck milter_set_macros(name, macros, val, nummac)
1496*3544Sjbeck 	char *name;
1497*3544Sjbeck 	char **macros;
1498*3544Sjbeck 	char *val;
1499*3544Sjbeck 	int nummac;
1500*3544Sjbeck {
1501*3544Sjbeck 	char *p;
1502*3544Sjbeck 
1503*3544Sjbeck 	p = newstr(val);
1504*3544Sjbeck 	while (*p != '\0')
1505*3544Sjbeck 	{
1506*3544Sjbeck 		char *macro;
1507*3544Sjbeck 
1508*3544Sjbeck 		/* Skip leading commas, spaces */
1509*3544Sjbeck 		while (*p != '\0' &&
1510*3544Sjbeck 		       (*p == ',' || (isascii(*p) && isspace(*p))))
1511*3544Sjbeck 			p++;
1512*3544Sjbeck 
1513*3544Sjbeck 		if (*p == '\0')
1514*3544Sjbeck 			break;
1515*3544Sjbeck 
1516*3544Sjbeck 		/* Find end of macro */
1517*3544Sjbeck 		macro = p;
1518*3544Sjbeck 		while (*p != '\0' && *p != ',' &&
1519*3544Sjbeck 		       isascii(*p) && !isspace(*p))
1520*3544Sjbeck 			p++;
1521*3544Sjbeck 		if (*p != '\0')
1522*3544Sjbeck 			*p++ = '\0';
1523*3544Sjbeck 
1524*3544Sjbeck 		if (nummac >= MAXFILTERMACROS)
1525*3544Sjbeck 		{
1526*3544Sjbeck 			syserr("milter_set_option: too many macros in Milter.%s (max %d)",
1527*3544Sjbeck 			       name, MAXFILTERMACROS);
1528*3544Sjbeck 			macros[nummac] = NULL;
1529*3544Sjbeck 			return -1;
1530*3544Sjbeck 		}
1531*3544Sjbeck 		macros[nummac++] = macro;
1532*3544Sjbeck 	}
1533*3544Sjbeck 	macros[nummac] = NULL;
1534*3544Sjbeck 	return nummac;
1535*3544Sjbeck }
1536*3544Sjbeck 
15370Sstevel@tonic-gate /*
15380Sstevel@tonic-gate **  MILTER_SET_OPTION -- set an individual milter option
15390Sstevel@tonic-gate **
15400Sstevel@tonic-gate **	Parameters:
15410Sstevel@tonic-gate **		name -- the name of the option.
15420Sstevel@tonic-gate **		val -- the value of the option.
15430Sstevel@tonic-gate **		sticky -- if set, don't let other setoptions override
15440Sstevel@tonic-gate **			this value.
15450Sstevel@tonic-gate **
15460Sstevel@tonic-gate **	Returns:
15470Sstevel@tonic-gate **		none.
15480Sstevel@tonic-gate */
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate /* set if Milter sub-option is stuck */
15510Sstevel@tonic-gate static BITMAP256	StickyMilterOpt;
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate static struct milteropt
15540Sstevel@tonic-gate {
15550Sstevel@tonic-gate 	char		*mo_name;	/* long name of milter option */
15560Sstevel@tonic-gate 	unsigned char	mo_code;	/* code for option */
15570Sstevel@tonic-gate } MilterOptTab[] =
15580Sstevel@tonic-gate {
1559*3544Sjbeck # define MO_MACROS_CONNECT		SMFIM_CONNECT
15600Sstevel@tonic-gate 	{ "macros.connect",		MO_MACROS_CONNECT		},
1561*3544Sjbeck # define MO_MACROS_HELO			SMFIM_HELO
15620Sstevel@tonic-gate 	{ "macros.helo",		MO_MACROS_HELO			},
1563*3544Sjbeck # define MO_MACROS_ENVFROM		SMFIM_ENVFROM
15640Sstevel@tonic-gate 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
1565*3544Sjbeck # define MO_MACROS_ENVRCPT		SMFIM_ENVRCPT
15660Sstevel@tonic-gate 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
1567*3544Sjbeck # define MO_MACROS_DATA			SMFIM_DATA
15680Sstevel@tonic-gate 	{ "macros.data",		MO_MACROS_DATA			},
1569*3544Sjbeck # define MO_MACROS_EOM			SMFIM_EOM
15700Sstevel@tonic-gate 	{ "macros.eom",			MO_MACROS_EOM			},
1571*3544Sjbeck # define MO_MACROS_EOH			SMFIM_EOH
1572*3544Sjbeck 	{ "macros.eoh",			MO_MACROS_EOH			},
1573*3544Sjbeck 
15740Sstevel@tonic-gate # define MO_LOGLEVEL			0x07
15750Sstevel@tonic-gate 	{ "loglevel",			MO_LOGLEVEL			},
15760Sstevel@tonic-gate # if _FFR_MAXDATASIZE
1577*3544Sjbeck #  define MO_MAXDATASIZE		0x08
15780Sstevel@tonic-gate 	{ "maxdatasize",		MO_MAXDATASIZE			},
15790Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */
1580*3544Sjbeck 	{ NULL,				(unsigned char)-1		},
15810Sstevel@tonic-gate };
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate void
15840Sstevel@tonic-gate milter_set_option(name, val, sticky)
15850Sstevel@tonic-gate 	char *name;
15860Sstevel@tonic-gate 	char *val;
15870Sstevel@tonic-gate 	bool sticky;
15880Sstevel@tonic-gate {
1589*3544Sjbeck 	int nummac, r;
1590*3544Sjbeck 	struct milteropt *mo;
15910Sstevel@tonic-gate 	char **macros = NULL;
15920Sstevel@tonic-gate 
1593*3544Sjbeck 	nummac = 0;
15940Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64, 5))
15950Sstevel@tonic-gate 		sm_dprintf("milter_set_option(%s = %s)", name, val);
15960Sstevel@tonic-gate 
15970Sstevel@tonic-gate 	if (name == NULL)
15980Sstevel@tonic-gate 	{
15990Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option, must specify suboption");
16000Sstevel@tonic-gate 		return;
16010Sstevel@tonic-gate 	}
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
16040Sstevel@tonic-gate 	{
16050Sstevel@tonic-gate 		if (sm_strcasecmp(mo->mo_name, name) == 0)
16060Sstevel@tonic-gate 			break;
16070Sstevel@tonic-gate 	}
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	if (mo->mo_name == NULL)
16100Sstevel@tonic-gate 	{
16110Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16120Sstevel@tonic-gate 		return;
16130Sstevel@tonic-gate 	}
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	/*
16160Sstevel@tonic-gate 	**  See if this option is preset for us.
16170Sstevel@tonic-gate 	*/
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
16200Sstevel@tonic-gate 	{
16210Sstevel@tonic-gate 		if (tTd(37, 2) || tTd(64,5))
16220Sstevel@tonic-gate 			sm_dprintf(" (ignored)\n");
16230Sstevel@tonic-gate 		return;
16240Sstevel@tonic-gate 	}
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64,5))
16270Sstevel@tonic-gate 		sm_dprintf("\n");
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 	switch (mo->mo_code)
16300Sstevel@tonic-gate 	{
16310Sstevel@tonic-gate 	  case MO_LOGLEVEL:
16320Sstevel@tonic-gate 		MilterLogLevel = atoi(val);
16330Sstevel@tonic-gate 		break;
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate #if _FFR_MAXDATASIZE
16360Sstevel@tonic-gate 	  case MO_MAXDATASIZE:
16370Sstevel@tonic-gate 		MilterMaxDataSize = (size_t)atol(val);
16380Sstevel@tonic-gate 		break;
16390Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 	  case MO_MACROS_CONNECT:
16420Sstevel@tonic-gate 		if (macros == NULL)
16430Sstevel@tonic-gate 			macros = MilterConnectMacros;
16440Sstevel@tonic-gate 		/* FALLTHROUGH */
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 	  case MO_MACROS_HELO:
16470Sstevel@tonic-gate 		if (macros == NULL)
16480Sstevel@tonic-gate 			macros = MilterHeloMacros;
16490Sstevel@tonic-gate 		/* FALLTHROUGH */
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 	  case MO_MACROS_ENVFROM:
16520Sstevel@tonic-gate 		if (macros == NULL)
16530Sstevel@tonic-gate 			macros = MilterEnvFromMacros;
16540Sstevel@tonic-gate 		/* FALLTHROUGH */
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 	  case MO_MACROS_ENVRCPT:
16570Sstevel@tonic-gate 		if (macros == NULL)
16580Sstevel@tonic-gate 			macros = MilterEnvRcptMacros;
16590Sstevel@tonic-gate 		/* FALLTHROUGH */
16600Sstevel@tonic-gate 
1661*3544Sjbeck 	  case MO_MACROS_EOH:
1662*3544Sjbeck 		if (macros == NULL)
1663*3544Sjbeck 			macros = MilterEOHMacros;
1664*3544Sjbeck 		/* FALLTHROUGH */
1665*3544Sjbeck 
16660Sstevel@tonic-gate 	  case MO_MACROS_EOM:
16670Sstevel@tonic-gate 		if (macros == NULL)
16680Sstevel@tonic-gate 			macros = MilterEOMMacros;
16690Sstevel@tonic-gate 		/* FALLTHROUGH */
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate 	  case MO_MACROS_DATA:
16720Sstevel@tonic-gate 		if (macros == NULL)
16730Sstevel@tonic-gate 			macros = MilterDataMacros;
16740Sstevel@tonic-gate 
1675*3544Sjbeck 		r = milter_set_macros(name, macros, val, nummac);
1676*3544Sjbeck 		if (r >= 0)
1677*3544Sjbeck 			nummac = r;
16780Sstevel@tonic-gate 		break;
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	  default:
16810Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16820Sstevel@tonic-gate 		break;
16830Sstevel@tonic-gate 	}
16840Sstevel@tonic-gate 	if (sticky)
16850Sstevel@tonic-gate 		setbitn(mo->mo_code, StickyMilterOpt);
16860Sstevel@tonic-gate }
1687*3544Sjbeck 
16880Sstevel@tonic-gate /*
16890Sstevel@tonic-gate **  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
16900Sstevel@tonic-gate **
16910Sstevel@tonic-gate **	Parameters:
16920Sstevel@tonic-gate **		e -- current envelope.
16930Sstevel@tonic-gate **
16940Sstevel@tonic-gate **	Returns:
16950Sstevel@tonic-gate **		0 if succesful, -1 otherwise
16960Sstevel@tonic-gate */
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate static int
16990Sstevel@tonic-gate milter_reopen_df(e)
17000Sstevel@tonic-gate 	ENVELOPE *e;
17010Sstevel@tonic-gate {
17020Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17030Sstevel@tonic-gate 
1704*3544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	/*
17070Sstevel@tonic-gate 	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
17080Sstevel@tonic-gate 	**  close and reopen writable (later close and reopen
17090Sstevel@tonic-gate 	**  read only again).
17100Sstevel@tonic-gate 	**
17110Sstevel@tonic-gate 	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
17120Sstevel@tonic-gate 	**  buffered file I/O descriptor, still open for writing so there
17130Sstevel@tonic-gate 	**  isn't any work to do here (except checking for consistency).
17140Sstevel@tonic-gate 	*/
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY)
17170Sstevel@tonic-gate 	{
17180Sstevel@tonic-gate 		/* close read-only data file */
17190Sstevel@tonic-gate 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
17200Sstevel@tonic-gate 		{
17210Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
17220Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
17230Sstevel@tonic-gate 		}
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 		/* open writable */
17260Sstevel@tonic-gate 		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17270Sstevel@tonic-gate 					   SM_IO_RDWR_B, NULL)) == NULL)
17280Sstevel@tonic-gate 		{
17290Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
17300Sstevel@tonic-gate 			return -1;
17310Sstevel@tonic-gate 		}
17320Sstevel@tonic-gate 	}
17330Sstevel@tonic-gate 	else if (e->e_dfp == NULL)
17340Sstevel@tonic-gate 	{
17350Sstevel@tonic-gate 		/* shouldn't happen */
17360Sstevel@tonic-gate 		errno = ENOENT;
17370Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
17380Sstevel@tonic-gate 		return -1;
17390Sstevel@tonic-gate 	}
17400Sstevel@tonic-gate 	return 0;
17410Sstevel@tonic-gate }
1742*3544Sjbeck 
17430Sstevel@tonic-gate /*
17440Sstevel@tonic-gate **  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
17450Sstevel@tonic-gate **
17460Sstevel@tonic-gate **	Parameters:
17470Sstevel@tonic-gate **		e -- current envelope.
17480Sstevel@tonic-gate **
17490Sstevel@tonic-gate **	Returns:
17500Sstevel@tonic-gate **		0 if succesful, -1 otherwise
17510Sstevel@tonic-gate */
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate static int
17540Sstevel@tonic-gate milter_reset_df(e)
17550Sstevel@tonic-gate 	ENVELOPE *e;
17560Sstevel@tonic-gate {
17570Sstevel@tonic-gate 	int afd;
17580Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17590Sstevel@tonic-gate 
1760*3544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
17630Sstevel@tonic-gate 	    sm_io_error(e->e_dfp))
17640Sstevel@tonic-gate 	{
17650Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
17660Sstevel@tonic-gate 		return -1;
17670Sstevel@tonic-gate 	}
17680Sstevel@tonic-gate 	else if (SuperSafe != SAFE_REALLY)
17690Sstevel@tonic-gate 	{
17700Sstevel@tonic-gate 		/* skip next few clauses */
17710Sstevel@tonic-gate 		/* EMPTY */
17720Sstevel@tonic-gate 	}
17730Sstevel@tonic-gate 	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
17740Sstevel@tonic-gate 		 && fsync(afd) < 0)
17750Sstevel@tonic-gate 	{
17760Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
17770Sstevel@tonic-gate 		return -1;
17780Sstevel@tonic-gate 	}
17790Sstevel@tonic-gate 	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
17800Sstevel@tonic-gate 	{
17810Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
17820Sstevel@tonic-gate 		return -1;
17830Sstevel@tonic-gate 	}
17840Sstevel@tonic-gate 	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17850Sstevel@tonic-gate 					SM_IO_RDONLY_B, NULL)) == NULL)
17860Sstevel@tonic-gate 	{
17870Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
17880Sstevel@tonic-gate 		return -1;
17890Sstevel@tonic-gate 	}
17900Sstevel@tonic-gate 	else
17910Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
17920Sstevel@tonic-gate 	return 0;
17930Sstevel@tonic-gate }
1794*3544Sjbeck 
17950Sstevel@tonic-gate /*
17960Sstevel@tonic-gate **  MILTER_CAN_DELRCPTS -- can any milter filters delete recipients?
17970Sstevel@tonic-gate **
17980Sstevel@tonic-gate **	Parameters:
17990Sstevel@tonic-gate **		none
18000Sstevel@tonic-gate **
18010Sstevel@tonic-gate **	Returns:
18020Sstevel@tonic-gate **		true if any filter deletes recipients, false otherwise
18030Sstevel@tonic-gate */
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate bool
18060Sstevel@tonic-gate milter_can_delrcpts()
18070Sstevel@tonic-gate {
18080Sstevel@tonic-gate 	bool can = false;
18090Sstevel@tonic-gate 	int i;
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 	if (tTd(64, 10))
18120Sstevel@tonic-gate 		sm_dprintf("milter_can_delrcpts:");
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
18150Sstevel@tonic-gate 	{
18160Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 		if (bitset(SMFIF_DELRCPT, m->mf_fflags))
18190Sstevel@tonic-gate 		{
18200Sstevel@tonic-gate 			can = true;
18210Sstevel@tonic-gate 			break;
18220Sstevel@tonic-gate 		}
18230Sstevel@tonic-gate 	}
18240Sstevel@tonic-gate 	if (tTd(64, 10))
18250Sstevel@tonic-gate 		sm_dprintf("%s\n", can ? "true" : "false");
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 	return can;
18280Sstevel@tonic-gate }
1829*3544Sjbeck 
18300Sstevel@tonic-gate /*
18310Sstevel@tonic-gate **  MILTER_QUIT_FILTER -- close down a single filter
18320Sstevel@tonic-gate **
18330Sstevel@tonic-gate **	Parameters:
18340Sstevel@tonic-gate **		m -- milter structure of filter to close down.
18350Sstevel@tonic-gate **		e -- current envelope.
18360Sstevel@tonic-gate **
18370Sstevel@tonic-gate **	Returns:
18380Sstevel@tonic-gate **		none
18390Sstevel@tonic-gate */
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate static void
18420Sstevel@tonic-gate milter_quit_filter(m, e)
18430Sstevel@tonic-gate 	struct milter *m;
18440Sstevel@tonic-gate 	ENVELOPE *e;
18450Sstevel@tonic-gate {
18460Sstevel@tonic-gate 	if (tTd(64, 10))
18470Sstevel@tonic-gate 		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
18480Sstevel@tonic-gate 	if (MilterLogLevel > 18)
18490Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
18500Sstevel@tonic-gate 			  m->mf_name);
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 	/* Never replace error state */
18530Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
18540Sstevel@tonic-gate 		return;
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18570Sstevel@tonic-gate 	    m->mf_state == SMFS_CLOSED ||
18580Sstevel@tonic-gate 	    m->mf_state == SMFS_READY)
18590Sstevel@tonic-gate 	{
18600Sstevel@tonic-gate 		m->mf_sock = -1;
18610Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18620Sstevel@tonic-gate 		return;
18630Sstevel@tonic-gate 	}
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
1866*3544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "quit_filter");
18670Sstevel@tonic-gate 	if (m->mf_sock >= 0)
18680Sstevel@tonic-gate 	{
18690Sstevel@tonic-gate 		(void) close(m->mf_sock);
18700Sstevel@tonic-gate 		m->mf_sock = -1;
18710Sstevel@tonic-gate 	}
18720Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18730Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18740Sstevel@tonic-gate }
1875*3544Sjbeck 
18760Sstevel@tonic-gate /*
18770Sstevel@tonic-gate **  MILTER_ABORT_FILTER -- tell filter to abort current message
18780Sstevel@tonic-gate **
18790Sstevel@tonic-gate **	Parameters:
18800Sstevel@tonic-gate **		m -- milter structure of filter to abort.
18810Sstevel@tonic-gate **		e -- current envelope.
18820Sstevel@tonic-gate **
18830Sstevel@tonic-gate **	Returns:
18840Sstevel@tonic-gate **		none
18850Sstevel@tonic-gate */
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate static void
18880Sstevel@tonic-gate milter_abort_filter(m, e)
18890Sstevel@tonic-gate 	struct milter *m;
18900Sstevel@tonic-gate 	ENVELOPE *e;
18910Sstevel@tonic-gate {
18920Sstevel@tonic-gate 	if (tTd(64, 10))
18930Sstevel@tonic-gate 		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
18940Sstevel@tonic-gate 	if (MilterLogLevel > 10)
18950Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
18960Sstevel@tonic-gate 			  m->mf_name);
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18990Sstevel@tonic-gate 	    m->mf_state != SMFS_INMSG)
19000Sstevel@tonic-gate 		return;
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
1903*3544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
19040Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
19050Sstevel@tonic-gate 		m->mf_state = SMFS_DONE;
19060Sstevel@tonic-gate }
1907*3544Sjbeck 
19080Sstevel@tonic-gate /*
19090Sstevel@tonic-gate **  MILTER_SEND_MACROS -- provide macros to the filters
19100Sstevel@tonic-gate **
19110Sstevel@tonic-gate **	Parameters:
19120Sstevel@tonic-gate **		m -- milter to send macros to.
19130Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
19140Sstevel@tonic-gate **		cmd -- which command the macros are associated with.
19150Sstevel@tonic-gate **		e -- current envelope (for macro access).
19160Sstevel@tonic-gate **
19170Sstevel@tonic-gate **	Returns:
19180Sstevel@tonic-gate **		none
19190Sstevel@tonic-gate */
19200Sstevel@tonic-gate 
19210Sstevel@tonic-gate static void
19220Sstevel@tonic-gate milter_send_macros(m, macros, cmd, e)
19230Sstevel@tonic-gate 	struct milter *m;
19240Sstevel@tonic-gate 	char **macros;
1925*3544Sjbeck 	int cmd;
19260Sstevel@tonic-gate 	ENVELOPE *e;
19270Sstevel@tonic-gate {
19280Sstevel@tonic-gate 	int i;
19290Sstevel@tonic-gate 	int mid;
1930*3544Sjbeck 	char command = (char) cmd;
19310Sstevel@tonic-gate 	char *v;
19320Sstevel@tonic-gate 	char *buf, *bp;
19330Sstevel@tonic-gate 	char exp[MAXLINE];
19340Sstevel@tonic-gate 	ssize_t s;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	/* sanity check */
19370Sstevel@tonic-gate 	if (macros == NULL || macros[0] == NULL)
19380Sstevel@tonic-gate 		return;
19390Sstevel@tonic-gate 
19400Sstevel@tonic-gate 	/* put together data */
19410Sstevel@tonic-gate 	s = 1;			/* for the command character */
19420Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19430Sstevel@tonic-gate 	{
19440Sstevel@tonic-gate 		mid = macid(macros[i]);
19450Sstevel@tonic-gate 		if (mid == 0)
19460Sstevel@tonic-gate 			continue;
19470Sstevel@tonic-gate 		v = macvalue(mid, e);
19480Sstevel@tonic-gate 		if (v == NULL)
19490Sstevel@tonic-gate 			continue;
19500Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19510Sstevel@tonic-gate 		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
19520Sstevel@tonic-gate 	}
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate 	if (s < 0)
19550Sstevel@tonic-gate 		return;
19560Sstevel@tonic-gate 
19570Sstevel@tonic-gate 	buf = (char *) xalloc(s);
19580Sstevel@tonic-gate 	bp = buf;
1959*3544Sjbeck 	*bp++ = command;
19600Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19610Sstevel@tonic-gate 	{
19620Sstevel@tonic-gate 		mid = macid(macros[i]);
19630Sstevel@tonic-gate 		if (mid == 0)
19640Sstevel@tonic-gate 			continue;
19650Sstevel@tonic-gate 		v = macvalue(mid, e);
19660Sstevel@tonic-gate 		if (v == NULL)
19670Sstevel@tonic-gate 			continue;
19680Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19690Sstevel@tonic-gate 
19700Sstevel@tonic-gate 		if (tTd(64, 10))
19710Sstevel@tonic-gate 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
1972*3544Sjbeck 				m->mf_name, command, macros[i], exp);
19730Sstevel@tonic-gate 
19740Sstevel@tonic-gate 		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
19750Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19760Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exp, s - (bp - buf));
19770Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19780Sstevel@tonic-gate 	}
19790Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_MACRO, buf, s,
1980*3544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "send_macros");
19810Sstevel@tonic-gate 	sm_free(buf);
19820Sstevel@tonic-gate }
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate /*
19850Sstevel@tonic-gate **  MILTER_SEND_COMMAND -- send a command and return the response for a filter
19860Sstevel@tonic-gate **
19870Sstevel@tonic-gate **	Parameters:
19880Sstevel@tonic-gate **		m -- current milter filter
1989*3544Sjbeck **		cmd -- command to send.
19900Sstevel@tonic-gate **		data -- optional command data.
19910Sstevel@tonic-gate **		sz -- length of buf.
19920Sstevel@tonic-gate **		e -- current envelope (for e->e_id).
19930Sstevel@tonic-gate **		state -- return state word.
19940Sstevel@tonic-gate **
19950Sstevel@tonic-gate **	Returns:
19960Sstevel@tonic-gate **		response string (may be NULL)
19970Sstevel@tonic-gate */
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate static char *
2000*3544Sjbeck milter_send_command(m, cmd, data, sz, e, state, where)
20010Sstevel@tonic-gate 	struct milter *m;
2002*3544Sjbeck 	int cmd;
20030Sstevel@tonic-gate 	void *data;
20040Sstevel@tonic-gate 	ssize_t sz;
20050Sstevel@tonic-gate 	ENVELOPE *e;
20060Sstevel@tonic-gate 	char *state;
2007*3544Sjbeck 	const char *where;
20080Sstevel@tonic-gate {
20090Sstevel@tonic-gate 	char rcmd;
20100Sstevel@tonic-gate 	ssize_t rlen;
20110Sstevel@tonic-gate 	unsigned long skipflag;
20120Sstevel@tonic-gate 	unsigned long norespflag = 0;
2013*3544Sjbeck 	char command = (char) cmd;
20140Sstevel@tonic-gate 	char *action;
20150Sstevel@tonic-gate 	char *defresponse;
20160Sstevel@tonic-gate 	char *response;
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 	if (tTd(64, 10))
20190Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
20200Sstevel@tonic-gate 			m->mf_name, (char) command, (long) sz);
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate 	/* find skip flag and default failure */
20230Sstevel@tonic-gate 	switch (command)
20240Sstevel@tonic-gate 	{
20250Sstevel@tonic-gate 	  case SMFIC_CONNECT:
20260Sstevel@tonic-gate 		skipflag = SMFIP_NOCONNECT;
2027*3544Sjbeck 		norespflag = SMFIP_NR_CONN;
20280Sstevel@tonic-gate 		action = "connect";
20290Sstevel@tonic-gate 		defresponse = "554 Command rejected";
20300Sstevel@tonic-gate 		break;
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate 	  case SMFIC_HELO:
20330Sstevel@tonic-gate 		skipflag = SMFIP_NOHELO;
2034*3544Sjbeck 		norespflag = SMFIP_NR_HELO;
20350Sstevel@tonic-gate 		action = "helo";
20360Sstevel@tonic-gate 		defresponse = "550 Command rejected";
20370Sstevel@tonic-gate 		break;
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 	  case SMFIC_MAIL:
20400Sstevel@tonic-gate 		skipflag = SMFIP_NOMAIL;
2041*3544Sjbeck 		norespflag = SMFIP_NR_MAIL;
20420Sstevel@tonic-gate 		action = "mail";
20430Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20440Sstevel@tonic-gate 		break;
20450Sstevel@tonic-gate 
20460Sstevel@tonic-gate 	  case SMFIC_RCPT:
20470Sstevel@tonic-gate 		skipflag = SMFIP_NORCPT;
2048*3544Sjbeck 		norespflag = SMFIP_NR_RCPT;
20490Sstevel@tonic-gate 		action = "rcpt";
20500Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20510Sstevel@tonic-gate 		break;
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 	  case SMFIC_HEADER:
20540Sstevel@tonic-gate 		skipflag = SMFIP_NOHDRS;
2055*3544Sjbeck 		norespflag = SMFIP_NR_HDR;
20560Sstevel@tonic-gate 		action = "header";
20570Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20580Sstevel@tonic-gate 		break;
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate 	  case SMFIC_BODY:
20610Sstevel@tonic-gate 		skipflag = SMFIP_NOBODY;
2062*3544Sjbeck 		norespflag = SMFIP_NR_BODY;
20630Sstevel@tonic-gate 		action = "body";
20640Sstevel@tonic-gate 		defresponse = "554 5.7.1 Command rejected";
20650Sstevel@tonic-gate 		break;
20660Sstevel@tonic-gate 
20670Sstevel@tonic-gate 	  case SMFIC_EOH:
20680Sstevel@tonic-gate 		skipflag = SMFIP_NOEOH;
2069*3544Sjbeck 		norespflag = SMFIP_NR_EOH;
20700Sstevel@tonic-gate 		action = "eoh";
20710Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20720Sstevel@tonic-gate 		break;
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate 	  case SMFIC_UNKNOWN:
2075616Sjbeck 		skipflag = SMFIP_NOUNKNOWN;
2076*3544Sjbeck 		norespflag = SMFIP_NR_UNKN;
20770Sstevel@tonic-gate 		action = "unknown";
20780Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20790Sstevel@tonic-gate 		break;
2080*3544Sjbeck 
2081616Sjbeck 	  case SMFIC_DATA:
2082616Sjbeck 		skipflag = SMFIP_NODATA;
2083*3544Sjbeck 		norespflag = SMFIP_NR_DATA;
2084616Sjbeck 		action = "data";
2085616Sjbeck 		defresponse = "550 5.7.1 Command rejected";
2086616Sjbeck 		break;
2087616Sjbeck 
20880Sstevel@tonic-gate 	  case SMFIC_BODYEOB:
20890Sstevel@tonic-gate 	  case SMFIC_OPTNEG:
20900Sstevel@tonic-gate 	  case SMFIC_MACRO:
20910Sstevel@tonic-gate 	  case SMFIC_ABORT:
20920Sstevel@tonic-gate 	  case SMFIC_QUIT:
20930Sstevel@tonic-gate 		/* NOTE: not handled by milter_send_command() */
20940Sstevel@tonic-gate 		/* FALLTHROUGH */
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 	  default:
20970Sstevel@tonic-gate 		skipflag = 0;
20980Sstevel@tonic-gate 		action = "default";
20990Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
21000Sstevel@tonic-gate 		break;
21010Sstevel@tonic-gate 	}
21020Sstevel@tonic-gate 
2103*3544Sjbeck 	if (tTd(64, 10))
2104*3544Sjbeck 		sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
2105*3544Sjbeck 			m->mf_name, skipflag, m->mf_pflags);
2106*3544Sjbeck 
21070Sstevel@tonic-gate 	/* check if filter wants this command */
2108*3544Sjbeck 	if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
21090Sstevel@tonic-gate 		return NULL;
21100Sstevel@tonic-gate 
21110Sstevel@tonic-gate 	/* send the command to the filter */
21120Sstevel@tonic-gate 	(void) milter_write(m, command, data, sz,
2113*3544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, where);
21140Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
21150Sstevel@tonic-gate 	{
21160Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
21170Sstevel@tonic-gate 		return NULL;
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	/* check if filter sends response to this command */
21210Sstevel@tonic-gate 	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
21220Sstevel@tonic-gate 		return NULL;
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	/* get the response from the filter */
21250Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen,
2126*3544Sjbeck 			       m->mf_timeout[SMFTO_READ], e, where);
21270Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
21280Sstevel@tonic-gate 	{
21290Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
21300Sstevel@tonic-gate 		return NULL;
21310Sstevel@tonic-gate 	}
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	if (tTd(64, 10))
21340Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): returned %c\n",
21350Sstevel@tonic-gate 			   m->mf_name, (char) rcmd);
21360Sstevel@tonic-gate 
21370Sstevel@tonic-gate 	switch (rcmd)
21380Sstevel@tonic-gate 	{
21390Sstevel@tonic-gate 	  case SMFIR_REPLYCODE:
21400Sstevel@tonic-gate 		MILTER_CHECK_REPLYCODE(defresponse);
21410Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2142*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2143*3544Sjbeck 				  "milter=%s, action=%s, reject=%s",
21440Sstevel@tonic-gate 				  m->mf_name, action, response);
21450Sstevel@tonic-gate 		*state = rcmd;
21460Sstevel@tonic-gate 		break;
21470Sstevel@tonic-gate 
21480Sstevel@tonic-gate 	  case SMFIR_REJECT:
21490Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2150*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2151*3544Sjbeck 				  "milter=%s, action=%s, reject",
21520Sstevel@tonic-gate 				  m->mf_name, action);
21530Sstevel@tonic-gate 		*state = rcmd;
21540Sstevel@tonic-gate 		break;
21550Sstevel@tonic-gate 
21560Sstevel@tonic-gate 	  case SMFIR_DISCARD:
21570Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2158*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2159*3544Sjbeck 				  "milter=%s, action=%s, discard",
21600Sstevel@tonic-gate 				  m->mf_name, action);
21610Sstevel@tonic-gate 		*state = rcmd;
21620Sstevel@tonic-gate 		break;
21630Sstevel@tonic-gate 
21640Sstevel@tonic-gate 	  case SMFIR_TEMPFAIL:
21650Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2166*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2167*3544Sjbeck 				  "milter=%s, action=%s, tempfail",
21680Sstevel@tonic-gate 				  m->mf_name, action);
21690Sstevel@tonic-gate 		*state = rcmd;
21700Sstevel@tonic-gate 		break;
21710Sstevel@tonic-gate 
21720Sstevel@tonic-gate 	  case SMFIR_ACCEPT:
21730Sstevel@tonic-gate 		/* this filter is done with message/connection */
21740Sstevel@tonic-gate 		if (command == SMFIC_HELO ||
21750Sstevel@tonic-gate 		    command == SMFIC_CONNECT)
21760Sstevel@tonic-gate 			m->mf_state = SMFS_CLOSABLE;
21770Sstevel@tonic-gate 		else
21780Sstevel@tonic-gate 			m->mf_state = SMFS_DONE;
21790Sstevel@tonic-gate 		if (MilterLogLevel > 10)
2180*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2181*3544Sjbeck 				  "milter=%s, action=%s, accepted",
21820Sstevel@tonic-gate 				  m->mf_name, action);
21830Sstevel@tonic-gate 		break;
21840Sstevel@tonic-gate 
21850Sstevel@tonic-gate 	  case SMFIR_CONTINUE:
21860Sstevel@tonic-gate 		/* if MAIL command is ok, filter is in message state */
21870Sstevel@tonic-gate 		if (command == SMFIC_MAIL)
21880Sstevel@tonic-gate 			m->mf_state = SMFS_INMSG;
21890Sstevel@tonic-gate 		if (MilterLogLevel > 12)
2190*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2191*3544Sjbeck 				  "milter=%s, action=%s, continue",
21920Sstevel@tonic-gate 				  m->mf_name, action);
21930Sstevel@tonic-gate 		break;
21940Sstevel@tonic-gate 
2195*3544Sjbeck 	  case SMFIR_SKIP:
2196*3544Sjbeck 		if (MilterLogLevel > 12)
2197*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
2198*3544Sjbeck 				  "milter=%s, action=%s, skip",
2199*3544Sjbeck 				  m->mf_name, action);
2200*3544Sjbeck 		m->mf_state = SMFS_SKIP;
2201*3544Sjbeck 		break;
2202*3544Sjbeck 
22030Sstevel@tonic-gate 	  default:
22040Sstevel@tonic-gate 		/* Invalid response to command */
22050Sstevel@tonic-gate 		if (MilterLogLevel > 0)
22060Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
22070Sstevel@tonic-gate 				  "milter_send_command(%s): action=%s returned bogus response %c",
22080Sstevel@tonic-gate 				  m->mf_name, action, rcmd);
22090Sstevel@tonic-gate 		milter_error(m, e);
22100Sstevel@tonic-gate 		break;
22110Sstevel@tonic-gate 	}
22120Sstevel@tonic-gate 
2213*3544Sjbeck 	if (*state != SMFIR_REPLYCODE && response != NULL)
22140Sstevel@tonic-gate 	{
22150Sstevel@tonic-gate 		sm_free(response); /* XXX */
22160Sstevel@tonic-gate 		response = NULL;
22170Sstevel@tonic-gate 	}
22180Sstevel@tonic-gate 	return response;
22190Sstevel@tonic-gate }
22200Sstevel@tonic-gate 
22210Sstevel@tonic-gate /*
22220Sstevel@tonic-gate **  MILTER_COMMAND -- send a command and return the response for each filter
22230Sstevel@tonic-gate **
22240Sstevel@tonic-gate **	Parameters:
2225*3544Sjbeck **		cmd -- command to send.
22260Sstevel@tonic-gate **		data -- optional command data.
22270Sstevel@tonic-gate **		sz -- length of buf.
22280Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
22290Sstevel@tonic-gate **		e -- current envelope (for macro access).
22300Sstevel@tonic-gate **		state -- return state word.
2231*3544Sjbeck **		where -- description of calling function (logging).
2232*3544Sjbeck **		cmd_error -- did the SMTP command cause an error?
22330Sstevel@tonic-gate **
22340Sstevel@tonic-gate **	Returns:
22350Sstevel@tonic-gate **		response string (may be NULL)
22360Sstevel@tonic-gate */
22370Sstevel@tonic-gate 
22380Sstevel@tonic-gate static char *
2239*3544Sjbeck milter_command(cmd, data, sz, macros, e, state, where, cmd_error)
2240*3544Sjbeck 	int cmd;
22410Sstevel@tonic-gate 	void *data;
22420Sstevel@tonic-gate 	ssize_t sz;
22430Sstevel@tonic-gate 	char **macros;
22440Sstevel@tonic-gate 	ENVELOPE *e;
22450Sstevel@tonic-gate 	char *state;
2246*3544Sjbeck 	const char *where;
2247*3544Sjbeck 	bool cmd_error;
22480Sstevel@tonic-gate {
22490Sstevel@tonic-gate 	int i;
2250*3544Sjbeck 	char command = (char) cmd;
22510Sstevel@tonic-gate 	char *response = NULL;
22520Sstevel@tonic-gate 	time_t tn = 0;
22530Sstevel@tonic-gate 
22540Sstevel@tonic-gate 	if (tTd(64, 10))
22550Sstevel@tonic-gate 		sm_dprintf("milter_command: cmd %c len %ld\n",
2256*3544Sjbeck 			command, (long) sz);
22570Sstevel@tonic-gate 
22580Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
22590Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
22600Sstevel@tonic-gate 	{
22610Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 		/* previous problem? */
22640Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
22650Sstevel@tonic-gate 		{
22660Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
22670Sstevel@tonic-gate 			break;
22680Sstevel@tonic-gate 		}
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate 		/* sanity check */
22710Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
22720Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
22730Sstevel@tonic-gate 			continue;
22740Sstevel@tonic-gate 
22750Sstevel@tonic-gate 		/* send macros (regardless of whether we send command) */
22760Sstevel@tonic-gate 		if (macros != NULL && macros[0] != NULL)
22770Sstevel@tonic-gate 		{
22780Sstevel@tonic-gate 			milter_send_macros(m, macros, command, e);
22790Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
22800Sstevel@tonic-gate 			{
22810Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, continue);
22820Sstevel@tonic-gate 				break;
22830Sstevel@tonic-gate 			}
22840Sstevel@tonic-gate 		}
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22870Sstevel@tonic-gate 			tn = curtime();
22880Sstevel@tonic-gate 
2289*3544Sjbeck 		/*
2290*3544Sjbeck 		**  send the command if
2291*3544Sjbeck 		**	there is no error
2292*3544Sjbeck 		**	or it's RCPT and the client asked for it:
2293*3544Sjbeck 		**	!cmd_error ||
2294*3544Sjbeck 		**	where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
2295*3544Sjbeck 		**  negate that condition and use continue
2296*3544Sjbeck 		*/
2297*3544Sjbeck 
2298*3544Sjbeck 		if (cmd_error &&
2299*3544Sjbeck 		    (strcmp(where, "rcpt") != 0 ||
2300*3544Sjbeck 		     (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
2301*3544Sjbeck 			continue;
2302*3544Sjbeck 
2303*3544Sjbeck 		response = milter_send_command(m, command, data, sz, e, state,
2304*3544Sjbeck 						where);
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate 		if (MilterLogLevel > 21)
23070Sstevel@tonic-gate 		{
23080Sstevel@tonic-gate 			/* log the time it took for the command per filter */
23090Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
23100Sstevel@tonic-gate 				  "Milter (%s): time command (%c), %d",
23110Sstevel@tonic-gate 				  m->mf_name, command, (int) (tn - curtime()));
23120Sstevel@tonic-gate 		}
23130Sstevel@tonic-gate 
23140Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE)
23150Sstevel@tonic-gate 			break;
23160Sstevel@tonic-gate 	}
23170Sstevel@tonic-gate 	return response;
23180Sstevel@tonic-gate }
2319*3544Sjbeck 
2320*3544Sjbeck static int milter_getsymlist __P((struct milter *, char *, int, int));
2321*3544Sjbeck 
2322*3544Sjbeck static int
2323*3544Sjbeck milter_getsymlist(m, buf, rlen, offset)
2324*3544Sjbeck 	struct milter *m;
2325*3544Sjbeck 	char *buf;
2326*3544Sjbeck 	int rlen;
2327*3544Sjbeck 	int offset;
2328*3544Sjbeck {
2329*3544Sjbeck 	int i, r, nummac;
2330*3544Sjbeck 	mi_int32 v;
2331*3544Sjbeck 
2332*3544Sjbeck 	SM_ASSERT(m != NULL);
2333*3544Sjbeck 	SM_ASSERT(buf != NULL);
2334*3544Sjbeck 
2335*3544Sjbeck 	while (offset + MILTER_LEN_BYTES < rlen)
2336*3544Sjbeck 	{
2337*3544Sjbeck 		size_t len;
2338*3544Sjbeck 		char **macros;
2339*3544Sjbeck 
2340*3544Sjbeck 		nummac = 0;
2341*3544Sjbeck 		(void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
2342*3544Sjbeck 		i = ntohl(v);
2343*3544Sjbeck 		if (i < SMFIM_FIRST || i > SMFIM_LAST)
2344*3544Sjbeck 			return -1;
2345*3544Sjbeck 		offset += MILTER_LEN_BYTES;
2346*3544Sjbeck 		macros = NULL;
2347*3544Sjbeck 
2348*3544Sjbeck 		switch (i)
2349*3544Sjbeck 		{
2350*3544Sjbeck 		  case MO_MACROS_CONNECT:
2351*3544Sjbeck 			if (macros == NULL)
2352*3544Sjbeck 				macros = MilterConnectMacros;
2353*3544Sjbeck 			/* FALLTHROUGH */
2354*3544Sjbeck 
2355*3544Sjbeck 		  case MO_MACROS_HELO:
2356*3544Sjbeck 			if (macros == NULL)
2357*3544Sjbeck 				macros = MilterHeloMacros;
2358*3544Sjbeck 			/* FALLTHROUGH */
2359*3544Sjbeck 
2360*3544Sjbeck 		  case MO_MACROS_ENVFROM:
2361*3544Sjbeck 			if (macros == NULL)
2362*3544Sjbeck 				macros = MilterEnvFromMacros;
2363*3544Sjbeck 			/* FALLTHROUGH */
2364*3544Sjbeck 
2365*3544Sjbeck 		  case MO_MACROS_ENVRCPT:
2366*3544Sjbeck 			if (macros == NULL)
2367*3544Sjbeck 				macros = MilterEnvRcptMacros;
2368*3544Sjbeck 			/* FALLTHROUGH */
2369*3544Sjbeck 
2370*3544Sjbeck 		  case MO_MACROS_EOM:
2371*3544Sjbeck 			if (macros == NULL)
2372*3544Sjbeck 				macros = MilterEOMMacros;
2373*3544Sjbeck 			/* FALLTHROUGH */
2374*3544Sjbeck 
2375*3544Sjbeck 		  case MO_MACROS_EOH:
2376*3544Sjbeck 			if (macros == NULL)
2377*3544Sjbeck 				macros = MilterEOHMacros;
2378*3544Sjbeck 			/* FALLTHROUGH */
2379*3544Sjbeck 
2380*3544Sjbeck 		  case MO_MACROS_DATA:
2381*3544Sjbeck 			if (macros == NULL)
2382*3544Sjbeck 				macros = MilterDataMacros;
2383*3544Sjbeck 
2384*3544Sjbeck 			len = strlen(buf + offset);
2385*3544Sjbeck 			if (len > 0)
2386*3544Sjbeck 			{
2387*3544Sjbeck 				r = milter_set_macros(m->mf_name, macros,
2388*3544Sjbeck 						buf + offset, nummac);
2389*3544Sjbeck 				if (r >= 0)
2390*3544Sjbeck 					nummac = r;
2391*3544Sjbeck 			}
2392*3544Sjbeck 			break;
2393*3544Sjbeck 
2394*3544Sjbeck 		  default:
2395*3544Sjbeck 			return -1;
2396*3544Sjbeck 		}
2397*3544Sjbeck 		if (len == 0)
2398*3544Sjbeck 			return -1;
2399*3544Sjbeck 		offset += len + 1;
2400*3544Sjbeck 	}
2401*3544Sjbeck 
2402*3544Sjbeck 	return 0;
2403*3544Sjbeck }
2404*3544Sjbeck 
24050Sstevel@tonic-gate /*
24060Sstevel@tonic-gate **  MILTER_NEGOTIATE -- get version and flags from filter
24070Sstevel@tonic-gate **
24080Sstevel@tonic-gate **	Parameters:
24090Sstevel@tonic-gate **		m -- milter filter structure.
24100Sstevel@tonic-gate **		e -- current envelope.
24110Sstevel@tonic-gate **
24120Sstevel@tonic-gate **	Returns:
24130Sstevel@tonic-gate **		0 on success, -1 otherwise
24140Sstevel@tonic-gate */
24150Sstevel@tonic-gate 
24160Sstevel@tonic-gate static int
24170Sstevel@tonic-gate milter_negotiate(m, e)
24180Sstevel@tonic-gate 	struct milter *m;
24190Sstevel@tonic-gate 	ENVELOPE *e;
24200Sstevel@tonic-gate {
24210Sstevel@tonic-gate 	char rcmd;
2422*3544Sjbeck 	mi_int32 fvers, fflags, pflags;
2423*3544Sjbeck 	mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
2424616Sjbeck 	ssize_t rlen;
24250Sstevel@tonic-gate 	char *response;
24260Sstevel@tonic-gate 	char data[MILTER_OPTLEN];
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate 	/* sanity check */
24290Sstevel@tonic-gate 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
24300Sstevel@tonic-gate 	{
24310Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24320Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24330Sstevel@tonic-gate 				  "Milter (%s): negotiate, impossible state",
24340Sstevel@tonic-gate 				  m->mf_name);
24350Sstevel@tonic-gate 		milter_error(m, e);
24360Sstevel@tonic-gate 		return -1;
24370Sstevel@tonic-gate 	}
24380Sstevel@tonic-gate 
2439*3544Sjbeck #if MILTER_CHECK
2440*3544Sjbeck 	mta_prot_vers = m->mf_mta_prot_version;
2441*3544Sjbeck 	mta_prot_flags = m->mf_mta_prot_flags;
2442*3544Sjbeck 	mta_actions = m->mf_mta_actions;
2443*3544Sjbeck #else /* MILTER_CHECK */
2444*3544Sjbeck 	mta_prot_vers = SMFI_PROT_VERSION;
2445*3544Sjbeck 	mta_prot_flags = SMFI_CURR_PROT;
2446*3544Sjbeck 	mta_actions = SMFI_CURR_ACTS;
2447*3544Sjbeck #endif /* MILTER_CHECK */
2448*3544Sjbeck 
2449*3544Sjbeck 	fvers = htonl(mta_prot_vers);
2450*3544Sjbeck 	pflags = htonl(mta_prot_flags);
2451*3544Sjbeck 	fflags = htonl(mta_actions);
24520Sstevel@tonic-gate 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
24530Sstevel@tonic-gate 	(void) memcpy(data + MILTER_LEN_BYTES,
24540Sstevel@tonic-gate 		      (char *) &fflags, MILTER_LEN_BYTES);
24550Sstevel@tonic-gate 	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
24560Sstevel@tonic-gate 		      (char *) &pflags, MILTER_LEN_BYTES);
2457*3544Sjbeck 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data),
2458*3544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "negotiate");
24590Sstevel@tonic-gate 
24600Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24610Sstevel@tonic-gate 		return -1;
24620Sstevel@tonic-gate 
2463*3544Sjbeck 	if (tTd(64, 5))
2464*3544Sjbeck 		sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
2465*3544Sjbeck 			m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
2466*3544Sjbeck 
2467*3544Sjbeck 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
2468*3544Sjbeck 				"negotiate");
24690Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24700Sstevel@tonic-gate 		return -1;
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	if (rcmd != SMFIC_OPTNEG)
24730Sstevel@tonic-gate 	{
24740Sstevel@tonic-gate 		if (tTd(64, 5))
24750Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
24760Sstevel@tonic-gate 				m->mf_name, rcmd, SMFIC_OPTNEG);
24770Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24780Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24790Sstevel@tonic-gate 				  "Milter (%s): negotiate: returned %c instead of %c",
24800Sstevel@tonic-gate 				  m->mf_name, rcmd, SMFIC_OPTNEG);
24810Sstevel@tonic-gate 		if (response != NULL)
24820Sstevel@tonic-gate 			sm_free(response); /* XXX */
24830Sstevel@tonic-gate 		milter_error(m, e);
24840Sstevel@tonic-gate 		return -1;
24850Sstevel@tonic-gate 	}
24860Sstevel@tonic-gate 
24870Sstevel@tonic-gate 	/* Make sure we have enough bytes for the version */
24880Sstevel@tonic-gate 	if (response == NULL || rlen < MILTER_LEN_BYTES)
24890Sstevel@tonic-gate 	{
24900Sstevel@tonic-gate 		if (tTd(64, 5))
24910Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
24920Sstevel@tonic-gate 				m->mf_name);
24930Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24940Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24950Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return valid info",
24960Sstevel@tonic-gate 				  m->mf_name);
24970Sstevel@tonic-gate 		if (response != NULL)
24980Sstevel@tonic-gate 			sm_free(response); /* XXX */
24990Sstevel@tonic-gate 		milter_error(m, e);
25000Sstevel@tonic-gate 		return -1;
25010Sstevel@tonic-gate 	}
25020Sstevel@tonic-gate 
25030Sstevel@tonic-gate 	/* extract information */
25040Sstevel@tonic-gate 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
25050Sstevel@tonic-gate 
25060Sstevel@tonic-gate 	/* Now make sure we have enough for the feature bitmap */
2507*3544Sjbeck 	if (rlen < MILTER_OPTLEN)
25080Sstevel@tonic-gate 	{
25090Sstevel@tonic-gate 		if (tTd(64, 5))
25100Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
25110Sstevel@tonic-gate 				m->mf_name);
25120Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25130Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25140Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return enough info",
25150Sstevel@tonic-gate 				  m->mf_name);
25160Sstevel@tonic-gate 		if (response != NULL)
25170Sstevel@tonic-gate 			sm_free(response); /* XXX */
25180Sstevel@tonic-gate 		milter_error(m, e);
25190Sstevel@tonic-gate 		return -1;
25200Sstevel@tonic-gate 	}
25210Sstevel@tonic-gate 
25220Sstevel@tonic-gate 	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
25230Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25240Sstevel@tonic-gate 	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
25250Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
25260Sstevel@tonic-gate 
25270Sstevel@tonic-gate 	m->mf_fvers = ntohl(fvers);
25280Sstevel@tonic-gate 	m->mf_fflags = ntohl(fflags);
25290Sstevel@tonic-gate 	m->mf_pflags = ntohl(pflags);
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 	/* check for version compatibility */
25320Sstevel@tonic-gate 	if (m->mf_fvers == 1 ||
25330Sstevel@tonic-gate 	    m->mf_fvers > SMFI_VERSION)
25340Sstevel@tonic-gate 	{
25350Sstevel@tonic-gate 		if (tTd(64, 5))
25360Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
25370Sstevel@tonic-gate 				m->mf_name, m->mf_fvers, SMFI_VERSION);
25380Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25390Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25400Sstevel@tonic-gate 				  "Milter (%s): negotiate: version %d != MTA milter version %d",
25410Sstevel@tonic-gate 				  m->mf_name, m->mf_fvers, SMFI_VERSION);
25420Sstevel@tonic-gate 		milter_error(m, e);
2543*3544Sjbeck 		goto error;
25440Sstevel@tonic-gate 	}
25450Sstevel@tonic-gate 
25460Sstevel@tonic-gate 	/* check for filter feature mismatch */
2547*3544Sjbeck 	if ((m->mf_fflags & mta_actions) != m->mf_fflags)
25480Sstevel@tonic-gate 	{
25490Sstevel@tonic-gate 		if (tTd(64, 5))
25500Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
25510Sstevel@tonic-gate 				m->mf_name, m->mf_fflags,
2552*3544Sjbeck 				(unsigned long) mta_actions);
25530Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25540Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25550Sstevel@tonic-gate 				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
25560Sstevel@tonic-gate 				  m->mf_name, m->mf_fflags,
2557*3544Sjbeck 				  (unsigned long) mta_actions);
25580Sstevel@tonic-gate 		milter_error(m, e);
2559*3544Sjbeck 		goto error;
25600Sstevel@tonic-gate 	}
25610Sstevel@tonic-gate 
25620Sstevel@tonic-gate 	/* check for protocol feature mismatch */
2563*3544Sjbeck 	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
25640Sstevel@tonic-gate 	{
25650Sstevel@tonic-gate 		if (tTd(64, 5))
25660Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
25670Sstevel@tonic-gate 				m->mf_name, m->mf_pflags,
2568*3544Sjbeck 				(unsigned long) mta_prot_flags);
25690Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25700Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25710Sstevel@tonic-gate 				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
25720Sstevel@tonic-gate 				  m->mf_name, m->mf_pflags,
2573*3544Sjbeck 				  (unsigned long) mta_prot_flags);
25740Sstevel@tonic-gate 		milter_error(m, e);
2575*3544Sjbeck 		goto error;
25760Sstevel@tonic-gate 	}
25770Sstevel@tonic-gate 
2578616Sjbeck 	if (m->mf_fvers <= 2)
2579616Sjbeck 		m->mf_pflags |= SMFIP_NOUNKNOWN;
2580616Sjbeck 	if (m->mf_fvers <= 3)
2581616Sjbeck 		m->mf_pflags |= SMFIP_NODATA;
2582616Sjbeck 
2583*3544Sjbeck 	if (rlen > MILTER_OPTLEN)
2584*3544Sjbeck 	{
2585*3544Sjbeck 		milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
2586*3544Sjbeck 	}
2587*3544Sjbeck 
25880Sstevel@tonic-gate 	if (tTd(64, 5))
2589*3544Sjbeck 		sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
25900Sstevel@tonic-gate 			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
25910Sstevel@tonic-gate 	return 0;
2592*3544Sjbeck 
2593*3544Sjbeck   error:
2594*3544Sjbeck 	if (response != NULL)
2595*3544Sjbeck 		sm_free(response); /* XXX */
2596*3544Sjbeck 	return -1;
25970Sstevel@tonic-gate }
2598*3544Sjbeck 
25990Sstevel@tonic-gate /*
26000Sstevel@tonic-gate **  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
26010Sstevel@tonic-gate **
26020Sstevel@tonic-gate **	Reduce code duplication by putting these checks in one place
26030Sstevel@tonic-gate **
26040Sstevel@tonic-gate **	Parameters:
26050Sstevel@tonic-gate **		e -- current envelope.
26060Sstevel@tonic-gate **
26070Sstevel@tonic-gate **	Returns:
26080Sstevel@tonic-gate **		none
26090Sstevel@tonic-gate */
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate static void
26120Sstevel@tonic-gate milter_per_connection_check(e)
26130Sstevel@tonic-gate 	ENVELOPE *e;
26140Sstevel@tonic-gate {
26150Sstevel@tonic-gate 	int i;
26160Sstevel@tonic-gate 
26170Sstevel@tonic-gate 	/* see if we are done with any of the filters */
26180Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
26190Sstevel@tonic-gate 	{
26200Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate 		if (m->mf_state == SMFS_CLOSABLE)
26230Sstevel@tonic-gate 			milter_quit_filter(m, e);
26240Sstevel@tonic-gate 	}
26250Sstevel@tonic-gate }
2626*3544Sjbeck 
26270Sstevel@tonic-gate /*
26280Sstevel@tonic-gate **  MILTER_ERROR -- Put a milter filter into error state
26290Sstevel@tonic-gate **
26300Sstevel@tonic-gate **	Parameters:
26310Sstevel@tonic-gate **		m -- the broken filter.
26320Sstevel@tonic-gate **		e -- current envelope.
26330Sstevel@tonic-gate **
26340Sstevel@tonic-gate **	Returns:
26350Sstevel@tonic-gate **		none
26360Sstevel@tonic-gate */
26370Sstevel@tonic-gate 
26380Sstevel@tonic-gate static void
26390Sstevel@tonic-gate milter_error(m, e)
26400Sstevel@tonic-gate 	struct milter *m;
26410Sstevel@tonic-gate 	ENVELOPE *e;
26420Sstevel@tonic-gate {
26430Sstevel@tonic-gate 	/*
26440Sstevel@tonic-gate 	**  We could send a quit here but we may have gotten here due to
26450Sstevel@tonic-gate 	**  an I/O error so we don't want to try to make things worse.
26460Sstevel@tonic-gate 	*/
26470Sstevel@tonic-gate 
26480Sstevel@tonic-gate 	if (m->mf_sock >= 0)
26490Sstevel@tonic-gate 	{
26500Sstevel@tonic-gate 		(void) close(m->mf_sock);
26510Sstevel@tonic-gate 		m->mf_sock = -1;
26520Sstevel@tonic-gate 	}
26530Sstevel@tonic-gate 	m->mf_state = SMFS_ERROR;
26540Sstevel@tonic-gate 
26550Sstevel@tonic-gate 	if (MilterLogLevel > 0)
26560Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
26570Sstevel@tonic-gate 			  m->mf_name);
26580Sstevel@tonic-gate }
2659*3544Sjbeck 
26600Sstevel@tonic-gate /*
26610Sstevel@tonic-gate **  MILTER_HEADERS -- send headers to a single milter filter
26620Sstevel@tonic-gate **
26630Sstevel@tonic-gate **	Parameters:
26640Sstevel@tonic-gate **		m -- current filter.
26650Sstevel@tonic-gate **		e -- current envelope.
26660Sstevel@tonic-gate **		state -- return state from response.
26670Sstevel@tonic-gate **
26680Sstevel@tonic-gate **	Returns:
26690Sstevel@tonic-gate **		response string (may be NULL)
26700Sstevel@tonic-gate */
26710Sstevel@tonic-gate 
26720Sstevel@tonic-gate static char *
26730Sstevel@tonic-gate milter_headers(m, e, state)
26740Sstevel@tonic-gate 	struct milter *m;
26750Sstevel@tonic-gate 	ENVELOPE *e;
26760Sstevel@tonic-gate 	char *state;
26770Sstevel@tonic-gate {
26780Sstevel@tonic-gate 	char *response = NULL;
26790Sstevel@tonic-gate 	HDR *h;
26800Sstevel@tonic-gate 
26810Sstevel@tonic-gate 	if (MilterLogLevel > 17)
26820Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
26830Sstevel@tonic-gate 			  m->mf_name);
26840Sstevel@tonic-gate 
26850Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
26860Sstevel@tonic-gate 	{
2687*3544Sjbeck 		int len_n, len_v, len_t, len_f;
2688*3544Sjbeck 		char *buf, *hv;
26890Sstevel@tonic-gate 
26900Sstevel@tonic-gate 		/* don't send over deleted headers */
26910Sstevel@tonic-gate 		if (h->h_value == NULL)
26920Sstevel@tonic-gate 		{
26930Sstevel@tonic-gate 			/* strip H_USER so not counted in milter_changeheader() */
26940Sstevel@tonic-gate 			h->h_flags &= ~H_USER;
26950Sstevel@tonic-gate 			continue;
26960Sstevel@tonic-gate 		}
26970Sstevel@tonic-gate 
26980Sstevel@tonic-gate 		/* skip auto-generated */
26990Sstevel@tonic-gate 		if (!bitset(H_USER, h->h_flags))
27000Sstevel@tonic-gate 			continue;
27010Sstevel@tonic-gate 
27020Sstevel@tonic-gate 		if (tTd(64, 10))
2703*3544Sjbeck 			sm_dprintf("milter_headers: %s:%s\n",
27040Sstevel@tonic-gate 				h->h_field, h->h_value);
27050Sstevel@tonic-gate 		if (MilterLogLevel > 21)
27060Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
27070Sstevel@tonic-gate 				  m->mf_name, h->h_field);
27080Sstevel@tonic-gate 
2709*3544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
2710*3544Sjbeck 		    || *(h->h_value) != ' ')
2711*3544Sjbeck 			hv = h->h_value;
2712*3544Sjbeck 		else
2713*3544Sjbeck 			hv = h->h_value + 1;
2714*3544Sjbeck 		len_f = strlen(h->h_field) + 1;
2715*3544Sjbeck 		len_t = len_f + strlen(hv) + 1;
2716*3544Sjbeck 		if (len_t < 0)
27170Sstevel@tonic-gate 			continue;
2718*3544Sjbeck 		buf = (char *) xalloc(len_t);
2719*3544Sjbeck 
2720*3544Sjbeck 		/*
2721*3544Sjbeck 		**  Note: currently the call to dequote_internal_chars()
2722*3544Sjbeck 		**  is not required as h_field is supposed to be 7-bit US-ASCII.
2723*3544Sjbeck 		*/
2724*3544Sjbeck 
2725*3544Sjbeck 		len_n = dequote_internal_chars(h->h_field, buf, len_f);
2726*3544Sjbeck 		SM_ASSERT(len_n < len_f);
2727*3544Sjbeck 		len_v = dequote_internal_chars(hv, buf + len_n + 1,
2728*3544Sjbeck 						len_t - len_n - 1);
2729*3544Sjbeck 		SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
2730*3544Sjbeck 		len_t = len_n + 1 + len_v + 1;
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 		/* send it over */
27330Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_HEADER, buf,
2734*3544Sjbeck 					       len_t, e, state, "header");
2735*3544Sjbeck 		sm_free(buf);
27360Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
27370Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
27380Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
27390Sstevel@tonic-gate 			break;
27400Sstevel@tonic-gate 	}
27410Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27420Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
27430Sstevel@tonic-gate 			  m->mf_name);
27440Sstevel@tonic-gate 	return response;
27450Sstevel@tonic-gate }
2746*3544Sjbeck 
27470Sstevel@tonic-gate /*
27480Sstevel@tonic-gate **  MILTER_BODY -- send the body to a filter
27490Sstevel@tonic-gate **
27500Sstevel@tonic-gate **	Parameters:
27510Sstevel@tonic-gate **		m -- current filter.
27520Sstevel@tonic-gate **		e -- current envelope.
27530Sstevel@tonic-gate **		state -- return state from response.
27540Sstevel@tonic-gate **
27550Sstevel@tonic-gate **	Returns:
27560Sstevel@tonic-gate **		response string (may be NULL)
27570Sstevel@tonic-gate */
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate static char *
27600Sstevel@tonic-gate milter_body(m, e, state)
27610Sstevel@tonic-gate 	struct milter *m;
27620Sstevel@tonic-gate 	ENVELOPE *e;
27630Sstevel@tonic-gate 	char *state;
27640Sstevel@tonic-gate {
27650Sstevel@tonic-gate 	char bufchar = '\0';
27660Sstevel@tonic-gate 	char prevchar = '\0';
27670Sstevel@tonic-gate 	int c;
27680Sstevel@tonic-gate 	char *response = NULL;
27690Sstevel@tonic-gate 	char *bp;
27700Sstevel@tonic-gate 	char buf[MILTER_CHUNK_SIZE];
27710Sstevel@tonic-gate 
27720Sstevel@tonic-gate 	if (tTd(64, 10))
27730Sstevel@tonic-gate 		sm_dprintf("milter_body\n");
27740Sstevel@tonic-gate 
27750Sstevel@tonic-gate 	if (bfrewind(e->e_dfp) < 0)
27760Sstevel@tonic-gate 	{
27770Sstevel@tonic-gate 		ExitStat = EX_IOERR;
27780Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
27790Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: rewind error",
27800Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
27810Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
27820Sstevel@tonic-gate 		return NULL;
27830Sstevel@tonic-gate 	}
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27860Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
27870Sstevel@tonic-gate 			  m->mf_name);
27880Sstevel@tonic-gate 	bp = buf;
27890Sstevel@tonic-gate 	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
27900Sstevel@tonic-gate 	{
27910Sstevel@tonic-gate 		/*  Change LF to CRLF */
27920Sstevel@tonic-gate 		if (c == '\n')
27930Sstevel@tonic-gate 		{
2794*3544Sjbeck #if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
27950Sstevel@tonic-gate 			/* Not a CRLF already? */
27960Sstevel@tonic-gate 			if (prevchar != '\r')
2797*3544Sjbeck #endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
27980Sstevel@tonic-gate 			{
27990Sstevel@tonic-gate 				/* Room for CR now? */
2800*3544Sjbeck 				if (bp + 2 > &buf[sizeof(buf)])
28010Sstevel@tonic-gate 				{
28020Sstevel@tonic-gate 					/* No room, buffer LF */
28030Sstevel@tonic-gate 					bufchar = c;
28040Sstevel@tonic-gate 
28050Sstevel@tonic-gate 					/* and send CR now */
28060Sstevel@tonic-gate 					c = '\r';
28070Sstevel@tonic-gate 				}
28080Sstevel@tonic-gate 				else
28090Sstevel@tonic-gate 				{
28100Sstevel@tonic-gate 					/* Room to do it now */
28110Sstevel@tonic-gate 					*bp++ = '\r';
28120Sstevel@tonic-gate 					prevchar = '\r';
28130Sstevel@tonic-gate 				}
28140Sstevel@tonic-gate 			}
28150Sstevel@tonic-gate 		}
28160Sstevel@tonic-gate 		*bp++ = (char) c;
28170Sstevel@tonic-gate 		prevchar = c;
2818*3544Sjbeck 		if (bp >= &buf[sizeof(buf)])
28190Sstevel@tonic-gate 		{
28200Sstevel@tonic-gate 			/* send chunk */
28210Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_BODY, buf,
2822*3544Sjbeck 						       bp - buf, e, state,
2823*3544Sjbeck 							"body chunk");
28240Sstevel@tonic-gate 			bp = buf;
28250Sstevel@tonic-gate 			if (bufchar != '\0')
28260Sstevel@tonic-gate 			{
28270Sstevel@tonic-gate 				*bp++ = bufchar;
28280Sstevel@tonic-gate 				bufchar = '\0';
28290Sstevel@tonic-gate 				prevchar = bufchar;
28300Sstevel@tonic-gate 			}
28310Sstevel@tonic-gate 		}
28320Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
28330Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
2834*3544Sjbeck 		    m->mf_state == SMFS_SKIP ||
28350Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
28360Sstevel@tonic-gate 			break;
28370Sstevel@tonic-gate 	}
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 	/* check for read errors */
28400Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
28410Sstevel@tonic-gate 	{
28420Sstevel@tonic-gate 		ExitStat = EX_IOERR;
28430Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
2844*3544Sjbeck 		    *state == SMFIR_ACCEPT ||
2845*3544Sjbeck 		    m->mf_state == SMFS_SKIP)
28460Sstevel@tonic-gate 		{
28470Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
28480Sstevel@tonic-gate 			if (response != NULL)
28490Sstevel@tonic-gate 			{
28500Sstevel@tonic-gate 				sm_free(response); /* XXX */
28510Sstevel@tonic-gate 				response = NULL;
28520Sstevel@tonic-gate 			}
28530Sstevel@tonic-gate 		}
28540Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: read error",
28550Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
28560Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
28570Sstevel@tonic-gate 		return response;
28580Sstevel@tonic-gate 	}
28590Sstevel@tonic-gate 
28600Sstevel@tonic-gate 	/* send last body chunk */
28610Sstevel@tonic-gate 	if (bp > buf &&
28620Sstevel@tonic-gate 	    m->mf_state != SMFS_ERROR &&
28630Sstevel@tonic-gate 	    m->mf_state != SMFS_DONE &&
2864*3544Sjbeck 	    m->mf_state != SMFS_SKIP &&
28650Sstevel@tonic-gate 	    *state == SMFIR_CONTINUE)
28660Sstevel@tonic-gate 	{
28670Sstevel@tonic-gate 		/* send chunk */
28680Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
2869*3544Sjbeck 					       e, state, "last body chunk");
28700Sstevel@tonic-gate 		bp = buf;
28710Sstevel@tonic-gate 	}
28720Sstevel@tonic-gate 	if (MilterLogLevel > 17)
28730Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
28740Sstevel@tonic-gate 			  m->mf_name);
2875*3544Sjbeck 	if (m->mf_state == SMFS_SKIP)
2876*3544Sjbeck 	{
2877*3544Sjbeck 		*state = SMFIR_CONTINUE;
2878*3544Sjbeck 		m->mf_state = SMFS_READY;
2879*3544Sjbeck 	}
2880*3544Sjbeck 
28810Sstevel@tonic-gate 	return response;
28820Sstevel@tonic-gate }
28830Sstevel@tonic-gate 
28840Sstevel@tonic-gate /*
28850Sstevel@tonic-gate **  Actions
28860Sstevel@tonic-gate */
28870Sstevel@tonic-gate 
28880Sstevel@tonic-gate /*
2889*3544Sjbeck **  ADDLEADINGSPACE -- Add a leading space to a string
2890*3544Sjbeck **
2891*3544Sjbeck **	Parameters:
2892*3544Sjbeck **		str -- string
2893*3544Sjbeck **		rp -- resource pool for allocations
2894*3544Sjbeck **
2895*3544Sjbeck **	Returns:
2896*3544Sjbeck **		pointer to new string
2897*3544Sjbeck */
2898*3544Sjbeck 
2899*3544Sjbeck static char *addleadingspace __P((char *, SM_RPOOL_T *));
2900*3544Sjbeck 
2901*3544Sjbeck static char *
2902*3544Sjbeck addleadingspace(str, rp)
2903*3544Sjbeck 	char *str;
2904*3544Sjbeck 	SM_RPOOL_T *rp;
2905*3544Sjbeck {
2906*3544Sjbeck 	size_t l;
2907*3544Sjbeck 	char *new;
2908*3544Sjbeck 
2909*3544Sjbeck 	SM_ASSERT(str != NULL);
2910*3544Sjbeck 	l = strlen(str);
2911*3544Sjbeck 	SM_ASSERT(l + 2 > l);
2912*3544Sjbeck 	new = sm_rpool_malloc_x(rp, l + 2);
2913*3544Sjbeck 	new[0] = ' ';
2914*3544Sjbeck 	new[1] = '\0';
2915*3544Sjbeck 	sm_strlcpy(new + 1, str, l + 1);
2916*3544Sjbeck 	return new;
2917*3544Sjbeck }
2918*3544Sjbeck 
2919*3544Sjbeck /*
29200Sstevel@tonic-gate **  MILTER_ADDHEADER -- Add the supplied header to the message
29210Sstevel@tonic-gate **
29220Sstevel@tonic-gate **	Parameters:
2923*3544Sjbeck **		m -- current filter.
29240Sstevel@tonic-gate **		response -- encoded form of header/value.
29250Sstevel@tonic-gate **		rlen -- length of response.
29260Sstevel@tonic-gate **		e -- current envelope.
29270Sstevel@tonic-gate **
29280Sstevel@tonic-gate **	Returns:
29290Sstevel@tonic-gate **		none
29300Sstevel@tonic-gate */
29310Sstevel@tonic-gate 
29320Sstevel@tonic-gate static void
2933*3544Sjbeck milter_addheader(m, response, rlen, e)
2934*3544Sjbeck 	struct milter *m;
29350Sstevel@tonic-gate 	char *response;
29360Sstevel@tonic-gate 	ssize_t rlen;
29370Sstevel@tonic-gate 	ENVELOPE *e;
29380Sstevel@tonic-gate {
2939*3544Sjbeck 	int mh_v_len;
2940*3544Sjbeck 	char *val, *mh_value;
29410Sstevel@tonic-gate 	HDR *h;
29420Sstevel@tonic-gate 
29430Sstevel@tonic-gate 	if (tTd(64, 10))
29440Sstevel@tonic-gate 		sm_dprintf("milter_addheader: ");
29450Sstevel@tonic-gate 
29460Sstevel@tonic-gate 	/* sanity checks */
29470Sstevel@tonic-gate 	if (response == NULL)
29480Sstevel@tonic-gate 	{
29490Sstevel@tonic-gate 		if (tTd(64, 10))
29500Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
29510Sstevel@tonic-gate 		return;
29520Sstevel@tonic-gate 	}
29530Sstevel@tonic-gate 
29540Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
29550Sstevel@tonic-gate 	{
29560Sstevel@tonic-gate 		if (tTd(64, 10))
2957*3544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
2958*3544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
29590Sstevel@tonic-gate 		return;
29600Sstevel@tonic-gate 	}
29610Sstevel@tonic-gate 
29620Sstevel@tonic-gate 	/* Find separating NUL */
29630Sstevel@tonic-gate 	val = response + strlen(response) + 1;
29640Sstevel@tonic-gate 
29650Sstevel@tonic-gate 	/* another sanity check */
29660Sstevel@tonic-gate 	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
29670Sstevel@tonic-gate 	{
29680Sstevel@tonic-gate 		if (tTd(64, 10))
29690Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
29700Sstevel@tonic-gate 		return;
29710Sstevel@tonic-gate 	}
29720Sstevel@tonic-gate 
29730Sstevel@tonic-gate 	if (*response == '\0')
29740Sstevel@tonic-gate 	{
29750Sstevel@tonic-gate 		if (tTd(64, 10))
29760Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
29770Sstevel@tonic-gate 		return;
29780Sstevel@tonic-gate 	}
29790Sstevel@tonic-gate 
29800Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
29810Sstevel@tonic-gate 	{
29820Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, response) == 0 &&
29830Sstevel@tonic-gate 		    !bitset(H_USER, h->h_flags) &&
29840Sstevel@tonic-gate 		    !bitset(H_TRACE, h->h_flags))
29850Sstevel@tonic-gate 			break;
29860Sstevel@tonic-gate 	}
29870Sstevel@tonic-gate 
2988*3544Sjbeck 	mh_v_len = 0;
2989*3544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
2990*3544Sjbeck 
29910Sstevel@tonic-gate 	/* add to e_msgsize */
29920Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
29930Sstevel@tonic-gate 
29940Sstevel@tonic-gate 	if (h != NULL)
29950Sstevel@tonic-gate 	{
29960Sstevel@tonic-gate 		if (tTd(64, 10))
29970Sstevel@tonic-gate 			sm_dprintf("Replace default header %s value with %s\n",
2998*3544Sjbeck 				   h->h_field, mh_value);
29990Sstevel@tonic-gate 		if (MilterLogLevel > 8)
30000Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
30010Sstevel@tonic-gate 				  "Milter change: default header %s value with %s",
3002*3544Sjbeck 				  h->h_field, mh_value);
3003*3544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3004*3544Sjbeck 			h->h_value = mh_value;
3005*3544Sjbeck 		else
3006*3544Sjbeck 		{
3007*3544Sjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
3008*3544Sjbeck 			SM_FREE(mh_value);
3009*3544Sjbeck 		}
30100Sstevel@tonic-gate 		h->h_flags |= H_USER;
30110Sstevel@tonic-gate 	}
30120Sstevel@tonic-gate 	else
30130Sstevel@tonic-gate 	{
30140Sstevel@tonic-gate 		if (tTd(64, 10))
3015*3544Sjbeck 			sm_dprintf("Add %s: %s\n", response, mh_value);
30160Sstevel@tonic-gate 		if (MilterLogLevel > 8)
3017*3544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
3018*3544Sjbeck 				  "Milter add: header: %s: %s",
3019*3544Sjbeck 				  response, mh_value);
3020*3544Sjbeck 		addheader(newstr(response), mh_value, H_USER, e,
3021*3544Sjbeck 			!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3022*3544Sjbeck 		SM_FREE(mh_value);
30230Sstevel@tonic-gate 	}
30240Sstevel@tonic-gate }
3025*3544Sjbeck 
30260Sstevel@tonic-gate /*
30270Sstevel@tonic-gate **  MILTER_INSHEADER -- Insert the supplied header
30280Sstevel@tonic-gate **
30290Sstevel@tonic-gate **	Parameters:
3030*3544Sjbeck **		m -- current filter.
30310Sstevel@tonic-gate **		response -- encoded form of header/value.
30320Sstevel@tonic-gate **		rlen -- length of response.
30330Sstevel@tonic-gate **		e -- current envelope.
30340Sstevel@tonic-gate **
30350Sstevel@tonic-gate **	Returns:
30360Sstevel@tonic-gate **		none
30370Sstevel@tonic-gate **
3038616Sjbeck **	Notes:
3039616Sjbeck **		Unlike milter_addheader(), this does not attempt to determine
3040616Sjbeck **		if the header already exists in the envelope, even a
3041616Sjbeck **		deleted version.  It just blindly inserts.
30420Sstevel@tonic-gate */
30430Sstevel@tonic-gate 
30440Sstevel@tonic-gate static void
3045*3544Sjbeck milter_insheader(m, response, rlen, e)
3046*3544Sjbeck 	struct milter *m;
30470Sstevel@tonic-gate 	char *response;
30480Sstevel@tonic-gate 	ssize_t rlen;
30490Sstevel@tonic-gate 	ENVELOPE *e;
30500Sstevel@tonic-gate {
30510Sstevel@tonic-gate 	mi_int32 idx, i;
3052*3544Sjbeck 	int mh_v_len;
3053*3544Sjbeck 	char *field, *val, *mh_value;
30540Sstevel@tonic-gate 
30550Sstevel@tonic-gate 	if (tTd(64, 10))
30560Sstevel@tonic-gate 		sm_dprintf("milter_insheader: ");
30570Sstevel@tonic-gate 
30580Sstevel@tonic-gate 	/* sanity checks */
30590Sstevel@tonic-gate 	if (response == NULL)
30600Sstevel@tonic-gate 	{
30610Sstevel@tonic-gate 		if (tTd(64, 10))
30620Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
30630Sstevel@tonic-gate 		return;
30640Sstevel@tonic-gate 	}
30650Sstevel@tonic-gate 
30660Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
30670Sstevel@tonic-gate 	{
30680Sstevel@tonic-gate 		if (tTd(64, 10))
30690Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
30700Sstevel@tonic-gate 		return;
30710Sstevel@tonic-gate 	}
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate 	/* decode */
30740Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
30750Sstevel@tonic-gate 	idx = ntohl(i);
30760Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
30770Sstevel@tonic-gate 	val = field + strlen(field) + 1;
30780Sstevel@tonic-gate 
30790Sstevel@tonic-gate 	/* another sanity check */
30800Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
30810Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
30820Sstevel@tonic-gate 	{
30830Sstevel@tonic-gate 		if (tTd(64, 10))
30840Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
30850Sstevel@tonic-gate 		return;
30860Sstevel@tonic-gate 	}
30870Sstevel@tonic-gate 
30880Sstevel@tonic-gate 	if (*field == '\0')
30890Sstevel@tonic-gate 	{
30900Sstevel@tonic-gate 		if (tTd(64, 10))
30910Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
30920Sstevel@tonic-gate 		return;
30930Sstevel@tonic-gate 	}
30940Sstevel@tonic-gate 
30950Sstevel@tonic-gate 	/* add to e_msgsize */
30960Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
30970Sstevel@tonic-gate 
30980Sstevel@tonic-gate 	if (tTd(64, 10))
3099*3544Sjbeck 		sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
31000Sstevel@tonic-gate 	if (MilterLogLevel > 8)
31010Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
3102616Sjbeck 			  "Milter insert (%d): header: %s: %s",
31030Sstevel@tonic-gate 			  idx, field, val);
3104*3544Sjbeck 	mh_v_len = 0;
3105*3544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3106*3544Sjbeck 	insheader(idx, newstr(field), mh_value, H_USER, e,
3107*3544Sjbeck 		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
3108*3544Sjbeck 	SM_FREE(mh_value);
31090Sstevel@tonic-gate }
3110*3544Sjbeck 
31110Sstevel@tonic-gate /*
31120Sstevel@tonic-gate **  MILTER_CHANGEHEADER -- Change the supplied header in the message
31130Sstevel@tonic-gate **
31140Sstevel@tonic-gate **	Parameters:
3115*3544Sjbeck **		m -- current filter.
31160Sstevel@tonic-gate **		response -- encoded form of header/index/value.
31170Sstevel@tonic-gate **		rlen -- length of response.
31180Sstevel@tonic-gate **		e -- current envelope.
31190Sstevel@tonic-gate **
31200Sstevel@tonic-gate **	Returns:
31210Sstevel@tonic-gate **		none
31220Sstevel@tonic-gate */
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate static void
3125*3544Sjbeck milter_changeheader(m, response, rlen, e)
3126*3544Sjbeck 	struct milter *m;
31270Sstevel@tonic-gate 	char *response;
31280Sstevel@tonic-gate 	ssize_t rlen;
31290Sstevel@tonic-gate 	ENVELOPE *e;
31300Sstevel@tonic-gate {
31310Sstevel@tonic-gate 	mi_int32 i, index;
3132*3544Sjbeck 	int mh_v_len;
3133*3544Sjbeck 	char *field, *val, *mh_value;
31340Sstevel@tonic-gate 	HDR *h, *sysheader;
31350Sstevel@tonic-gate 
31360Sstevel@tonic-gate 	if (tTd(64, 10))
31370Sstevel@tonic-gate 		sm_dprintf("milter_changeheader: ");
31380Sstevel@tonic-gate 
31390Sstevel@tonic-gate 	/* sanity checks */
31400Sstevel@tonic-gate 	if (response == NULL)
31410Sstevel@tonic-gate 	{
31420Sstevel@tonic-gate 		if (tTd(64, 10))
31430Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
31440Sstevel@tonic-gate 		return;
31450Sstevel@tonic-gate 	}
31460Sstevel@tonic-gate 
31470Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
31480Sstevel@tonic-gate 	{
31490Sstevel@tonic-gate 		if (tTd(64, 10))
31500Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
31510Sstevel@tonic-gate 		return;
31520Sstevel@tonic-gate 	}
31530Sstevel@tonic-gate 
31540Sstevel@tonic-gate 	/* Find separating NUL */
31550Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
31560Sstevel@tonic-gate 	index = ntohl(i);
31570Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
31580Sstevel@tonic-gate 	val = field + strlen(field) + 1;
31590Sstevel@tonic-gate 
31600Sstevel@tonic-gate 	/* another sanity check */
31610Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
31620Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
31630Sstevel@tonic-gate 	{
31640Sstevel@tonic-gate 		if (tTd(64, 10))
31650Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
31660Sstevel@tonic-gate 		return;
31670Sstevel@tonic-gate 	}
31680Sstevel@tonic-gate 
31690Sstevel@tonic-gate 	if (*field == '\0')
31700Sstevel@tonic-gate 	{
31710Sstevel@tonic-gate 		if (tTd(64, 10))
31720Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
31730Sstevel@tonic-gate 		return;
31740Sstevel@tonic-gate 	}
31750Sstevel@tonic-gate 
3176*3544Sjbeck 	mh_v_len = 0;
3177*3544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
3178*3544Sjbeck 
31790Sstevel@tonic-gate 	sysheader = NULL;
31800Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
31810Sstevel@tonic-gate 	{
31820Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, field) == 0)
31830Sstevel@tonic-gate 		{
3184*3544Sjbeck 			if (bitset(H_USER, h->h_flags) && --index <= 0)
31850Sstevel@tonic-gate 			{
31860Sstevel@tonic-gate 				sysheader = NULL;
31870Sstevel@tonic-gate 				break;
31880Sstevel@tonic-gate 			}
31890Sstevel@tonic-gate 			else if (!bitset(H_USER, h->h_flags) &&
31900Sstevel@tonic-gate 				 !bitset(H_TRACE, h->h_flags))
31910Sstevel@tonic-gate 			{
31920Sstevel@tonic-gate 				/*
31930Sstevel@tonic-gate 				**  DRUMS msg-fmt draft says can only have
31940Sstevel@tonic-gate 				**  multiple occurences of trace fields,
31950Sstevel@tonic-gate 				**  so make sure we replace any non-trace,
31960Sstevel@tonic-gate 				**  non-user field.
31970Sstevel@tonic-gate 				*/
31980Sstevel@tonic-gate 
31990Sstevel@tonic-gate 				sysheader = h;
32000Sstevel@tonic-gate 			}
32010Sstevel@tonic-gate 		}
32020Sstevel@tonic-gate 	}
32030Sstevel@tonic-gate 
32040Sstevel@tonic-gate 	/* if not found as user-provided header at index, use sysheader */
32050Sstevel@tonic-gate 	if (h == NULL)
32060Sstevel@tonic-gate 		h = sysheader;
32070Sstevel@tonic-gate 
32080Sstevel@tonic-gate 	if (h == NULL)
32090Sstevel@tonic-gate 	{
32100Sstevel@tonic-gate 		if (*val == '\0')
32110Sstevel@tonic-gate 		{
32120Sstevel@tonic-gate 			if (tTd(64, 10))
32130Sstevel@tonic-gate 				sm_dprintf("Delete (noop) %s\n", field);
32140Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32150Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32160Sstevel@tonic-gate 					"Milter delete (noop): header: %s"
32170Sstevel@tonic-gate 					, field);
32180Sstevel@tonic-gate 		}
32190Sstevel@tonic-gate 		else
32200Sstevel@tonic-gate 		{
32210Sstevel@tonic-gate 			/* treat modify value with no existing header as add */
32220Sstevel@tonic-gate 			if (tTd(64, 10))
3223*3544Sjbeck 				sm_dprintf("Add %s: %s\n", field, mh_value);
32240Sstevel@tonic-gate 			if (MilterLogLevel > 8)
32250Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32260Sstevel@tonic-gate 					"Milter change (add): header: %s: %s"
3227*3544Sjbeck 					, field, mh_value);
3228*3544Sjbeck 			addheader(newstr(field), mh_value, H_USER, e,
3229*3544Sjbeck 				!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
32300Sstevel@tonic-gate 		}
32310Sstevel@tonic-gate 		return;
32320Sstevel@tonic-gate 	}
32330Sstevel@tonic-gate 
32340Sstevel@tonic-gate 	if (tTd(64, 10))
32350Sstevel@tonic-gate 	{
32360Sstevel@tonic-gate 		if (*val == '\0')
32370Sstevel@tonic-gate 		{
3238*3544Sjbeck 			sm_dprintf("Delete%s %s:%s\n",
32390Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32400Sstevel@tonic-gate 				   field,
32410Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value);
32420Sstevel@tonic-gate 		}
32430Sstevel@tonic-gate 		else
32440Sstevel@tonic-gate 		{
32450Sstevel@tonic-gate 			sm_dprintf("Change%s %s: from %s to %s\n",
32460Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32470Sstevel@tonic-gate 				   field,
32480Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value,
3249*3544Sjbeck 				   mh_value);
32500Sstevel@tonic-gate 		}
32510Sstevel@tonic-gate 	}
32520Sstevel@tonic-gate 
32530Sstevel@tonic-gate 	if (MilterLogLevel > 8)
32540Sstevel@tonic-gate 	{
32550Sstevel@tonic-gate 		if (*val == '\0')
32560Sstevel@tonic-gate 		{
32570Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
3258*3544Sjbeck 				  "Milter delete: header%s %s:%s",
32590Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32600Sstevel@tonic-gate 				  field,
32610Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value);
32620Sstevel@tonic-gate 		}
32630Sstevel@tonic-gate 		else
32640Sstevel@tonic-gate 		{
32650Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32660Sstevel@tonic-gate 				  "Milter change: header%s %s: from %s to %s",
32670Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32680Sstevel@tonic-gate 				  field,
32690Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value,
3270*3544Sjbeck 				  mh_value);
32710Sstevel@tonic-gate 		}
32720Sstevel@tonic-gate 	}
32730Sstevel@tonic-gate 
32740Sstevel@tonic-gate 	if (h != sysheader && h->h_value != NULL)
32750Sstevel@tonic-gate 	{
32760Sstevel@tonic-gate 		size_t l;
32770Sstevel@tonic-gate 
32780Sstevel@tonic-gate 		l = strlen(h->h_value);
32790Sstevel@tonic-gate 		if (l > e->e_msgsize)
32800Sstevel@tonic-gate 			e->e_msgsize = 0;
32810Sstevel@tonic-gate 		else
32820Sstevel@tonic-gate 			e->e_msgsize -= l;
32830Sstevel@tonic-gate 		/* rpool, don't free: sm_free(h->h_value); XXX */
32840Sstevel@tonic-gate 	}
32850Sstevel@tonic-gate 
32860Sstevel@tonic-gate 	if (*val == '\0')
32870Sstevel@tonic-gate 	{
32880Sstevel@tonic-gate 		/* Remove "Field: " from message size */
32890Sstevel@tonic-gate 		if (h != sysheader)
32900Sstevel@tonic-gate 		{
32910Sstevel@tonic-gate 			size_t l;
32920Sstevel@tonic-gate 
32930Sstevel@tonic-gate 			l = strlen(h->h_field) + 2;
32940Sstevel@tonic-gate 			if (l > e->e_msgsize)
32950Sstevel@tonic-gate 				e->e_msgsize = 0;
32960Sstevel@tonic-gate 			else
32970Sstevel@tonic-gate 				e->e_msgsize -= l;
32980Sstevel@tonic-gate 		}
32990Sstevel@tonic-gate 		h->h_value = NULL;
3300*3544Sjbeck 		SM_FREE(mh_value);
33010Sstevel@tonic-gate 	}
33020Sstevel@tonic-gate 	else
33030Sstevel@tonic-gate 	{
3304*3544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
3305*3544Sjbeck 			h->h_value = mh_value;
3306*3544Sjbeck 		else
3307*3544Sjbeck 		{
3308*3544Sjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
3309*3544Sjbeck 			SM_FREE(mh_value);
3310*3544Sjbeck 		}
33110Sstevel@tonic-gate 		h->h_flags |= H_USER;
33120Sstevel@tonic-gate 		e->e_msgsize += strlen(h->h_value);
33130Sstevel@tonic-gate 	}
33140Sstevel@tonic-gate }
3315*3544Sjbeck 
3316*3544Sjbeck /*
3317*3544Sjbeck **  MILTER_SPLIT_RESPONSE -- Split response into fields.
3318*3544Sjbeck **
3319*3544Sjbeck **	Parameters:
3320*3544Sjbeck **		response -- encoded repsonse.
3321*3544Sjbeck **		rlen -- length of response.
3322*3544Sjbeck **		pargc -- number of arguments (ouput)
3323*3544Sjbeck **
3324*3544Sjbeck **	Returns:
3325*3544Sjbeck **		array of pointers to the individual strings
3326*3544Sjbeck */
3327*3544Sjbeck 
3328*3544Sjbeck static char **milter_split_response __P((char *, ssize_t, int *));
3329*3544Sjbeck 
3330*3544Sjbeck static char **
3331*3544Sjbeck milter_split_response(response, rlen, pargc)
3332*3544Sjbeck 	char *response;
3333*3544Sjbeck 	ssize_t rlen;
3334*3544Sjbeck 	int *pargc;
3335*3544Sjbeck {
3336*3544Sjbeck 	char **s;
3337*3544Sjbeck 	size_t i;
3338*3544Sjbeck 	int elem, nelem;
3339*3544Sjbeck 
3340*3544Sjbeck 	SM_ASSERT(response != NULL);
3341*3544Sjbeck 	SM_ASSERT(pargc != NULL);
3342*3544Sjbeck 	*pargc = 0;
3343*3544Sjbeck 	if (rlen < 2 || strlen(response) >= (size_t) rlen)
3344*3544Sjbeck 	{
3345*3544Sjbeck 		if (tTd(64, 10))
3346*3544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3347*3544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
3348*3544Sjbeck 		return NULL;
3349*3544Sjbeck 	}
3350*3544Sjbeck 
3351*3544Sjbeck 	nelem = 0;
3352*3544Sjbeck 	for (i = 0; i < rlen; i++)
3353*3544Sjbeck 	{
3354*3544Sjbeck 		if (response[i] == '\0')
3355*3544Sjbeck 			++nelem;
3356*3544Sjbeck 	}
3357*3544Sjbeck 	if (nelem == 0)
3358*3544Sjbeck 		return NULL;
3359*3544Sjbeck 
3360*3544Sjbeck 	/* last entry is only for the name */
3361*3544Sjbeck 	s = (char **)malloc(nelem * (sizeof(*s)));
3362*3544Sjbeck 	if (s == NULL)
3363*3544Sjbeck 		return NULL;
3364*3544Sjbeck 	s[0] = response;
3365*3544Sjbeck 	for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
3366*3544Sjbeck 	{
3367*3544Sjbeck 		if (response[i] == '\0')
3368*3544Sjbeck 		{
3369*3544Sjbeck 			++elem;
3370*3544Sjbeck 			if (i + 1 >= rlen)
3371*3544Sjbeck 				s[elem] = NULL;
3372*3544Sjbeck 			else
3373*3544Sjbeck 				s[elem] = &(response[i + 1]);
3374*3544Sjbeck 		}
3375*3544Sjbeck 	}
3376*3544Sjbeck 	*pargc = nelem;
3377*3544Sjbeck 
3378*3544Sjbeck 	if (tTd(64, 10))
3379*3544Sjbeck 	{
3380*3544Sjbeck 		for (elem = 0; elem < nelem; elem++)
3381*3544Sjbeck 			sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
3382*3544Sjbeck 	}
3383*3544Sjbeck 
3384*3544Sjbeck 	/* overwrite last entry (already done above, just paranoia) */
3385*3544Sjbeck 	s[elem] = NULL;
3386*3544Sjbeck 	return s;
3387*3544Sjbeck }
3388*3544Sjbeck 
3389*3544Sjbeck /*
3390*3544Sjbeck **  MILTER_CHGFROM -- Change the envelope sender address
3391*3544Sjbeck **
3392*3544Sjbeck **	Parameters:
3393*3544Sjbeck **		response -- encoded form of recipient address.
3394*3544Sjbeck **		rlen -- length of response.
3395*3544Sjbeck **		e -- current envelope.
3396*3544Sjbeck **
3397*3544Sjbeck **	Returns:
3398*3544Sjbeck **		none
3399*3544Sjbeck */
3400*3544Sjbeck 
3401*3544Sjbeck static void
3402*3544Sjbeck milter_chgfrom(response, rlen, e)
3403*3544Sjbeck 	char *response;
3404*3544Sjbeck 	ssize_t rlen;
3405*3544Sjbeck 	ENVELOPE *e;
3406*3544Sjbeck {
3407*3544Sjbeck 	int olderrors, argc;
3408*3544Sjbeck 	char **argv;
3409*3544Sjbeck 
3410*3544Sjbeck 	if (tTd(64, 10))
3411*3544Sjbeck 		sm_dprintf("milter_chgfrom: ");
3412*3544Sjbeck 
3413*3544Sjbeck 	/* sanity checks */
3414*3544Sjbeck 	if (response == NULL)
3415*3544Sjbeck 	{
3416*3544Sjbeck 		if (tTd(64, 10))
3417*3544Sjbeck 			sm_dprintf("NULL response\n");
3418*3544Sjbeck 		return;
3419*3544Sjbeck 	}
3420*3544Sjbeck 
3421*3544Sjbeck 	if (*response == '\0' ||
3422*3544Sjbeck 	    strlen(response) + 1 > (size_t) rlen)
3423*3544Sjbeck 	{
3424*3544Sjbeck 		if (tTd(64, 10))
3425*3544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3426*3544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
3427*3544Sjbeck 		return;
3428*3544Sjbeck 	}
3429*3544Sjbeck 
3430*3544Sjbeck 	if (tTd(64, 10))
3431*3544Sjbeck 		sm_dprintf("%s\n", response);
3432*3544Sjbeck 	if (MilterLogLevel > 8)
3433*3544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
3434*3544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
3435*3544Sjbeck 	if (argc < 1 || argc > 2)
3436*3544Sjbeck 	{
3437*3544Sjbeck 		if (tTd(64, 10))
3438*3544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3439*3544Sjbeck 		return;
3440*3544Sjbeck 	}
3441*3544Sjbeck 
3442*3544Sjbeck 	olderrors = Errors;
3443*3544Sjbeck 	setsender(argv[0], e, NULL, '\0', false);
3444*3544Sjbeck 	if (argc == 2)
3445*3544Sjbeck 	{
3446*3544Sjbeck 		reset_mail_esmtp_args(e);
3447*3544Sjbeck 
3448*3544Sjbeck 		/*
3449*3544Sjbeck 		**  need "features" here: how to get those? via e?
3450*3544Sjbeck 		**  "fake" it for now: allow everything.
3451*3544Sjbeck 		*/
3452*3544Sjbeck 
3453*3544Sjbeck 		parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
3454*3544Sjbeck 				mail_esmtp_args);
3455*3544Sjbeck 	}
3456*3544Sjbeck 	Errors = olderrors;
3457*3544Sjbeck 	return;
3458*3544Sjbeck }
3459*3544Sjbeck 
3460*3544Sjbeck /*
3461*3544Sjbeck **  MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message
3462*3544Sjbeck **
3463*3544Sjbeck **	Parameters:
3464*3544Sjbeck **		response -- encoded form of recipient address.
3465*3544Sjbeck **		rlen -- length of response.
3466*3544Sjbeck **		e -- current envelope.
3467*3544Sjbeck **
3468*3544Sjbeck **	Returns:
3469*3544Sjbeck **		none
3470*3544Sjbeck */
3471*3544Sjbeck 
3472*3544Sjbeck static void
3473*3544Sjbeck milter_addrcpt_par(response, rlen, e)
3474*3544Sjbeck 	char *response;
3475*3544Sjbeck 	ssize_t rlen;
3476*3544Sjbeck 	ENVELOPE *e;
3477*3544Sjbeck {
3478*3544Sjbeck 	int olderrors, argc;
3479*3544Sjbeck 	char *delimptr;
3480*3544Sjbeck 	char **argv;
3481*3544Sjbeck 	ADDRESS *a;
3482*3544Sjbeck 
3483*3544Sjbeck 	if (tTd(64, 10))
3484*3544Sjbeck 		sm_dprintf("milter_addrcpt_par: ");
3485*3544Sjbeck 
3486*3544Sjbeck 	/* sanity checks */
3487*3544Sjbeck 	if (response == NULL)
3488*3544Sjbeck 	{
3489*3544Sjbeck 		if (tTd(64, 10))
3490*3544Sjbeck 			sm_dprintf("NULL response\n");
3491*3544Sjbeck 		return;
3492*3544Sjbeck 	}
3493*3544Sjbeck 
3494*3544Sjbeck 	if (tTd(64, 10))
3495*3544Sjbeck 		sm_dprintf("%s\n", response);
3496*3544Sjbeck 	if (MilterLogLevel > 8)
3497*3544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
3498*3544Sjbeck 
3499*3544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
3500*3544Sjbeck 	if (argc < 1 || argc > 2)
3501*3544Sjbeck 	{
3502*3544Sjbeck 		if (tTd(64, 10))
3503*3544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
3504*3544Sjbeck 		return;
3505*3544Sjbeck 	}
3506*3544Sjbeck 	olderrors = Errors;
3507*3544Sjbeck 
3508*3544Sjbeck 	/* how to set ESMTP arguments? */
3509*3544Sjbeck 	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
3510*3544Sjbeck 
3511*3544Sjbeck 	if (a != NULL && olderrors == Errors)
3512*3544Sjbeck 	{
3513*3544Sjbeck 		parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
3514*3544Sjbeck 				rcpt_esmtp_args);
3515*3544Sjbeck 		if (olderrors == Errors)
3516*3544Sjbeck 			a = recipient(a, &e->e_sendqueue, 0, e);
3517*3544Sjbeck 		else
3518*3544Sjbeck 			sm_dprintf("olderrors=%d, Errors=%d\n",
3519*3544Sjbeck 				olderrors, Errors);
3520*3544Sjbeck 	}
3521*3544Sjbeck 	else
3522*3544Sjbeck 	{
3523*3544Sjbeck 		sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
3524*3544Sjbeck 			a, olderrors, Errors);
3525*3544Sjbeck 	}
3526*3544Sjbeck 
3527*3544Sjbeck 	Errors = olderrors;
3528*3544Sjbeck 	return;
3529*3544Sjbeck }
3530*3544Sjbeck 
35310Sstevel@tonic-gate /*
35320Sstevel@tonic-gate **  MILTER_ADDRCPT -- Add the supplied recipient to the message
35330Sstevel@tonic-gate **
35340Sstevel@tonic-gate **	Parameters:
35350Sstevel@tonic-gate **		response -- encoded form of recipient address.
35360Sstevel@tonic-gate **		rlen -- length of response.
35370Sstevel@tonic-gate **		e -- current envelope.
35380Sstevel@tonic-gate **
35390Sstevel@tonic-gate **	Returns:
35400Sstevel@tonic-gate **		none
35410Sstevel@tonic-gate */
35420Sstevel@tonic-gate 
35430Sstevel@tonic-gate static void
35440Sstevel@tonic-gate milter_addrcpt(response, rlen, e)
35450Sstevel@tonic-gate 	char *response;
35460Sstevel@tonic-gate 	ssize_t rlen;
35470Sstevel@tonic-gate 	ENVELOPE *e;
35480Sstevel@tonic-gate {
35490Sstevel@tonic-gate 	int olderrors;
35500Sstevel@tonic-gate 
35510Sstevel@tonic-gate 	if (tTd(64, 10))
35520Sstevel@tonic-gate 		sm_dprintf("milter_addrcpt: ");
35530Sstevel@tonic-gate 
35540Sstevel@tonic-gate 	/* sanity checks */
35550Sstevel@tonic-gate 	if (response == NULL)
35560Sstevel@tonic-gate 	{
35570Sstevel@tonic-gate 		if (tTd(64, 10))
35580Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
35590Sstevel@tonic-gate 		return;
35600Sstevel@tonic-gate 	}
35610Sstevel@tonic-gate 
35620Sstevel@tonic-gate 	if (*response == '\0' ||
35630Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
35640Sstevel@tonic-gate 	{
35650Sstevel@tonic-gate 		if (tTd(64, 10))
35660Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
35670Sstevel@tonic-gate 				   (int) strlen(response), (int) (rlen - 1));
35680Sstevel@tonic-gate 		return;
35690Sstevel@tonic-gate 	}
35700Sstevel@tonic-gate 
35710Sstevel@tonic-gate 	if (tTd(64, 10))
35720Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
35730Sstevel@tonic-gate 	if (MilterLogLevel > 8)
35740Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
35750Sstevel@tonic-gate 	olderrors = Errors;
35760Sstevel@tonic-gate 	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
35770Sstevel@tonic-gate 	Errors = olderrors;
35780Sstevel@tonic-gate 	return;
35790Sstevel@tonic-gate }
3580*3544Sjbeck 
35810Sstevel@tonic-gate /*
35820Sstevel@tonic-gate **  MILTER_DELRCPT -- Delete the supplied recipient from the message
35830Sstevel@tonic-gate **
35840Sstevel@tonic-gate **	Parameters:
35850Sstevel@tonic-gate **		response -- encoded form of recipient address.
35860Sstevel@tonic-gate **		rlen -- length of response.
35870Sstevel@tonic-gate **		e -- current envelope.
35880Sstevel@tonic-gate **
35890Sstevel@tonic-gate **	Returns:
35900Sstevel@tonic-gate **		none
35910Sstevel@tonic-gate */
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate static void
35940Sstevel@tonic-gate milter_delrcpt(response, rlen, e)
35950Sstevel@tonic-gate 	char *response;
35960Sstevel@tonic-gate 	ssize_t rlen;
35970Sstevel@tonic-gate 	ENVELOPE *e;
35980Sstevel@tonic-gate {
35990Sstevel@tonic-gate 	if (tTd(64, 10))
36000Sstevel@tonic-gate 		sm_dprintf("milter_delrcpt: ");
36010Sstevel@tonic-gate 
36020Sstevel@tonic-gate 	/* sanity checks */
36030Sstevel@tonic-gate 	if (response == NULL)
36040Sstevel@tonic-gate 	{
36050Sstevel@tonic-gate 		if (tTd(64, 10))
36060Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
36070Sstevel@tonic-gate 		return;
36080Sstevel@tonic-gate 	}
36090Sstevel@tonic-gate 
36100Sstevel@tonic-gate 	if (*response == '\0' ||
36110Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
36120Sstevel@tonic-gate 	{
36130Sstevel@tonic-gate 		if (tTd(64, 10))
3614*3544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
3615*3544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
36160Sstevel@tonic-gate 		return;
36170Sstevel@tonic-gate 	}
36180Sstevel@tonic-gate 
36190Sstevel@tonic-gate 	if (tTd(64, 10))
36200Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
36210Sstevel@tonic-gate 	if (MilterLogLevel > 8)
36220Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
36230Sstevel@tonic-gate 			  response);
36240Sstevel@tonic-gate 	(void) removefromlist(response, &e->e_sendqueue, e);
36250Sstevel@tonic-gate 	return;
36260Sstevel@tonic-gate }
3627*3544Sjbeck 
36280Sstevel@tonic-gate /*
36290Sstevel@tonic-gate **  MILTER_REPLBODY -- Replace the current data file with new body
36300Sstevel@tonic-gate **
36310Sstevel@tonic-gate **	Parameters:
36320Sstevel@tonic-gate **		response -- encoded form of new body.
36330Sstevel@tonic-gate **		rlen -- length of response.
36340Sstevel@tonic-gate **		newfilter -- if first time called by a new filter
36350Sstevel@tonic-gate **		e -- current envelope.
36360Sstevel@tonic-gate **
36370Sstevel@tonic-gate **	Returns:
36380Sstevel@tonic-gate **		0 upon success, -1 upon failure
36390Sstevel@tonic-gate */
36400Sstevel@tonic-gate 
36410Sstevel@tonic-gate static int
36420Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e)
36430Sstevel@tonic-gate 	char *response;
36440Sstevel@tonic-gate 	ssize_t rlen;
36450Sstevel@tonic-gate 	bool newfilter;
36460Sstevel@tonic-gate 	ENVELOPE *e;
36470Sstevel@tonic-gate {
36480Sstevel@tonic-gate 	static char prevchar;
36490Sstevel@tonic-gate 	int i;
36500Sstevel@tonic-gate 
36510Sstevel@tonic-gate 	if (tTd(64, 10))
36520Sstevel@tonic-gate 		sm_dprintf("milter_replbody\n");
36530Sstevel@tonic-gate 
36540Sstevel@tonic-gate 	/* If a new filter, reset previous character and truncate data file */
36550Sstevel@tonic-gate 	if (newfilter)
36560Sstevel@tonic-gate 	{
36570Sstevel@tonic-gate 		off_t prevsize;
36580Sstevel@tonic-gate 		char dfname[MAXPATHLEN];
36590Sstevel@tonic-gate 
36600Sstevel@tonic-gate 		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
3661*3544Sjbeck 				  sizeof(dfname));
36620Sstevel@tonic-gate 
36630Sstevel@tonic-gate 		/* Reset prevchar */
36640Sstevel@tonic-gate 		prevchar = '\0';
36650Sstevel@tonic-gate 
36660Sstevel@tonic-gate 		/* Get the current data file information */
36670Sstevel@tonic-gate 		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
36680Sstevel@tonic-gate 		if (prevsize < 0)
36690Sstevel@tonic-gate 			prevsize = 0;
36700Sstevel@tonic-gate 
36710Sstevel@tonic-gate 		/* truncate current data file */
36720Sstevel@tonic-gate 		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
36730Sstevel@tonic-gate 		{
36740Sstevel@tonic-gate 			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
36750Sstevel@tonic-gate 			{
36760Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
36770Sstevel@tonic-gate 				return -1;
36780Sstevel@tonic-gate 			}
36790Sstevel@tonic-gate 		}
36800Sstevel@tonic-gate 		else
36810Sstevel@tonic-gate 		{
36820Sstevel@tonic-gate 			int err;
36830Sstevel@tonic-gate 
36840Sstevel@tonic-gate 			err = sm_io_error(e->e_dfp);
36850Sstevel@tonic-gate 			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
36860Sstevel@tonic-gate 
36870Sstevel@tonic-gate 			/*
36880Sstevel@tonic-gate 			**  Clear error if tried to fflush()
36890Sstevel@tonic-gate 			**  a read-only file pointer and
36900Sstevel@tonic-gate 			**  there wasn't a previous error.
36910Sstevel@tonic-gate 			*/
36920Sstevel@tonic-gate 
36930Sstevel@tonic-gate 			if (err == 0)
36940Sstevel@tonic-gate 				sm_io_clearerr(e->e_dfp);
36950Sstevel@tonic-gate 
36960Sstevel@tonic-gate 			/* errno is set implicitly by fseek() before return */
36970Sstevel@tonic-gate 			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
36980Sstevel@tonic-gate 					 0, SEEK_SET);
36990Sstevel@tonic-gate 			if (err < 0)
37000Sstevel@tonic-gate 			{
37010Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
37020Sstevel@tonic-gate 				return -1;
37030Sstevel@tonic-gate 			}
37040Sstevel@tonic-gate # if NOFTRUNCATE
37050Sstevel@tonic-gate 			/* XXX: Not much we can do except rewind it */
37060Sstevel@tonic-gate 			errno = EINVAL;
37070Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
37080Sstevel@tonic-gate 			return -1;
37090Sstevel@tonic-gate # else /* NOFTRUNCATE */
37100Sstevel@tonic-gate 			err = ftruncate(sm_io_getinfo(e->e_dfp,
37110Sstevel@tonic-gate 						      SM_IO_WHAT_FD, NULL),
37120Sstevel@tonic-gate 					0);
37130Sstevel@tonic-gate 			if (err < 0)
37140Sstevel@tonic-gate 			{
37150Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
37160Sstevel@tonic-gate 				return -1;
37170Sstevel@tonic-gate 			}
37180Sstevel@tonic-gate # endif /* NOFTRUNCATE */
37190Sstevel@tonic-gate 		}
37200Sstevel@tonic-gate 
37210Sstevel@tonic-gate 		if (prevsize > e->e_msgsize)
37220Sstevel@tonic-gate 			e->e_msgsize = 0;
37230Sstevel@tonic-gate 		else
37240Sstevel@tonic-gate 			e->e_msgsize -= prevsize;
37250Sstevel@tonic-gate 	}
37260Sstevel@tonic-gate 
37270Sstevel@tonic-gate 	if (newfilter && MilterLogLevel > 8)
37280Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
37290Sstevel@tonic-gate 
37300Sstevel@tonic-gate 	if (response == NULL)
37310Sstevel@tonic-gate 	{
37320Sstevel@tonic-gate 		/* Flush the buffered '\r' */
37330Sstevel@tonic-gate 		if (prevchar == '\r')
37340Sstevel@tonic-gate 		{
37350Sstevel@tonic-gate 			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
37360Sstevel@tonic-gate 			e->e_msgsize++;
37370Sstevel@tonic-gate 		}
37380Sstevel@tonic-gate 		return 0;
37390Sstevel@tonic-gate 	}
37400Sstevel@tonic-gate 
37410Sstevel@tonic-gate 	for (i = 0; i < rlen; i++)
37420Sstevel@tonic-gate 	{
37430Sstevel@tonic-gate 		/* Buffered char from last chunk */
37440Sstevel@tonic-gate 		if (i == 0 && prevchar == '\r')
37450Sstevel@tonic-gate 		{
37460Sstevel@tonic-gate 			/* Not CRLF, output prevchar */
37470Sstevel@tonic-gate 			if (response[i] != '\n')
37480Sstevel@tonic-gate 			{
37490Sstevel@tonic-gate 				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
37500Sstevel@tonic-gate 						  prevchar);
37510Sstevel@tonic-gate 				e->e_msgsize++;
37520Sstevel@tonic-gate 			}
37530Sstevel@tonic-gate 			prevchar = '\0';
37540Sstevel@tonic-gate 		}
37550Sstevel@tonic-gate 
37560Sstevel@tonic-gate 		/* Turn CRLF into LF */
37570Sstevel@tonic-gate 		if (response[i] == '\r')
37580Sstevel@tonic-gate 		{
37590Sstevel@tonic-gate 			/* check if at end of chunk */
37600Sstevel@tonic-gate 			if (i + 1 < rlen)
37610Sstevel@tonic-gate 			{
37620Sstevel@tonic-gate 				/* If LF, strip CR */
37630Sstevel@tonic-gate 				if (response[i + 1] == '\n')
37640Sstevel@tonic-gate 					i++;
37650Sstevel@tonic-gate 			}
37660Sstevel@tonic-gate 			else
37670Sstevel@tonic-gate 			{
37680Sstevel@tonic-gate 				/* check next chunk */
37690Sstevel@tonic-gate 				prevchar = '\r';
37700Sstevel@tonic-gate 				continue;
37710Sstevel@tonic-gate 			}
37720Sstevel@tonic-gate 		}
37730Sstevel@tonic-gate 		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
37740Sstevel@tonic-gate 		e->e_msgsize++;
37750Sstevel@tonic-gate 	}
37760Sstevel@tonic-gate 	return 0;
37770Sstevel@tonic-gate }
37780Sstevel@tonic-gate 
37790Sstevel@tonic-gate /*
37800Sstevel@tonic-gate **  MTA callouts
37810Sstevel@tonic-gate */
37820Sstevel@tonic-gate 
37830Sstevel@tonic-gate /*
37840Sstevel@tonic-gate **  MILTER_INIT -- open and negotiate with all of the filters
37850Sstevel@tonic-gate **
37860Sstevel@tonic-gate **	Parameters:
37870Sstevel@tonic-gate **		e -- current envelope.
37880Sstevel@tonic-gate **		state -- return state from response.
37890Sstevel@tonic-gate **
37900Sstevel@tonic-gate **	Returns:
37910Sstevel@tonic-gate **		true iff at least one filter is active
37920Sstevel@tonic-gate */
37930Sstevel@tonic-gate 
37940Sstevel@tonic-gate /* ARGSUSED */
37950Sstevel@tonic-gate bool
37960Sstevel@tonic-gate milter_init(e, state)
37970Sstevel@tonic-gate 	ENVELOPE *e;
37980Sstevel@tonic-gate 	char *state;
37990Sstevel@tonic-gate {
38000Sstevel@tonic-gate 	int i;
38010Sstevel@tonic-gate 
38020Sstevel@tonic-gate 	if (tTd(64, 10))
38030Sstevel@tonic-gate 		sm_dprintf("milter_init\n");
38040Sstevel@tonic-gate 
38050Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
38060Sstevel@tonic-gate 	if (InputFilters[0] == NULL)
38070Sstevel@tonic-gate 	{
38080Sstevel@tonic-gate 		if (MilterLogLevel > 10)
38090Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38100Sstevel@tonic-gate 				  "Milter: no active filter");
38110Sstevel@tonic-gate 		return false;
38120Sstevel@tonic-gate 	}
38130Sstevel@tonic-gate 
38140Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
38150Sstevel@tonic-gate 	{
38160Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
38170Sstevel@tonic-gate 
38180Sstevel@tonic-gate 		m->mf_sock = milter_open(m, false, e);
38190Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
38200Sstevel@tonic-gate 		{
38210Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38220Sstevel@tonic-gate 			break;
38230Sstevel@tonic-gate 		}
38240Sstevel@tonic-gate 
38250Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
38260Sstevel@tonic-gate 		    milter_negotiate(m, e) < 0 ||
38270Sstevel@tonic-gate 		    m->mf_state == SMFS_ERROR)
38280Sstevel@tonic-gate 		{
38290Sstevel@tonic-gate 			if (tTd(64, 5))
38300Sstevel@tonic-gate 				sm_dprintf("milter_init(%s): failed to %s\n",
38310Sstevel@tonic-gate 					   m->mf_name,
38320Sstevel@tonic-gate 					   m->mf_sock < 0 ? "open" :
38330Sstevel@tonic-gate 							    "negotiate");
38340Sstevel@tonic-gate 			if (MilterLogLevel > 0)
38350Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
38360Sstevel@tonic-gate 					  "Milter (%s): init failed to %s",
38370Sstevel@tonic-gate 					  m->mf_name,
38380Sstevel@tonic-gate 					  m->mf_sock < 0 ? "open" :
38390Sstevel@tonic-gate 							   "negotiate");
38400Sstevel@tonic-gate 
38410Sstevel@tonic-gate 			/* if negotation failure, close socket */
38420Sstevel@tonic-gate 			milter_error(m, e);
38430Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38440Sstevel@tonic-gate 			continue;
38450Sstevel@tonic-gate 		}
38460Sstevel@tonic-gate 		if (MilterLogLevel > 9)
38470Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38480Sstevel@tonic-gate 				  "Milter (%s): init success to %s",
38490Sstevel@tonic-gate 				  m->mf_name,
38500Sstevel@tonic-gate 				  m->mf_sock < 0 ? "open" : "negotiate");
38510Sstevel@tonic-gate 	}
38520Sstevel@tonic-gate 
38530Sstevel@tonic-gate 	/*
38540Sstevel@tonic-gate 	**  If something temp/perm failed with one of the filters,
38550Sstevel@tonic-gate 	**  we won't be using any of them, so clear any existing
38560Sstevel@tonic-gate 	**  connections.
38570Sstevel@tonic-gate 	*/
38580Sstevel@tonic-gate 
38590Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
38600Sstevel@tonic-gate 		milter_quit(e);
38610Sstevel@tonic-gate 
38620Sstevel@tonic-gate 	return true;
38630Sstevel@tonic-gate }
3864*3544Sjbeck 
38650Sstevel@tonic-gate /*
38660Sstevel@tonic-gate **  MILTER_CONNECT -- send connection info to milter filters
38670Sstevel@tonic-gate **
38680Sstevel@tonic-gate **	Parameters:
38690Sstevel@tonic-gate **		hostname -- hostname of remote machine.
38700Sstevel@tonic-gate **		addr -- address of remote machine.
38710Sstevel@tonic-gate **		e -- current envelope.
38720Sstevel@tonic-gate **		state -- return state from response.
38730Sstevel@tonic-gate **
38740Sstevel@tonic-gate **	Returns:
38750Sstevel@tonic-gate **		response string (may be NULL)
38760Sstevel@tonic-gate */
38770Sstevel@tonic-gate 
38780Sstevel@tonic-gate char *
38790Sstevel@tonic-gate milter_connect(hostname, addr, e, state)
38800Sstevel@tonic-gate 	char *hostname;
38810Sstevel@tonic-gate 	SOCKADDR addr;
38820Sstevel@tonic-gate 	ENVELOPE *e;
38830Sstevel@tonic-gate 	char *state;
38840Sstevel@tonic-gate {
38850Sstevel@tonic-gate 	char family;
38860Sstevel@tonic-gate 	unsigned short port;
38870Sstevel@tonic-gate 	char *buf, *bp;
38880Sstevel@tonic-gate 	char *response;
38890Sstevel@tonic-gate 	char *sockinfo = NULL;
38900Sstevel@tonic-gate 	ssize_t s;
38910Sstevel@tonic-gate # if NETINET6
38920Sstevel@tonic-gate 	char buf6[INET6_ADDRSTRLEN];
38930Sstevel@tonic-gate # endif /* NETINET6 */
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 	if (tTd(64, 10))
38960Sstevel@tonic-gate 		sm_dprintf("milter_connect(%s)\n", hostname);
38970Sstevel@tonic-gate 	if (MilterLogLevel > 9)
38980Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
38990Sstevel@tonic-gate 
39000Sstevel@tonic-gate 	/* gather data */
39010Sstevel@tonic-gate 	switch (addr.sa.sa_family)
39020Sstevel@tonic-gate 	{
39030Sstevel@tonic-gate # if NETUNIX
39040Sstevel@tonic-gate 	  case AF_UNIX:
39050Sstevel@tonic-gate 		family = SMFIA_UNIX;
39060Sstevel@tonic-gate 		port = htons(0);
39070Sstevel@tonic-gate 		sockinfo = addr.sunix.sun_path;
39080Sstevel@tonic-gate 		break;
39090Sstevel@tonic-gate # endif /* NETUNIX */
39100Sstevel@tonic-gate 
39110Sstevel@tonic-gate # if NETINET
39120Sstevel@tonic-gate 	  case AF_INET:
39130Sstevel@tonic-gate 		family = SMFIA_INET;
39140Sstevel@tonic-gate 		port = addr.sin.sin_port;
39150Sstevel@tonic-gate 		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
39160Sstevel@tonic-gate 		break;
39170Sstevel@tonic-gate # endif /* NETINET */
39180Sstevel@tonic-gate 
39190Sstevel@tonic-gate # if NETINET6
39200Sstevel@tonic-gate 	  case AF_INET6:
39210Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
39220Sstevel@tonic-gate 			family = SMFIA_INET;
39230Sstevel@tonic-gate 		else
39240Sstevel@tonic-gate 			family = SMFIA_INET6;
39250Sstevel@tonic-gate 		port = addr.sin6.sin6_port;
39260Sstevel@tonic-gate 		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
3927*3544Sjbeck 				       sizeof(buf6));
39280Sstevel@tonic-gate 		if (sockinfo == NULL)
39290Sstevel@tonic-gate 			sockinfo = "";
39300Sstevel@tonic-gate 		break;
39310Sstevel@tonic-gate # endif /* NETINET6 */
39320Sstevel@tonic-gate 
39330Sstevel@tonic-gate 	  default:
39340Sstevel@tonic-gate 		family = SMFIA_UNKNOWN;
39350Sstevel@tonic-gate 		break;
39360Sstevel@tonic-gate 	}
39370Sstevel@tonic-gate 
39380Sstevel@tonic-gate 	s = strlen(hostname) + 1 + sizeof(family);
39390Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39400Sstevel@tonic-gate 		s += sizeof(port) + strlen(sockinfo) + 1;
39410Sstevel@tonic-gate 
39420Sstevel@tonic-gate 	buf = (char *) xalloc(s);
39430Sstevel@tonic-gate 	bp = buf;
39440Sstevel@tonic-gate 
39450Sstevel@tonic-gate 	/* put together data */
39460Sstevel@tonic-gate 	(void) memcpy(bp, hostname, strlen(hostname));
39470Sstevel@tonic-gate 	bp += strlen(hostname);
39480Sstevel@tonic-gate 	*bp++ = '\0';
3949*3544Sjbeck 	(void) memcpy(bp, &family, sizeof(family));
3950*3544Sjbeck 	bp += sizeof(family);
39510Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39520Sstevel@tonic-gate 	{
3953*3544Sjbeck 		(void) memcpy(bp, &port, sizeof(port));
3954*3544Sjbeck 		bp += sizeof(port);
39550Sstevel@tonic-gate 
39560Sstevel@tonic-gate 		/* include trailing '\0' */
39570Sstevel@tonic-gate 		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
39580Sstevel@tonic-gate 	}
39590Sstevel@tonic-gate 
3960*3544Sjbeck 	response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros,
3961*3544Sjbeck 				e, state, "connect", false);
39620Sstevel@tonic-gate 	sm_free(buf); /* XXX */
39630Sstevel@tonic-gate 
39640Sstevel@tonic-gate 	/*
39650Sstevel@tonic-gate 	**  If this message connection is done for,
39660Sstevel@tonic-gate 	**  close the filters.
39670Sstevel@tonic-gate 	*/
39680Sstevel@tonic-gate 
39690Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
39700Sstevel@tonic-gate 	{
39710Sstevel@tonic-gate 		if (MilterLogLevel > 9)
39720Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
39730Sstevel@tonic-gate 		milter_quit(e);
39740Sstevel@tonic-gate 	}
39750Sstevel@tonic-gate 	else
39760Sstevel@tonic-gate 		milter_per_connection_check(e);
39770Sstevel@tonic-gate 
39780Sstevel@tonic-gate 	/*
39790Sstevel@tonic-gate 	**  SMFIR_REPLYCODE can't work with connect due to
39800Sstevel@tonic-gate 	**  the requirements of SMTP.  Therefore, ignore the
39810Sstevel@tonic-gate 	**  reply code text but keep the state it would reflect.
39820Sstevel@tonic-gate 	*/
39830Sstevel@tonic-gate 
39840Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE)
39850Sstevel@tonic-gate 	{
39860Sstevel@tonic-gate 		if (response != NULL &&
39870Sstevel@tonic-gate 		    *response == '4')
39880Sstevel@tonic-gate 		{
39890Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
39900Sstevel@tonic-gate 				*state = SMFIR_SHUTDOWN;
39910Sstevel@tonic-gate 			else
39920Sstevel@tonic-gate 				*state = SMFIR_TEMPFAIL;
39930Sstevel@tonic-gate 		}
39940Sstevel@tonic-gate 		else
39950Sstevel@tonic-gate 			*state = SMFIR_REJECT;
39960Sstevel@tonic-gate 		if (response != NULL)
39970Sstevel@tonic-gate 		{
39980Sstevel@tonic-gate 			sm_free(response); /* XXX */
39990Sstevel@tonic-gate 			response = NULL;
40000Sstevel@tonic-gate 		}
40010Sstevel@tonic-gate 	}
40020Sstevel@tonic-gate 	return response;
40030Sstevel@tonic-gate }
4004*3544Sjbeck 
40050Sstevel@tonic-gate /*
40060Sstevel@tonic-gate **  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
40070Sstevel@tonic-gate **
40080Sstevel@tonic-gate **	Parameters:
40090Sstevel@tonic-gate **		helo -- argument to SMTP HELO/EHLO command.
40100Sstevel@tonic-gate **		e -- current envelope.
40110Sstevel@tonic-gate **		state -- return state from response.
40120Sstevel@tonic-gate **
40130Sstevel@tonic-gate **	Returns:
40140Sstevel@tonic-gate **		response string (may be NULL)
40150Sstevel@tonic-gate */
40160Sstevel@tonic-gate 
40170Sstevel@tonic-gate char *
40180Sstevel@tonic-gate milter_helo(helo, e, state)
40190Sstevel@tonic-gate 	char *helo;
40200Sstevel@tonic-gate 	ENVELOPE *e;
40210Sstevel@tonic-gate 	char *state;
40220Sstevel@tonic-gate {
40230Sstevel@tonic-gate 	int i;
40240Sstevel@tonic-gate 	char *response;
40250Sstevel@tonic-gate 
40260Sstevel@tonic-gate 	if (tTd(64, 10))
40270Sstevel@tonic-gate 		sm_dprintf("milter_helo(%s)\n", helo);
40280Sstevel@tonic-gate 
40290Sstevel@tonic-gate 	/* HELO/EHLO can come at any point */
40300Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40310Sstevel@tonic-gate 	{
40320Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40330Sstevel@tonic-gate 
40340Sstevel@tonic-gate 		switch (m->mf_state)
40350Sstevel@tonic-gate 		{
40360Sstevel@tonic-gate 		  case SMFS_INMSG:
40370Sstevel@tonic-gate 			/* abort in message filters */
40380Sstevel@tonic-gate 			milter_abort_filter(m, e);
40390Sstevel@tonic-gate 			/* FALLTHROUGH */
40400Sstevel@tonic-gate 
40410Sstevel@tonic-gate 		  case SMFS_DONE:
40420Sstevel@tonic-gate 			/* reset done filters */
40430Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
40440Sstevel@tonic-gate 			break;
40450Sstevel@tonic-gate 		}
40460Sstevel@tonic-gate 	}
40470Sstevel@tonic-gate 
40480Sstevel@tonic-gate 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
4049*3544Sjbeck 				  MilterHeloMacros, e, state, "helo", false);
40500Sstevel@tonic-gate 	milter_per_connection_check(e);
40510Sstevel@tonic-gate 	return response;
40520Sstevel@tonic-gate }
4053*3544Sjbeck 
40540Sstevel@tonic-gate /*
40550Sstevel@tonic-gate **  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
40560Sstevel@tonic-gate **
40570Sstevel@tonic-gate **	Parameters:
40580Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == sender).
40590Sstevel@tonic-gate **		e -- current envelope.
40600Sstevel@tonic-gate **		state -- return state from response.
40610Sstevel@tonic-gate **
40620Sstevel@tonic-gate **	Returns:
40630Sstevel@tonic-gate **		response string (may be NULL)
40640Sstevel@tonic-gate */
40650Sstevel@tonic-gate 
40660Sstevel@tonic-gate char *
40670Sstevel@tonic-gate milter_envfrom(args, e, state)
40680Sstevel@tonic-gate 	char **args;
40690Sstevel@tonic-gate 	ENVELOPE *e;
40700Sstevel@tonic-gate 	char *state;
40710Sstevel@tonic-gate {
40720Sstevel@tonic-gate 	int i;
40730Sstevel@tonic-gate 	char *buf, *bp;
40740Sstevel@tonic-gate 	char *response;
40750Sstevel@tonic-gate 	ssize_t s;
40760Sstevel@tonic-gate 
40770Sstevel@tonic-gate 	if (tTd(64, 10))
40780Sstevel@tonic-gate 	{
40790Sstevel@tonic-gate 		sm_dprintf("milter_envfrom:");
40800Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
40810Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
40820Sstevel@tonic-gate 		sm_dprintf("\n");
40830Sstevel@tonic-gate 	}
40840Sstevel@tonic-gate 
40850Sstevel@tonic-gate 	/* sanity check */
40860Sstevel@tonic-gate 	if (args[0] == NULL)
40870Sstevel@tonic-gate 	{
40880Sstevel@tonic-gate 		*state = SMFIR_REJECT;
40890Sstevel@tonic-gate 		if (MilterLogLevel > 10)
40900Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
40910Sstevel@tonic-gate 				  "Milter: reject, no sender");
40920Sstevel@tonic-gate 		return NULL;
40930Sstevel@tonic-gate 	}
40940Sstevel@tonic-gate 
40950Sstevel@tonic-gate 	/* new message, so ... */
40960Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40970Sstevel@tonic-gate 	{
40980Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40990Sstevel@tonic-gate 
41000Sstevel@tonic-gate 		switch (m->mf_state)
41010Sstevel@tonic-gate 		{
41020Sstevel@tonic-gate 		  case SMFS_INMSG:
41030Sstevel@tonic-gate 			/* abort in message filters */
41040Sstevel@tonic-gate 			milter_abort_filter(m, e);
41050Sstevel@tonic-gate 			/* FALLTHROUGH */
41060Sstevel@tonic-gate 
41070Sstevel@tonic-gate 		  case SMFS_DONE:
41080Sstevel@tonic-gate 			/* reset done filters */
41090Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
41100Sstevel@tonic-gate 			break;
41110Sstevel@tonic-gate 		}
41120Sstevel@tonic-gate 	}
41130Sstevel@tonic-gate 
41140Sstevel@tonic-gate 	/* put together data */
41150Sstevel@tonic-gate 	s = 0;
41160Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41170Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41180Sstevel@tonic-gate 
41190Sstevel@tonic-gate 	if (s < 0)
41200Sstevel@tonic-gate 	{
41210Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
41220Sstevel@tonic-gate 		return NULL;
41230Sstevel@tonic-gate 	}
41240Sstevel@tonic-gate 
41250Sstevel@tonic-gate 	buf = (char *) xalloc(s);
41260Sstevel@tonic-gate 	bp = buf;
41270Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41280Sstevel@tonic-gate 	{
41290Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
41300Sstevel@tonic-gate 		bp += strlen(bp) + 1;
41310Sstevel@tonic-gate 	}
41320Sstevel@tonic-gate 
41330Sstevel@tonic-gate 	if (MilterLogLevel > 14)
4134*3544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
41350Sstevel@tonic-gate 
41360Sstevel@tonic-gate 	/* send it over */
4137*3544Sjbeck 	response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros,
4138*3544Sjbeck 				e, state, "mail", false);
41390Sstevel@tonic-gate 	sm_free(buf); /* XXX */
41400Sstevel@tonic-gate 
41410Sstevel@tonic-gate 	/*
41420Sstevel@tonic-gate 	**  If filter rejects/discards a per message command,
41430Sstevel@tonic-gate 	**  abort the other filters since we are done with the
41440Sstevel@tonic-gate 	**  current message.
41450Sstevel@tonic-gate 	*/
41460Sstevel@tonic-gate 
41470Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
41480Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
4149*3544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
41500Sstevel@tonic-gate 	return response;
41510Sstevel@tonic-gate }
41520Sstevel@tonic-gate 
41530Sstevel@tonic-gate /*
41540Sstevel@tonic-gate **  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
41550Sstevel@tonic-gate **
41560Sstevel@tonic-gate **	Parameters:
41570Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == recipient).
41580Sstevel@tonic-gate **		e -- current envelope.
41590Sstevel@tonic-gate **		state -- return state from response.
4160*3544Sjbeck **		rcpt_error -- does RCPT have an error?
41610Sstevel@tonic-gate **
41620Sstevel@tonic-gate **	Returns:
41630Sstevel@tonic-gate **		response string (may be NULL)
41640Sstevel@tonic-gate */
41650Sstevel@tonic-gate 
41660Sstevel@tonic-gate char *
4167*3544Sjbeck milter_envrcpt(args, e, state, rcpt_error)
41680Sstevel@tonic-gate 	char **args;
41690Sstevel@tonic-gate 	ENVELOPE *e;
41700Sstevel@tonic-gate 	char *state;
4171*3544Sjbeck 	bool rcpt_error;
41720Sstevel@tonic-gate {
41730Sstevel@tonic-gate 	int i;
41740Sstevel@tonic-gate 	char *buf, *bp;
41750Sstevel@tonic-gate 	char *response;
41760Sstevel@tonic-gate 	ssize_t s;
41770Sstevel@tonic-gate 
41780Sstevel@tonic-gate 	if (tTd(64, 10))
41790Sstevel@tonic-gate 	{
41800Sstevel@tonic-gate 		sm_dprintf("milter_envrcpt:");
41810Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
41820Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
41830Sstevel@tonic-gate 		sm_dprintf("\n");
41840Sstevel@tonic-gate 	}
41850Sstevel@tonic-gate 
41860Sstevel@tonic-gate 	/* sanity check */
41870Sstevel@tonic-gate 	if (args[0] == NULL)
41880Sstevel@tonic-gate 	{
41890Sstevel@tonic-gate 		*state = SMFIR_REJECT;
41900Sstevel@tonic-gate 		if (MilterLogLevel > 10)
41910Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
41920Sstevel@tonic-gate 		return NULL;
41930Sstevel@tonic-gate 	}
41940Sstevel@tonic-gate 
41950Sstevel@tonic-gate 	/* put together data */
41960Sstevel@tonic-gate 	s = 0;
41970Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41980Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41990Sstevel@tonic-gate 
42000Sstevel@tonic-gate 	if (s < 0)
42010Sstevel@tonic-gate 	{
42020Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
42030Sstevel@tonic-gate 		return NULL;
42040Sstevel@tonic-gate 	}
42050Sstevel@tonic-gate 
42060Sstevel@tonic-gate 	buf = (char *) xalloc(s);
42070Sstevel@tonic-gate 	bp = buf;
42080Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
42090Sstevel@tonic-gate 	{
42100Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
42110Sstevel@tonic-gate 		bp += strlen(bp) + 1;
42120Sstevel@tonic-gate 	}
42130Sstevel@tonic-gate 
42140Sstevel@tonic-gate 	if (MilterLogLevel > 14)
42150Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
42160Sstevel@tonic-gate 
42170Sstevel@tonic-gate 	/* send it over */
4218*3544Sjbeck 	response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros,
4219*3544Sjbeck 				e, state, "rcpt", rcpt_error);
42200Sstevel@tonic-gate 	sm_free(buf); /* XXX */
42210Sstevel@tonic-gate 	return response;
42220Sstevel@tonic-gate }
42230Sstevel@tonic-gate 
42240Sstevel@tonic-gate /*
42250Sstevel@tonic-gate **  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
42260Sstevel@tonic-gate **
42270Sstevel@tonic-gate **	Parameters:
42280Sstevel@tonic-gate **		e -- current envelope.
42290Sstevel@tonic-gate **		state -- return state from response.
42300Sstevel@tonic-gate **
42310Sstevel@tonic-gate **	Returns:
42320Sstevel@tonic-gate **		response string (may be NULL)
42330Sstevel@tonic-gate */
42340Sstevel@tonic-gate 
42350Sstevel@tonic-gate char *
42360Sstevel@tonic-gate milter_data_cmd(e, state)
42370Sstevel@tonic-gate 	ENVELOPE *e;
42380Sstevel@tonic-gate 	char *state;
42390Sstevel@tonic-gate {
42400Sstevel@tonic-gate 	if (tTd(64, 10))
42410Sstevel@tonic-gate 		sm_dprintf("milter_data_cmd\n");
42420Sstevel@tonic-gate 
42430Sstevel@tonic-gate 	/* send it over */
4244*3544Sjbeck 	return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state,
4245*3544Sjbeck 				"data", false);
42460Sstevel@tonic-gate }
42470Sstevel@tonic-gate 
42480Sstevel@tonic-gate /*
42490Sstevel@tonic-gate **  MILTER_DATA -- send message headers/body and gather final message results
42500Sstevel@tonic-gate **
42510Sstevel@tonic-gate **	Parameters:
42520Sstevel@tonic-gate **		e -- current envelope.
42530Sstevel@tonic-gate **		state -- return state from response.
42540Sstevel@tonic-gate **
42550Sstevel@tonic-gate **	Returns:
42560Sstevel@tonic-gate **		response string (may be NULL)
42570Sstevel@tonic-gate **
42580Sstevel@tonic-gate **	Side effects:
42590Sstevel@tonic-gate **		- Uses e->e_dfp for access to the body
42600Sstevel@tonic-gate **		- Can call the various milter action routines to
42610Sstevel@tonic-gate **		  modify the envelope or message.
42620Sstevel@tonic-gate */
42630Sstevel@tonic-gate 
42640Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \
42650Sstevel@tonic-gate 	if (*state == SMFIR_ACCEPT || \
42660Sstevel@tonic-gate 	    m->mf_state == SMFS_DONE || \
42670Sstevel@tonic-gate 	    m->mf_state == SMFS_ERROR) \
42680Sstevel@tonic-gate 	{ \
42690Sstevel@tonic-gate 		if (m->mf_state != SMFS_ERROR) \
42700Sstevel@tonic-gate 			m->mf_state = SMFS_DONE; \
42710Sstevel@tonic-gate 		continue;	/* to next filter */ \
42720Sstevel@tonic-gate 	} \
42730Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE) \
42740Sstevel@tonic-gate 	{ \
42750Sstevel@tonic-gate 		m->mf_state = SMFS_DONE; \
42760Sstevel@tonic-gate 		goto finishup; \
42770Sstevel@tonic-gate 	}
42780Sstevel@tonic-gate 
42790Sstevel@tonic-gate char *
42800Sstevel@tonic-gate milter_data(e, state)
42810Sstevel@tonic-gate 	ENVELOPE *e;
42820Sstevel@tonic-gate 	char *state;
42830Sstevel@tonic-gate {
42840Sstevel@tonic-gate 	bool replbody = false;		/* milter_replbody() called? */
42850Sstevel@tonic-gate 	bool replfailed = false;	/* milter_replbody() failed? */
42860Sstevel@tonic-gate 	bool rewind = false;		/* rewind data file? */
42870Sstevel@tonic-gate 	bool dfopen = false;		/* data file open for writing? */
42880Sstevel@tonic-gate 	bool newfilter;			/* reset on each new filter */
42890Sstevel@tonic-gate 	char rcmd;
42900Sstevel@tonic-gate 	int i;
42910Sstevel@tonic-gate 	int save_errno;
42920Sstevel@tonic-gate 	char *response = NULL;
42930Sstevel@tonic-gate 	time_t eomsent;
42940Sstevel@tonic-gate 	ssize_t rlen;
42950Sstevel@tonic-gate 
42960Sstevel@tonic-gate 	if (tTd(64, 10))
42970Sstevel@tonic-gate 		sm_dprintf("milter_data\n");
42980Sstevel@tonic-gate 
42990Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
43000Sstevel@tonic-gate 
43010Sstevel@tonic-gate 	/*
43020Sstevel@tonic-gate 	**  XXX: Should actually send body chunks to each filter
43030Sstevel@tonic-gate 	**  a chunk at a time instead of sending the whole body to
43040Sstevel@tonic-gate 	**  each filter in turn.  However, only if the filters don't
43050Sstevel@tonic-gate 	**  change the body.
43060Sstevel@tonic-gate 	*/
43070Sstevel@tonic-gate 
43080Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
43090Sstevel@tonic-gate 	{
43100Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
43110Sstevel@tonic-gate 
43120Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE &&
43130Sstevel@tonic-gate 		    *state != SMFIR_ACCEPT)
43140Sstevel@tonic-gate 		{
43150Sstevel@tonic-gate 			/*
43160Sstevel@tonic-gate 			**  A previous filter has dealt with the message,
43170Sstevel@tonic-gate 			**  safe to stop processing the filters.
43180Sstevel@tonic-gate 			*/
43190Sstevel@tonic-gate 
43200Sstevel@tonic-gate 			break;
43210Sstevel@tonic-gate 		}
43220Sstevel@tonic-gate 
43230Sstevel@tonic-gate 		/* Now reset state for later evaluation */
43240Sstevel@tonic-gate 		*state = SMFIR_CONTINUE;
43250Sstevel@tonic-gate 		newfilter = true;
43260Sstevel@tonic-gate 
43270Sstevel@tonic-gate 		/* previous problem? */
43280Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
43290Sstevel@tonic-gate 		{
43300Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
43310Sstevel@tonic-gate 			break;
43320Sstevel@tonic-gate 		}
43330Sstevel@tonic-gate 
43340Sstevel@tonic-gate 		/* sanity checks */
43350Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
43360Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
43370Sstevel@tonic-gate 			continue;
43380Sstevel@tonic-gate 
43390Sstevel@tonic-gate 		m->mf_state = SMFS_INMSG;
43400Sstevel@tonic-gate 
43410Sstevel@tonic-gate 		/* check if filter wants the headers */
43420Sstevel@tonic-gate 		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
43430Sstevel@tonic-gate 		{
43440Sstevel@tonic-gate 			response = milter_headers(m, e, state);
43450Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43460Sstevel@tonic-gate 		}
43470Sstevel@tonic-gate 
43480Sstevel@tonic-gate 		/* check if filter wants EOH */
43490Sstevel@tonic-gate 		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
43500Sstevel@tonic-gate 		{
43510Sstevel@tonic-gate 			if (tTd(64, 10))
43520Sstevel@tonic-gate 				sm_dprintf("milter_data: eoh\n");
43530Sstevel@tonic-gate 
4354*3544Sjbeck 			if (MilterEOHMacros[0] != NULL)
4355*3544Sjbeck 			{
4356*3544Sjbeck 				milter_send_macros(m, MilterEOHMacros,
4357*3544Sjbeck 					   SMFIC_EOH, e);
4358*3544Sjbeck 				MILTER_CHECK_RESULTS();
4359*3544Sjbeck 			}
4360*3544Sjbeck 
43610Sstevel@tonic-gate 			/* send it over */
43620Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
4363*3544Sjbeck 						       e, state, "eoh");
43640Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43650Sstevel@tonic-gate 		}
43660Sstevel@tonic-gate 
43670Sstevel@tonic-gate 		/* check if filter wants the body */
43680Sstevel@tonic-gate 		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
43690Sstevel@tonic-gate 		    e->e_dfp != NULL)
43700Sstevel@tonic-gate 		{
43710Sstevel@tonic-gate 			rewind = true;
43720Sstevel@tonic-gate 			response = milter_body(m, e, state);
43730Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43740Sstevel@tonic-gate 		}
43750Sstevel@tonic-gate 
43760Sstevel@tonic-gate 		if (MilterEOMMacros[0] != NULL)
43770Sstevel@tonic-gate 		{
43780Sstevel@tonic-gate 			milter_send_macros(m, MilterEOMMacros,
43790Sstevel@tonic-gate 					   SMFIC_BODYEOB, e);
43800Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43810Sstevel@tonic-gate 		}
43820Sstevel@tonic-gate 
43830Sstevel@tonic-gate 		/* send the final body chunk */
43840Sstevel@tonic-gate 		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
4385*3544Sjbeck 				    m->mf_timeout[SMFTO_WRITE], e, "eom");
43860Sstevel@tonic-gate 
43870Sstevel@tonic-gate 		/* Get time EOM sent for timeout */
43880Sstevel@tonic-gate 		eomsent = curtime();
43890Sstevel@tonic-gate 
43900Sstevel@tonic-gate 		/* deal with the possibility of multiple responses */
43910Sstevel@tonic-gate 		while (*state == SMFIR_CONTINUE)
43920Sstevel@tonic-gate 		{
43930Sstevel@tonic-gate 			/* Check total timeout from EOM to final ACK/NAK */
43940Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
43950Sstevel@tonic-gate 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
43960Sstevel@tonic-gate 			{
43970Sstevel@tonic-gate 				if (tTd(64, 5))
43980Sstevel@tonic-gate 					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
43990Sstevel@tonic-gate 						m->mf_name);
44000Sstevel@tonic-gate 				if (MilterLogLevel > 0)
44010Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
44020Sstevel@tonic-gate 						  "milter_data(%s): EOM ACK/NAK timeout",
44030Sstevel@tonic-gate 						  m->mf_name);
44040Sstevel@tonic-gate 				milter_error(m, e);
44050Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, break);
44060Sstevel@tonic-gate 				break;
44070Sstevel@tonic-gate 			}
44080Sstevel@tonic-gate 
44090Sstevel@tonic-gate 			response = milter_read(m, &rcmd, &rlen,
4410*3544Sjbeck 					       m->mf_timeout[SMFTO_READ], e,
4411*3544Sjbeck 						"body");
44120Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
44130Sstevel@tonic-gate 				break;
44140Sstevel@tonic-gate 
44150Sstevel@tonic-gate 			if (tTd(64, 10))
44160Sstevel@tonic-gate 				sm_dprintf("milter_data(%s): state %c\n",
44170Sstevel@tonic-gate 					   m->mf_name, (char) rcmd);
44180Sstevel@tonic-gate 
44190Sstevel@tonic-gate 			switch (rcmd)
44200Sstevel@tonic-gate 			{
44210Sstevel@tonic-gate 			  case SMFIR_REPLYCODE:
44220Sstevel@tonic-gate 				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
44230Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44240Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
44250Sstevel@tonic-gate 						  m->mf_name, response);
44260Sstevel@tonic-gate 				*state = rcmd;
44270Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44280Sstevel@tonic-gate 				break;
44290Sstevel@tonic-gate 
44300Sstevel@tonic-gate 			  case SMFIR_REJECT: /* log msg at end of function */
44310Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44320Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
44330Sstevel@tonic-gate 						  m->mf_name);
44340Sstevel@tonic-gate 				*state = rcmd;
44350Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44360Sstevel@tonic-gate 				break;
44370Sstevel@tonic-gate 
44380Sstevel@tonic-gate 			  case SMFIR_DISCARD:
44390Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44400Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
44410Sstevel@tonic-gate 						  m->mf_name);
44420Sstevel@tonic-gate 				*state = rcmd;
44430Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44440Sstevel@tonic-gate 				break;
44450Sstevel@tonic-gate 
44460Sstevel@tonic-gate 			  case SMFIR_TEMPFAIL:
44470Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44480Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
44490Sstevel@tonic-gate 						  m->mf_name);
44500Sstevel@tonic-gate 				*state = rcmd;
44510Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44520Sstevel@tonic-gate 				break;
44530Sstevel@tonic-gate 
44540Sstevel@tonic-gate 			  case SMFIR_CONTINUE:
44550Sstevel@tonic-gate 			  case SMFIR_ACCEPT:
44560Sstevel@tonic-gate 				/* this filter is done with message */
44570Sstevel@tonic-gate 				if (replfailed)
44580Sstevel@tonic-gate 					*state = SMFIR_TEMPFAIL;
44590Sstevel@tonic-gate 				else
44600Sstevel@tonic-gate 					*state = SMFIR_ACCEPT;
44610Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44620Sstevel@tonic-gate 				break;
44630Sstevel@tonic-gate 
44640Sstevel@tonic-gate 			  case SMFIR_PROGRESS:
44650Sstevel@tonic-gate 				break;
44660Sstevel@tonic-gate 
44670Sstevel@tonic-gate 			  case SMFIR_QUARANTINE:
44680Sstevel@tonic-gate 				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
44690Sstevel@tonic-gate 				{
44700Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44710Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44720Sstevel@tonic-gate 							  "milter_data(%s): lied about quarantining, honoring request anyway",
44730Sstevel@tonic-gate 							  m->mf_name);
44740Sstevel@tonic-gate 				}
44750Sstevel@tonic-gate 				if (response == NULL)
44760Sstevel@tonic-gate 					response = newstr("");
44770Sstevel@tonic-gate 				if (MilterLogLevel > 3)
44780Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
44790Sstevel@tonic-gate 						  "milter=%s, quarantine=%s",
44800Sstevel@tonic-gate 						  m->mf_name, response);
44810Sstevel@tonic-gate 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
44820Sstevel@tonic-gate 								 response);
44830Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
44840Sstevel@tonic-gate 					  macid("{quarantine}"), e->e_quarmsg);
44850Sstevel@tonic-gate 				break;
44860Sstevel@tonic-gate 
44870Sstevel@tonic-gate 			  case SMFIR_ADDHEADER:
44880Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
44890Sstevel@tonic-gate 				{
44900Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44910Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44920Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
44930Sstevel@tonic-gate 							  m->mf_name);
44940Sstevel@tonic-gate 				}
4495*3544Sjbeck 				milter_addheader(m, response, rlen, e);
44960Sstevel@tonic-gate 				break;
44970Sstevel@tonic-gate 
44980Sstevel@tonic-gate 			  case SMFIR_INSHEADER:
44990Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
45000Sstevel@tonic-gate 				{
45010Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45020Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45030Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
45040Sstevel@tonic-gate 							  m->mf_name);
45050Sstevel@tonic-gate 				}
4506*3544Sjbeck 				milter_insheader(m, response, rlen, e);
45070Sstevel@tonic-gate 				break;
45080Sstevel@tonic-gate 
45090Sstevel@tonic-gate 			  case SMFIR_CHGHEADER:
45100Sstevel@tonic-gate 				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
45110Sstevel@tonic-gate 				{
45120Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45130Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45140Sstevel@tonic-gate 							  "milter_data(%s): lied about changing headers, honoring request anyway",
45150Sstevel@tonic-gate 							  m->mf_name);
45160Sstevel@tonic-gate 				}
4517*3544Sjbeck 				milter_changeheader(m, response, rlen, e);
4518*3544Sjbeck 				break;
4519*3544Sjbeck 
4520*3544Sjbeck 			  case SMFIR_CHGFROM:
4521*3544Sjbeck 				if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
4522*3544Sjbeck 				{
4523*3544Sjbeck 					if (MilterLogLevel > 9)
4524*3544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
4525*3544Sjbeck 							  "milter_data(%s) lied about changing sender, honoring request anyway",
4526*3544Sjbeck 							  m->mf_name);
4527*3544Sjbeck 				}
4528*3544Sjbeck 				milter_chgfrom(response, rlen, e);
45290Sstevel@tonic-gate 				break;
45300Sstevel@tonic-gate 
45310Sstevel@tonic-gate 			  case SMFIR_ADDRCPT:
45320Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
45330Sstevel@tonic-gate 				{
45340Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45350Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45360Sstevel@tonic-gate 							  "milter_data(%s) lied about adding recipients, honoring request anyway",
45370Sstevel@tonic-gate 							  m->mf_name);
45380Sstevel@tonic-gate 				}
45390Sstevel@tonic-gate 				milter_addrcpt(response, rlen, e);
45400Sstevel@tonic-gate 				break;
45410Sstevel@tonic-gate 
4542*3544Sjbeck 			  case SMFIR_ADDRCPT_PAR:
4543*3544Sjbeck 				if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
4544*3544Sjbeck 				{
4545*3544Sjbeck 					if (MilterLogLevel > 9)
4546*3544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
4547*3544Sjbeck 							  "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
4548*3544Sjbeck 							  m->mf_name);
4549*3544Sjbeck 				}
4550*3544Sjbeck 				milter_addrcpt_par(response, rlen, e);
4551*3544Sjbeck 				break;
4552*3544Sjbeck 
45530Sstevel@tonic-gate 			  case SMFIR_DELRCPT:
45540Sstevel@tonic-gate 				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
45550Sstevel@tonic-gate 				{
45560Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45570Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45580Sstevel@tonic-gate 							  "milter_data(%s): lied about removing recipients, honoring request anyway",
45590Sstevel@tonic-gate 							  m->mf_name);
45600Sstevel@tonic-gate 				}
45610Sstevel@tonic-gate 				milter_delrcpt(response, rlen, e);
45620Sstevel@tonic-gate 				break;
45630Sstevel@tonic-gate 
45640Sstevel@tonic-gate 			  case SMFIR_REPLBODY:
45650Sstevel@tonic-gate 				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
45660Sstevel@tonic-gate 				{
45670Sstevel@tonic-gate 					if (MilterLogLevel > 0)
45680Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
45690Sstevel@tonic-gate 							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
45700Sstevel@tonic-gate 							  m->mf_name);
45710Sstevel@tonic-gate 					replfailed = true;
45720Sstevel@tonic-gate 					break;
45730Sstevel@tonic-gate 				}
45740Sstevel@tonic-gate 
45750Sstevel@tonic-gate 				/* already failed in attempt */
45760Sstevel@tonic-gate 				if (replfailed)
45770Sstevel@tonic-gate 					break;
45780Sstevel@tonic-gate 
45790Sstevel@tonic-gate 				if (!dfopen)
45800Sstevel@tonic-gate 				{
45810Sstevel@tonic-gate 					if (milter_reopen_df(e) < 0)
45820Sstevel@tonic-gate 					{
45830Sstevel@tonic-gate 						replfailed = true;
45840Sstevel@tonic-gate 						break;
45850Sstevel@tonic-gate 					}
45860Sstevel@tonic-gate 					dfopen = true;
45870Sstevel@tonic-gate 					rewind = true;
45880Sstevel@tonic-gate 				}
45890Sstevel@tonic-gate 
45900Sstevel@tonic-gate 				if (milter_replbody(response, rlen,
45910Sstevel@tonic-gate 						    newfilter, e) < 0)
45920Sstevel@tonic-gate 					replfailed = true;
45930Sstevel@tonic-gate 				newfilter = false;
45940Sstevel@tonic-gate 				replbody = true;
45950Sstevel@tonic-gate 				break;
45960Sstevel@tonic-gate 
45970Sstevel@tonic-gate 			  default:
45980Sstevel@tonic-gate 				/* Invalid response to command */
45990Sstevel@tonic-gate 				if (MilterLogLevel > 0)
46000Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
46010Sstevel@tonic-gate 						  "milter_data(%s): returned bogus response %c",
46020Sstevel@tonic-gate 						  m->mf_name, rcmd);
46030Sstevel@tonic-gate 				milter_error(m, e);
46040Sstevel@tonic-gate 				break;
46050Sstevel@tonic-gate 			}
46060Sstevel@tonic-gate 			if (rcmd != SMFIR_REPLYCODE && response != NULL)
46070Sstevel@tonic-gate 			{
46080Sstevel@tonic-gate 				sm_free(response); /* XXX */
46090Sstevel@tonic-gate 				response = NULL;
46100Sstevel@tonic-gate 			}
46110Sstevel@tonic-gate 
46120Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
46130Sstevel@tonic-gate 				break;
46140Sstevel@tonic-gate 		}
46150Sstevel@tonic-gate 
46160Sstevel@tonic-gate 		if (replbody && !replfailed)
46170Sstevel@tonic-gate 		{
46180Sstevel@tonic-gate 			/* flush possible buffered character */
46190Sstevel@tonic-gate 			milter_replbody(NULL, 0, !replbody, e);
46200Sstevel@tonic-gate 			replbody = false;
46210Sstevel@tonic-gate 		}
46220Sstevel@tonic-gate 
46230Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
46240Sstevel@tonic-gate 		{
46250Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
46260Sstevel@tonic-gate 			goto finishup;
46270Sstevel@tonic-gate 		}
46280Sstevel@tonic-gate 	}
46290Sstevel@tonic-gate 
46300Sstevel@tonic-gate finishup:
46310Sstevel@tonic-gate 	/* leave things in the expected state if we touched it */
46320Sstevel@tonic-gate 	if (replfailed)
46330Sstevel@tonic-gate 	{
46340Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46350Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46360Sstevel@tonic-gate 		{
46370Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46380Sstevel@tonic-gate 			SM_FREE_CLR(response);
46390Sstevel@tonic-gate 		}
46400Sstevel@tonic-gate 
46410Sstevel@tonic-gate 		if (dfopen)
46420Sstevel@tonic-gate 		{
46430Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
46440Sstevel@tonic-gate 			e->e_dfp = NULL;
46450Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
46460Sstevel@tonic-gate 			dfopen = false;
46470Sstevel@tonic-gate 		}
46480Sstevel@tonic-gate 		rewind = false;
46490Sstevel@tonic-gate 	}
46500Sstevel@tonic-gate 
46510Sstevel@tonic-gate 	if ((dfopen && milter_reset_df(e) < 0) ||
46520Sstevel@tonic-gate 	    (rewind && bfrewind(e->e_dfp) < 0))
46530Sstevel@tonic-gate 	{
46540Sstevel@tonic-gate 		save_errno = errno;
46550Sstevel@tonic-gate 		ExitStat = EX_IOERR;
46560Sstevel@tonic-gate 
46570Sstevel@tonic-gate 		/*
46580Sstevel@tonic-gate 		**  If filter told us to keep message but we had
46590Sstevel@tonic-gate 		**  an error, we can't really keep it, tempfail it.
46600Sstevel@tonic-gate 		*/
46610Sstevel@tonic-gate 
46620Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46630Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46640Sstevel@tonic-gate 		{
46650Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46660Sstevel@tonic-gate 			SM_FREE_CLR(response);
46670Sstevel@tonic-gate 		}
46680Sstevel@tonic-gate 
46690Sstevel@tonic-gate 		errno = save_errno;
46700Sstevel@tonic-gate 		syserr("milter_data: %s/%cf%s: read error",
46710Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
46720Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
46730Sstevel@tonic-gate 	}
46740Sstevel@tonic-gate 
46750Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
46760Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
46770Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
46780Sstevel@tonic-gate 	return response;
46790Sstevel@tonic-gate }
46800Sstevel@tonic-gate 
46810Sstevel@tonic-gate /*
46820Sstevel@tonic-gate **  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
46830Sstevel@tonic-gate **			string to milter filters
46840Sstevel@tonic-gate **
46850Sstevel@tonic-gate **	Parameters:
4686*3544Sjbeck **		smtpcmd -- the string itself.
46870Sstevel@tonic-gate **		e -- current envelope.
46880Sstevel@tonic-gate **		state -- return state from response.
46890Sstevel@tonic-gate **
46900Sstevel@tonic-gate **
46910Sstevel@tonic-gate **	Returns:
46920Sstevel@tonic-gate **		response string (may be NULL)
46930Sstevel@tonic-gate */
46940Sstevel@tonic-gate 
46950Sstevel@tonic-gate char *
4696*3544Sjbeck milter_unknown(smtpcmd, e, state)
4697*3544Sjbeck 	char *smtpcmd;
46980Sstevel@tonic-gate 	ENVELOPE *e;
46990Sstevel@tonic-gate 	char *state;
47000Sstevel@tonic-gate {
47010Sstevel@tonic-gate 	if (tTd(64, 10))
4702*3544Sjbeck 		sm_dprintf("milter_unknown(%s)\n", smtpcmd);
4703*3544Sjbeck 
4704*3544Sjbeck 	return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
4705*3544Sjbeck 				NULL, e, state, "unknown", false);
47060Sstevel@tonic-gate }
47070Sstevel@tonic-gate 
47080Sstevel@tonic-gate /*
47090Sstevel@tonic-gate **  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
47100Sstevel@tonic-gate **
47110Sstevel@tonic-gate **	Parameters:
47120Sstevel@tonic-gate **		e -- current envelope.
47130Sstevel@tonic-gate **
47140Sstevel@tonic-gate **	Returns:
47150Sstevel@tonic-gate **		none
47160Sstevel@tonic-gate */
47170Sstevel@tonic-gate 
47180Sstevel@tonic-gate void
47190Sstevel@tonic-gate milter_quit(e)
47200Sstevel@tonic-gate 	ENVELOPE *e;
47210Sstevel@tonic-gate {
47220Sstevel@tonic-gate 	int i;
47230Sstevel@tonic-gate 
47240Sstevel@tonic-gate 	if (tTd(64, 10))
47250Sstevel@tonic-gate 		sm_dprintf("milter_quit(%s)\n", e->e_id);
47260Sstevel@tonic-gate 
47270Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47280Sstevel@tonic-gate 		milter_quit_filter(InputFilters[i], e);
47290Sstevel@tonic-gate }
4730*3544Sjbeck 
47310Sstevel@tonic-gate /*
47320Sstevel@tonic-gate **  MILTER_ABORT -- informs the filter(s) that we are aborting current message
47330Sstevel@tonic-gate **
47340Sstevel@tonic-gate **	Parameters:
47350Sstevel@tonic-gate **		e -- current envelope.
47360Sstevel@tonic-gate **
47370Sstevel@tonic-gate **	Returns:
47380Sstevel@tonic-gate **		none
47390Sstevel@tonic-gate */
47400Sstevel@tonic-gate 
47410Sstevel@tonic-gate void
47420Sstevel@tonic-gate milter_abort(e)
47430Sstevel@tonic-gate 	ENVELOPE *e;
47440Sstevel@tonic-gate {
47450Sstevel@tonic-gate 	int i;
47460Sstevel@tonic-gate 
47470Sstevel@tonic-gate 	if (tTd(64, 10))
47480Sstevel@tonic-gate 		sm_dprintf("milter_abort\n");
47490Sstevel@tonic-gate 
47500Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47510Sstevel@tonic-gate 	{
47520Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
47530Sstevel@tonic-gate 
47540Sstevel@tonic-gate 		/* sanity checks */
47550Sstevel@tonic-gate 		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
47560Sstevel@tonic-gate 			continue;
47570Sstevel@tonic-gate 
47580Sstevel@tonic-gate 		milter_abort_filter(m, e);
47590Sstevel@tonic-gate 	}
47600Sstevel@tonic-gate }
47610Sstevel@tonic-gate #endif /* MILTER */
4762