xref: /onnv-gate/usr/src/cmd/sendmail/src/milter.c (revision 5402:a17d5e4e8666)
10Sstevel@tonic-gate /*
23544Sjbeck  * 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*5402Sjbeck SM_RCSID("@(#)$Id: milter.c,v 8.269 2007/06/06 17:26:12 ca Exp $")
160Sstevel@tonic-gate 
170Sstevel@tonic-gate #if MILTER
183544Sjbeck # 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>
283544Sjbeck #  if MILTER_NO_NAGLE
290Sstevel@tonic-gate #   include <netinet/tcp.h>
303544Sjbeck #  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 *));
393544Sjbeck static char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
403544Sjbeck 			ENVELOPE *, const char *));
413544Sjbeck static char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
423544Sjbeck 			ENVELOPE *, const char *));
433544Sjbeck static char	*milter_write __P((struct milter *, int, char *, ssize_t,
443544Sjbeck 			time_t, ENVELOPE *, const char *));
453544Sjbeck static char	*milter_send_command __P((struct milter *, int, void *,
463544Sjbeck 			ssize_t, ENVELOPE *, char *, const char *));
473544Sjbeck static char	*milter_command __P((int, void *, ssize_t, char **,
483544Sjbeck 			ENVELOPE *, char *, const char *, bool));
493544Sjbeck static char	*milter_body __P((struct milter *, ENVELOPE *, char *));
503544Sjbeck static int	milter_reopen_df __P((ENVELOPE *));
513544Sjbeck static int	milter_reset_df __P((ENVELOPE *));
523544Sjbeck static void	milter_quit_filter __P((struct milter *, ENVELOPE *));
533544Sjbeck static void	milter_abort_filter __P((struct milter *, ENVELOPE *));
543544Sjbeck static void	milter_send_macros __P((struct milter *, char **, int,
553544Sjbeck 			ENVELOPE *));
56*5402Sjbeck static int	milter_negotiate __P((struct milter *, ENVELOPE *,
57*5402Sjbeck 			milters_T *));
583544Sjbeck static void	milter_per_connection_check __P((ENVELOPE *));
593544Sjbeck static char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
603544Sjbeck static void	milter_addheader __P((struct milter *, char *, ssize_t,
613544Sjbeck 			ENVELOPE *));
623544Sjbeck static void	milter_insheader __P((struct milter *, char *, ssize_t,
633544Sjbeck 			ENVELOPE *));
643544Sjbeck static void	milter_changeheader __P((struct milter *, char *, ssize_t,
653544Sjbeck 			ENVELOPE *));
663544Sjbeck static void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
673544Sjbeck static void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
683544Sjbeck static void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
693544Sjbeck static void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
703544Sjbeck static int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
713544Sjbeck static int	milter_set_macros __P((char *, char **, char *, int));
723544Sjbeck 
733544Sjbeck 
743544Sjbeck /* milter states */
753544Sjbeck # define SMFS_CLOSED		'C'	/* closed for all further actions */
763544Sjbeck # define SMFS_OPEN		'O'	/* connected to remote milter filter */
773544Sjbeck # define SMFS_INMSG		'M'	/* currently servicing a message */
783544Sjbeck # define SMFS_DONE		'D'	/* done with current message */
793544Sjbeck # define SMFS_CLOSABLE		'Q'	/* done with current connection */
803544Sjbeck # define SMFS_ERROR		'E'	/* error state */
813544Sjbeck # define SMFS_READY		'R'	/* ready for action */
823544Sjbeck # define SMFS_SKIP		'S'	/* skip body */
830Sstevel@tonic-gate 
840Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
850Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
860Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
870Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
880Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
890Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
903544Sjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1];
910Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
940Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
950Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
960Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
970Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
980Sstevel@tonic-gate 	{ \
990Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
1000Sstevel@tonic-gate 		milter_abort(e); \
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
1040Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
1050Sstevel@tonic-gate 	{ \
1060Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1070Sstevel@tonic-gate 		{ \
1080Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1090Sstevel@tonic-gate 							 "filter failure"); \
1100Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1110Sstevel@tonic-gate 				  e->e_quarmsg); \
1120Sstevel@tonic-gate 		} \
1130Sstevel@tonic-gate 	} \
1140Sstevel@tonic-gate 	else if (tTd(71, 101)) \
1150Sstevel@tonic-gate 	{ \
1160Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1170Sstevel@tonic-gate 		{ \
1180Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1190Sstevel@tonic-gate 							 "filter failure"); \
1200Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1210Sstevel@tonic-gate 				  e->e_quarmsg); \
1220Sstevel@tonic-gate 		} \
1230Sstevel@tonic-gate 	} \
1240Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
1250Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
1260Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
1270Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
1280Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
1290Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
1300Sstevel@tonic-gate 	else \
1310Sstevel@tonic-gate 		action;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
1340Sstevel@tonic-gate 	if (response == NULL || \
1350Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
1360Sstevel@tonic-gate 	    rlen < 3 || \
1370Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
1380Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
1390Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
1400Sstevel@tonic-gate 	{ \
1410Sstevel@tonic-gate 		if (response != NULL) \
1420Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
1430Sstevel@tonic-gate 		response = newstr(default); \
1440Sstevel@tonic-gate 	} \
1450Sstevel@tonic-gate 	else \
1460Sstevel@tonic-gate 	{ \
1470Sstevel@tonic-gate 		char *ptr = response; \
1480Sstevel@tonic-gate  \
1490Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
1500Sstevel@tonic-gate 		while (*ptr != '\0') \
1510Sstevel@tonic-gate 		{ \
1520Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
1530Sstevel@tonic-gate 			{ \
1540Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
1550Sstevel@tonic-gate 				response = newstr(default); \
1560Sstevel@tonic-gate 				break; \
1570Sstevel@tonic-gate 			} \
1580Sstevel@tonic-gate 			ptr++; \
1590Sstevel@tonic-gate 		} \
1600Sstevel@tonic-gate 	}
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
1630Sstevel@tonic-gate { \
1640Sstevel@tonic-gate 	int save_errno = errno; \
1650Sstevel@tonic-gate  \
1660Sstevel@tonic-gate 	if (tTd(64, 5)) \
1670Sstevel@tonic-gate 	{ \
1680Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
1690Sstevel@tonic-gate 		sm_dprintf("\n"); \
1700Sstevel@tonic-gate 	} \
1710Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
1720Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
1730Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
1740Sstevel@tonic-gate 	{ \
1750Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
1760Sstevel@tonic-gate 		{ \
1770Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
1780Sstevel@tonic-gate 			e->e_dfp = NULL; \
1790Sstevel@tonic-gate 		} \
1800Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
1810Sstevel@tonic-gate 	} \
1820Sstevel@tonic-gate 	errno = save_errno; \
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
1870Sstevel@tonic-gate **
1880Sstevel@tonic-gate **	Parameters:
1890Sstevel@tonic-gate **		routine -- routine name for debug/logging
1900Sstevel@tonic-gate **		secs -- number of seconds in timeout
1910Sstevel@tonic-gate **		write -- waiting to read or write?
1920Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
1930Sstevel@tonic-gate **
1940Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
1950Sstevel@tonic-gate */
1960Sstevel@tonic-gate 
1973544Sjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \
1980Sstevel@tonic-gate { \
1990Sstevel@tonic-gate 	int ret; \
2000Sstevel@tonic-gate 	int save_errno; \
2010Sstevel@tonic-gate 	fd_set fds; \
2020Sstevel@tonic-gate 	struct timeval tv; \
2030Sstevel@tonic-gate  \
2040Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
2050Sstevel@tonic-gate 	{ \
2060Sstevel@tonic-gate 		if (tTd(64, 5)) \
2070Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
2080Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
2090Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
2100Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2110Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2120Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
2130Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
2140Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
2150Sstevel@tonic-gate 		milter_error(m, e); \
2160Sstevel@tonic-gate 		return NULL; \
2170Sstevel@tonic-gate 	} \
2180Sstevel@tonic-gate  \
2190Sstevel@tonic-gate 	do \
2200Sstevel@tonic-gate 	{ \
2210Sstevel@tonic-gate 		FD_ZERO(&fds); \
2220Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
2230Sstevel@tonic-gate 		tv.tv_sec = (secs); \
2240Sstevel@tonic-gate 		tv.tv_usec = 0; \
2250Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
2260Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
2270Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
2280Sstevel@tonic-gate 			     NULL, &tv); \
2290Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
2300Sstevel@tonic-gate  \
2310Sstevel@tonic-gate 	switch (ret) \
2320Sstevel@tonic-gate 	{ \
2330Sstevel@tonic-gate 	  case 0: \
2340Sstevel@tonic-gate 		if (tTd(64, 5)) \
2353544Sjbeck 			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
2363544Sjbeck 				(routine), m->mf_name, (function)); \
2370Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2380Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2393544Sjbeck 				  "Milter (%s): timeout %s data %s, where=%s", \
2403544Sjbeck 				  m->mf_name, \
2410Sstevel@tonic-gate 				  started ? "during" : "before", \
2423544Sjbeck 				  (routine), (function)); \
2430Sstevel@tonic-gate 		milter_error(m, e); \
2440Sstevel@tonic-gate 		return NULL; \
2450Sstevel@tonic-gate  \
2460Sstevel@tonic-gate 	  case -1: \
2470Sstevel@tonic-gate 		save_errno = errno; \
2480Sstevel@tonic-gate 		if (tTd(64, 5)) \
2490Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
2500Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
2510Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2520Sstevel@tonic-gate 		{ \
2530Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2540Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
2550Sstevel@tonic-gate 				  m->mf_name, (routine), \
2560Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
2570Sstevel@tonic-gate 		} \
2580Sstevel@tonic-gate 		milter_error(m, e); \
2590Sstevel@tonic-gate 		return NULL; \
2600Sstevel@tonic-gate  \
2610Sstevel@tonic-gate 	  default: \
2620Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
2630Sstevel@tonic-gate 			break; \
2640Sstevel@tonic-gate 		if (tTd(64, 5)) \
2650Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
2660Sstevel@tonic-gate 				(routine), m->mf_name); \
2670Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2680Sstevel@tonic-gate 		{ \
2690Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2700Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
2710Sstevel@tonic-gate 				  m->mf_name, (routine)); \
2720Sstevel@tonic-gate 		} \
2730Sstevel@tonic-gate 		milter_error(m, e); \
2740Sstevel@tonic-gate 		return NULL; \
2750Sstevel@tonic-gate 	} \
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate /*
2790Sstevel@tonic-gate **  Low level functions
2800Sstevel@tonic-gate */
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate /*
2830Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
2840Sstevel@tonic-gate **
2850Sstevel@tonic-gate **	Parameters:
2860Sstevel@tonic-gate **		m -- milter to read from.
2870Sstevel@tonic-gate **		cmd -- return param for command read.
2880Sstevel@tonic-gate **		rlen -- return length of response string.
2890Sstevel@tonic-gate **		to -- timeout in seconds.
2900Sstevel@tonic-gate **		e -- current envelope.
2910Sstevel@tonic-gate **
2920Sstevel@tonic-gate **	Returns:
2930Sstevel@tonic-gate **		response string (may be NULL)
2940Sstevel@tonic-gate */
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate static char *
2973544Sjbeck milter_sysread(m, buf, sz, to, e, where)
2980Sstevel@tonic-gate 	struct milter *m;
2990Sstevel@tonic-gate 	char *buf;
3000Sstevel@tonic-gate 	ssize_t sz;
3010Sstevel@tonic-gate 	time_t to;
3020Sstevel@tonic-gate 	ENVELOPE *e;
3033544Sjbeck 	const char *where;
3040Sstevel@tonic-gate {
3050Sstevel@tonic-gate 	time_t readstart = 0;
3060Sstevel@tonic-gate 	ssize_t len, curl;
3070Sstevel@tonic-gate 	bool started = false;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	curl = 0;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	if (to > 0)
3120Sstevel@tonic-gate 		readstart = curtime();
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	for (;;)
3150Sstevel@tonic-gate 	{
3160Sstevel@tonic-gate 		if (to > 0)
3170Sstevel@tonic-gate 		{
3180Sstevel@tonic-gate 			time_t now;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 			now = curtime();
3210Sstevel@tonic-gate 			if (now - readstart >= to)
3220Sstevel@tonic-gate 			{
3230Sstevel@tonic-gate 				if (tTd(64, 5))
3243544Sjbeck 					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
3253544Sjbeck 						  m->mf_name,
3260Sstevel@tonic-gate 						  started ? "during" : "before",
3273544Sjbeck 						  where);
3280Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3290Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
3303544Sjbeck 						  "Milter (%s): timeout %s data read in %s",
3313544Sjbeck 						  m->mf_name,
3320Sstevel@tonic-gate 						  started ? "during" : "before",
3333544Sjbeck 						  where);
3340Sstevel@tonic-gate 				milter_error(m, e);
3350Sstevel@tonic-gate 				return NULL;
3360Sstevel@tonic-gate 			}
3370Sstevel@tonic-gate 			to -= now - readstart;
3380Sstevel@tonic-gate 			readstart = now;
3393544Sjbeck 			MILTER_TIMEOUT("read", to, false, started, where);
3400Sstevel@tonic-gate 		}
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 		if (len < 0)
3450Sstevel@tonic-gate 		{
3460Sstevel@tonic-gate 			int save_errno = errno;
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 			if (tTd(64, 5))
3493544Sjbeck 				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
3500Sstevel@tonic-gate 					m->mf_name, (long) len,
3510Sstevel@tonic-gate 					sm_errstring(save_errno));
3520Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3530Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3540Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
3550Sstevel@tonic-gate 					  m->mf_name, (long) len,
3560Sstevel@tonic-gate 					  sm_errstring(save_errno));
3570Sstevel@tonic-gate 			milter_error(m, e);
3580Sstevel@tonic-gate 			return NULL;
3590Sstevel@tonic-gate 		}
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 		started = true;
3620Sstevel@tonic-gate 		curl += len;
3630Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
3640Sstevel@tonic-gate 			break;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	if (curl != sz)
3690Sstevel@tonic-gate 	{
3700Sstevel@tonic-gate 		if (tTd(64, 5))
3713544Sjbeck 			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
3720Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
3730Sstevel@tonic-gate 		if (MilterLogLevel > 0)
3740Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
3753544Sjbeck 				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
3760Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
3770Sstevel@tonic-gate 		milter_error(m, e);
3780Sstevel@tonic-gate 		return NULL;
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 	return buf;
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate static char *
3843544Sjbeck milter_read(m, cmd, rlen, to, e, where)
3850Sstevel@tonic-gate 	struct milter *m;
3860Sstevel@tonic-gate 	char *cmd;
3870Sstevel@tonic-gate 	ssize_t *rlen;
3880Sstevel@tonic-gate 	time_t to;
3890Sstevel@tonic-gate 	ENVELOPE *e;
3903544Sjbeck 	const char *where;
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate 	time_t readstart = 0;
3930Sstevel@tonic-gate 	ssize_t expl;
3940Sstevel@tonic-gate 	mi_int32 i;
3953544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
3960Sstevel@tonic-gate 	int cork = 0;
3973544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
3980Sstevel@tonic-gate 	char *buf;
3990Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	if (m->mf_sock < 0)
4020Sstevel@tonic-gate 	{
4030Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4040Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
4053544Sjbeck 				  "milter_read(%s): socket closed, where=%s",
4063544Sjbeck 				  m->mf_name, where);
4070Sstevel@tonic-gate 		milter_error(m, e);
4080Sstevel@tonic-gate 		return NULL;
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	*rlen = 0;
4120Sstevel@tonic-gate 	*cmd = '\0';
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	if (to > 0)
4150Sstevel@tonic-gate 		readstart = curtime();
4160Sstevel@tonic-gate 
4173544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4180Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4190Sstevel@tonic-gate 		   sizeof(cork));
4203544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4213544Sjbeck 
4223544Sjbeck 	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
4230Sstevel@tonic-gate 		return NULL;
4240Sstevel@tonic-gate 
4253544Sjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4260Sstevel@tonic-gate 	cork = 1;
4270Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4280Sstevel@tonic-gate 		   sizeof(cork));
4293544Sjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	/* reset timeout */
4320Sstevel@tonic-gate 	if (to > 0)
4330Sstevel@tonic-gate 	{
4340Sstevel@tonic-gate 		time_t now;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 		now = curtime();
4370Sstevel@tonic-gate 		if (now - readstart >= to)
4380Sstevel@tonic-gate 		{
4390Sstevel@tonic-gate 			if (tTd(64, 5))
4403544Sjbeck 				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
4413544Sjbeck 					m->mf_name, where);
4420Sstevel@tonic-gate 			if (MilterLogLevel > 0)
4430Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
4443544Sjbeck 					  "Milter read(%s): timeout before data read, where=%s",
4453544Sjbeck 					  m->mf_name, where);
4460Sstevel@tonic-gate 			milter_error(m, e);
4470Sstevel@tonic-gate 			return NULL;
4480Sstevel@tonic-gate 		}
4490Sstevel@tonic-gate 		to -= now - readstart;
4500Sstevel@tonic-gate 	}
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
4530Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
4540Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
4550Sstevel@tonic-gate 	expl = ntohl(i) - 1;
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	if (tTd(64, 25))
4580Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
4590Sstevel@tonic-gate 			m->mf_name, (long) expl);
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	if (expl < 0)
4620Sstevel@tonic-gate 	{
4630Sstevel@tonic-gate 		if (tTd(64, 5))
4643544Sjbeck 			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
4653544Sjbeck 				m->mf_name, (long) expl, where);
4660Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4670Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
4683544Sjbeck 				  "milter_read(%s): read size %ld out of range, where=%s",
4693544Sjbeck 				  m->mf_name, (long) expl, where);
4700Sstevel@tonic-gate 		milter_error(m, e);
4710Sstevel@tonic-gate 		return NULL;
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if (expl == 0)
4750Sstevel@tonic-gate 		return NULL;
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
4780Sstevel@tonic-gate 
4793544Sjbeck 	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
4800Sstevel@tonic-gate 	{
4810Sstevel@tonic-gate 		sm_free(buf); /* XXX */
4820Sstevel@tonic-gate 		return NULL;
4830Sstevel@tonic-gate 	}
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	if (tTd(64, 50))
4860Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
4870Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
4880Sstevel@tonic-gate 	*rlen = expl;
4890Sstevel@tonic-gate 	return buf;
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate /*
4930Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
4940Sstevel@tonic-gate **
4950Sstevel@tonic-gate **	Parameters:
4960Sstevel@tonic-gate **		m -- milter to read from.
4970Sstevel@tonic-gate **		cmd -- command to send.
4980Sstevel@tonic-gate **		buf -- optional command data.
4990Sstevel@tonic-gate **		len -- length of buf.
5000Sstevel@tonic-gate **		to -- timeout in seconds.
5010Sstevel@tonic-gate **		e -- current envelope.
5020Sstevel@tonic-gate **
5030Sstevel@tonic-gate **	Returns:
5040Sstevel@tonic-gate **		buf if successful, NULL otherwise
5050Sstevel@tonic-gate **		Not actually used anywhere but function prototype
5060Sstevel@tonic-gate **			must match milter_read()
5070Sstevel@tonic-gate */
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate static char *
5103544Sjbeck milter_write(m, cmd, buf, len, to, e, where)
5110Sstevel@tonic-gate 	struct milter *m;
5123544Sjbeck 	int cmd;
5130Sstevel@tonic-gate 	char *buf;
5140Sstevel@tonic-gate 	ssize_t len;
5150Sstevel@tonic-gate 	time_t to;
5160Sstevel@tonic-gate 	ENVELOPE *e;
5173544Sjbeck 	const char *where;
5180Sstevel@tonic-gate {
5190Sstevel@tonic-gate 	time_t writestart = (time_t) 0;
5200Sstevel@tonic-gate 	ssize_t sl, i;
5210Sstevel@tonic-gate 	int num_vectors;
5220Sstevel@tonic-gate 	mi_int32 nl;
5233544Sjbeck 	char command = (char) cmd;
5240Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
5250Sstevel@tonic-gate 	bool started = false;
5260Sstevel@tonic-gate 	struct iovec vector[2];
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	/*
5290Sstevel@tonic-gate 	**  At most two buffers will be written, though
5300Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
5310Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
5320Sstevel@tonic-gate 	*/
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
5350Sstevel@tonic-gate 	{
5360Sstevel@tonic-gate 		if (tTd(64, 5))
5370Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): length %ld out of range\n",
5380Sstevel@tonic-gate 				m->mf_name, (long) len);
5390Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5400Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5410Sstevel@tonic-gate 				  "milter_write(%s): length %ld out of range",
5420Sstevel@tonic-gate 				  m->mf_name, (long) len);
5430Sstevel@tonic-gate 		milter_error(m, e);
5440Sstevel@tonic-gate 		return NULL;
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 	if (m->mf_sock < 0)
5470Sstevel@tonic-gate 	{
5480Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5490Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5500Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
5510Sstevel@tonic-gate 				  m->mf_name);
5520Sstevel@tonic-gate 		milter_error(m, e);
5530Sstevel@tonic-gate 		return NULL;
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	if (tTd(64, 20))
5570Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
5583544Sjbeck 			   m->mf_name, command, (long) len);
5593544Sjbeck 
5603544Sjbeck 	nl = htonl(len + 1);	/* add 1 for the command char */
5610Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
5623544Sjbeck 	data[MILTER_LEN_BYTES] = command;
5630Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	/* set up the vector for the size / command */
5660Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
5670Sstevel@tonic-gate 	vector[0].iov_len  = sl;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	/*
5700Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
5710Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
5720Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
5730Sstevel@tonic-gate 	*/
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
5760Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
5770Sstevel@tonic-gate 	{
5780Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
5790Sstevel@tonic-gate 		num_vectors = 1;
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 	else
5820Sstevel@tonic-gate 	{
5830Sstevel@tonic-gate 		/*
5840Sstevel@tonic-gate 		**  There is both size / command and command data.
5850Sstevel@tonic-gate 		**  Set up the vector for the command data.
5860Sstevel@tonic-gate 		*/
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 		num_vectors = 2;
5890Sstevel@tonic-gate 		sl += len;
5900Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
5910Sstevel@tonic-gate 		vector[1].iov_len  = len;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		if (tTd(64, 50))
5940Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
5950Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
5960Sstevel@tonic-gate 	}
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	if (to > 0)
5990Sstevel@tonic-gate 	{
6000Sstevel@tonic-gate 		writestart = curtime();
6013544Sjbeck 		MILTER_TIMEOUT("write", to, true, started, where);
6020Sstevel@tonic-gate 	}
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	/* write the vector(s) */
6050Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
6060Sstevel@tonic-gate 	if (i != sl)
6070Sstevel@tonic-gate 	{
6080Sstevel@tonic-gate 		int save_errno = errno;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 		if (tTd(64, 5))
6110Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
6123544Sjbeck 				   m->mf_name, command, (long) i, (long) sl,
6130Sstevel@tonic-gate 				   sm_errstring(save_errno));
6140Sstevel@tonic-gate 		if (MilterLogLevel > 0)
6150Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6160Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
6173544Sjbeck 				  m->mf_name, command, (long) i, (long) sl,
6180Sstevel@tonic-gate 				  sm_errstring(save_errno));
6190Sstevel@tonic-gate 		milter_error(m, e);
6200Sstevel@tonic-gate 		return NULL;
6210Sstevel@tonic-gate 	}
6220Sstevel@tonic-gate 	return buf;
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate /*
6260Sstevel@tonic-gate **  Utility functions
6270Sstevel@tonic-gate */
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate /*
6300Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
6310Sstevel@tonic-gate **
6320Sstevel@tonic-gate **	Parameters:
6330Sstevel@tonic-gate **		m -- milter to connect to.
6340Sstevel@tonic-gate **		parseonly -- parse but don't connect.
6350Sstevel@tonic-gate **		e -- current envelope.
6360Sstevel@tonic-gate **
6370Sstevel@tonic-gate **	Returns:
6380Sstevel@tonic-gate **		connected socket if successful && !parseonly,
6390Sstevel@tonic-gate **		0 upon parse success if parseonly,
6400Sstevel@tonic-gate **		-1 otherwise.
6410Sstevel@tonic-gate */
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate static int
6460Sstevel@tonic-gate milter_open(m, parseonly, e)
6470Sstevel@tonic-gate 	struct milter *m;
6480Sstevel@tonic-gate 	bool parseonly;
6490Sstevel@tonic-gate 	ENVELOPE *e;
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	int sock = 0;
6520Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
6530Sstevel@tonic-gate 	int addrno = 0;
6540Sstevel@tonic-gate 	int save_errno;
6550Sstevel@tonic-gate 	char *p;
6560Sstevel@tonic-gate 	char *colon;
6570Sstevel@tonic-gate 	char *at;
6580Sstevel@tonic-gate 	struct hostent *hp = NULL;
6590Sstevel@tonic-gate 	SOCKADDR addr;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
6620Sstevel@tonic-gate 	{
6630Sstevel@tonic-gate 		if (tTd(64, 5))
6640Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
6650Sstevel@tonic-gate 				   m->mf_name);
6660Sstevel@tonic-gate 		if (parseonly)
6670Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
6680Sstevel@tonic-gate 			       m->mf_name);
6690Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
6700Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6710Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
6720Sstevel@tonic-gate 				  m->mf_name);
6730Sstevel@tonic-gate 		milter_error(m, e);
6740Sstevel@tonic-gate 		return -1;
6750Sstevel@tonic-gate 	}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
6783544Sjbeck 	memset(&addr, '\0', sizeof(addr));
6790Sstevel@tonic-gate 	p = m->mf_conn;
6800Sstevel@tonic-gate 	colon = strchr(p, ':');
6810Sstevel@tonic-gate 	if (colon != NULL)
6820Sstevel@tonic-gate 	{
6830Sstevel@tonic-gate 		*colon = '\0';
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 		if (*p == '\0')
6860Sstevel@tonic-gate 		{
6870Sstevel@tonic-gate # if NETUNIX
6880Sstevel@tonic-gate 			/* default to AF_UNIX */
6890Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
6900Sstevel@tonic-gate # else /* NETUNIX */
6910Sstevel@tonic-gate #  if NETINET
6920Sstevel@tonic-gate 			/* default to AF_INET */
6930Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
6940Sstevel@tonic-gate #  else /* NETINET */
6950Sstevel@tonic-gate #   if NETINET6
6960Sstevel@tonic-gate 			/* default to AF_INET6 */
6970Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
6980Sstevel@tonic-gate #   else /* NETINET6 */
6990Sstevel@tonic-gate 			/* no protocols available */
7000Sstevel@tonic-gate 			if (MilterLogLevel > 0)
7010Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7020Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
7030Sstevel@tonic-gate 					  m->mf_name);
7040Sstevel@tonic-gate 			milter_error(m, e);
7050Sstevel@tonic-gate 			return -1;
7060Sstevel@tonic-gate #   endif /* NETINET6 */
7070Sstevel@tonic-gate #  endif /* NETINET */
7080Sstevel@tonic-gate # endif /* NETUNIX */
7090Sstevel@tonic-gate 		}
7100Sstevel@tonic-gate # if NETUNIX
7110Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
7120Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
7130Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
7140Sstevel@tonic-gate # endif /* NETUNIX */
7150Sstevel@tonic-gate # if NETINET
7160Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
7170Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
7180Sstevel@tonic-gate # endif /* NETINET */
7190Sstevel@tonic-gate # if NETINET6
7200Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
7210Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
7220Sstevel@tonic-gate # endif /* NETINET6 */
7230Sstevel@tonic-gate 		else
7240Sstevel@tonic-gate 		{
7250Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
7260Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
7270Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
7280Sstevel@tonic-gate 			errno = EINVAL;
7290Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
7300Sstevel@tonic-gate 			if (tTd(64, 5))
7310Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
7320Sstevel@tonic-gate 					m->mf_name, p);
7330Sstevel@tonic-gate 			if (parseonly)
7340Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
7350Sstevel@tonic-gate 				       m->mf_name, p);
7360Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7370Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7380Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
7390Sstevel@tonic-gate 					  m->mf_name, p);
7400Sstevel@tonic-gate 			milter_error(m, e);
7410Sstevel@tonic-gate 			return -1;
7420Sstevel@tonic-gate 		}
7430Sstevel@tonic-gate 		*colon++ = ':';
7440Sstevel@tonic-gate 	}
7450Sstevel@tonic-gate 	else
7460Sstevel@tonic-gate 	{
7470Sstevel@tonic-gate 		/* default to AF_UNIX */
7480Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
7490Sstevel@tonic-gate 		colon = p;
7500Sstevel@tonic-gate 	}
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate # if NETUNIX
7530Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
7540Sstevel@tonic-gate 	{
7550Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 		at = colon;
7583544Sjbeck 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7590Sstevel@tonic-gate 		{
7600Sstevel@tonic-gate 			if (tTd(64, 5))
7610Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
7620Sstevel@tonic-gate 					m->mf_name, colon);
7630Sstevel@tonic-gate 			errno = EINVAL;
7640Sstevel@tonic-gate 			if (parseonly)
7650Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
7660Sstevel@tonic-gate 				       m->mf_name, colon);
7670Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7680Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7690Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
7700Sstevel@tonic-gate 					  m->mf_name, colon);
7710Sstevel@tonic-gate 			milter_error(m, e);
7720Sstevel@tonic-gate 			return -1;
7730Sstevel@tonic-gate 		}
7740Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7750Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
7780Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
7790Sstevel@tonic-gate 		{
7800Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
7810Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
7820Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7830Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
7840Sstevel@tonic-gate 						     m->mf_name, colon);
7850Sstevel@tonic-gate 		}
7860Sstevel@tonic-gate 		else if (errno != 0)
7870Sstevel@tonic-gate 		{
7880Sstevel@tonic-gate 			/* if not safe, don't create */
7890Sstevel@tonic-gate 			save_errno = errno;
7900Sstevel@tonic-gate 			if (tTd(64, 5))
7910Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
7920Sstevel@tonic-gate 					m->mf_name, colon);
7930Sstevel@tonic-gate 			errno = save_errno;
7940Sstevel@tonic-gate 			if (parseonly)
7950Sstevel@tonic-gate 			{
7960Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
7970Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
7980Sstevel@tonic-gate 				    OpMode == MD_SMTP)
7990Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
8000Sstevel@tonic-gate 					       m->mf_name, colon);
8010Sstevel@tonic-gate 			}
8020Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8030Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8040Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
8050Sstevel@tonic-gate 					  m->mf_name, colon);
8060Sstevel@tonic-gate 			milter_error(m, e);
8070Sstevel@tonic-gate 			return -1;
8080Sstevel@tonic-gate 		}
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
8113544Sjbeck 			       sizeof(addr.sunix.sun_path));
8123544Sjbeck 		addrlen = sizeof(struct sockaddr_un);
8130Sstevel@tonic-gate 	}
8140Sstevel@tonic-gate 	else
8150Sstevel@tonic-gate # endif /* NETUNIX */
8160Sstevel@tonic-gate # if NETINET || NETINET6
8170Sstevel@tonic-gate 	if (false
8180Sstevel@tonic-gate #  if NETINET
8190Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
8200Sstevel@tonic-gate #  endif /* NETINET */
8210Sstevel@tonic-gate #  if NETINET6
8220Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
8230Sstevel@tonic-gate #  endif /* NETINET6 */
8240Sstevel@tonic-gate 		 )
8250Sstevel@tonic-gate 	{
8260Sstevel@tonic-gate 		unsigned short port;
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 		/* Parse port@host */
8290Sstevel@tonic-gate 		at = strchr(colon, '@');
8300Sstevel@tonic-gate 		if (at == NULL)
8310Sstevel@tonic-gate 		{
8320Sstevel@tonic-gate 			if (tTd(64, 5))
8330Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
8340Sstevel@tonic-gate 					m->mf_name, colon);
8350Sstevel@tonic-gate 			if (parseonly)
8360Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
8370Sstevel@tonic-gate 				       m->mf_name, colon);
8380Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8390Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8400Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
8410Sstevel@tonic-gate 					  m->mf_name, colon);
8420Sstevel@tonic-gate 			milter_error(m, e);
8430Sstevel@tonic-gate 			return -1;
8440Sstevel@tonic-gate 		}
8450Sstevel@tonic-gate 		*at = '\0';
8460Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
8470Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
8480Sstevel@tonic-gate 		else
8490Sstevel@tonic-gate 		{
8500Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
8510Sstevel@tonic-gate 			if (tTd(64, 5))
8520Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
8530Sstevel@tonic-gate 					m->mf_name, colon);
8540Sstevel@tonic-gate 			if (parseonly)
8550Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
8560Sstevel@tonic-gate 				       m->mf_name, colon);
8570Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8580Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8590Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
8600Sstevel@tonic-gate 					  m->mf_name, colon);
8610Sstevel@tonic-gate 			milter_error(m, e);
8620Sstevel@tonic-gate 			return -1;
8630Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
8643544Sjbeck 			struct servent *sp;
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
8670Sstevel@tonic-gate 			if (sp == NULL)
8680Sstevel@tonic-gate 			{
8690Sstevel@tonic-gate 				save_errno = errno;
8700Sstevel@tonic-gate 				if (tTd(64, 5))
8710Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
8720Sstevel@tonic-gate 						m->mf_name, colon);
8730Sstevel@tonic-gate 				errno = save_errno;
8740Sstevel@tonic-gate 				if (parseonly)
8750Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
8760Sstevel@tonic-gate 					       m->mf_name, colon);
8770Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
8780Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
8790Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
8800Sstevel@tonic-gate 						  m->mf_name, colon);
8810Sstevel@tonic-gate 				milter_error(m, e);
8820Sstevel@tonic-gate 				return -1;
8830Sstevel@tonic-gate 			}
8840Sstevel@tonic-gate 			port = sp->s_port;
8850Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
8860Sstevel@tonic-gate 		}
8870Sstevel@tonic-gate 		*at++ = '@';
8880Sstevel@tonic-gate 		if (*at == '[')
8890Sstevel@tonic-gate 		{
8900Sstevel@tonic-gate 			char *end;
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 			end = strchr(at, ']');
8930Sstevel@tonic-gate 			if (end != NULL)
8940Sstevel@tonic-gate 			{
8950Sstevel@tonic-gate 				bool found = false;
8960Sstevel@tonic-gate #  if NETINET
8970Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
8980Sstevel@tonic-gate #  endif /* NETINET */
8990Sstevel@tonic-gate #  if NETINET6
9000Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
9010Sstevel@tonic-gate #  endif /* NETINET6 */
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 				*end = '\0';
9040Sstevel@tonic-gate #  if NETINET
9050Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
9060Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
9070Sstevel@tonic-gate 				{
9080Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
9090Sstevel@tonic-gate 					addr.sin.sin_port = port;
9100Sstevel@tonic-gate 					found = true;
9110Sstevel@tonic-gate 				}
9120Sstevel@tonic-gate #  endif /* NETINET */
9130Sstevel@tonic-gate #  if NETINET6
9143544Sjbeck 				(void) memset(&hid6, '\0', sizeof(hid6));
9150Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
9160Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
9170Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
9180Sstevel@tonic-gate 				{
9190Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
9200Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
9210Sstevel@tonic-gate 					found = true;
9220Sstevel@tonic-gate 				}
9230Sstevel@tonic-gate #  endif /* NETINET6 */
9240Sstevel@tonic-gate 				*end = ']';
9250Sstevel@tonic-gate 				if (!found)
9260Sstevel@tonic-gate 				{
9270Sstevel@tonic-gate 					if (tTd(64, 5))
9280Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9290Sstevel@tonic-gate 							m->mf_name, at);
9300Sstevel@tonic-gate 					if (parseonly)
9310Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
9320Sstevel@tonic-gate 						       m->mf_name, at);
9330Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
9340Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9350Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
9360Sstevel@tonic-gate 							  m->mf_name, at);
9370Sstevel@tonic-gate 					milter_error(m, e);
9380Sstevel@tonic-gate 					return -1;
9390Sstevel@tonic-gate 				}
9400Sstevel@tonic-gate 			}
9410Sstevel@tonic-gate 			else
9420Sstevel@tonic-gate 			{
9430Sstevel@tonic-gate 				if (tTd(64, 5))
9440Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9450Sstevel@tonic-gate 						m->mf_name, at);
9460Sstevel@tonic-gate 				if (parseonly)
9470Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
9480Sstevel@tonic-gate 					       m->mf_name, at);
9490Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9500Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9510Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
9520Sstevel@tonic-gate 						  m->mf_name, at);
9530Sstevel@tonic-gate 				milter_error(m, e);
9540Sstevel@tonic-gate 				return -1;
9550Sstevel@tonic-gate 			}
9560Sstevel@tonic-gate 		}
9570Sstevel@tonic-gate 		else
9580Sstevel@tonic-gate 		{
9590Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
9600Sstevel@tonic-gate 			if (hp == NULL)
9610Sstevel@tonic-gate 			{
9620Sstevel@tonic-gate 				save_errno = errno;
9630Sstevel@tonic-gate 				if (tTd(64, 5))
9640Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
9650Sstevel@tonic-gate 						   m->mf_name, at);
9660Sstevel@tonic-gate 				errno = save_errno;
9670Sstevel@tonic-gate 				if (parseonly)
9680Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
9690Sstevel@tonic-gate 					       m->mf_name, at);
9700Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9710Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9720Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
9730Sstevel@tonic-gate 						  m->mf_name, at);
9740Sstevel@tonic-gate 				milter_error(m, e);
9750Sstevel@tonic-gate 				return -1;
9760Sstevel@tonic-gate 			}
9770Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
9780Sstevel@tonic-gate 			switch (hp->h_addrtype)
9790Sstevel@tonic-gate 			{
9800Sstevel@tonic-gate #  if NETINET
9810Sstevel@tonic-gate 			  case AF_INET:
9820Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
9830Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
9840Sstevel@tonic-gate 				addr.sin.sin_port = port;
9853544Sjbeck 				addrlen = sizeof(struct sockaddr_in);
9860Sstevel@tonic-gate 				addrno = 1;
9870Sstevel@tonic-gate 				break;
9880Sstevel@tonic-gate #  endif /* NETINET */
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate #  if NETINET6
9910Sstevel@tonic-gate 			  case AF_INET6:
9920Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
9930Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
9940Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
9953544Sjbeck 				addrlen = sizeof(struct sockaddr_in6);
9960Sstevel@tonic-gate 				addrno = 1;
9970Sstevel@tonic-gate 				break;
9980Sstevel@tonic-gate #  endif /* NETINET6 */
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 			  default:
10010Sstevel@tonic-gate 				if (tTd(64, 5))
10020Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
10030Sstevel@tonic-gate 						   m->mf_name, at,
10040Sstevel@tonic-gate 						   hp->h_addrtype);
10050Sstevel@tonic-gate 				if (parseonly)
10060Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
10070Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
10080Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
10090Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
10100Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
10110Sstevel@tonic-gate 						  m->mf_name, at,
10120Sstevel@tonic-gate 						  hp->h_addrtype);
10130Sstevel@tonic-gate 				milter_error(m, e);
10140Sstevel@tonic-gate #  if NETINET6
10150Sstevel@tonic-gate 				freehostent(hp);
10160Sstevel@tonic-gate #  endif /* NETINET6 */
10170Sstevel@tonic-gate 				return -1;
10180Sstevel@tonic-gate 			}
10190Sstevel@tonic-gate 		}
10200Sstevel@tonic-gate 	}
10210Sstevel@tonic-gate 	else
10220Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
10230Sstevel@tonic-gate 	{
10240Sstevel@tonic-gate 		if (tTd(64, 5))
10250Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
10260Sstevel@tonic-gate 				   m->mf_name);
10270Sstevel@tonic-gate 		if (parseonly)
10280Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
10290Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
10300Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
10310Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
10320Sstevel@tonic-gate 				  m->mf_name);
10330Sstevel@tonic-gate 		milter_error(m, e);
10340Sstevel@tonic-gate 		return -1;
10350Sstevel@tonic-gate 	}
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate 	/* just parsing through? */
10380Sstevel@tonic-gate 	if (parseonly)
10390Sstevel@tonic-gate 	{
10400Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
10410Sstevel@tonic-gate # if NETINET6
10420Sstevel@tonic-gate 		if (hp != NULL)
10430Sstevel@tonic-gate 			freehostent(hp);
10440Sstevel@tonic-gate # endif /* NETINET6 */
10450Sstevel@tonic-gate 		return 0;
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 	/* sanity check */
10490Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
10500Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
10510Sstevel@tonic-gate 	{
10520Sstevel@tonic-gate 		/* shouldn't happen */
10530Sstevel@tonic-gate 		if (tTd(64, 1))
10540Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
10550Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
10560Sstevel@tonic-gate 		milter_error(m, e);
10570Sstevel@tonic-gate # if NETINET6
10580Sstevel@tonic-gate 		if (hp != NULL)
10590Sstevel@tonic-gate 			freehostent(hp);
10600Sstevel@tonic-gate # endif /* NETINET6 */
10610Sstevel@tonic-gate 		return -1;
10620Sstevel@tonic-gate 	}
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	/* nope, actually connecting */
10650Sstevel@tonic-gate 	for (;;)
10660Sstevel@tonic-gate 	{
10670Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
10680Sstevel@tonic-gate 		if (sock < 0)
10690Sstevel@tonic-gate 		{
10700Sstevel@tonic-gate 			save_errno = errno;
10710Sstevel@tonic-gate 			if (tTd(64, 5))
10720Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
10730Sstevel@tonic-gate 					   m->mf_name,
10740Sstevel@tonic-gate 					   sm_errstring(save_errno));
10750Sstevel@tonic-gate 			if (MilterLogLevel > 0)
10760Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
10770Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
10780Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
10790Sstevel@tonic-gate 			milter_error(m, e);
10800Sstevel@tonic-gate # if NETINET6
10810Sstevel@tonic-gate 			if (hp != NULL)
10820Sstevel@tonic-gate 				freehostent(hp);
10830Sstevel@tonic-gate # endif /* NETINET6 */
10840Sstevel@tonic-gate 			return -1;
10850Sstevel@tonic-gate 		}
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
10880Sstevel@tonic-gate 		{
10890Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
10900Sstevel@tonic-gate 			int i;
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
10930Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
10940Sstevel@tonic-gate 						 milter_connect_timeout, 0);
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
10970Sstevel@tonic-gate 			save_errno = errno;
10980Sstevel@tonic-gate 			if (ev != NULL)
10990Sstevel@tonic-gate 				sm_clrevent(ev);
11000Sstevel@tonic-gate 			errno = save_errno;
11010Sstevel@tonic-gate 			if (i >= 0)
11020Sstevel@tonic-gate 				break;
11030Sstevel@tonic-gate 		}
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate 		/* couldn't connect.... try next address */
11060Sstevel@tonic-gate 		save_errno = errno;
11070Sstevel@tonic-gate 		p = CurHostName;
11080Sstevel@tonic-gate 		CurHostName = at;
11090Sstevel@tonic-gate 		if (tTd(64, 5))
11100Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
11110Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
11120Sstevel@tonic-gate 		if (MilterLogLevel > 13)
11130Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
11140Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
11150Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
11160Sstevel@tonic-gate 		CurHostName = p;
11170Sstevel@tonic-gate 		(void) close(sock);
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 		/* try next address */
11200Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
11210Sstevel@tonic-gate 		{
11220Sstevel@tonic-gate 			switch (addr.sa.sa_family)
11230Sstevel@tonic-gate 			{
11240Sstevel@tonic-gate # if NETINET
11250Sstevel@tonic-gate 			  case AF_INET:
11260Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
11270Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11280Sstevel@tonic-gate 					INADDRSZ);
11290Sstevel@tonic-gate 				break;
11300Sstevel@tonic-gate # endif /* NETINET */
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate # if NETINET6
11330Sstevel@tonic-gate 			  case AF_INET6:
11340Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
11350Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11360Sstevel@tonic-gate 					IN6ADDRSZ);
11370Sstevel@tonic-gate 				break;
11380Sstevel@tonic-gate # endif /* NETINET6 */
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 			  default:
11410Sstevel@tonic-gate 				if (tTd(64, 5))
11420Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
11430Sstevel@tonic-gate 						   m->mf_name, at,
11440Sstevel@tonic-gate 						   hp->h_addrtype);
11450Sstevel@tonic-gate 				if (MilterLogLevel > 0)
11460Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
11470Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
11480Sstevel@tonic-gate 						  m->mf_name, at,
11490Sstevel@tonic-gate 						  hp->h_addrtype);
11500Sstevel@tonic-gate 				milter_error(m, e);
11510Sstevel@tonic-gate # if NETINET6
11520Sstevel@tonic-gate 				freehostent(hp);
11530Sstevel@tonic-gate # endif /* NETINET6 */
11540Sstevel@tonic-gate 				return -1;
11550Sstevel@tonic-gate 			}
11560Sstevel@tonic-gate 			continue;
11570Sstevel@tonic-gate 		}
11580Sstevel@tonic-gate 		p = CurHostName;
11590Sstevel@tonic-gate 		CurHostName = at;
11600Sstevel@tonic-gate 		if (tTd(64, 5))
11610Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
11620Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno));
11630Sstevel@tonic-gate 		if (MilterLogLevel > 0)
11640Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
11650Sstevel@tonic-gate 				  "Milter (%s): error connecting to filter: %s",
11660Sstevel@tonic-gate 				  m->mf_name, sm_errstring(save_errno));
11670Sstevel@tonic-gate 		CurHostName = p;
11680Sstevel@tonic-gate 		milter_error(m, e);
11690Sstevel@tonic-gate # if NETINET6
11700Sstevel@tonic-gate 		if (hp != NULL)
11710Sstevel@tonic-gate 			freehostent(hp);
11720Sstevel@tonic-gate # endif /* NETINET6 */
11730Sstevel@tonic-gate 		return -1;
11740Sstevel@tonic-gate 	}
11750Sstevel@tonic-gate 	m->mf_state = SMFS_OPEN;
11760Sstevel@tonic-gate # if NETINET6
11770Sstevel@tonic-gate 	if (hp != NULL)
11780Sstevel@tonic-gate 	{
11790Sstevel@tonic-gate 		freehostent(hp);
11800Sstevel@tonic-gate 		hp = NULL;
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate # endif /* NETINET6 */
11833544Sjbeck # if MILTER_NO_NAGLE && !defined(TCP_CORK)
11840Sstevel@tonic-gate 	{
11850Sstevel@tonic-gate 		int nodelay = 1;
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 		setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
11880Sstevel@tonic-gate 			   (char *)&nodelay, sizeof(nodelay));
11890Sstevel@tonic-gate 	}
11903544Sjbeck # endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
11910Sstevel@tonic-gate 	return sock;
11920Sstevel@tonic-gate }
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate static void
11950Sstevel@tonic-gate milter_connect_timeout(ignore)
11960Sstevel@tonic-gate 	int ignore;
11970Sstevel@tonic-gate {
11980Sstevel@tonic-gate 	/*
11990Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
12000Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
12010Sstevel@tonic-gate 	**	DOING.
12020Sstevel@tonic-gate 	*/
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	errno = ETIMEDOUT;
12050Sstevel@tonic-gate 	longjmp(MilterConnectTimeout, 1);
12060Sstevel@tonic-gate }
12073544Sjbeck 
12080Sstevel@tonic-gate /*
12090Sstevel@tonic-gate **  MILTER_SETUP -- setup structure for a mail filter
12100Sstevel@tonic-gate **
12110Sstevel@tonic-gate **	Parameters:
12120Sstevel@tonic-gate **		line -- the options line.
12130Sstevel@tonic-gate **
12140Sstevel@tonic-gate **	Returns:
12150Sstevel@tonic-gate **		none
12160Sstevel@tonic-gate */
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate void
12190Sstevel@tonic-gate milter_setup(line)
12200Sstevel@tonic-gate 	char *line;
12210Sstevel@tonic-gate {
12220Sstevel@tonic-gate 	char fcode;
12233544Sjbeck 	char *p;
12243544Sjbeck 	struct milter *m;
12250Sstevel@tonic-gate 	STAB *s;
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	/* collect the filter name */
12280Sstevel@tonic-gate 	for (p = line;
12290Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
12300Sstevel@tonic-gate 	     p++)
12310Sstevel@tonic-gate 		continue;
12320Sstevel@tonic-gate 	if (*p != '\0')
12330Sstevel@tonic-gate 		*p++ = '\0';
12340Sstevel@tonic-gate 	if (line[0] == '\0')
12350Sstevel@tonic-gate 	{
12360Sstevel@tonic-gate 		syserr("name required for mail filter");
12370Sstevel@tonic-gate 		return;
12380Sstevel@tonic-gate 	}
12393544Sjbeck 	m = (struct milter *) xalloc(sizeof(*m));
12403544Sjbeck 	memset((char *) m, '\0', sizeof(*m));
12410Sstevel@tonic-gate 	m->mf_name = newstr(line);
12420Sstevel@tonic-gate 	m->mf_state = SMFS_READY;
12430Sstevel@tonic-gate 	m->mf_sock = -1;
12440Sstevel@tonic-gate 	m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
12450Sstevel@tonic-gate 	m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
12460Sstevel@tonic-gate 	m->mf_timeout[SMFTO_READ] = (time_t) 10;
12470Sstevel@tonic-gate 	m->mf_timeout[SMFTO_EOM] = (time_t) 300;
12483966Sjbeck #if _FFR_MILTER_CHECK
12493544Sjbeck 	m->mf_mta_prot_version = SMFI_PROT_VERSION;
12503544Sjbeck 	m->mf_mta_prot_flags = SMFI_CURR_PROT;
12513544Sjbeck 	m->mf_mta_actions = SMFI_CURR_ACTS;
12523966Sjbeck #endif /* _FFR_MILTER_CHECK */
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
12550Sstevel@tonic-gate 	while (*p != '\0')
12560Sstevel@tonic-gate 	{
12570Sstevel@tonic-gate 		char *delimptr;
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 		while (*p != '\0' &&
12600Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
12610Sstevel@tonic-gate 			p++;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 		/* p now points to field code */
12640Sstevel@tonic-gate 		fcode = *p;
12650Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
12660Sstevel@tonic-gate 			p++;
12670Sstevel@tonic-gate 		if (*p++ != '=')
12680Sstevel@tonic-gate 		{
12690Sstevel@tonic-gate 			syserr("X%s: `=' expected", m->mf_name);
12700Sstevel@tonic-gate 			return;
12710Sstevel@tonic-gate 		}
12720Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
12730Sstevel@tonic-gate 			p++;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 		/* p now points to the field body */
12760Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 		/* install the field into the filter struct */
12790Sstevel@tonic-gate 		switch (fcode)
12800Sstevel@tonic-gate 		{
12810Sstevel@tonic-gate 		  case 'S':		/* socket */
12820Sstevel@tonic-gate 			if (p == NULL)
12830Sstevel@tonic-gate 				m->mf_conn = NULL;
12840Sstevel@tonic-gate 			else
12850Sstevel@tonic-gate 				m->mf_conn = newstr(p);
12860Sstevel@tonic-gate 			break;
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 		  case 'F':		/* Milter flags configured on MTA */
12890Sstevel@tonic-gate 			for (; *p != '\0'; p++)
12900Sstevel@tonic-gate 			{
12910Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
12920Sstevel@tonic-gate 					setbitn(bitidx(*p), m->mf_flags);
12930Sstevel@tonic-gate 			}
12940Sstevel@tonic-gate 			break;
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 		  case 'T':		/* timeouts */
12970Sstevel@tonic-gate 			milter_parse_timeouts(p, m);
12980Sstevel@tonic-gate 			break;
12990Sstevel@tonic-gate 
13003966Sjbeck #if _FFR_MILTER_CHECK
13013544Sjbeck 		  case 'a':
13023544Sjbeck 			m->mf_mta_actions = strtoul(p, NULL, 0);
13033544Sjbeck 			break;
13043544Sjbeck 		  case 'f':
13053544Sjbeck 			m->mf_mta_prot_flags = strtoul(p, NULL, 0);
13063544Sjbeck 			break;
13073544Sjbeck 		  case 'v':
13083544Sjbeck 			m->mf_mta_prot_version = strtoul(p, NULL, 0);
13093544Sjbeck 			break;
13103966Sjbeck #endif /* _FFR_MILTER_CHECK */
13113544Sjbeck 
13120Sstevel@tonic-gate 		  default:
13130Sstevel@tonic-gate 			syserr("X%s: unknown filter equate %c=",
13140Sstevel@tonic-gate 			       m->mf_name, fcode);
13150Sstevel@tonic-gate 			break;
13160Sstevel@tonic-gate 		}
13170Sstevel@tonic-gate 		p = delimptr;
13180Sstevel@tonic-gate 	}
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	/* early check for errors */
13210Sstevel@tonic-gate 	(void) milter_open(m, true, CurEnv);
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 	/* enter the filter into the symbol table */
13240Sstevel@tonic-gate 	s = stab(m->mf_name, ST_MILTER, ST_ENTER);
13250Sstevel@tonic-gate 	if (s->s_milter != NULL)
13260Sstevel@tonic-gate 		syserr("X%s: duplicate filter definition", m->mf_name);
13270Sstevel@tonic-gate 	else
13280Sstevel@tonic-gate 		s->s_milter = m;
13290Sstevel@tonic-gate }
13303544Sjbeck 
13310Sstevel@tonic-gate /*
13320Sstevel@tonic-gate **  MILTER_CONFIG -- parse option list into an array and check config
13330Sstevel@tonic-gate **
13340Sstevel@tonic-gate **	Called when reading configuration file.
13350Sstevel@tonic-gate **
13360Sstevel@tonic-gate **	Parameters:
13370Sstevel@tonic-gate **		spec -- the filter list.
13380Sstevel@tonic-gate **		list -- the array to fill in.
13390Sstevel@tonic-gate **		max -- the maximum number of entries in list.
13400Sstevel@tonic-gate **
13410Sstevel@tonic-gate **	Returns:
13420Sstevel@tonic-gate **		none
13430Sstevel@tonic-gate */
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate void
13460Sstevel@tonic-gate milter_config(spec, list, max)
13470Sstevel@tonic-gate 	char *spec;
13480Sstevel@tonic-gate 	struct milter **list;
13490Sstevel@tonic-gate 	int max;
13500Sstevel@tonic-gate {
13510Sstevel@tonic-gate 	int numitems = 0;
13523544Sjbeck 	char *p;
13530Sstevel@tonic-gate 
13540Sstevel@tonic-gate 	/* leave one for the NULL signifying the end of the list */
13550Sstevel@tonic-gate 	max--;
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 	for (p = spec; p != NULL; )
13580Sstevel@tonic-gate 	{
13590Sstevel@tonic-gate 		STAB *s;
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
13620Sstevel@tonic-gate 			p++;
13630Sstevel@tonic-gate 		if (*p == '\0')
13640Sstevel@tonic-gate 			break;
13650Sstevel@tonic-gate 		spec = p;
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 		if (numitems >= max)
13680Sstevel@tonic-gate 		{
13690Sstevel@tonic-gate 			syserr("Too many filters defined, %d max", max);
13700Sstevel@tonic-gate 			if (max > 0)
13710Sstevel@tonic-gate 				list[0] = NULL;
13720Sstevel@tonic-gate 			return;
13730Sstevel@tonic-gate 		}
13740Sstevel@tonic-gate 		p = strpbrk(p, ";,");
13750Sstevel@tonic-gate 		if (p != NULL)
13760Sstevel@tonic-gate 			*p++ = '\0';
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 		s = stab(spec, ST_MILTER, ST_FIND);
13790Sstevel@tonic-gate 		if (s == NULL)
13800Sstevel@tonic-gate 		{
13810Sstevel@tonic-gate 			syserr("InputFilter %s not defined", spec);
13820Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
13830Sstevel@tonic-gate 			return;
13840Sstevel@tonic-gate 		}
13850Sstevel@tonic-gate 		list[numitems++] = s->s_milter;
13860Sstevel@tonic-gate 	}
13870Sstevel@tonic-gate 	list[numitems] = NULL;
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	/* if not set, set to LogLevel */
13900Sstevel@tonic-gate 	if (MilterLogLevel == -1)
13910Sstevel@tonic-gate 		MilterLogLevel = LogLevel;
13920Sstevel@tonic-gate }
13933544Sjbeck 
13940Sstevel@tonic-gate /*
13950Sstevel@tonic-gate **  MILTER_PARSE_TIMEOUTS -- parse timeout list
13960Sstevel@tonic-gate **
13970Sstevel@tonic-gate **	Called when reading configuration file.
13980Sstevel@tonic-gate **
13990Sstevel@tonic-gate **	Parameters:
14000Sstevel@tonic-gate **		spec -- the timeout list.
14010Sstevel@tonic-gate **		m -- milter to set.
14020Sstevel@tonic-gate **
14030Sstevel@tonic-gate **	Returns:
14040Sstevel@tonic-gate **		none
14050Sstevel@tonic-gate */
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate static void
14080Sstevel@tonic-gate milter_parse_timeouts(spec, m)
14090Sstevel@tonic-gate 	char *spec;
14100Sstevel@tonic-gate 	struct milter *m;
14110Sstevel@tonic-gate {
14120Sstevel@tonic-gate 	char fcode;
14130Sstevel@tonic-gate 	int tcode;
14143544Sjbeck 	char *p;
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	p = spec;
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
14190Sstevel@tonic-gate 	while (*p != '\0')
14200Sstevel@tonic-gate 	{
14210Sstevel@tonic-gate 		char *delimptr;
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 		while (*p != '\0' &&
14240Sstevel@tonic-gate 		       (*p == ';' || (isascii(*p) && isspace(*p))))
14250Sstevel@tonic-gate 			p++;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 		/* p now points to field code */
14280Sstevel@tonic-gate 		fcode = *p;
14290Sstevel@tonic-gate 		while (*p != '\0' && *p != ':')
14300Sstevel@tonic-gate 			p++;
14310Sstevel@tonic-gate 		if (*p++ != ':')
14320Sstevel@tonic-gate 		{
14330Sstevel@tonic-gate 			syserr("X%s, T=: `:' expected", m->mf_name);
14340Sstevel@tonic-gate 			return;
14350Sstevel@tonic-gate 		}
14360Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
14370Sstevel@tonic-gate 			p++;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 		/* p now points to the field body */
14400Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ';');
14410Sstevel@tonic-gate 		tcode = -1;
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 		/* install the field into the filter struct */
14440Sstevel@tonic-gate 		switch (fcode)
14450Sstevel@tonic-gate 		{
14460Sstevel@tonic-gate 		  case 'C':
14470Sstevel@tonic-gate 			tcode = SMFTO_CONNECT;
14480Sstevel@tonic-gate 			break;
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 		  case 'S':
14510Sstevel@tonic-gate 			tcode = SMFTO_WRITE;
14520Sstevel@tonic-gate 			break;
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate 		  case 'R':
14550Sstevel@tonic-gate 			tcode = SMFTO_READ;
14560Sstevel@tonic-gate 			break;
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 		  case 'E':
14590Sstevel@tonic-gate 			tcode = SMFTO_EOM;
14600Sstevel@tonic-gate 			break;
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 		  default:
14630Sstevel@tonic-gate 			if (tTd(64, 5))
14640Sstevel@tonic-gate 				sm_dprintf("X%s: %c unknown\n",
14650Sstevel@tonic-gate 					   m->mf_name, fcode);
14660Sstevel@tonic-gate 			syserr("X%s: unknown filter timeout %c",
14670Sstevel@tonic-gate 			       m->mf_name, fcode);
14680Sstevel@tonic-gate 			break;
14690Sstevel@tonic-gate 		}
14700Sstevel@tonic-gate 		if (tcode >= 0)
14710Sstevel@tonic-gate 		{
14720Sstevel@tonic-gate 			m->mf_timeout[tcode] = convtime(p, 's');
14730Sstevel@tonic-gate 			if (tTd(64, 5))
14740Sstevel@tonic-gate 				sm_dprintf("X%s: %c=%ld\n",
14750Sstevel@tonic-gate 					   m->mf_name, fcode,
14760Sstevel@tonic-gate 					   (u_long) m->mf_timeout[tcode]);
14770Sstevel@tonic-gate 		}
14780Sstevel@tonic-gate 		p = delimptr;
14790Sstevel@tonic-gate 	}
14800Sstevel@tonic-gate }
14813544Sjbeck 
14823544Sjbeck /*
14833544Sjbeck **  MILTER_SET_MACROS -- set milter macros
14843544Sjbeck **
14853544Sjbeck **	Parameters:
14863544Sjbeck **		name -- name of milter.
14873544Sjbeck **		macros -- where to store macros.
14883544Sjbeck **		val -- the value of the option.
14893544Sjbeck **		nummac -- current number of macros
14903544Sjbeck **
14913544Sjbeck **	Returns:
14923544Sjbeck **		new number of macros
14933544Sjbeck */
14943544Sjbeck 
14953544Sjbeck static int
14963544Sjbeck milter_set_macros(name, macros, val, nummac)
14973544Sjbeck 	char *name;
14983544Sjbeck 	char **macros;
14993544Sjbeck 	char *val;
15003544Sjbeck 	int nummac;
15013544Sjbeck {
15023544Sjbeck 	char *p;
15033544Sjbeck 
15043544Sjbeck 	p = newstr(val);
15053544Sjbeck 	while (*p != '\0')
15063544Sjbeck 	{
15073544Sjbeck 		char *macro;
15083544Sjbeck 
15093544Sjbeck 		/* Skip leading commas, spaces */
15103544Sjbeck 		while (*p != '\0' &&
15113544Sjbeck 		       (*p == ',' || (isascii(*p) && isspace(*p))))
15123544Sjbeck 			p++;
15133544Sjbeck 
15143544Sjbeck 		if (*p == '\0')
15153544Sjbeck 			break;
15163544Sjbeck 
15173544Sjbeck 		/* Find end of macro */
15183544Sjbeck 		macro = p;
15193544Sjbeck 		while (*p != '\0' && *p != ',' &&
15203544Sjbeck 		       isascii(*p) && !isspace(*p))
15213544Sjbeck 			p++;
15223544Sjbeck 		if (*p != '\0')
15233544Sjbeck 			*p++ = '\0';
15243544Sjbeck 
15253544Sjbeck 		if (nummac >= MAXFILTERMACROS)
15263544Sjbeck 		{
15273544Sjbeck 			syserr("milter_set_option: too many macros in Milter.%s (max %d)",
15283544Sjbeck 			       name, MAXFILTERMACROS);
15293544Sjbeck 			macros[nummac] = NULL;
15303544Sjbeck 			return -1;
15313544Sjbeck 		}
15323544Sjbeck 		macros[nummac++] = macro;
15333544Sjbeck 	}
15343544Sjbeck 	macros[nummac] = NULL;
15353544Sjbeck 	return nummac;
15363544Sjbeck }
15373544Sjbeck 
15380Sstevel@tonic-gate /*
15390Sstevel@tonic-gate **  MILTER_SET_OPTION -- set an individual milter option
15400Sstevel@tonic-gate **
15410Sstevel@tonic-gate **	Parameters:
15420Sstevel@tonic-gate **		name -- the name of the option.
15430Sstevel@tonic-gate **		val -- the value of the option.
15440Sstevel@tonic-gate **		sticky -- if set, don't let other setoptions override
15450Sstevel@tonic-gate **			this value.
15460Sstevel@tonic-gate **
15470Sstevel@tonic-gate **	Returns:
15480Sstevel@tonic-gate **		none.
15490Sstevel@tonic-gate */
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate /* set if Milter sub-option is stuck */
15520Sstevel@tonic-gate static BITMAP256	StickyMilterOpt;
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate static struct milteropt
15550Sstevel@tonic-gate {
15560Sstevel@tonic-gate 	char		*mo_name;	/* long name of milter option */
15570Sstevel@tonic-gate 	unsigned char	mo_code;	/* code for option */
15580Sstevel@tonic-gate } MilterOptTab[] =
15590Sstevel@tonic-gate {
15603544Sjbeck # define MO_MACROS_CONNECT		SMFIM_CONNECT
15610Sstevel@tonic-gate 	{ "macros.connect",		MO_MACROS_CONNECT		},
15623544Sjbeck # define MO_MACROS_HELO			SMFIM_HELO
15630Sstevel@tonic-gate 	{ "macros.helo",		MO_MACROS_HELO			},
15643544Sjbeck # define MO_MACROS_ENVFROM		SMFIM_ENVFROM
15650Sstevel@tonic-gate 	{ "macros.envfrom",		MO_MACROS_ENVFROM		},
15663544Sjbeck # define MO_MACROS_ENVRCPT		SMFIM_ENVRCPT
15670Sstevel@tonic-gate 	{ "macros.envrcpt",		MO_MACROS_ENVRCPT		},
15683544Sjbeck # define MO_MACROS_DATA			SMFIM_DATA
15690Sstevel@tonic-gate 	{ "macros.data",		MO_MACROS_DATA			},
15703544Sjbeck # define MO_MACROS_EOM			SMFIM_EOM
15710Sstevel@tonic-gate 	{ "macros.eom",			MO_MACROS_EOM			},
15723544Sjbeck # define MO_MACROS_EOH			SMFIM_EOH
15733544Sjbeck 	{ "macros.eoh",			MO_MACROS_EOH			},
15743544Sjbeck 
15750Sstevel@tonic-gate # define MO_LOGLEVEL			0x07
15760Sstevel@tonic-gate 	{ "loglevel",			MO_LOGLEVEL			},
15770Sstevel@tonic-gate # if _FFR_MAXDATASIZE
15783544Sjbeck #  define MO_MAXDATASIZE		0x08
15790Sstevel@tonic-gate 	{ "maxdatasize",		MO_MAXDATASIZE			},
15800Sstevel@tonic-gate # endif /* _FFR_MAXDATASIZE */
15813544Sjbeck 	{ NULL,				(unsigned char)-1		},
15820Sstevel@tonic-gate };
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate void
15850Sstevel@tonic-gate milter_set_option(name, val, sticky)
15860Sstevel@tonic-gate 	char *name;
15870Sstevel@tonic-gate 	char *val;
15880Sstevel@tonic-gate 	bool sticky;
15890Sstevel@tonic-gate {
15903544Sjbeck 	int nummac, r;
15913544Sjbeck 	struct milteropt *mo;
15920Sstevel@tonic-gate 	char **macros = NULL;
15930Sstevel@tonic-gate 
15943544Sjbeck 	nummac = 0;
15950Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64, 5))
15960Sstevel@tonic-gate 		sm_dprintf("milter_set_option(%s = %s)", name, val);
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 	if (name == NULL)
15990Sstevel@tonic-gate 	{
16000Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option, must specify suboption");
16010Sstevel@tonic-gate 		return;
16020Sstevel@tonic-gate 	}
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
16050Sstevel@tonic-gate 	{
16060Sstevel@tonic-gate 		if (sm_strcasecmp(mo->mo_name, name) == 0)
16070Sstevel@tonic-gate 			break;
16080Sstevel@tonic-gate 	}
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate 	if (mo->mo_name == NULL)
16110Sstevel@tonic-gate 	{
16120Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16130Sstevel@tonic-gate 		return;
16140Sstevel@tonic-gate 	}
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	/*
16170Sstevel@tonic-gate 	**  See if this option is preset for us.
16180Sstevel@tonic-gate 	*/
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
16210Sstevel@tonic-gate 	{
16220Sstevel@tonic-gate 		if (tTd(37, 2) || tTd(64,5))
16230Sstevel@tonic-gate 			sm_dprintf(" (ignored)\n");
16240Sstevel@tonic-gate 		return;
16250Sstevel@tonic-gate 	}
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 	if (tTd(37, 2) || tTd(64,5))
16280Sstevel@tonic-gate 		sm_dprintf("\n");
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 	switch (mo->mo_code)
16310Sstevel@tonic-gate 	{
16320Sstevel@tonic-gate 	  case MO_LOGLEVEL:
16330Sstevel@tonic-gate 		MilterLogLevel = atoi(val);
16340Sstevel@tonic-gate 		break;
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate #if _FFR_MAXDATASIZE
16370Sstevel@tonic-gate 	  case MO_MAXDATASIZE:
16380Sstevel@tonic-gate 		MilterMaxDataSize = (size_t)atol(val);
16390Sstevel@tonic-gate 		break;
16400Sstevel@tonic-gate #endif /* _FFR_MAXDATASIZE */
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 	  case MO_MACROS_CONNECT:
16430Sstevel@tonic-gate 		if (macros == NULL)
16440Sstevel@tonic-gate 			macros = MilterConnectMacros;
16450Sstevel@tonic-gate 		/* FALLTHROUGH */
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate 	  case MO_MACROS_HELO:
16480Sstevel@tonic-gate 		if (macros == NULL)
16490Sstevel@tonic-gate 			macros = MilterHeloMacros;
16500Sstevel@tonic-gate 		/* FALLTHROUGH */
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 	  case MO_MACROS_ENVFROM:
16530Sstevel@tonic-gate 		if (macros == NULL)
16540Sstevel@tonic-gate 			macros = MilterEnvFromMacros;
16550Sstevel@tonic-gate 		/* FALLTHROUGH */
16560Sstevel@tonic-gate 
16570Sstevel@tonic-gate 	  case MO_MACROS_ENVRCPT:
16580Sstevel@tonic-gate 		if (macros == NULL)
16590Sstevel@tonic-gate 			macros = MilterEnvRcptMacros;
16600Sstevel@tonic-gate 		/* FALLTHROUGH */
16610Sstevel@tonic-gate 
16623544Sjbeck 	  case MO_MACROS_EOH:
16633544Sjbeck 		if (macros == NULL)
16643544Sjbeck 			macros = MilterEOHMacros;
16653544Sjbeck 		/* FALLTHROUGH */
16663544Sjbeck 
16670Sstevel@tonic-gate 	  case MO_MACROS_EOM:
16680Sstevel@tonic-gate 		if (macros == NULL)
16690Sstevel@tonic-gate 			macros = MilterEOMMacros;
16700Sstevel@tonic-gate 		/* FALLTHROUGH */
16710Sstevel@tonic-gate 
16720Sstevel@tonic-gate 	  case MO_MACROS_DATA:
16730Sstevel@tonic-gate 		if (macros == NULL)
16740Sstevel@tonic-gate 			macros = MilterDataMacros;
16750Sstevel@tonic-gate 
16763544Sjbeck 		r = milter_set_macros(name, macros, val, nummac);
16773544Sjbeck 		if (r >= 0)
16783544Sjbeck 			nummac = r;
16790Sstevel@tonic-gate 		break;
16800Sstevel@tonic-gate 
16810Sstevel@tonic-gate 	  default:
16820Sstevel@tonic-gate 		syserr("milter_set_option: invalid Milter option %s", name);
16830Sstevel@tonic-gate 		break;
16840Sstevel@tonic-gate 	}
16850Sstevel@tonic-gate 	if (sticky)
16860Sstevel@tonic-gate 		setbitn(mo->mo_code, StickyMilterOpt);
16870Sstevel@tonic-gate }
16883544Sjbeck 
16890Sstevel@tonic-gate /*
16900Sstevel@tonic-gate **  MILTER_REOPEN_DF -- open & truncate the data file (for replbody)
16910Sstevel@tonic-gate **
16920Sstevel@tonic-gate **	Parameters:
16930Sstevel@tonic-gate **		e -- current envelope.
16940Sstevel@tonic-gate **
16950Sstevel@tonic-gate **	Returns:
16960Sstevel@tonic-gate **		0 if succesful, -1 otherwise
16970Sstevel@tonic-gate */
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate static int
17000Sstevel@tonic-gate milter_reopen_df(e)
17010Sstevel@tonic-gate 	ENVELOPE *e;
17020Sstevel@tonic-gate {
17030Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17040Sstevel@tonic-gate 
17053544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	/*
17080Sstevel@tonic-gate 	**  In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
17090Sstevel@tonic-gate 	**  close and reopen writable (later close and reopen
17100Sstevel@tonic-gate 	**  read only again).
17110Sstevel@tonic-gate 	**
17120Sstevel@tonic-gate 	**  In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
17130Sstevel@tonic-gate 	**  buffered file I/O descriptor, still open for writing so there
17140Sstevel@tonic-gate 	**  isn't any work to do here (except checking for consistency).
17150Sstevel@tonic-gate 	*/
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY)
17180Sstevel@tonic-gate 	{
17190Sstevel@tonic-gate 		/* close read-only data file */
17200Sstevel@tonic-gate 		if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
17210Sstevel@tonic-gate 		{
17220Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
17230Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
17240Sstevel@tonic-gate 		}
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 		/* open writable */
17270Sstevel@tonic-gate 		if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17280Sstevel@tonic-gate 					   SM_IO_RDWR_B, NULL)) == NULL)
17290Sstevel@tonic-gate 		{
17300Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_reopen_df: sm_io_open %s: %s");
17310Sstevel@tonic-gate 			return -1;
17320Sstevel@tonic-gate 		}
17330Sstevel@tonic-gate 	}
17340Sstevel@tonic-gate 	else if (e->e_dfp == NULL)
17350Sstevel@tonic-gate 	{
17360Sstevel@tonic-gate 		/* shouldn't happen */
17370Sstevel@tonic-gate 		errno = ENOENT;
17380Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reopen_df: NULL e_dfp (%s: %s)");
17390Sstevel@tonic-gate 		return -1;
17400Sstevel@tonic-gate 	}
17410Sstevel@tonic-gate 	return 0;
17420Sstevel@tonic-gate }
17433544Sjbeck 
17440Sstevel@tonic-gate /*
17450Sstevel@tonic-gate **  MILTER_RESET_DF -- re-open read-only the data file (for replbody)
17460Sstevel@tonic-gate **
17470Sstevel@tonic-gate **	Parameters:
17480Sstevel@tonic-gate **		e -- current envelope.
17490Sstevel@tonic-gate **
17500Sstevel@tonic-gate **	Returns:
17510Sstevel@tonic-gate **		0 if succesful, -1 otherwise
17520Sstevel@tonic-gate */
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate static int
17550Sstevel@tonic-gate milter_reset_df(e)
17560Sstevel@tonic-gate 	ENVELOPE *e;
17570Sstevel@tonic-gate {
17580Sstevel@tonic-gate 	int afd;
17590Sstevel@tonic-gate 	char dfname[MAXPATHLEN];
17600Sstevel@tonic-gate 
17613544Sjbeck 	(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate 	if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
17640Sstevel@tonic-gate 	    sm_io_error(e->e_dfp))
17650Sstevel@tonic-gate 	{
17660Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
17670Sstevel@tonic-gate 		return -1;
17680Sstevel@tonic-gate 	}
17690Sstevel@tonic-gate 	else if (SuperSafe != SAFE_REALLY)
17700Sstevel@tonic-gate 	{
17710Sstevel@tonic-gate 		/* skip next few clauses */
17720Sstevel@tonic-gate 		/* EMPTY */
17730Sstevel@tonic-gate 	}
17740Sstevel@tonic-gate 	else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
17750Sstevel@tonic-gate 		 && fsync(afd) < 0)
17760Sstevel@tonic-gate 	{
17770Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
17780Sstevel@tonic-gate 		return -1;
17790Sstevel@tonic-gate 	}
17800Sstevel@tonic-gate 	else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
17810Sstevel@tonic-gate 	{
17820Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
17830Sstevel@tonic-gate 		return -1;
17840Sstevel@tonic-gate 	}
17850Sstevel@tonic-gate 	else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
17860Sstevel@tonic-gate 					SM_IO_RDONLY_B, NULL)) == NULL)
17870Sstevel@tonic-gate 	{
17880Sstevel@tonic-gate 		MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
17890Sstevel@tonic-gate 		return -1;
17900Sstevel@tonic-gate 	}
17910Sstevel@tonic-gate 	else
17920Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
17930Sstevel@tonic-gate 	return 0;
17940Sstevel@tonic-gate }
17953544Sjbeck 
17960Sstevel@tonic-gate /*
17970Sstevel@tonic-gate **  MILTER_QUIT_FILTER -- close down a single filter
17980Sstevel@tonic-gate **
17990Sstevel@tonic-gate **	Parameters:
18000Sstevel@tonic-gate **		m -- milter structure of filter to close down.
18010Sstevel@tonic-gate **		e -- current envelope.
18020Sstevel@tonic-gate **
18030Sstevel@tonic-gate **	Returns:
18040Sstevel@tonic-gate **		none
18050Sstevel@tonic-gate */
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate static void
18080Sstevel@tonic-gate milter_quit_filter(m, e)
18090Sstevel@tonic-gate 	struct milter *m;
18100Sstevel@tonic-gate 	ENVELOPE *e;
18110Sstevel@tonic-gate {
18120Sstevel@tonic-gate 	if (tTd(64, 10))
18130Sstevel@tonic-gate 		sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
18140Sstevel@tonic-gate 	if (MilterLogLevel > 18)
18150Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
18160Sstevel@tonic-gate 			  m->mf_name);
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 	/* Never replace error state */
18190Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
18200Sstevel@tonic-gate 		return;
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18230Sstevel@tonic-gate 	    m->mf_state == SMFS_CLOSED ||
18240Sstevel@tonic-gate 	    m->mf_state == SMFS_READY)
18250Sstevel@tonic-gate 	{
18260Sstevel@tonic-gate 		m->mf_sock = -1;
18270Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18280Sstevel@tonic-gate 		return;
18290Sstevel@tonic-gate 	}
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_QUIT, (char *) NULL, 0,
18323544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "quit_filter");
18330Sstevel@tonic-gate 	if (m->mf_sock >= 0)
18340Sstevel@tonic-gate 	{
18350Sstevel@tonic-gate 		(void) close(m->mf_sock);
18360Sstevel@tonic-gate 		m->mf_sock = -1;
18370Sstevel@tonic-gate 	}
18380Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18390Sstevel@tonic-gate 		m->mf_state = SMFS_CLOSED;
18400Sstevel@tonic-gate }
18413544Sjbeck 
18420Sstevel@tonic-gate /*
18430Sstevel@tonic-gate **  MILTER_ABORT_FILTER -- tell filter to abort current message
18440Sstevel@tonic-gate **
18450Sstevel@tonic-gate **	Parameters:
18460Sstevel@tonic-gate **		m -- milter structure of filter to abort.
18470Sstevel@tonic-gate **		e -- current envelope.
18480Sstevel@tonic-gate **
18490Sstevel@tonic-gate **	Returns:
18500Sstevel@tonic-gate **		none
18510Sstevel@tonic-gate */
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate static void
18540Sstevel@tonic-gate milter_abort_filter(m, e)
18550Sstevel@tonic-gate 	struct milter *m;
18560Sstevel@tonic-gate 	ENVELOPE *e;
18570Sstevel@tonic-gate {
18580Sstevel@tonic-gate 	if (tTd(64, 10))
18590Sstevel@tonic-gate 		sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
18600Sstevel@tonic-gate 	if (MilterLogLevel > 10)
18610Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
18620Sstevel@tonic-gate 			  m->mf_name);
18630Sstevel@tonic-gate 
18640Sstevel@tonic-gate 	if (m->mf_sock < 0 ||
18650Sstevel@tonic-gate 	    m->mf_state != SMFS_INMSG)
18660Sstevel@tonic-gate 		return;
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
18693544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
18700Sstevel@tonic-gate 	if (m->mf_state != SMFS_ERROR)
18710Sstevel@tonic-gate 		m->mf_state = SMFS_DONE;
18720Sstevel@tonic-gate }
18733544Sjbeck 
18740Sstevel@tonic-gate /*
18750Sstevel@tonic-gate **  MILTER_SEND_MACROS -- provide macros to the filters
18760Sstevel@tonic-gate **
18770Sstevel@tonic-gate **	Parameters:
18780Sstevel@tonic-gate **		m -- milter to send macros to.
18790Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
18800Sstevel@tonic-gate **		cmd -- which command the macros are associated with.
18810Sstevel@tonic-gate **		e -- current envelope (for macro access).
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_send_macros(m, macros, cmd, e)
18890Sstevel@tonic-gate 	struct milter *m;
18900Sstevel@tonic-gate 	char **macros;
18913544Sjbeck 	int cmd;
18920Sstevel@tonic-gate 	ENVELOPE *e;
18930Sstevel@tonic-gate {
18940Sstevel@tonic-gate 	int i;
18950Sstevel@tonic-gate 	int mid;
18963544Sjbeck 	char command = (char) cmd;
18970Sstevel@tonic-gate 	char *v;
18980Sstevel@tonic-gate 	char *buf, *bp;
18990Sstevel@tonic-gate 	char exp[MAXLINE];
19000Sstevel@tonic-gate 	ssize_t s;
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 	/* sanity check */
19030Sstevel@tonic-gate 	if (macros == NULL || macros[0] == NULL)
19040Sstevel@tonic-gate 		return;
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 	/* put together data */
19070Sstevel@tonic-gate 	s = 1;			/* for the command character */
19080Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19090Sstevel@tonic-gate 	{
19100Sstevel@tonic-gate 		mid = macid(macros[i]);
19110Sstevel@tonic-gate 		if (mid == 0)
19120Sstevel@tonic-gate 			continue;
19130Sstevel@tonic-gate 		v = macvalue(mid, e);
19140Sstevel@tonic-gate 		if (v == NULL)
19150Sstevel@tonic-gate 			continue;
19160Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19170Sstevel@tonic-gate 		s += strlen(macros[i]) + 1 + strlen(exp) + 1;
19180Sstevel@tonic-gate 	}
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 	if (s < 0)
19210Sstevel@tonic-gate 		return;
19220Sstevel@tonic-gate 
19230Sstevel@tonic-gate 	buf = (char *) xalloc(s);
19240Sstevel@tonic-gate 	bp = buf;
19253544Sjbeck 	*bp++ = command;
19260Sstevel@tonic-gate 	for (i = 0; macros[i] != NULL; i++)
19270Sstevel@tonic-gate 	{
19280Sstevel@tonic-gate 		mid = macid(macros[i]);
19290Sstevel@tonic-gate 		if (mid == 0)
19300Sstevel@tonic-gate 			continue;
19310Sstevel@tonic-gate 		v = macvalue(mid, e);
19320Sstevel@tonic-gate 		if (v == NULL)
19330Sstevel@tonic-gate 			continue;
19340Sstevel@tonic-gate 		expand(v, exp, sizeof(exp), e);
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 		if (tTd(64, 10))
19370Sstevel@tonic-gate 			sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
19383544Sjbeck 				m->mf_name, command, macros[i], exp);
19390Sstevel@tonic-gate 
19400Sstevel@tonic-gate 		(void) sm_strlcpy(bp, macros[i], s - (bp - buf));
19410Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19420Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exp, s - (bp - buf));
19430Sstevel@tonic-gate 		bp += strlen(bp) + 1;
19440Sstevel@tonic-gate 	}
19450Sstevel@tonic-gate 	(void) milter_write(m, SMFIC_MACRO, buf, s,
19463544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "send_macros");
19470Sstevel@tonic-gate 	sm_free(buf);
19480Sstevel@tonic-gate }
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate /*
19510Sstevel@tonic-gate **  MILTER_SEND_COMMAND -- send a command and return the response for a filter
19520Sstevel@tonic-gate **
19530Sstevel@tonic-gate **	Parameters:
19540Sstevel@tonic-gate **		m -- current milter filter
19553544Sjbeck **		cmd -- command to send.
19560Sstevel@tonic-gate **		data -- optional command data.
19570Sstevel@tonic-gate **		sz -- length of buf.
19580Sstevel@tonic-gate **		e -- current envelope (for e->e_id).
19590Sstevel@tonic-gate **		state -- return state word.
19600Sstevel@tonic-gate **
19610Sstevel@tonic-gate **	Returns:
19620Sstevel@tonic-gate **		response string (may be NULL)
19630Sstevel@tonic-gate */
19640Sstevel@tonic-gate 
19650Sstevel@tonic-gate static char *
19663544Sjbeck milter_send_command(m, cmd, data, sz, e, state, where)
19670Sstevel@tonic-gate 	struct milter *m;
19683544Sjbeck 	int cmd;
19690Sstevel@tonic-gate 	void *data;
19700Sstevel@tonic-gate 	ssize_t sz;
19710Sstevel@tonic-gate 	ENVELOPE *e;
19720Sstevel@tonic-gate 	char *state;
19733544Sjbeck 	const char *where;
19740Sstevel@tonic-gate {
19750Sstevel@tonic-gate 	char rcmd;
19760Sstevel@tonic-gate 	ssize_t rlen;
19770Sstevel@tonic-gate 	unsigned long skipflag;
19780Sstevel@tonic-gate 	unsigned long norespflag = 0;
19793544Sjbeck 	char command = (char) cmd;
19800Sstevel@tonic-gate 	char *action;
19810Sstevel@tonic-gate 	char *defresponse;
19820Sstevel@tonic-gate 	char *response;
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate 	if (tTd(64, 10))
19850Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): cmd %c len %ld\n",
19860Sstevel@tonic-gate 			m->mf_name, (char) command, (long) sz);
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 	/* find skip flag and default failure */
19890Sstevel@tonic-gate 	switch (command)
19900Sstevel@tonic-gate 	{
19910Sstevel@tonic-gate 	  case SMFIC_CONNECT:
19920Sstevel@tonic-gate 		skipflag = SMFIP_NOCONNECT;
19933544Sjbeck 		norespflag = SMFIP_NR_CONN;
19940Sstevel@tonic-gate 		action = "connect";
19950Sstevel@tonic-gate 		defresponse = "554 Command rejected";
19960Sstevel@tonic-gate 		break;
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate 	  case SMFIC_HELO:
19990Sstevel@tonic-gate 		skipflag = SMFIP_NOHELO;
20003544Sjbeck 		norespflag = SMFIP_NR_HELO;
20010Sstevel@tonic-gate 		action = "helo";
20020Sstevel@tonic-gate 		defresponse = "550 Command rejected";
20030Sstevel@tonic-gate 		break;
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	  case SMFIC_MAIL:
20060Sstevel@tonic-gate 		skipflag = SMFIP_NOMAIL;
20073544Sjbeck 		norespflag = SMFIP_NR_MAIL;
20080Sstevel@tonic-gate 		action = "mail";
20090Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20100Sstevel@tonic-gate 		break;
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 	  case SMFIC_RCPT:
20130Sstevel@tonic-gate 		skipflag = SMFIP_NORCPT;
20143544Sjbeck 		norespflag = SMFIP_NR_RCPT;
20150Sstevel@tonic-gate 		action = "rcpt";
20160Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20170Sstevel@tonic-gate 		break;
20180Sstevel@tonic-gate 
20190Sstevel@tonic-gate 	  case SMFIC_HEADER:
20200Sstevel@tonic-gate 		skipflag = SMFIP_NOHDRS;
20213544Sjbeck 		norespflag = SMFIP_NR_HDR;
20220Sstevel@tonic-gate 		action = "header";
20230Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20240Sstevel@tonic-gate 		break;
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	  case SMFIC_BODY:
20270Sstevel@tonic-gate 		skipflag = SMFIP_NOBODY;
20283544Sjbeck 		norespflag = SMFIP_NR_BODY;
20290Sstevel@tonic-gate 		action = "body";
20300Sstevel@tonic-gate 		defresponse = "554 5.7.1 Command rejected";
20310Sstevel@tonic-gate 		break;
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate 	  case SMFIC_EOH:
20340Sstevel@tonic-gate 		skipflag = SMFIP_NOEOH;
20353544Sjbeck 		norespflag = SMFIP_NR_EOH;
20360Sstevel@tonic-gate 		action = "eoh";
20370Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20380Sstevel@tonic-gate 		break;
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate 	  case SMFIC_UNKNOWN:
2041616Sjbeck 		skipflag = SMFIP_NOUNKNOWN;
20423544Sjbeck 		norespflag = SMFIP_NR_UNKN;
20430Sstevel@tonic-gate 		action = "unknown";
20440Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20450Sstevel@tonic-gate 		break;
20463544Sjbeck 
2047616Sjbeck 	  case SMFIC_DATA:
2048616Sjbeck 		skipflag = SMFIP_NODATA;
20493544Sjbeck 		norespflag = SMFIP_NR_DATA;
2050616Sjbeck 		action = "data";
2051616Sjbeck 		defresponse = "550 5.7.1 Command rejected";
2052616Sjbeck 		break;
2053616Sjbeck 
20540Sstevel@tonic-gate 	  case SMFIC_BODYEOB:
20550Sstevel@tonic-gate 	  case SMFIC_OPTNEG:
20560Sstevel@tonic-gate 	  case SMFIC_MACRO:
20570Sstevel@tonic-gate 	  case SMFIC_ABORT:
20580Sstevel@tonic-gate 	  case SMFIC_QUIT:
20590Sstevel@tonic-gate 		/* NOTE: not handled by milter_send_command() */
20600Sstevel@tonic-gate 		/* FALLTHROUGH */
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate 	  default:
20630Sstevel@tonic-gate 		skipflag = 0;
20640Sstevel@tonic-gate 		action = "default";
20650Sstevel@tonic-gate 		defresponse = "550 5.7.1 Command rejected";
20660Sstevel@tonic-gate 		break;
20670Sstevel@tonic-gate 	}
20680Sstevel@tonic-gate 
20693544Sjbeck 	if (tTd(64, 10))
20703544Sjbeck 		sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
20713544Sjbeck 			m->mf_name, skipflag, m->mf_pflags);
20723544Sjbeck 
20730Sstevel@tonic-gate 	/* check if filter wants this command */
20743544Sjbeck 	if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
20750Sstevel@tonic-gate 		return NULL;
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	/* send the command to the filter */
20780Sstevel@tonic-gate 	(void) milter_write(m, command, data, sz,
20793544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, where);
20800Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
20810Sstevel@tonic-gate 	{
20820Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
20830Sstevel@tonic-gate 		return NULL;
20840Sstevel@tonic-gate 	}
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 	/* check if filter sends response to this command */
20870Sstevel@tonic-gate 	if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
20880Sstevel@tonic-gate 		return NULL;
20890Sstevel@tonic-gate 
20900Sstevel@tonic-gate 	/* get the response from the filter */
20910Sstevel@tonic-gate 	response = milter_read(m, &rcmd, &rlen,
20923544Sjbeck 			       m->mf_timeout[SMFTO_READ], e, where);
20930Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
20940Sstevel@tonic-gate 	{
20950Sstevel@tonic-gate 		MILTER_CHECK_ERROR(false, return NULL);
20960Sstevel@tonic-gate 		return NULL;
20970Sstevel@tonic-gate 	}
20980Sstevel@tonic-gate 
20990Sstevel@tonic-gate 	if (tTd(64, 10))
21000Sstevel@tonic-gate 		sm_dprintf("milter_send_command(%s): returned %c\n",
21010Sstevel@tonic-gate 			   m->mf_name, (char) rcmd);
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 	switch (rcmd)
21040Sstevel@tonic-gate 	{
21050Sstevel@tonic-gate 	  case SMFIR_REPLYCODE:
21060Sstevel@tonic-gate 		MILTER_CHECK_REPLYCODE(defresponse);
21070Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21083544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21093544Sjbeck 				  "milter=%s, action=%s, reject=%s",
21100Sstevel@tonic-gate 				  m->mf_name, action, response);
21110Sstevel@tonic-gate 		*state = rcmd;
21120Sstevel@tonic-gate 		break;
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 	  case SMFIR_REJECT:
21150Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21163544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21173544Sjbeck 				  "milter=%s, action=%s, reject",
21180Sstevel@tonic-gate 				  m->mf_name, action);
21190Sstevel@tonic-gate 		*state = rcmd;
21200Sstevel@tonic-gate 		break;
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 	  case SMFIR_DISCARD:
21230Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21243544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21253544Sjbeck 				  "milter=%s, action=%s, discard",
21260Sstevel@tonic-gate 				  m->mf_name, action);
21270Sstevel@tonic-gate 		*state = rcmd;
21280Sstevel@tonic-gate 		break;
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 	  case SMFIR_TEMPFAIL:
21310Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21323544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21333544Sjbeck 				  "milter=%s, action=%s, tempfail",
21340Sstevel@tonic-gate 				  m->mf_name, action);
21350Sstevel@tonic-gate 		*state = rcmd;
21360Sstevel@tonic-gate 		break;
21370Sstevel@tonic-gate 
21380Sstevel@tonic-gate 	  case SMFIR_ACCEPT:
21390Sstevel@tonic-gate 		/* this filter is done with message/connection */
21400Sstevel@tonic-gate 		if (command == SMFIC_HELO ||
21410Sstevel@tonic-gate 		    command == SMFIC_CONNECT)
21420Sstevel@tonic-gate 			m->mf_state = SMFS_CLOSABLE;
21430Sstevel@tonic-gate 		else
21440Sstevel@tonic-gate 			m->mf_state = SMFS_DONE;
21450Sstevel@tonic-gate 		if (MilterLogLevel > 10)
21463544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21473544Sjbeck 				  "milter=%s, action=%s, accepted",
21480Sstevel@tonic-gate 				  m->mf_name, action);
21490Sstevel@tonic-gate 		break;
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate 	  case SMFIR_CONTINUE:
21520Sstevel@tonic-gate 		/* if MAIL command is ok, filter is in message state */
21530Sstevel@tonic-gate 		if (command == SMFIC_MAIL)
21540Sstevel@tonic-gate 			m->mf_state = SMFS_INMSG;
21550Sstevel@tonic-gate 		if (MilterLogLevel > 12)
21563544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21573544Sjbeck 				  "milter=%s, action=%s, continue",
21580Sstevel@tonic-gate 				  m->mf_name, action);
21590Sstevel@tonic-gate 		break;
21600Sstevel@tonic-gate 
21613544Sjbeck 	  case SMFIR_SKIP:
21623544Sjbeck 		if (MilterLogLevel > 12)
21633544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
21643544Sjbeck 				  "milter=%s, action=%s, skip",
21653544Sjbeck 				  m->mf_name, action);
21663544Sjbeck 		m->mf_state = SMFS_SKIP;
21673544Sjbeck 		break;
21683544Sjbeck 
21690Sstevel@tonic-gate 	  default:
21700Sstevel@tonic-gate 		/* Invalid response to command */
21710Sstevel@tonic-gate 		if (MilterLogLevel > 0)
21720Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
21730Sstevel@tonic-gate 				  "milter_send_command(%s): action=%s returned bogus response %c",
21740Sstevel@tonic-gate 				  m->mf_name, action, rcmd);
21750Sstevel@tonic-gate 		milter_error(m, e);
21760Sstevel@tonic-gate 		break;
21770Sstevel@tonic-gate 	}
21780Sstevel@tonic-gate 
21793544Sjbeck 	if (*state != SMFIR_REPLYCODE && response != NULL)
21800Sstevel@tonic-gate 	{
21810Sstevel@tonic-gate 		sm_free(response); /* XXX */
21820Sstevel@tonic-gate 		response = NULL;
21830Sstevel@tonic-gate 	}
21840Sstevel@tonic-gate 	return response;
21850Sstevel@tonic-gate }
21860Sstevel@tonic-gate 
21870Sstevel@tonic-gate /*
21880Sstevel@tonic-gate **  MILTER_COMMAND -- send a command and return the response for each filter
21890Sstevel@tonic-gate **
21900Sstevel@tonic-gate **	Parameters:
21913544Sjbeck **		cmd -- command to send.
21920Sstevel@tonic-gate **		data -- optional command data.
21930Sstevel@tonic-gate **		sz -- length of buf.
21940Sstevel@tonic-gate **		macros -- macros to send for filter smfi_getsymval().
21950Sstevel@tonic-gate **		e -- current envelope (for macro access).
21960Sstevel@tonic-gate **		state -- return state word.
21973544Sjbeck **		where -- description of calling function (logging).
21983544Sjbeck **		cmd_error -- did the SMTP command cause an error?
21990Sstevel@tonic-gate **
22000Sstevel@tonic-gate **	Returns:
22010Sstevel@tonic-gate **		response string (may be NULL)
22020Sstevel@tonic-gate */
22030Sstevel@tonic-gate 
22040Sstevel@tonic-gate static char *
22053544Sjbeck milter_command(cmd, data, sz, macros, e, state, where, cmd_error)
22063544Sjbeck 	int cmd;
22070Sstevel@tonic-gate 	void *data;
22080Sstevel@tonic-gate 	ssize_t sz;
22090Sstevel@tonic-gate 	char **macros;
22100Sstevel@tonic-gate 	ENVELOPE *e;
22110Sstevel@tonic-gate 	char *state;
22123544Sjbeck 	const char *where;
22133544Sjbeck 	bool cmd_error;
22140Sstevel@tonic-gate {
22150Sstevel@tonic-gate 	int i;
22163544Sjbeck 	char command = (char) cmd;
22170Sstevel@tonic-gate 	char *response = NULL;
22180Sstevel@tonic-gate 	time_t tn = 0;
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	if (tTd(64, 10))
22210Sstevel@tonic-gate 		sm_dprintf("milter_command: cmd %c len %ld\n",
22223544Sjbeck 			command, (long) sz);
22230Sstevel@tonic-gate 
22240Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
22250Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
22260Sstevel@tonic-gate 	{
22270Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 		/* previous problem? */
22300Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
22310Sstevel@tonic-gate 		{
22320Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
22330Sstevel@tonic-gate 			break;
22340Sstevel@tonic-gate 		}
22350Sstevel@tonic-gate 
22360Sstevel@tonic-gate 		/* sanity check */
22370Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
22380Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
22390Sstevel@tonic-gate 			continue;
22400Sstevel@tonic-gate 
22410Sstevel@tonic-gate 		/* send macros (regardless of whether we send command) */
22420Sstevel@tonic-gate 		if (macros != NULL && macros[0] != NULL)
22430Sstevel@tonic-gate 		{
22440Sstevel@tonic-gate 			milter_send_macros(m, macros, command, e);
22450Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
22460Sstevel@tonic-gate 			{
22470Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, continue);
22480Sstevel@tonic-gate 				break;
22490Sstevel@tonic-gate 			}
22500Sstevel@tonic-gate 		}
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22530Sstevel@tonic-gate 			tn = curtime();
22540Sstevel@tonic-gate 
22553544Sjbeck 		/*
22563544Sjbeck 		**  send the command if
22573544Sjbeck 		**	there is no error
22583544Sjbeck 		**	or it's RCPT and the client asked for it:
22593544Sjbeck 		**	!cmd_error ||
22603544Sjbeck 		**	where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
22613544Sjbeck 		**  negate that condition and use continue
22623544Sjbeck 		*/
22633544Sjbeck 
22643544Sjbeck 		if (cmd_error &&
22653544Sjbeck 		    (strcmp(where, "rcpt") != 0 ||
22663544Sjbeck 		     (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
22673544Sjbeck 			continue;
22683544Sjbeck 
22693544Sjbeck 		response = milter_send_command(m, command, data, sz, e, state,
22703544Sjbeck 						where);
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 		if (MilterLogLevel > 21)
22730Sstevel@tonic-gate 		{
22740Sstevel@tonic-gate 			/* log the time it took for the command per filter */
22750Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
22760Sstevel@tonic-gate 				  "Milter (%s): time command (%c), %d",
22770Sstevel@tonic-gate 				  m->mf_name, command, (int) (tn - curtime()));
22780Sstevel@tonic-gate 		}
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE)
22810Sstevel@tonic-gate 			break;
22820Sstevel@tonic-gate 	}
22830Sstevel@tonic-gate 	return response;
22840Sstevel@tonic-gate }
22853544Sjbeck 
22863544Sjbeck static int milter_getsymlist __P((struct milter *, char *, int, int));
22873544Sjbeck 
22883544Sjbeck static int
22893544Sjbeck milter_getsymlist(m, buf, rlen, offset)
22903544Sjbeck 	struct milter *m;
22913544Sjbeck 	char *buf;
22923544Sjbeck 	int rlen;
22933544Sjbeck 	int offset;
22943544Sjbeck {
22953544Sjbeck 	int i, r, nummac;
22963544Sjbeck 	mi_int32 v;
22973544Sjbeck 
22983544Sjbeck 	SM_ASSERT(m != NULL);
22993544Sjbeck 	SM_ASSERT(buf != NULL);
23003544Sjbeck 
23013544Sjbeck 	while (offset + MILTER_LEN_BYTES < rlen)
23023544Sjbeck 	{
23033544Sjbeck 		size_t len;
23043544Sjbeck 		char **macros;
23053544Sjbeck 
23063544Sjbeck 		nummac = 0;
23073544Sjbeck 		(void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
23083544Sjbeck 		i = ntohl(v);
23093544Sjbeck 		if (i < SMFIM_FIRST || i > SMFIM_LAST)
23103544Sjbeck 			return -1;
23113544Sjbeck 		offset += MILTER_LEN_BYTES;
23123544Sjbeck 		macros = NULL;
23133544Sjbeck 
23143544Sjbeck 		switch (i)
23153544Sjbeck 		{
23163544Sjbeck 		  case MO_MACROS_CONNECT:
23173544Sjbeck 			if (macros == NULL)
23183544Sjbeck 				macros = MilterConnectMacros;
23193544Sjbeck 			/* FALLTHROUGH */
23203544Sjbeck 
23213544Sjbeck 		  case MO_MACROS_HELO:
23223544Sjbeck 			if (macros == NULL)
23233544Sjbeck 				macros = MilterHeloMacros;
23243544Sjbeck 			/* FALLTHROUGH */
23253544Sjbeck 
23263544Sjbeck 		  case MO_MACROS_ENVFROM:
23273544Sjbeck 			if (macros == NULL)
23283544Sjbeck 				macros = MilterEnvFromMacros;
23293544Sjbeck 			/* FALLTHROUGH */
23303544Sjbeck 
23313544Sjbeck 		  case MO_MACROS_ENVRCPT:
23323544Sjbeck 			if (macros == NULL)
23333544Sjbeck 				macros = MilterEnvRcptMacros;
23343544Sjbeck 			/* FALLTHROUGH */
23353544Sjbeck 
23363544Sjbeck 		  case MO_MACROS_EOM:
23373544Sjbeck 			if (macros == NULL)
23383544Sjbeck 				macros = MilterEOMMacros;
23393544Sjbeck 			/* FALLTHROUGH */
23403544Sjbeck 
23413544Sjbeck 		  case MO_MACROS_EOH:
23423544Sjbeck 			if (macros == NULL)
23433544Sjbeck 				macros = MilterEOHMacros;
23443544Sjbeck 			/* FALLTHROUGH */
23453544Sjbeck 
23463544Sjbeck 		  case MO_MACROS_DATA:
23473544Sjbeck 			if (macros == NULL)
23483544Sjbeck 				macros = MilterDataMacros;
23493544Sjbeck 
23503544Sjbeck 			len = strlen(buf + offset);
23513544Sjbeck 			if (len > 0)
23523544Sjbeck 			{
23533544Sjbeck 				r = milter_set_macros(m->mf_name, macros,
23543544Sjbeck 						buf + offset, nummac);
23553544Sjbeck 				if (r >= 0)
23563544Sjbeck 					nummac = r;
23573544Sjbeck 			}
23583544Sjbeck 			break;
23593544Sjbeck 
23603544Sjbeck 		  default:
23613544Sjbeck 			return -1;
23623544Sjbeck 		}
23633544Sjbeck 		if (len == 0)
23643544Sjbeck 			return -1;
23653544Sjbeck 		offset += len + 1;
23663544Sjbeck 	}
23673544Sjbeck 
23683544Sjbeck 	return 0;
23693544Sjbeck }
23703544Sjbeck 
23710Sstevel@tonic-gate /*
23720Sstevel@tonic-gate **  MILTER_NEGOTIATE -- get version and flags from filter
23730Sstevel@tonic-gate **
23740Sstevel@tonic-gate **	Parameters:
23750Sstevel@tonic-gate **		m -- milter filter structure.
23760Sstevel@tonic-gate **		e -- current envelope.
2377*5402Sjbeck **		milters -- milters structure.
23780Sstevel@tonic-gate **
23790Sstevel@tonic-gate **	Returns:
23800Sstevel@tonic-gate **		0 on success, -1 otherwise
23810Sstevel@tonic-gate */
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate static int
2384*5402Sjbeck milter_negotiate(m, e, milters)
23850Sstevel@tonic-gate 	struct milter *m;
23860Sstevel@tonic-gate 	ENVELOPE *e;
2387*5402Sjbeck 	milters_T *milters;
23880Sstevel@tonic-gate {
23890Sstevel@tonic-gate 	char rcmd;
23903544Sjbeck 	mi_int32 fvers, fflags, pflags;
23913544Sjbeck 	mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
2392616Sjbeck 	ssize_t rlen;
23930Sstevel@tonic-gate 	char *response;
23940Sstevel@tonic-gate 	char data[MILTER_OPTLEN];
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 	/* sanity check */
23970Sstevel@tonic-gate 	if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
23980Sstevel@tonic-gate 	{
23990Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24000Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24010Sstevel@tonic-gate 				  "Milter (%s): negotiate, impossible state",
24020Sstevel@tonic-gate 				  m->mf_name);
24030Sstevel@tonic-gate 		milter_error(m, e);
24040Sstevel@tonic-gate 		return -1;
24050Sstevel@tonic-gate 	}
24060Sstevel@tonic-gate 
24073966Sjbeck #if _FFR_MILTER_CHECK
24083544Sjbeck 	mta_prot_vers = m->mf_mta_prot_version;
24093544Sjbeck 	mta_prot_flags = m->mf_mta_prot_flags;
24103544Sjbeck 	mta_actions = m->mf_mta_actions;
24113966Sjbeck #else /* _FFR_MILTER_CHECK */
24123544Sjbeck 	mta_prot_vers = SMFI_PROT_VERSION;
24133544Sjbeck 	mta_prot_flags = SMFI_CURR_PROT;
24143544Sjbeck 	mta_actions = SMFI_CURR_ACTS;
24153966Sjbeck #endif /* _FFR_MILTER_CHECK */
24163544Sjbeck 
24173544Sjbeck 	fvers = htonl(mta_prot_vers);
24183544Sjbeck 	pflags = htonl(mta_prot_flags);
24193544Sjbeck 	fflags = htonl(mta_actions);
24200Sstevel@tonic-gate 	(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
24210Sstevel@tonic-gate 	(void) memcpy(data + MILTER_LEN_BYTES,
24220Sstevel@tonic-gate 		      (char *) &fflags, MILTER_LEN_BYTES);
24230Sstevel@tonic-gate 	(void) memcpy(data + (MILTER_LEN_BYTES * 2),
24240Sstevel@tonic-gate 		      (char *) &pflags, MILTER_LEN_BYTES);
24253544Sjbeck 	(void) milter_write(m, SMFIC_OPTNEG, data, sizeof(data),
24263544Sjbeck 			    m->mf_timeout[SMFTO_WRITE], e, "negotiate");
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24290Sstevel@tonic-gate 		return -1;
24300Sstevel@tonic-gate 
24313544Sjbeck 	if (tTd(64, 5))
24323544Sjbeck 		sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
24333544Sjbeck 			m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
24343544Sjbeck 
24353544Sjbeck 	response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
24363544Sjbeck 				"negotiate");
24370Sstevel@tonic-gate 	if (m->mf_state == SMFS_ERROR)
24380Sstevel@tonic-gate 		return -1;
24390Sstevel@tonic-gate 
24400Sstevel@tonic-gate 	if (rcmd != SMFIC_OPTNEG)
24410Sstevel@tonic-gate 	{
24420Sstevel@tonic-gate 		if (tTd(64, 5))
24430Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
24440Sstevel@tonic-gate 				m->mf_name, rcmd, SMFIC_OPTNEG);
24450Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24460Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24470Sstevel@tonic-gate 				  "Milter (%s): negotiate: returned %c instead of %c",
24480Sstevel@tonic-gate 				  m->mf_name, rcmd, SMFIC_OPTNEG);
24490Sstevel@tonic-gate 		if (response != NULL)
24500Sstevel@tonic-gate 			sm_free(response); /* XXX */
24510Sstevel@tonic-gate 		milter_error(m, e);
24520Sstevel@tonic-gate 		return -1;
24530Sstevel@tonic-gate 	}
24540Sstevel@tonic-gate 
24550Sstevel@tonic-gate 	/* Make sure we have enough bytes for the version */
24560Sstevel@tonic-gate 	if (response == NULL || rlen < MILTER_LEN_BYTES)
24570Sstevel@tonic-gate 	{
24580Sstevel@tonic-gate 		if (tTd(64, 5))
24590Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return valid info\n",
24600Sstevel@tonic-gate 				m->mf_name);
24610Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24620Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24630Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return valid info",
24640Sstevel@tonic-gate 				  m->mf_name);
24650Sstevel@tonic-gate 		if (response != NULL)
24660Sstevel@tonic-gate 			sm_free(response); /* XXX */
24670Sstevel@tonic-gate 		milter_error(m, e);
24680Sstevel@tonic-gate 		return -1;
24690Sstevel@tonic-gate 	}
24700Sstevel@tonic-gate 
24710Sstevel@tonic-gate 	/* extract information */
24720Sstevel@tonic-gate 	(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
24730Sstevel@tonic-gate 
24740Sstevel@tonic-gate 	/* Now make sure we have enough for the feature bitmap */
24753544Sjbeck 	if (rlen < MILTER_OPTLEN)
24760Sstevel@tonic-gate 	{
24770Sstevel@tonic-gate 		if (tTd(64, 5))
24780Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): did not return enough info\n",
24790Sstevel@tonic-gate 				m->mf_name);
24800Sstevel@tonic-gate 		if (MilterLogLevel > 0)
24810Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
24820Sstevel@tonic-gate 				  "Milter (%s): negotiate: did not return enough info",
24830Sstevel@tonic-gate 				  m->mf_name);
24840Sstevel@tonic-gate 		if (response != NULL)
24850Sstevel@tonic-gate 			sm_free(response); /* XXX */
24860Sstevel@tonic-gate 		milter_error(m, e);
24870Sstevel@tonic-gate 		return -1;
24880Sstevel@tonic-gate 	}
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 	(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
24910Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
24920Sstevel@tonic-gate 	(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
24930Sstevel@tonic-gate 		      MILTER_LEN_BYTES);
24940Sstevel@tonic-gate 
24950Sstevel@tonic-gate 	m->mf_fvers = ntohl(fvers);
24960Sstevel@tonic-gate 	m->mf_fflags = ntohl(fflags);
24970Sstevel@tonic-gate 	m->mf_pflags = ntohl(pflags);
24980Sstevel@tonic-gate 
24990Sstevel@tonic-gate 	/* check for version compatibility */
25000Sstevel@tonic-gate 	if (m->mf_fvers == 1 ||
25010Sstevel@tonic-gate 	    m->mf_fvers > SMFI_VERSION)
25020Sstevel@tonic-gate 	{
25030Sstevel@tonic-gate 		if (tTd(64, 5))
25040Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
25050Sstevel@tonic-gate 				m->mf_name, m->mf_fvers, SMFI_VERSION);
25060Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25070Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25080Sstevel@tonic-gate 				  "Milter (%s): negotiate: version %d != MTA milter version %d",
25090Sstevel@tonic-gate 				  m->mf_name, m->mf_fvers, SMFI_VERSION);
25100Sstevel@tonic-gate 		milter_error(m, e);
25113544Sjbeck 		goto error;
25120Sstevel@tonic-gate 	}
25130Sstevel@tonic-gate 
25140Sstevel@tonic-gate 	/* check for filter feature mismatch */
25153544Sjbeck 	if ((m->mf_fflags & mta_actions) != m->mf_fflags)
25160Sstevel@tonic-gate 	{
25170Sstevel@tonic-gate 		if (tTd(64, 5))
25180Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
25190Sstevel@tonic-gate 				m->mf_name, m->mf_fflags,
25203544Sjbeck 				(unsigned long) mta_actions);
25210Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25220Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25230Sstevel@tonic-gate 				  "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
25240Sstevel@tonic-gate 				  m->mf_name, m->mf_fflags,
25253544Sjbeck 				  (unsigned long) mta_actions);
25260Sstevel@tonic-gate 		milter_error(m, e);
25273544Sjbeck 		goto error;
25280Sstevel@tonic-gate 	}
25290Sstevel@tonic-gate 
25300Sstevel@tonic-gate 	/* check for protocol feature mismatch */
25313544Sjbeck 	if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
25320Sstevel@tonic-gate 	{
25330Sstevel@tonic-gate 		if (tTd(64, 5))
25340Sstevel@tonic-gate 			sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
25350Sstevel@tonic-gate 				m->mf_name, m->mf_pflags,
25363544Sjbeck 				(unsigned long) mta_prot_flags);
25370Sstevel@tonic-gate 		if (MilterLogLevel > 0)
25380Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
25390Sstevel@tonic-gate 				  "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
25400Sstevel@tonic-gate 				  m->mf_name, m->mf_pflags,
25413544Sjbeck 				  (unsigned long) mta_prot_flags);
25420Sstevel@tonic-gate 		milter_error(m, e);
25433544Sjbeck 		goto error;
25440Sstevel@tonic-gate 	}
25450Sstevel@tonic-gate 
2546616Sjbeck 	if (m->mf_fvers <= 2)
2547616Sjbeck 		m->mf_pflags |= SMFIP_NOUNKNOWN;
2548616Sjbeck 	if (m->mf_fvers <= 3)
2549616Sjbeck 		m->mf_pflags |= SMFIP_NODATA;
2550616Sjbeck 
25513544Sjbeck 	if (rlen > MILTER_OPTLEN)
25523544Sjbeck 	{
25533544Sjbeck 		milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
25543544Sjbeck 	}
25553544Sjbeck 
2556*5402Sjbeck 	if (bitset(SMFIF_DELRCPT, m->mf_fflags))
2557*5402Sjbeck 		milters->mis_flags |= MIS_FL_DEL_RCPT;
2558*5402Sjbeck 	if (!bitset(SMFIP_NORCPT, m->mf_pflags) &&
2559*5402Sjbeck 	    !bitset(SMFIP_NR_RCPT, m->mf_pflags))
2560*5402Sjbeck 		milters->mis_flags |= MIS_FL_REJ_RCPT;
2561*5402Sjbeck 
25620Sstevel@tonic-gate 	if (tTd(64, 5))
25633544Sjbeck 		sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
25640Sstevel@tonic-gate 			m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
25650Sstevel@tonic-gate 	return 0;
25663544Sjbeck 
25673544Sjbeck   error:
25683544Sjbeck 	if (response != NULL)
25693544Sjbeck 		sm_free(response); /* XXX */
25703544Sjbeck 	return -1;
25710Sstevel@tonic-gate }
25723544Sjbeck 
25730Sstevel@tonic-gate /*
25740Sstevel@tonic-gate **  MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
25750Sstevel@tonic-gate **
25760Sstevel@tonic-gate **	Reduce code duplication by putting these checks in one place
25770Sstevel@tonic-gate **
25780Sstevel@tonic-gate **	Parameters:
25790Sstevel@tonic-gate **		e -- current envelope.
25800Sstevel@tonic-gate **
25810Sstevel@tonic-gate **	Returns:
25820Sstevel@tonic-gate **		none
25830Sstevel@tonic-gate */
25840Sstevel@tonic-gate 
25850Sstevel@tonic-gate static void
25860Sstevel@tonic-gate milter_per_connection_check(e)
25870Sstevel@tonic-gate 	ENVELOPE *e;
25880Sstevel@tonic-gate {
25890Sstevel@tonic-gate 	int i;
25900Sstevel@tonic-gate 
25910Sstevel@tonic-gate 	/* see if we are done with any of the filters */
25920Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
25930Sstevel@tonic-gate 	{
25940Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 		if (m->mf_state == SMFS_CLOSABLE)
25970Sstevel@tonic-gate 			milter_quit_filter(m, e);
25980Sstevel@tonic-gate 	}
25990Sstevel@tonic-gate }
26003544Sjbeck 
26010Sstevel@tonic-gate /*
26020Sstevel@tonic-gate **  MILTER_ERROR -- Put a milter filter into error state
26030Sstevel@tonic-gate **
26040Sstevel@tonic-gate **	Parameters:
26050Sstevel@tonic-gate **		m -- the broken filter.
26060Sstevel@tonic-gate **		e -- current envelope.
26070Sstevel@tonic-gate **
26080Sstevel@tonic-gate **	Returns:
26090Sstevel@tonic-gate **		none
26100Sstevel@tonic-gate */
26110Sstevel@tonic-gate 
26120Sstevel@tonic-gate static void
26130Sstevel@tonic-gate milter_error(m, e)
26140Sstevel@tonic-gate 	struct milter *m;
26150Sstevel@tonic-gate 	ENVELOPE *e;
26160Sstevel@tonic-gate {
26170Sstevel@tonic-gate 	/*
26180Sstevel@tonic-gate 	**  We could send a quit here but we may have gotten here due to
26190Sstevel@tonic-gate 	**  an I/O error so we don't want to try to make things worse.
26200Sstevel@tonic-gate 	*/
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate 	if (m->mf_sock >= 0)
26230Sstevel@tonic-gate 	{
26240Sstevel@tonic-gate 		(void) close(m->mf_sock);
26250Sstevel@tonic-gate 		m->mf_sock = -1;
26260Sstevel@tonic-gate 	}
26270Sstevel@tonic-gate 	m->mf_state = SMFS_ERROR;
26280Sstevel@tonic-gate 
26290Sstevel@tonic-gate 	if (MilterLogLevel > 0)
26300Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
26310Sstevel@tonic-gate 			  m->mf_name);
26320Sstevel@tonic-gate }
26333544Sjbeck 
26340Sstevel@tonic-gate /*
26350Sstevel@tonic-gate **  MILTER_HEADERS -- send headers to a single milter filter
26360Sstevel@tonic-gate **
26370Sstevel@tonic-gate **	Parameters:
26380Sstevel@tonic-gate **		m -- current filter.
26390Sstevel@tonic-gate **		e -- current envelope.
26400Sstevel@tonic-gate **		state -- return state from response.
26410Sstevel@tonic-gate **
26420Sstevel@tonic-gate **	Returns:
26430Sstevel@tonic-gate **		response string (may be NULL)
26440Sstevel@tonic-gate */
26450Sstevel@tonic-gate 
26460Sstevel@tonic-gate static char *
26470Sstevel@tonic-gate milter_headers(m, e, state)
26480Sstevel@tonic-gate 	struct milter *m;
26490Sstevel@tonic-gate 	ENVELOPE *e;
26500Sstevel@tonic-gate 	char *state;
26510Sstevel@tonic-gate {
26520Sstevel@tonic-gate 	char *response = NULL;
26530Sstevel@tonic-gate 	HDR *h;
26540Sstevel@tonic-gate 
26550Sstevel@tonic-gate 	if (MilterLogLevel > 17)
26560Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
26570Sstevel@tonic-gate 			  m->mf_name);
26580Sstevel@tonic-gate 
26590Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
26600Sstevel@tonic-gate 	{
26613544Sjbeck 		int len_n, len_v, len_t, len_f;
26623544Sjbeck 		char *buf, *hv;
26630Sstevel@tonic-gate 
26640Sstevel@tonic-gate 		/* don't send over deleted headers */
26650Sstevel@tonic-gate 		if (h->h_value == NULL)
26660Sstevel@tonic-gate 		{
26670Sstevel@tonic-gate 			/* strip H_USER so not counted in milter_changeheader() */
26680Sstevel@tonic-gate 			h->h_flags &= ~H_USER;
26690Sstevel@tonic-gate 			continue;
26700Sstevel@tonic-gate 		}
26710Sstevel@tonic-gate 
26720Sstevel@tonic-gate 		/* skip auto-generated */
26730Sstevel@tonic-gate 		if (!bitset(H_USER, h->h_flags))
26740Sstevel@tonic-gate 			continue;
26750Sstevel@tonic-gate 
26760Sstevel@tonic-gate 		if (tTd(64, 10))
26773544Sjbeck 			sm_dprintf("milter_headers: %s:%s\n",
26780Sstevel@tonic-gate 				h->h_field, h->h_value);
26790Sstevel@tonic-gate 		if (MilterLogLevel > 21)
26800Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
26810Sstevel@tonic-gate 				  m->mf_name, h->h_field);
26820Sstevel@tonic-gate 
26833544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
26843544Sjbeck 		    || *(h->h_value) != ' ')
26853544Sjbeck 			hv = h->h_value;
26863544Sjbeck 		else
26873544Sjbeck 			hv = h->h_value + 1;
26883544Sjbeck 		len_f = strlen(h->h_field) + 1;
26893544Sjbeck 		len_t = len_f + strlen(hv) + 1;
26903544Sjbeck 		if (len_t < 0)
26910Sstevel@tonic-gate 			continue;
26923544Sjbeck 		buf = (char *) xalloc(len_t);
26933544Sjbeck 
26943544Sjbeck 		/*
26953544Sjbeck 		**  Note: currently the call to dequote_internal_chars()
26963544Sjbeck 		**  is not required as h_field is supposed to be 7-bit US-ASCII.
26973544Sjbeck 		*/
26983544Sjbeck 
26993544Sjbeck 		len_n = dequote_internal_chars(h->h_field, buf, len_f);
27003544Sjbeck 		SM_ASSERT(len_n < len_f);
27013544Sjbeck 		len_v = dequote_internal_chars(hv, buf + len_n + 1,
27023544Sjbeck 						len_t - len_n - 1);
27033544Sjbeck 		SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
27043544Sjbeck 		len_t = len_n + 1 + len_v + 1;
27050Sstevel@tonic-gate 
27060Sstevel@tonic-gate 		/* send it over */
27070Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_HEADER, buf,
27083544Sjbeck 					       len_t, e, state, "header");
27093544Sjbeck 		sm_free(buf);
27100Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
27110Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
27120Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
27130Sstevel@tonic-gate 			break;
27140Sstevel@tonic-gate 	}
27150Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27160Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
27170Sstevel@tonic-gate 			  m->mf_name);
27180Sstevel@tonic-gate 	return response;
27190Sstevel@tonic-gate }
27203544Sjbeck 
27210Sstevel@tonic-gate /*
27220Sstevel@tonic-gate **  MILTER_BODY -- send the body to a filter
27230Sstevel@tonic-gate **
27240Sstevel@tonic-gate **	Parameters:
27250Sstevel@tonic-gate **		m -- current filter.
27260Sstevel@tonic-gate **		e -- current envelope.
27270Sstevel@tonic-gate **		state -- return state from response.
27280Sstevel@tonic-gate **
27290Sstevel@tonic-gate **	Returns:
27300Sstevel@tonic-gate **		response string (may be NULL)
27310Sstevel@tonic-gate */
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate static char *
27340Sstevel@tonic-gate milter_body(m, e, state)
27350Sstevel@tonic-gate 	struct milter *m;
27360Sstevel@tonic-gate 	ENVELOPE *e;
27370Sstevel@tonic-gate 	char *state;
27380Sstevel@tonic-gate {
27390Sstevel@tonic-gate 	char bufchar = '\0';
27400Sstevel@tonic-gate 	char prevchar = '\0';
27410Sstevel@tonic-gate 	int c;
27420Sstevel@tonic-gate 	char *response = NULL;
27430Sstevel@tonic-gate 	char *bp;
27440Sstevel@tonic-gate 	char buf[MILTER_CHUNK_SIZE];
27450Sstevel@tonic-gate 
27460Sstevel@tonic-gate 	if (tTd(64, 10))
27470Sstevel@tonic-gate 		sm_dprintf("milter_body\n");
27480Sstevel@tonic-gate 
27490Sstevel@tonic-gate 	if (bfrewind(e->e_dfp) < 0)
27500Sstevel@tonic-gate 	{
27510Sstevel@tonic-gate 		ExitStat = EX_IOERR;
27520Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
27530Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: rewind error",
27540Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
27550Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
27560Sstevel@tonic-gate 		return NULL;
27570Sstevel@tonic-gate 	}
27580Sstevel@tonic-gate 
27590Sstevel@tonic-gate 	if (MilterLogLevel > 17)
27600Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
27610Sstevel@tonic-gate 			  m->mf_name);
27620Sstevel@tonic-gate 	bp = buf;
27630Sstevel@tonic-gate 	while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
27640Sstevel@tonic-gate 	{
27650Sstevel@tonic-gate 		/*  Change LF to CRLF */
27660Sstevel@tonic-gate 		if (c == '\n')
27670Sstevel@tonic-gate 		{
27683544Sjbeck #if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
27690Sstevel@tonic-gate 			/* Not a CRLF already? */
27700Sstevel@tonic-gate 			if (prevchar != '\r')
27713544Sjbeck #endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
27720Sstevel@tonic-gate 			{
27730Sstevel@tonic-gate 				/* Room for CR now? */
27743544Sjbeck 				if (bp + 2 > &buf[sizeof(buf)])
27750Sstevel@tonic-gate 				{
27760Sstevel@tonic-gate 					/* No room, buffer LF */
27770Sstevel@tonic-gate 					bufchar = c;
27780Sstevel@tonic-gate 
27790Sstevel@tonic-gate 					/* and send CR now */
27800Sstevel@tonic-gate 					c = '\r';
27810Sstevel@tonic-gate 				}
27820Sstevel@tonic-gate 				else
27830Sstevel@tonic-gate 				{
27840Sstevel@tonic-gate 					/* Room to do it now */
27850Sstevel@tonic-gate 					*bp++ = '\r';
27860Sstevel@tonic-gate 					prevchar = '\r';
27870Sstevel@tonic-gate 				}
27880Sstevel@tonic-gate 			}
27890Sstevel@tonic-gate 		}
27900Sstevel@tonic-gate 		*bp++ = (char) c;
27910Sstevel@tonic-gate 		prevchar = c;
27923544Sjbeck 		if (bp >= &buf[sizeof(buf)])
27930Sstevel@tonic-gate 		{
27940Sstevel@tonic-gate 			/* send chunk */
27950Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_BODY, buf,
27963544Sjbeck 						       bp - buf, e, state,
27973544Sjbeck 							"body chunk");
27980Sstevel@tonic-gate 			bp = buf;
27990Sstevel@tonic-gate 			if (bufchar != '\0')
28000Sstevel@tonic-gate 			{
28010Sstevel@tonic-gate 				*bp++ = bufchar;
28020Sstevel@tonic-gate 				bufchar = '\0';
28030Sstevel@tonic-gate 				prevchar = bufchar;
28040Sstevel@tonic-gate 			}
28050Sstevel@tonic-gate 		}
28060Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR ||
28070Sstevel@tonic-gate 		    m->mf_state == SMFS_DONE ||
28083544Sjbeck 		    m->mf_state == SMFS_SKIP ||
28090Sstevel@tonic-gate 		    *state != SMFIR_CONTINUE)
28100Sstevel@tonic-gate 			break;
28110Sstevel@tonic-gate 	}
28120Sstevel@tonic-gate 
28130Sstevel@tonic-gate 	/* check for read errors */
28140Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
28150Sstevel@tonic-gate 	{
28160Sstevel@tonic-gate 		ExitStat = EX_IOERR;
28170Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
28183544Sjbeck 		    *state == SMFIR_ACCEPT ||
28193544Sjbeck 		    m->mf_state == SMFS_SKIP)
28200Sstevel@tonic-gate 		{
28210Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
28220Sstevel@tonic-gate 			if (response != NULL)
28230Sstevel@tonic-gate 			{
28240Sstevel@tonic-gate 				sm_free(response); /* XXX */
28250Sstevel@tonic-gate 				response = NULL;
28260Sstevel@tonic-gate 			}
28270Sstevel@tonic-gate 		}
28280Sstevel@tonic-gate 		syserr("milter_body: %s/%cf%s: read error",
28290Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
28300Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
28310Sstevel@tonic-gate 		return response;
28320Sstevel@tonic-gate 	}
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate 	/* send last body chunk */
28350Sstevel@tonic-gate 	if (bp > buf &&
28360Sstevel@tonic-gate 	    m->mf_state != SMFS_ERROR &&
28370Sstevel@tonic-gate 	    m->mf_state != SMFS_DONE &&
28383544Sjbeck 	    m->mf_state != SMFS_SKIP &&
28390Sstevel@tonic-gate 	    *state == SMFIR_CONTINUE)
28400Sstevel@tonic-gate 	{
28410Sstevel@tonic-gate 		/* send chunk */
28420Sstevel@tonic-gate 		response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
28433544Sjbeck 					       e, state, "last body chunk");
28440Sstevel@tonic-gate 		bp = buf;
28450Sstevel@tonic-gate 	}
28460Sstevel@tonic-gate 	if (MilterLogLevel > 17)
28470Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
28480Sstevel@tonic-gate 			  m->mf_name);
28493544Sjbeck 	if (m->mf_state == SMFS_SKIP)
28503544Sjbeck 	{
28513544Sjbeck 		*state = SMFIR_CONTINUE;
28523544Sjbeck 		m->mf_state = SMFS_READY;
28533544Sjbeck 	}
28543544Sjbeck 
28550Sstevel@tonic-gate 	return response;
28560Sstevel@tonic-gate }
28570Sstevel@tonic-gate 
28580Sstevel@tonic-gate /*
28590Sstevel@tonic-gate **  Actions
28600Sstevel@tonic-gate */
28610Sstevel@tonic-gate 
28620Sstevel@tonic-gate /*
28633544Sjbeck **  ADDLEADINGSPACE -- Add a leading space to a string
28643544Sjbeck **
28653544Sjbeck **	Parameters:
28663544Sjbeck **		str -- string
28673544Sjbeck **		rp -- resource pool for allocations
28683544Sjbeck **
28693544Sjbeck **	Returns:
28703544Sjbeck **		pointer to new string
28713544Sjbeck */
28723544Sjbeck 
28733544Sjbeck static char *addleadingspace __P((char *, SM_RPOOL_T *));
28743544Sjbeck 
28753544Sjbeck static char *
28763544Sjbeck addleadingspace(str, rp)
28773544Sjbeck 	char *str;
28783544Sjbeck 	SM_RPOOL_T *rp;
28793544Sjbeck {
28803544Sjbeck 	size_t l;
28813544Sjbeck 	char *new;
28823544Sjbeck 
28833544Sjbeck 	SM_ASSERT(str != NULL);
28843544Sjbeck 	l = strlen(str);
28853544Sjbeck 	SM_ASSERT(l + 2 > l);
28863544Sjbeck 	new = sm_rpool_malloc_x(rp, l + 2);
28873544Sjbeck 	new[0] = ' ';
28883544Sjbeck 	new[1] = '\0';
28893544Sjbeck 	sm_strlcpy(new + 1, str, l + 1);
28903544Sjbeck 	return new;
28913544Sjbeck }
28923544Sjbeck 
28933544Sjbeck /*
28940Sstevel@tonic-gate **  MILTER_ADDHEADER -- Add the supplied header to the message
28950Sstevel@tonic-gate **
28960Sstevel@tonic-gate **	Parameters:
28973544Sjbeck **		m -- current filter.
28980Sstevel@tonic-gate **		response -- encoded form of header/value.
28990Sstevel@tonic-gate **		rlen -- length of response.
29000Sstevel@tonic-gate **		e -- current envelope.
29010Sstevel@tonic-gate **
29020Sstevel@tonic-gate **	Returns:
29030Sstevel@tonic-gate **		none
29040Sstevel@tonic-gate */
29050Sstevel@tonic-gate 
29060Sstevel@tonic-gate static void
29073544Sjbeck milter_addheader(m, response, rlen, e)
29083544Sjbeck 	struct milter *m;
29090Sstevel@tonic-gate 	char *response;
29100Sstevel@tonic-gate 	ssize_t rlen;
29110Sstevel@tonic-gate 	ENVELOPE *e;
29120Sstevel@tonic-gate {
29133544Sjbeck 	int mh_v_len;
29143544Sjbeck 	char *val, *mh_value;
29150Sstevel@tonic-gate 	HDR *h;
29160Sstevel@tonic-gate 
29170Sstevel@tonic-gate 	if (tTd(64, 10))
29180Sstevel@tonic-gate 		sm_dprintf("milter_addheader: ");
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 	/* sanity checks */
29210Sstevel@tonic-gate 	if (response == NULL)
29220Sstevel@tonic-gate 	{
29230Sstevel@tonic-gate 		if (tTd(64, 10))
29240Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
29250Sstevel@tonic-gate 		return;
29260Sstevel@tonic-gate 	}
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
29290Sstevel@tonic-gate 	{
29300Sstevel@tonic-gate 		if (tTd(64, 10))
29313544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
29323544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
29330Sstevel@tonic-gate 		return;
29340Sstevel@tonic-gate 	}
29350Sstevel@tonic-gate 
29360Sstevel@tonic-gate 	/* Find separating NUL */
29370Sstevel@tonic-gate 	val = response + strlen(response) + 1;
29380Sstevel@tonic-gate 
29390Sstevel@tonic-gate 	/* another sanity check */
29400Sstevel@tonic-gate 	if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
29410Sstevel@tonic-gate 	{
29420Sstevel@tonic-gate 		if (tTd(64, 10))
29430Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
29440Sstevel@tonic-gate 		return;
29450Sstevel@tonic-gate 	}
29460Sstevel@tonic-gate 
29470Sstevel@tonic-gate 	if (*response == '\0')
29480Sstevel@tonic-gate 	{
29490Sstevel@tonic-gate 		if (tTd(64, 10))
29500Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
29510Sstevel@tonic-gate 		return;
29520Sstevel@tonic-gate 	}
29530Sstevel@tonic-gate 
29540Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
29550Sstevel@tonic-gate 	{
29560Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, response) == 0 &&
29570Sstevel@tonic-gate 		    !bitset(H_USER, h->h_flags) &&
29580Sstevel@tonic-gate 		    !bitset(H_TRACE, h->h_flags))
29590Sstevel@tonic-gate 			break;
29600Sstevel@tonic-gate 	}
29610Sstevel@tonic-gate 
29623544Sjbeck 	mh_v_len = 0;
29633544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
29643544Sjbeck 
29650Sstevel@tonic-gate 	/* add to e_msgsize */
29660Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
29670Sstevel@tonic-gate 
29680Sstevel@tonic-gate 	if (h != NULL)
29690Sstevel@tonic-gate 	{
29700Sstevel@tonic-gate 		if (tTd(64, 10))
29710Sstevel@tonic-gate 			sm_dprintf("Replace default header %s value with %s\n",
29723544Sjbeck 				   h->h_field, mh_value);
29730Sstevel@tonic-gate 		if (MilterLogLevel > 8)
29740Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
29750Sstevel@tonic-gate 				  "Milter change: default header %s value with %s",
29763544Sjbeck 				  h->h_field, mh_value);
29773544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
29783544Sjbeck 			h->h_value = mh_value;
29793544Sjbeck 		else
29803544Sjbeck 		{
29813544Sjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
29823544Sjbeck 			SM_FREE(mh_value);
29833544Sjbeck 		}
29840Sstevel@tonic-gate 		h->h_flags |= H_USER;
29850Sstevel@tonic-gate 	}
29860Sstevel@tonic-gate 	else
29870Sstevel@tonic-gate 	{
29880Sstevel@tonic-gate 		if (tTd(64, 10))
29893544Sjbeck 			sm_dprintf("Add %s: %s\n", response, mh_value);
29900Sstevel@tonic-gate 		if (MilterLogLevel > 8)
29913544Sjbeck 			sm_syslog(LOG_INFO, e->e_id,
29923544Sjbeck 				  "Milter add: header: %s: %s",
29933544Sjbeck 				  response, mh_value);
29943544Sjbeck 		addheader(newstr(response), mh_value, H_USER, e,
29953544Sjbeck 			!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
29963544Sjbeck 		SM_FREE(mh_value);
29970Sstevel@tonic-gate 	}
29980Sstevel@tonic-gate }
29993544Sjbeck 
30000Sstevel@tonic-gate /*
30010Sstevel@tonic-gate **  MILTER_INSHEADER -- Insert the supplied header
30020Sstevel@tonic-gate **
30030Sstevel@tonic-gate **	Parameters:
30043544Sjbeck **		m -- current filter.
30050Sstevel@tonic-gate **		response -- encoded form of header/value.
30060Sstevel@tonic-gate **		rlen -- length of response.
30070Sstevel@tonic-gate **		e -- current envelope.
30080Sstevel@tonic-gate **
30090Sstevel@tonic-gate **	Returns:
30100Sstevel@tonic-gate **		none
30110Sstevel@tonic-gate **
3012616Sjbeck **	Notes:
3013616Sjbeck **		Unlike milter_addheader(), this does not attempt to determine
3014616Sjbeck **		if the header already exists in the envelope, even a
3015616Sjbeck **		deleted version.  It just blindly inserts.
30160Sstevel@tonic-gate */
30170Sstevel@tonic-gate 
30180Sstevel@tonic-gate static void
30193544Sjbeck milter_insheader(m, response, rlen, e)
30203544Sjbeck 	struct milter *m;
30210Sstevel@tonic-gate 	char *response;
30220Sstevel@tonic-gate 	ssize_t rlen;
30230Sstevel@tonic-gate 	ENVELOPE *e;
30240Sstevel@tonic-gate {
30250Sstevel@tonic-gate 	mi_int32 idx, i;
30263544Sjbeck 	int mh_v_len;
30273544Sjbeck 	char *field, *val, *mh_value;
30280Sstevel@tonic-gate 
30290Sstevel@tonic-gate 	if (tTd(64, 10))
30300Sstevel@tonic-gate 		sm_dprintf("milter_insheader: ");
30310Sstevel@tonic-gate 
30320Sstevel@tonic-gate 	/* sanity checks */
30330Sstevel@tonic-gate 	if (response == NULL)
30340Sstevel@tonic-gate 	{
30350Sstevel@tonic-gate 		if (tTd(64, 10))
30360Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
30370Sstevel@tonic-gate 		return;
30380Sstevel@tonic-gate 	}
30390Sstevel@tonic-gate 
30400Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
30410Sstevel@tonic-gate 	{
30420Sstevel@tonic-gate 		if (tTd(64, 10))
30430Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
30440Sstevel@tonic-gate 		return;
30450Sstevel@tonic-gate 	}
30460Sstevel@tonic-gate 
30470Sstevel@tonic-gate 	/* decode */
30480Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
30490Sstevel@tonic-gate 	idx = ntohl(i);
30500Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
30510Sstevel@tonic-gate 	val = field + strlen(field) + 1;
30520Sstevel@tonic-gate 
30530Sstevel@tonic-gate 	/* another sanity check */
30540Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
30550Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
30560Sstevel@tonic-gate 	{
30570Sstevel@tonic-gate 		if (tTd(64, 10))
30580Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
30590Sstevel@tonic-gate 		return;
30600Sstevel@tonic-gate 	}
30610Sstevel@tonic-gate 
30620Sstevel@tonic-gate 	if (*field == '\0')
30630Sstevel@tonic-gate 	{
30640Sstevel@tonic-gate 		if (tTd(64, 10))
30650Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
30660Sstevel@tonic-gate 		return;
30670Sstevel@tonic-gate 	}
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	/* add to e_msgsize */
30700Sstevel@tonic-gate 	e->e_msgsize += strlen(response) + 2 + strlen(val);
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate 	if (tTd(64, 10))
30733544Sjbeck 		sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
30740Sstevel@tonic-gate 	if (MilterLogLevel > 8)
30750Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
3076616Sjbeck 			  "Milter insert (%d): header: %s: %s",
30770Sstevel@tonic-gate 			  idx, field, val);
30783544Sjbeck 	mh_v_len = 0;
30793544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
30803544Sjbeck 	insheader(idx, newstr(field), mh_value, H_USER, e,
30813544Sjbeck 		!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
30823544Sjbeck 	SM_FREE(mh_value);
30830Sstevel@tonic-gate }
30843544Sjbeck 
30850Sstevel@tonic-gate /*
30860Sstevel@tonic-gate **  MILTER_CHANGEHEADER -- Change the supplied header in the message
30870Sstevel@tonic-gate **
30880Sstevel@tonic-gate **	Parameters:
30893544Sjbeck **		m -- current filter.
30900Sstevel@tonic-gate **		response -- encoded form of header/index/value.
30910Sstevel@tonic-gate **		rlen -- length of response.
30920Sstevel@tonic-gate **		e -- current envelope.
30930Sstevel@tonic-gate **
30940Sstevel@tonic-gate **	Returns:
30950Sstevel@tonic-gate **		none
30960Sstevel@tonic-gate */
30970Sstevel@tonic-gate 
30980Sstevel@tonic-gate static void
30993544Sjbeck milter_changeheader(m, response, rlen, e)
31003544Sjbeck 	struct milter *m;
31010Sstevel@tonic-gate 	char *response;
31020Sstevel@tonic-gate 	ssize_t rlen;
31030Sstevel@tonic-gate 	ENVELOPE *e;
31040Sstevel@tonic-gate {
31050Sstevel@tonic-gate 	mi_int32 i, index;
31063544Sjbeck 	int mh_v_len;
31073544Sjbeck 	char *field, *val, *mh_value;
31080Sstevel@tonic-gate 	HDR *h, *sysheader;
31090Sstevel@tonic-gate 
31100Sstevel@tonic-gate 	if (tTd(64, 10))
31110Sstevel@tonic-gate 		sm_dprintf("milter_changeheader: ");
31120Sstevel@tonic-gate 
31130Sstevel@tonic-gate 	/* sanity checks */
31140Sstevel@tonic-gate 	if (response == NULL)
31150Sstevel@tonic-gate 	{
31160Sstevel@tonic-gate 		if (tTd(64, 10))
31170Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
31180Sstevel@tonic-gate 		return;
31190Sstevel@tonic-gate 	}
31200Sstevel@tonic-gate 
31210Sstevel@tonic-gate 	if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
31220Sstevel@tonic-gate 	{
31230Sstevel@tonic-gate 		if (tTd(64, 10))
31240Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len)\n");
31250Sstevel@tonic-gate 		return;
31260Sstevel@tonic-gate 	}
31270Sstevel@tonic-gate 
31280Sstevel@tonic-gate 	/* Find separating NUL */
31290Sstevel@tonic-gate 	(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
31300Sstevel@tonic-gate 	index = ntohl(i);
31310Sstevel@tonic-gate 	field = response + MILTER_LEN_BYTES;
31320Sstevel@tonic-gate 	val = field + strlen(field) + 1;
31330Sstevel@tonic-gate 
31340Sstevel@tonic-gate 	/* another sanity check */
31350Sstevel@tonic-gate 	if (MILTER_LEN_BYTES + strlen(field) + 1 +
31360Sstevel@tonic-gate 	    strlen(val) + 1 != (size_t) rlen)
31370Sstevel@tonic-gate 	{
31380Sstevel@tonic-gate 		if (tTd(64, 10))
31390Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (part len)\n");
31400Sstevel@tonic-gate 		return;
31410Sstevel@tonic-gate 	}
31420Sstevel@tonic-gate 
31430Sstevel@tonic-gate 	if (*field == '\0')
31440Sstevel@tonic-gate 	{
31450Sstevel@tonic-gate 		if (tTd(64, 10))
31460Sstevel@tonic-gate 			sm_dprintf("empty field name\n");
31470Sstevel@tonic-gate 		return;
31480Sstevel@tonic-gate 	}
31490Sstevel@tonic-gate 
31503544Sjbeck 	mh_v_len = 0;
31513544Sjbeck 	mh_value = quote_internal_chars(val, NULL, &mh_v_len);
31523544Sjbeck 
31530Sstevel@tonic-gate 	sysheader = NULL;
31540Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
31550Sstevel@tonic-gate 	{
31560Sstevel@tonic-gate 		if (sm_strcasecmp(h->h_field, field) == 0)
31570Sstevel@tonic-gate 		{
31583544Sjbeck 			if (bitset(H_USER, h->h_flags) && --index <= 0)
31590Sstevel@tonic-gate 			{
31600Sstevel@tonic-gate 				sysheader = NULL;
31610Sstevel@tonic-gate 				break;
31620Sstevel@tonic-gate 			}
31630Sstevel@tonic-gate 			else if (!bitset(H_USER, h->h_flags) &&
31640Sstevel@tonic-gate 				 !bitset(H_TRACE, h->h_flags))
31650Sstevel@tonic-gate 			{
31660Sstevel@tonic-gate 				/*
31670Sstevel@tonic-gate 				**  DRUMS msg-fmt draft says can only have
31680Sstevel@tonic-gate 				**  multiple occurences of trace fields,
31690Sstevel@tonic-gate 				**  so make sure we replace any non-trace,
31700Sstevel@tonic-gate 				**  non-user field.
31710Sstevel@tonic-gate 				*/
31720Sstevel@tonic-gate 
31730Sstevel@tonic-gate 				sysheader = h;
31740Sstevel@tonic-gate 			}
31750Sstevel@tonic-gate 		}
31760Sstevel@tonic-gate 	}
31770Sstevel@tonic-gate 
31780Sstevel@tonic-gate 	/* if not found as user-provided header at index, use sysheader */
31790Sstevel@tonic-gate 	if (h == NULL)
31800Sstevel@tonic-gate 		h = sysheader;
31810Sstevel@tonic-gate 
31820Sstevel@tonic-gate 	if (h == NULL)
31830Sstevel@tonic-gate 	{
31840Sstevel@tonic-gate 		if (*val == '\0')
31850Sstevel@tonic-gate 		{
31860Sstevel@tonic-gate 			if (tTd(64, 10))
31870Sstevel@tonic-gate 				sm_dprintf("Delete (noop) %s\n", field);
31880Sstevel@tonic-gate 			if (MilterLogLevel > 8)
31890Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
31900Sstevel@tonic-gate 					"Milter delete (noop): header: %s"
31910Sstevel@tonic-gate 					, field);
31920Sstevel@tonic-gate 		}
31930Sstevel@tonic-gate 		else
31940Sstevel@tonic-gate 		{
31950Sstevel@tonic-gate 			/* treat modify value with no existing header as add */
31960Sstevel@tonic-gate 			if (tTd(64, 10))
31973544Sjbeck 				sm_dprintf("Add %s: %s\n", field, mh_value);
31980Sstevel@tonic-gate 			if (MilterLogLevel > 8)
31990Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
32000Sstevel@tonic-gate 					"Milter change (add): header: %s: %s"
32013544Sjbeck 					, field, mh_value);
32023544Sjbeck 			addheader(newstr(field), mh_value, H_USER, e,
32033544Sjbeck 				!bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
32040Sstevel@tonic-gate 		}
32050Sstevel@tonic-gate 		return;
32060Sstevel@tonic-gate 	}
32070Sstevel@tonic-gate 
32080Sstevel@tonic-gate 	if (tTd(64, 10))
32090Sstevel@tonic-gate 	{
32100Sstevel@tonic-gate 		if (*val == '\0')
32110Sstevel@tonic-gate 		{
32123544Sjbeck 			sm_dprintf("Delete%s %s:%s\n",
32130Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32140Sstevel@tonic-gate 				   field,
32150Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value);
32160Sstevel@tonic-gate 		}
32170Sstevel@tonic-gate 		else
32180Sstevel@tonic-gate 		{
32190Sstevel@tonic-gate 			sm_dprintf("Change%s %s: from %s to %s\n",
32200Sstevel@tonic-gate 				   h == sysheader ? " (default header)" : "",
32210Sstevel@tonic-gate 				   field,
32220Sstevel@tonic-gate 				   h->h_value == NULL ? "<NULL>" : h->h_value,
32233544Sjbeck 				   mh_value);
32240Sstevel@tonic-gate 		}
32250Sstevel@tonic-gate 	}
32260Sstevel@tonic-gate 
32270Sstevel@tonic-gate 	if (MilterLogLevel > 8)
32280Sstevel@tonic-gate 	{
32290Sstevel@tonic-gate 		if (*val == '\0')
32300Sstevel@tonic-gate 		{
32310Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32323544Sjbeck 				  "Milter delete: header%s %s:%s",
32330Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32340Sstevel@tonic-gate 				  field,
32350Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value);
32360Sstevel@tonic-gate 		}
32370Sstevel@tonic-gate 		else
32380Sstevel@tonic-gate 		{
32390Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
32400Sstevel@tonic-gate 				  "Milter change: header%s %s: from %s to %s",
32410Sstevel@tonic-gate 				  h == sysheader ? " (default header)" : "",
32420Sstevel@tonic-gate 				  field,
32430Sstevel@tonic-gate 				  h->h_value == NULL ? "<NULL>" : h->h_value,
32443544Sjbeck 				  mh_value);
32450Sstevel@tonic-gate 		}
32460Sstevel@tonic-gate 	}
32470Sstevel@tonic-gate 
32480Sstevel@tonic-gate 	if (h != sysheader && h->h_value != NULL)
32490Sstevel@tonic-gate 	{
32500Sstevel@tonic-gate 		size_t l;
32510Sstevel@tonic-gate 
32520Sstevel@tonic-gate 		l = strlen(h->h_value);
32530Sstevel@tonic-gate 		if (l > e->e_msgsize)
32540Sstevel@tonic-gate 			e->e_msgsize = 0;
32550Sstevel@tonic-gate 		else
32560Sstevel@tonic-gate 			e->e_msgsize -= l;
32570Sstevel@tonic-gate 		/* rpool, don't free: sm_free(h->h_value); XXX */
32580Sstevel@tonic-gate 	}
32590Sstevel@tonic-gate 
32600Sstevel@tonic-gate 	if (*val == '\0')
32610Sstevel@tonic-gate 	{
32620Sstevel@tonic-gate 		/* Remove "Field: " from message size */
32630Sstevel@tonic-gate 		if (h != sysheader)
32640Sstevel@tonic-gate 		{
32650Sstevel@tonic-gate 			size_t l;
32660Sstevel@tonic-gate 
32670Sstevel@tonic-gate 			l = strlen(h->h_field) + 2;
32680Sstevel@tonic-gate 			if (l > e->e_msgsize)
32690Sstevel@tonic-gate 				e->e_msgsize = 0;
32700Sstevel@tonic-gate 			else
32710Sstevel@tonic-gate 				e->e_msgsize -= l;
32720Sstevel@tonic-gate 		}
32730Sstevel@tonic-gate 		h->h_value = NULL;
32743544Sjbeck 		SM_FREE(mh_value);
32750Sstevel@tonic-gate 	}
32760Sstevel@tonic-gate 	else
32770Sstevel@tonic-gate 	{
32783544Sjbeck 		if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
32793544Sjbeck 			h->h_value = mh_value;
32803544Sjbeck 		else
32813544Sjbeck 		{
32823544Sjbeck 			h->h_value = addleadingspace (mh_value, e->e_rpool);
32833544Sjbeck 			SM_FREE(mh_value);
32843544Sjbeck 		}
32850Sstevel@tonic-gate 		h->h_flags |= H_USER;
32860Sstevel@tonic-gate 		e->e_msgsize += strlen(h->h_value);
32870Sstevel@tonic-gate 	}
32880Sstevel@tonic-gate }
32893544Sjbeck 
32903544Sjbeck /*
32913544Sjbeck **  MILTER_SPLIT_RESPONSE -- Split response into fields.
32923544Sjbeck **
32933544Sjbeck **	Parameters:
32943544Sjbeck **		response -- encoded repsonse.
32953544Sjbeck **		rlen -- length of response.
32963544Sjbeck **		pargc -- number of arguments (ouput)
32973544Sjbeck **
32983544Sjbeck **	Returns:
32993544Sjbeck **		array of pointers to the individual strings
33003544Sjbeck */
33013544Sjbeck 
33023544Sjbeck static char **milter_split_response __P((char *, ssize_t, int *));
33033544Sjbeck 
33043544Sjbeck static char **
33053544Sjbeck milter_split_response(response, rlen, pargc)
33063544Sjbeck 	char *response;
33073544Sjbeck 	ssize_t rlen;
33083544Sjbeck 	int *pargc;
33093544Sjbeck {
33103544Sjbeck 	char **s;
33113544Sjbeck 	size_t i;
33123544Sjbeck 	int elem, nelem;
33133544Sjbeck 
33143544Sjbeck 	SM_ASSERT(response != NULL);
33153544Sjbeck 	SM_ASSERT(pargc != NULL);
33163544Sjbeck 	*pargc = 0;
33173544Sjbeck 	if (rlen < 2 || strlen(response) >= (size_t) rlen)
33183544Sjbeck 	{
33193544Sjbeck 		if (tTd(64, 10))
33203544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
33213544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
33223544Sjbeck 		return NULL;
33233544Sjbeck 	}
33243544Sjbeck 
33253544Sjbeck 	nelem = 0;
33263544Sjbeck 	for (i = 0; i < rlen; i++)
33273544Sjbeck 	{
33283544Sjbeck 		if (response[i] == '\0')
33293544Sjbeck 			++nelem;
33303544Sjbeck 	}
33313544Sjbeck 	if (nelem == 0)
33323544Sjbeck 		return NULL;
33333544Sjbeck 
33343544Sjbeck 	/* last entry is only for the name */
33353544Sjbeck 	s = (char **)malloc(nelem * (sizeof(*s)));
33363544Sjbeck 	if (s == NULL)
33373544Sjbeck 		return NULL;
33383544Sjbeck 	s[0] = response;
33393544Sjbeck 	for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
33403544Sjbeck 	{
33413544Sjbeck 		if (response[i] == '\0')
33423544Sjbeck 		{
33433544Sjbeck 			++elem;
33443544Sjbeck 			if (i + 1 >= rlen)
33453544Sjbeck 				s[elem] = NULL;
33463544Sjbeck 			else
33473544Sjbeck 				s[elem] = &(response[i + 1]);
33483544Sjbeck 		}
33493544Sjbeck 	}
33503544Sjbeck 	*pargc = nelem;
33513544Sjbeck 
33523544Sjbeck 	if (tTd(64, 10))
33533544Sjbeck 	{
33543544Sjbeck 		for (elem = 0; elem < nelem; elem++)
33553544Sjbeck 			sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
33563544Sjbeck 	}
33573544Sjbeck 
33583544Sjbeck 	/* overwrite last entry (already done above, just paranoia) */
33593544Sjbeck 	s[elem] = NULL;
33603544Sjbeck 	return s;
33613544Sjbeck }
33623544Sjbeck 
33633544Sjbeck /*
33643544Sjbeck **  MILTER_CHGFROM -- Change the envelope sender address
33653544Sjbeck **
33663544Sjbeck **	Parameters:
33673544Sjbeck **		response -- encoded form of recipient address.
33683544Sjbeck **		rlen -- length of response.
33693544Sjbeck **		e -- current envelope.
33703544Sjbeck **
33713544Sjbeck **	Returns:
33723544Sjbeck **		none
33733544Sjbeck */
33743544Sjbeck 
33753544Sjbeck static void
33763544Sjbeck milter_chgfrom(response, rlen, e)
33773544Sjbeck 	char *response;
33783544Sjbeck 	ssize_t rlen;
33793544Sjbeck 	ENVELOPE *e;
33803544Sjbeck {
33813544Sjbeck 	int olderrors, argc;
33823544Sjbeck 	char **argv;
33833544Sjbeck 
33843544Sjbeck 	if (tTd(64, 10))
33853544Sjbeck 		sm_dprintf("milter_chgfrom: ");
33863544Sjbeck 
33873544Sjbeck 	/* sanity checks */
33883544Sjbeck 	if (response == NULL)
33893544Sjbeck 	{
33903544Sjbeck 		if (tTd(64, 10))
33913544Sjbeck 			sm_dprintf("NULL response\n");
33923544Sjbeck 		return;
33933544Sjbeck 	}
33943544Sjbeck 
33953544Sjbeck 	if (*response == '\0' ||
33963544Sjbeck 	    strlen(response) + 1 > (size_t) rlen)
33973544Sjbeck 	{
33983544Sjbeck 		if (tTd(64, 10))
33993544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
34003544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
34013544Sjbeck 		return;
34023544Sjbeck 	}
34033544Sjbeck 
34043544Sjbeck 	if (tTd(64, 10))
34053544Sjbeck 		sm_dprintf("%s\n", response);
34063544Sjbeck 	if (MilterLogLevel > 8)
34073544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
34083544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
34093544Sjbeck 	if (argc < 1 || argc > 2)
34103544Sjbeck 	{
34113544Sjbeck 		if (tTd(64, 10))
34123544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
34133544Sjbeck 		return;
34143544Sjbeck 	}
34153544Sjbeck 
34163544Sjbeck 	olderrors = Errors;
34173544Sjbeck 	setsender(argv[0], e, NULL, '\0', false);
34183544Sjbeck 	if (argc == 2)
34193544Sjbeck 	{
34203544Sjbeck 		reset_mail_esmtp_args(e);
34213544Sjbeck 
34223544Sjbeck 		/*
34233544Sjbeck 		**  need "features" here: how to get those? via e?
34243544Sjbeck 		**  "fake" it for now: allow everything.
34253544Sjbeck 		*/
34263544Sjbeck 
34273544Sjbeck 		parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
34283544Sjbeck 				mail_esmtp_args);
34293544Sjbeck 	}
34303544Sjbeck 	Errors = olderrors;
34313544Sjbeck 	return;
34323544Sjbeck }
34333544Sjbeck 
34343544Sjbeck /*
34353544Sjbeck **  MILTER_ADDRCPT_PAR -- Add the supplied recipient to the message
34363544Sjbeck **
34373544Sjbeck **	Parameters:
34383544Sjbeck **		response -- encoded form of recipient address.
34393544Sjbeck **		rlen -- length of response.
34403544Sjbeck **		e -- current envelope.
34413544Sjbeck **
34423544Sjbeck **	Returns:
34433544Sjbeck **		none
34443544Sjbeck */
34453544Sjbeck 
34463544Sjbeck static void
34473544Sjbeck milter_addrcpt_par(response, rlen, e)
34483544Sjbeck 	char *response;
34493544Sjbeck 	ssize_t rlen;
34503544Sjbeck 	ENVELOPE *e;
34513544Sjbeck {
34523544Sjbeck 	int olderrors, argc;
34533544Sjbeck 	char *delimptr;
34543544Sjbeck 	char **argv;
34553544Sjbeck 	ADDRESS *a;
34563544Sjbeck 
34573544Sjbeck 	if (tTd(64, 10))
34583544Sjbeck 		sm_dprintf("milter_addrcpt_par: ");
34593544Sjbeck 
34603544Sjbeck 	/* sanity checks */
34613544Sjbeck 	if (response == NULL)
34623544Sjbeck 	{
34633544Sjbeck 		if (tTd(64, 10))
34643544Sjbeck 			sm_dprintf("NULL response\n");
34653544Sjbeck 		return;
34663544Sjbeck 	}
34673544Sjbeck 
34683544Sjbeck 	if (tTd(64, 10))
34693544Sjbeck 		sm_dprintf("%s\n", response);
34703544Sjbeck 	if (MilterLogLevel > 8)
34713544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
34723544Sjbeck 
34733544Sjbeck 	argv = milter_split_response(response, rlen, &argc);
34743544Sjbeck 	if (argc < 1 || argc > 2)
34753544Sjbeck 	{
34763544Sjbeck 		if (tTd(64, 10))
34773544Sjbeck 			sm_dprintf("didn't follow protocol argc=%d\n", argc);
34783544Sjbeck 		return;
34793544Sjbeck 	}
34803544Sjbeck 	olderrors = Errors;
34813544Sjbeck 
34823544Sjbeck 	/* how to set ESMTP arguments? */
34833544Sjbeck 	a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
34843544Sjbeck 
34853544Sjbeck 	if (a != NULL && olderrors == Errors)
34863544Sjbeck 	{
34873544Sjbeck 		parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
34883544Sjbeck 				rcpt_esmtp_args);
34893544Sjbeck 		if (olderrors == Errors)
34903544Sjbeck 			a = recipient(a, &e->e_sendqueue, 0, e);
34913544Sjbeck 		else
34923544Sjbeck 			sm_dprintf("olderrors=%d, Errors=%d\n",
34933544Sjbeck 				olderrors, Errors);
34943544Sjbeck 	}
34953544Sjbeck 	else
34963544Sjbeck 	{
34973544Sjbeck 		sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
34983544Sjbeck 			a, olderrors, Errors);
34993544Sjbeck 	}
35003544Sjbeck 
35013544Sjbeck 	Errors = olderrors;
35023544Sjbeck 	return;
35033544Sjbeck }
35043544Sjbeck 
35050Sstevel@tonic-gate /*
35060Sstevel@tonic-gate **  MILTER_ADDRCPT -- Add the supplied recipient to the message
35070Sstevel@tonic-gate **
35080Sstevel@tonic-gate **	Parameters:
35090Sstevel@tonic-gate **		response -- encoded form of recipient address.
35100Sstevel@tonic-gate **		rlen -- length of response.
35110Sstevel@tonic-gate **		e -- current envelope.
35120Sstevel@tonic-gate **
35130Sstevel@tonic-gate **	Returns:
35140Sstevel@tonic-gate **		none
35150Sstevel@tonic-gate */
35160Sstevel@tonic-gate 
35170Sstevel@tonic-gate static void
35180Sstevel@tonic-gate milter_addrcpt(response, rlen, e)
35190Sstevel@tonic-gate 	char *response;
35200Sstevel@tonic-gate 	ssize_t rlen;
35210Sstevel@tonic-gate 	ENVELOPE *e;
35220Sstevel@tonic-gate {
35230Sstevel@tonic-gate 	int olderrors;
35240Sstevel@tonic-gate 
35250Sstevel@tonic-gate 	if (tTd(64, 10))
35260Sstevel@tonic-gate 		sm_dprintf("milter_addrcpt: ");
35270Sstevel@tonic-gate 
35280Sstevel@tonic-gate 	/* sanity checks */
35290Sstevel@tonic-gate 	if (response == NULL)
35300Sstevel@tonic-gate 	{
35310Sstevel@tonic-gate 		if (tTd(64, 10))
35320Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
35330Sstevel@tonic-gate 		return;
35340Sstevel@tonic-gate 	}
35350Sstevel@tonic-gate 
35360Sstevel@tonic-gate 	if (*response == '\0' ||
35370Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
35380Sstevel@tonic-gate 	{
35390Sstevel@tonic-gate 		if (tTd(64, 10))
35400Sstevel@tonic-gate 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
35410Sstevel@tonic-gate 				   (int) strlen(response), (int) (rlen - 1));
35420Sstevel@tonic-gate 		return;
35430Sstevel@tonic-gate 	}
35440Sstevel@tonic-gate 
35450Sstevel@tonic-gate 	if (tTd(64, 10))
35460Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
35470Sstevel@tonic-gate 	if (MilterLogLevel > 8)
35480Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
35490Sstevel@tonic-gate 	olderrors = Errors;
35500Sstevel@tonic-gate 	(void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
35510Sstevel@tonic-gate 	Errors = olderrors;
35520Sstevel@tonic-gate 	return;
35530Sstevel@tonic-gate }
35543544Sjbeck 
35550Sstevel@tonic-gate /*
35560Sstevel@tonic-gate **  MILTER_DELRCPT -- Delete the supplied recipient from the message
35570Sstevel@tonic-gate **
35580Sstevel@tonic-gate **	Parameters:
35590Sstevel@tonic-gate **		response -- encoded form of recipient address.
35600Sstevel@tonic-gate **		rlen -- length of response.
35610Sstevel@tonic-gate **		e -- current envelope.
35620Sstevel@tonic-gate **
35630Sstevel@tonic-gate **	Returns:
35640Sstevel@tonic-gate **		none
35650Sstevel@tonic-gate */
35660Sstevel@tonic-gate 
35670Sstevel@tonic-gate static void
35680Sstevel@tonic-gate milter_delrcpt(response, rlen, e)
35690Sstevel@tonic-gate 	char *response;
35700Sstevel@tonic-gate 	ssize_t rlen;
35710Sstevel@tonic-gate 	ENVELOPE *e;
35720Sstevel@tonic-gate {
35730Sstevel@tonic-gate 	if (tTd(64, 10))
35740Sstevel@tonic-gate 		sm_dprintf("milter_delrcpt: ");
35750Sstevel@tonic-gate 
35760Sstevel@tonic-gate 	/* sanity checks */
35770Sstevel@tonic-gate 	if (response == NULL)
35780Sstevel@tonic-gate 	{
35790Sstevel@tonic-gate 		if (tTd(64, 10))
35800Sstevel@tonic-gate 			sm_dprintf("NULL response\n");
35810Sstevel@tonic-gate 		return;
35820Sstevel@tonic-gate 	}
35830Sstevel@tonic-gate 
35840Sstevel@tonic-gate 	if (*response == '\0' ||
35850Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen)
35860Sstevel@tonic-gate 	{
35870Sstevel@tonic-gate 		if (tTd(64, 10))
35883544Sjbeck 			sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
35893544Sjbeck 				   (int) strlen(response), (int) (rlen - 1));
35900Sstevel@tonic-gate 		return;
35910Sstevel@tonic-gate 	}
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 	if (tTd(64, 10))
35940Sstevel@tonic-gate 		sm_dprintf("%s\n", response);
35950Sstevel@tonic-gate 	if (MilterLogLevel > 8)
35960Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
35970Sstevel@tonic-gate 			  response);
35980Sstevel@tonic-gate 	(void) removefromlist(response, &e->e_sendqueue, e);
35990Sstevel@tonic-gate 	return;
36000Sstevel@tonic-gate }
36013544Sjbeck 
36020Sstevel@tonic-gate /*
36030Sstevel@tonic-gate **  MILTER_REPLBODY -- Replace the current data file with new body
36040Sstevel@tonic-gate **
36050Sstevel@tonic-gate **	Parameters:
36060Sstevel@tonic-gate **		response -- encoded form of new body.
36070Sstevel@tonic-gate **		rlen -- length of response.
36080Sstevel@tonic-gate **		newfilter -- if first time called by a new filter
36090Sstevel@tonic-gate **		e -- current envelope.
36100Sstevel@tonic-gate **
36110Sstevel@tonic-gate **	Returns:
36120Sstevel@tonic-gate **		0 upon success, -1 upon failure
36130Sstevel@tonic-gate */
36140Sstevel@tonic-gate 
36150Sstevel@tonic-gate static int
36160Sstevel@tonic-gate milter_replbody(response, rlen, newfilter, e)
36170Sstevel@tonic-gate 	char *response;
36180Sstevel@tonic-gate 	ssize_t rlen;
36190Sstevel@tonic-gate 	bool newfilter;
36200Sstevel@tonic-gate 	ENVELOPE *e;
36210Sstevel@tonic-gate {
36220Sstevel@tonic-gate 	static char prevchar;
36230Sstevel@tonic-gate 	int i;
36240Sstevel@tonic-gate 
36250Sstevel@tonic-gate 	if (tTd(64, 10))
36260Sstevel@tonic-gate 		sm_dprintf("milter_replbody\n");
36270Sstevel@tonic-gate 
36280Sstevel@tonic-gate 	/* If a new filter, reset previous character and truncate data file */
36290Sstevel@tonic-gate 	if (newfilter)
36300Sstevel@tonic-gate 	{
36310Sstevel@tonic-gate 		off_t prevsize;
36320Sstevel@tonic-gate 		char dfname[MAXPATHLEN];
36330Sstevel@tonic-gate 
36340Sstevel@tonic-gate 		(void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
36353544Sjbeck 				  sizeof(dfname));
36360Sstevel@tonic-gate 
36370Sstevel@tonic-gate 		/* Reset prevchar */
36380Sstevel@tonic-gate 		prevchar = '\0';
36390Sstevel@tonic-gate 
36400Sstevel@tonic-gate 		/* Get the current data file information */
36410Sstevel@tonic-gate 		prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
36420Sstevel@tonic-gate 		if (prevsize < 0)
36430Sstevel@tonic-gate 			prevsize = 0;
36440Sstevel@tonic-gate 
36450Sstevel@tonic-gate 		/* truncate current data file */
36460Sstevel@tonic-gate 		if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
36470Sstevel@tonic-gate 		{
36480Sstevel@tonic-gate 			if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
36490Sstevel@tonic-gate 			{
36500Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
36510Sstevel@tonic-gate 				return -1;
36520Sstevel@tonic-gate 			}
36530Sstevel@tonic-gate 		}
36540Sstevel@tonic-gate 		else
36550Sstevel@tonic-gate 		{
36560Sstevel@tonic-gate 			int err;
36570Sstevel@tonic-gate 
36580Sstevel@tonic-gate 			err = sm_io_error(e->e_dfp);
36590Sstevel@tonic-gate 			(void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
36600Sstevel@tonic-gate 
36610Sstevel@tonic-gate 			/*
36620Sstevel@tonic-gate 			**  Clear error if tried to fflush()
36630Sstevel@tonic-gate 			**  a read-only file pointer and
36640Sstevel@tonic-gate 			**  there wasn't a previous error.
36650Sstevel@tonic-gate 			*/
36660Sstevel@tonic-gate 
36670Sstevel@tonic-gate 			if (err == 0)
36680Sstevel@tonic-gate 				sm_io_clearerr(e->e_dfp);
36690Sstevel@tonic-gate 
36700Sstevel@tonic-gate 			/* errno is set implicitly by fseek() before return */
36710Sstevel@tonic-gate 			err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
36720Sstevel@tonic-gate 					 0, SEEK_SET);
36730Sstevel@tonic-gate 			if (err < 0)
36740Sstevel@tonic-gate 			{
36750Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
36760Sstevel@tonic-gate 				return -1;
36770Sstevel@tonic-gate 			}
36780Sstevel@tonic-gate # if NOFTRUNCATE
36790Sstevel@tonic-gate 			/* XXX: Not much we can do except rewind it */
36800Sstevel@tonic-gate 			errno = EINVAL;
36810Sstevel@tonic-gate 			MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
36820Sstevel@tonic-gate 			return -1;
36830Sstevel@tonic-gate # else /* NOFTRUNCATE */
36840Sstevel@tonic-gate 			err = ftruncate(sm_io_getinfo(e->e_dfp,
36850Sstevel@tonic-gate 						      SM_IO_WHAT_FD, NULL),
36860Sstevel@tonic-gate 					0);
36870Sstevel@tonic-gate 			if (err < 0)
36880Sstevel@tonic-gate 			{
36890Sstevel@tonic-gate 				MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
36900Sstevel@tonic-gate 				return -1;
36910Sstevel@tonic-gate 			}
36920Sstevel@tonic-gate # endif /* NOFTRUNCATE */
36930Sstevel@tonic-gate 		}
36940Sstevel@tonic-gate 
36950Sstevel@tonic-gate 		if (prevsize > e->e_msgsize)
36960Sstevel@tonic-gate 			e->e_msgsize = 0;
36970Sstevel@tonic-gate 		else
36980Sstevel@tonic-gate 			e->e_msgsize -= prevsize;
36990Sstevel@tonic-gate 	}
37000Sstevel@tonic-gate 
37010Sstevel@tonic-gate 	if (newfilter && MilterLogLevel > 8)
37020Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
37030Sstevel@tonic-gate 
37040Sstevel@tonic-gate 	if (response == NULL)
37050Sstevel@tonic-gate 	{
37060Sstevel@tonic-gate 		/* Flush the buffered '\r' */
37070Sstevel@tonic-gate 		if (prevchar == '\r')
37080Sstevel@tonic-gate 		{
37090Sstevel@tonic-gate 			(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
37100Sstevel@tonic-gate 			e->e_msgsize++;
37110Sstevel@tonic-gate 		}
37120Sstevel@tonic-gate 		return 0;
37130Sstevel@tonic-gate 	}
37140Sstevel@tonic-gate 
37150Sstevel@tonic-gate 	for (i = 0; i < rlen; i++)
37160Sstevel@tonic-gate 	{
37170Sstevel@tonic-gate 		/* Buffered char from last chunk */
37180Sstevel@tonic-gate 		if (i == 0 && prevchar == '\r')
37190Sstevel@tonic-gate 		{
37200Sstevel@tonic-gate 			/* Not CRLF, output prevchar */
37210Sstevel@tonic-gate 			if (response[i] != '\n')
37220Sstevel@tonic-gate 			{
37230Sstevel@tonic-gate 				(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
37240Sstevel@tonic-gate 						  prevchar);
37250Sstevel@tonic-gate 				e->e_msgsize++;
37260Sstevel@tonic-gate 			}
37270Sstevel@tonic-gate 			prevchar = '\0';
37280Sstevel@tonic-gate 		}
37290Sstevel@tonic-gate 
37300Sstevel@tonic-gate 		/* Turn CRLF into LF */
37310Sstevel@tonic-gate 		if (response[i] == '\r')
37320Sstevel@tonic-gate 		{
37330Sstevel@tonic-gate 			/* check if at end of chunk */
37340Sstevel@tonic-gate 			if (i + 1 < rlen)
37350Sstevel@tonic-gate 			{
37360Sstevel@tonic-gate 				/* If LF, strip CR */
37370Sstevel@tonic-gate 				if (response[i + 1] == '\n')
37380Sstevel@tonic-gate 					i++;
37390Sstevel@tonic-gate 			}
37400Sstevel@tonic-gate 			else
37410Sstevel@tonic-gate 			{
37420Sstevel@tonic-gate 				/* check next chunk */
37430Sstevel@tonic-gate 				prevchar = '\r';
37440Sstevel@tonic-gate 				continue;
37450Sstevel@tonic-gate 			}
37460Sstevel@tonic-gate 		}
37470Sstevel@tonic-gate 		(void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
37480Sstevel@tonic-gate 		e->e_msgsize++;
37490Sstevel@tonic-gate 	}
37500Sstevel@tonic-gate 	return 0;
37510Sstevel@tonic-gate }
37520Sstevel@tonic-gate 
37530Sstevel@tonic-gate /*
37540Sstevel@tonic-gate **  MTA callouts
37550Sstevel@tonic-gate */
37560Sstevel@tonic-gate 
37570Sstevel@tonic-gate /*
37580Sstevel@tonic-gate **  MILTER_INIT -- open and negotiate with all of the filters
37590Sstevel@tonic-gate **
37600Sstevel@tonic-gate **	Parameters:
37610Sstevel@tonic-gate **		e -- current envelope.
37620Sstevel@tonic-gate **		state -- return state from response.
3763*5402Sjbeck **		milters -- milters structure.
37640Sstevel@tonic-gate **
37650Sstevel@tonic-gate **	Returns:
37660Sstevel@tonic-gate **		true iff at least one filter is active
37670Sstevel@tonic-gate */
37680Sstevel@tonic-gate 
37690Sstevel@tonic-gate /* ARGSUSED */
37700Sstevel@tonic-gate bool
3771*5402Sjbeck milter_init(e, state, milters)
37720Sstevel@tonic-gate 	ENVELOPE *e;
37730Sstevel@tonic-gate 	char *state;
3774*5402Sjbeck 	milters_T *milters;
37750Sstevel@tonic-gate {
37760Sstevel@tonic-gate 	int i;
37770Sstevel@tonic-gate 
37780Sstevel@tonic-gate 	if (tTd(64, 10))
37790Sstevel@tonic-gate 		sm_dprintf("milter_init\n");
37800Sstevel@tonic-gate 
3781*5402Sjbeck 	memset(milters, '\0', sizeof(*milters));
37820Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
37830Sstevel@tonic-gate 	if (InputFilters[0] == NULL)
37840Sstevel@tonic-gate 	{
37850Sstevel@tonic-gate 		if (MilterLogLevel > 10)
37860Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
37870Sstevel@tonic-gate 				  "Milter: no active filter");
37880Sstevel@tonic-gate 		return false;
37890Sstevel@tonic-gate 	}
37900Sstevel@tonic-gate 
37910Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
37920Sstevel@tonic-gate 	{
37930Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
37940Sstevel@tonic-gate 
37950Sstevel@tonic-gate 		m->mf_sock = milter_open(m, false, e);
37960Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
37970Sstevel@tonic-gate 		{
37980Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
37990Sstevel@tonic-gate 			break;
38000Sstevel@tonic-gate 		}
38010Sstevel@tonic-gate 
38020Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
3803*5402Sjbeck 		    milter_negotiate(m, e, milters) < 0 ||
38040Sstevel@tonic-gate 		    m->mf_state == SMFS_ERROR)
38050Sstevel@tonic-gate 		{
38060Sstevel@tonic-gate 			if (tTd(64, 5))
38070Sstevel@tonic-gate 				sm_dprintf("milter_init(%s): failed to %s\n",
38080Sstevel@tonic-gate 					   m->mf_name,
38090Sstevel@tonic-gate 					   m->mf_sock < 0 ? "open" :
38100Sstevel@tonic-gate 							    "negotiate");
38110Sstevel@tonic-gate 			if (MilterLogLevel > 0)
38120Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
38130Sstevel@tonic-gate 					  "Milter (%s): init failed to %s",
38140Sstevel@tonic-gate 					  m->mf_name,
38150Sstevel@tonic-gate 					  m->mf_sock < 0 ? "open" :
38160Sstevel@tonic-gate 							   "negotiate");
38170Sstevel@tonic-gate 
38180Sstevel@tonic-gate 			/* if negotation failure, close socket */
38190Sstevel@tonic-gate 			milter_error(m, e);
38200Sstevel@tonic-gate 			MILTER_CHECK_ERROR(true, continue);
38210Sstevel@tonic-gate 			continue;
38220Sstevel@tonic-gate 		}
38230Sstevel@tonic-gate 		if (MilterLogLevel > 9)
38240Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
38250Sstevel@tonic-gate 				  "Milter (%s): init success to %s",
38260Sstevel@tonic-gate 				  m->mf_name,
38270Sstevel@tonic-gate 				  m->mf_sock < 0 ? "open" : "negotiate");
38280Sstevel@tonic-gate 	}
38290Sstevel@tonic-gate 
38300Sstevel@tonic-gate 	/*
38310Sstevel@tonic-gate 	**  If something temp/perm failed with one of the filters,
38320Sstevel@tonic-gate 	**  we won't be using any of them, so clear any existing
38330Sstevel@tonic-gate 	**  connections.
38340Sstevel@tonic-gate 	*/
38350Sstevel@tonic-gate 
38360Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
38370Sstevel@tonic-gate 		milter_quit(e);
38380Sstevel@tonic-gate 
38390Sstevel@tonic-gate 	return true;
38400Sstevel@tonic-gate }
38413544Sjbeck 
38420Sstevel@tonic-gate /*
38430Sstevel@tonic-gate **  MILTER_CONNECT -- send connection info to milter filters
38440Sstevel@tonic-gate **
38450Sstevel@tonic-gate **	Parameters:
38460Sstevel@tonic-gate **		hostname -- hostname of remote machine.
38470Sstevel@tonic-gate **		addr -- address of remote machine.
38480Sstevel@tonic-gate **		e -- current envelope.
38490Sstevel@tonic-gate **		state -- return state from response.
38500Sstevel@tonic-gate **
38510Sstevel@tonic-gate **	Returns:
38520Sstevel@tonic-gate **		response string (may be NULL)
38530Sstevel@tonic-gate */
38540Sstevel@tonic-gate 
38550Sstevel@tonic-gate char *
38560Sstevel@tonic-gate milter_connect(hostname, addr, e, state)
38570Sstevel@tonic-gate 	char *hostname;
38580Sstevel@tonic-gate 	SOCKADDR addr;
38590Sstevel@tonic-gate 	ENVELOPE *e;
38600Sstevel@tonic-gate 	char *state;
38610Sstevel@tonic-gate {
38620Sstevel@tonic-gate 	char family;
38630Sstevel@tonic-gate 	unsigned short port;
38640Sstevel@tonic-gate 	char *buf, *bp;
38650Sstevel@tonic-gate 	char *response;
38660Sstevel@tonic-gate 	char *sockinfo = NULL;
38670Sstevel@tonic-gate 	ssize_t s;
38680Sstevel@tonic-gate # if NETINET6
38690Sstevel@tonic-gate 	char buf6[INET6_ADDRSTRLEN];
38700Sstevel@tonic-gate # endif /* NETINET6 */
38710Sstevel@tonic-gate 
38720Sstevel@tonic-gate 	if (tTd(64, 10))
38730Sstevel@tonic-gate 		sm_dprintf("milter_connect(%s)\n", hostname);
38740Sstevel@tonic-gate 	if (MilterLogLevel > 9)
38750Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
38760Sstevel@tonic-gate 
38770Sstevel@tonic-gate 	/* gather data */
38780Sstevel@tonic-gate 	switch (addr.sa.sa_family)
38790Sstevel@tonic-gate 	{
38800Sstevel@tonic-gate # if NETUNIX
38810Sstevel@tonic-gate 	  case AF_UNIX:
38820Sstevel@tonic-gate 		family = SMFIA_UNIX;
38830Sstevel@tonic-gate 		port = htons(0);
38840Sstevel@tonic-gate 		sockinfo = addr.sunix.sun_path;
38850Sstevel@tonic-gate 		break;
38860Sstevel@tonic-gate # endif /* NETUNIX */
38870Sstevel@tonic-gate 
38880Sstevel@tonic-gate # if NETINET
38890Sstevel@tonic-gate 	  case AF_INET:
38900Sstevel@tonic-gate 		family = SMFIA_INET;
38910Sstevel@tonic-gate 		port = addr.sin.sin_port;
38920Sstevel@tonic-gate 		sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
38930Sstevel@tonic-gate 		break;
38940Sstevel@tonic-gate # endif /* NETINET */
38950Sstevel@tonic-gate 
38960Sstevel@tonic-gate # if NETINET6
38970Sstevel@tonic-gate 	  case AF_INET6:
38980Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
38990Sstevel@tonic-gate 			family = SMFIA_INET;
39000Sstevel@tonic-gate 		else
39010Sstevel@tonic-gate 			family = SMFIA_INET6;
39020Sstevel@tonic-gate 		port = addr.sin6.sin6_port;
39030Sstevel@tonic-gate 		sockinfo = anynet_ntop(&addr.sin6.sin6_addr, buf6,
39043544Sjbeck 				       sizeof(buf6));
39050Sstevel@tonic-gate 		if (sockinfo == NULL)
39060Sstevel@tonic-gate 			sockinfo = "";
39070Sstevel@tonic-gate 		break;
39080Sstevel@tonic-gate # endif /* NETINET6 */
39090Sstevel@tonic-gate 
39100Sstevel@tonic-gate 	  default:
39110Sstevel@tonic-gate 		family = SMFIA_UNKNOWN;
39120Sstevel@tonic-gate 		break;
39130Sstevel@tonic-gate 	}
39140Sstevel@tonic-gate 
39150Sstevel@tonic-gate 	s = strlen(hostname) + 1 + sizeof(family);
39160Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39170Sstevel@tonic-gate 		s += sizeof(port) + strlen(sockinfo) + 1;
39180Sstevel@tonic-gate 
39190Sstevel@tonic-gate 	buf = (char *) xalloc(s);
39200Sstevel@tonic-gate 	bp = buf;
39210Sstevel@tonic-gate 
39220Sstevel@tonic-gate 	/* put together data */
39230Sstevel@tonic-gate 	(void) memcpy(bp, hostname, strlen(hostname));
39240Sstevel@tonic-gate 	bp += strlen(hostname);
39250Sstevel@tonic-gate 	*bp++ = '\0';
39263544Sjbeck 	(void) memcpy(bp, &family, sizeof(family));
39273544Sjbeck 	bp += sizeof(family);
39280Sstevel@tonic-gate 	if (family != SMFIA_UNKNOWN)
39290Sstevel@tonic-gate 	{
39303544Sjbeck 		(void) memcpy(bp, &port, sizeof(port));
39313544Sjbeck 		bp += sizeof(port);
39320Sstevel@tonic-gate 
39330Sstevel@tonic-gate 		/* include trailing '\0' */
39340Sstevel@tonic-gate 		(void) memcpy(bp, sockinfo, strlen(sockinfo) + 1);
39350Sstevel@tonic-gate 	}
39360Sstevel@tonic-gate 
39373544Sjbeck 	response = milter_command(SMFIC_CONNECT, buf, s, MilterConnectMacros,
39383544Sjbeck 				e, state, "connect", false);
39390Sstevel@tonic-gate 	sm_free(buf); /* XXX */
39400Sstevel@tonic-gate 
39410Sstevel@tonic-gate 	/*
39420Sstevel@tonic-gate 	**  If this message connection is done for,
39430Sstevel@tonic-gate 	**  close the filters.
39440Sstevel@tonic-gate 	*/
39450Sstevel@tonic-gate 
39460Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE)
39470Sstevel@tonic-gate 	{
39480Sstevel@tonic-gate 		if (MilterLogLevel > 9)
39490Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
39500Sstevel@tonic-gate 		milter_quit(e);
39510Sstevel@tonic-gate 	}
39520Sstevel@tonic-gate 	else
39530Sstevel@tonic-gate 		milter_per_connection_check(e);
39540Sstevel@tonic-gate 
39550Sstevel@tonic-gate 	/*
39560Sstevel@tonic-gate 	**  SMFIR_REPLYCODE can't work with connect due to
39570Sstevel@tonic-gate 	**  the requirements of SMTP.  Therefore, ignore the
39580Sstevel@tonic-gate 	**  reply code text but keep the state it would reflect.
39590Sstevel@tonic-gate 	*/
39600Sstevel@tonic-gate 
39610Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE)
39620Sstevel@tonic-gate 	{
39630Sstevel@tonic-gate 		if (response != NULL &&
39640Sstevel@tonic-gate 		    *response == '4')
39650Sstevel@tonic-gate 		{
39660Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
39670Sstevel@tonic-gate 				*state = SMFIR_SHUTDOWN;
39680Sstevel@tonic-gate 			else
39690Sstevel@tonic-gate 				*state = SMFIR_TEMPFAIL;
39700Sstevel@tonic-gate 		}
39710Sstevel@tonic-gate 		else
39720Sstevel@tonic-gate 			*state = SMFIR_REJECT;
39730Sstevel@tonic-gate 		if (response != NULL)
39740Sstevel@tonic-gate 		{
39750Sstevel@tonic-gate 			sm_free(response); /* XXX */
39760Sstevel@tonic-gate 			response = NULL;
39770Sstevel@tonic-gate 		}
39780Sstevel@tonic-gate 	}
39790Sstevel@tonic-gate 	return response;
39800Sstevel@tonic-gate }
39813544Sjbeck 
39820Sstevel@tonic-gate /*
39830Sstevel@tonic-gate **  MILTER_HELO -- send SMTP HELO/EHLO command info to milter filters
39840Sstevel@tonic-gate **
39850Sstevel@tonic-gate **	Parameters:
39860Sstevel@tonic-gate **		helo -- argument to SMTP HELO/EHLO command.
39870Sstevel@tonic-gate **		e -- current envelope.
39880Sstevel@tonic-gate **		state -- return state from response.
39890Sstevel@tonic-gate **
39900Sstevel@tonic-gate **	Returns:
39910Sstevel@tonic-gate **		response string (may be NULL)
39920Sstevel@tonic-gate */
39930Sstevel@tonic-gate 
39940Sstevel@tonic-gate char *
39950Sstevel@tonic-gate milter_helo(helo, e, state)
39960Sstevel@tonic-gate 	char *helo;
39970Sstevel@tonic-gate 	ENVELOPE *e;
39980Sstevel@tonic-gate 	char *state;
39990Sstevel@tonic-gate {
40000Sstevel@tonic-gate 	int i;
40010Sstevel@tonic-gate 	char *response;
40020Sstevel@tonic-gate 
40030Sstevel@tonic-gate 	if (tTd(64, 10))
40040Sstevel@tonic-gate 		sm_dprintf("milter_helo(%s)\n", helo);
40050Sstevel@tonic-gate 
40060Sstevel@tonic-gate 	/* HELO/EHLO can come at any point */
40070Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40080Sstevel@tonic-gate 	{
40090Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40100Sstevel@tonic-gate 
40110Sstevel@tonic-gate 		switch (m->mf_state)
40120Sstevel@tonic-gate 		{
40130Sstevel@tonic-gate 		  case SMFS_INMSG:
40140Sstevel@tonic-gate 			/* abort in message filters */
40150Sstevel@tonic-gate 			milter_abort_filter(m, e);
40160Sstevel@tonic-gate 			/* FALLTHROUGH */
40170Sstevel@tonic-gate 
40180Sstevel@tonic-gate 		  case SMFS_DONE:
40190Sstevel@tonic-gate 			/* reset done filters */
40200Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
40210Sstevel@tonic-gate 			break;
40220Sstevel@tonic-gate 		}
40230Sstevel@tonic-gate 	}
40240Sstevel@tonic-gate 
40250Sstevel@tonic-gate 	response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
40263544Sjbeck 				  MilterHeloMacros, e, state, "helo", false);
40270Sstevel@tonic-gate 	milter_per_connection_check(e);
40280Sstevel@tonic-gate 	return response;
40290Sstevel@tonic-gate }
40303544Sjbeck 
40310Sstevel@tonic-gate /*
40320Sstevel@tonic-gate **  MILTER_ENVFROM -- send SMTP MAIL command info to milter filters
40330Sstevel@tonic-gate **
40340Sstevel@tonic-gate **	Parameters:
40350Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == sender).
40360Sstevel@tonic-gate **		e -- current envelope.
40370Sstevel@tonic-gate **		state -- return state from response.
40380Sstevel@tonic-gate **
40390Sstevel@tonic-gate **	Returns:
40400Sstevel@tonic-gate **		response string (may be NULL)
40410Sstevel@tonic-gate */
40420Sstevel@tonic-gate 
40430Sstevel@tonic-gate char *
40440Sstevel@tonic-gate milter_envfrom(args, e, state)
40450Sstevel@tonic-gate 	char **args;
40460Sstevel@tonic-gate 	ENVELOPE *e;
40470Sstevel@tonic-gate 	char *state;
40480Sstevel@tonic-gate {
40490Sstevel@tonic-gate 	int i;
40500Sstevel@tonic-gate 	char *buf, *bp;
40510Sstevel@tonic-gate 	char *response;
40520Sstevel@tonic-gate 	ssize_t s;
40530Sstevel@tonic-gate 
40540Sstevel@tonic-gate 	if (tTd(64, 10))
40550Sstevel@tonic-gate 	{
40560Sstevel@tonic-gate 		sm_dprintf("milter_envfrom:");
40570Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
40580Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
40590Sstevel@tonic-gate 		sm_dprintf("\n");
40600Sstevel@tonic-gate 	}
40610Sstevel@tonic-gate 
40620Sstevel@tonic-gate 	/* sanity check */
40630Sstevel@tonic-gate 	if (args[0] == NULL)
40640Sstevel@tonic-gate 	{
40650Sstevel@tonic-gate 		*state = SMFIR_REJECT;
40660Sstevel@tonic-gate 		if (MilterLogLevel > 10)
40670Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
40680Sstevel@tonic-gate 				  "Milter: reject, no sender");
40690Sstevel@tonic-gate 		return NULL;
40700Sstevel@tonic-gate 	}
40710Sstevel@tonic-gate 
40720Sstevel@tonic-gate 	/* new message, so ... */
40730Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
40740Sstevel@tonic-gate 	{
40750Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
40760Sstevel@tonic-gate 
40770Sstevel@tonic-gate 		switch (m->mf_state)
40780Sstevel@tonic-gate 		{
40790Sstevel@tonic-gate 		  case SMFS_INMSG:
40800Sstevel@tonic-gate 			/* abort in message filters */
40810Sstevel@tonic-gate 			milter_abort_filter(m, e);
40820Sstevel@tonic-gate 			/* FALLTHROUGH */
40830Sstevel@tonic-gate 
40840Sstevel@tonic-gate 		  case SMFS_DONE:
40850Sstevel@tonic-gate 			/* reset done filters */
40860Sstevel@tonic-gate 			m->mf_state = SMFS_OPEN;
40870Sstevel@tonic-gate 			break;
40880Sstevel@tonic-gate 		}
40890Sstevel@tonic-gate 	}
40900Sstevel@tonic-gate 
40910Sstevel@tonic-gate 	/* put together data */
40920Sstevel@tonic-gate 	s = 0;
40930Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
40940Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
40950Sstevel@tonic-gate 
40960Sstevel@tonic-gate 	if (s < 0)
40970Sstevel@tonic-gate 	{
40980Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
40990Sstevel@tonic-gate 		return NULL;
41000Sstevel@tonic-gate 	}
41010Sstevel@tonic-gate 
41020Sstevel@tonic-gate 	buf = (char *) xalloc(s);
41030Sstevel@tonic-gate 	bp = buf;
41040Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41050Sstevel@tonic-gate 	{
41060Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
41070Sstevel@tonic-gate 		bp += strlen(bp) + 1;
41080Sstevel@tonic-gate 	}
41090Sstevel@tonic-gate 
41100Sstevel@tonic-gate 	if (MilterLogLevel > 14)
41113544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
41120Sstevel@tonic-gate 
41130Sstevel@tonic-gate 	/* send it over */
41143544Sjbeck 	response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros,
41153544Sjbeck 				e, state, "mail", false);
41160Sstevel@tonic-gate 	sm_free(buf); /* XXX */
41170Sstevel@tonic-gate 
41180Sstevel@tonic-gate 	/*
41190Sstevel@tonic-gate 	**  If filter rejects/discards a per message command,
41200Sstevel@tonic-gate 	**  abort the other filters since we are done with the
41210Sstevel@tonic-gate 	**  current message.
41220Sstevel@tonic-gate 	*/
41230Sstevel@tonic-gate 
41240Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
41250Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
41263544Sjbeck 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
41270Sstevel@tonic-gate 	return response;
41280Sstevel@tonic-gate }
41290Sstevel@tonic-gate 
41300Sstevel@tonic-gate /*
41310Sstevel@tonic-gate **  MILTER_ENVRCPT -- send SMTP RCPT command info to milter filters
41320Sstevel@tonic-gate **
41330Sstevel@tonic-gate **	Parameters:
41340Sstevel@tonic-gate **		args -- SMTP MAIL command args (args[0] == recipient).
41350Sstevel@tonic-gate **		e -- current envelope.
41360Sstevel@tonic-gate **		state -- return state from response.
41373544Sjbeck **		rcpt_error -- does RCPT have an error?
41380Sstevel@tonic-gate **
41390Sstevel@tonic-gate **	Returns:
41400Sstevel@tonic-gate **		response string (may be NULL)
41410Sstevel@tonic-gate */
41420Sstevel@tonic-gate 
41430Sstevel@tonic-gate char *
41443544Sjbeck milter_envrcpt(args, e, state, rcpt_error)
41450Sstevel@tonic-gate 	char **args;
41460Sstevel@tonic-gate 	ENVELOPE *e;
41470Sstevel@tonic-gate 	char *state;
41483544Sjbeck 	bool rcpt_error;
41490Sstevel@tonic-gate {
41500Sstevel@tonic-gate 	int i;
41510Sstevel@tonic-gate 	char *buf, *bp;
41520Sstevel@tonic-gate 	char *response;
41530Sstevel@tonic-gate 	ssize_t s;
41540Sstevel@tonic-gate 
41550Sstevel@tonic-gate 	if (tTd(64, 10))
41560Sstevel@tonic-gate 	{
41570Sstevel@tonic-gate 		sm_dprintf("milter_envrcpt:");
41580Sstevel@tonic-gate 		for (i = 0; args[i] != NULL; i++)
41590Sstevel@tonic-gate 			sm_dprintf(" %s", args[i]);
41600Sstevel@tonic-gate 		sm_dprintf("\n");
41610Sstevel@tonic-gate 	}
41620Sstevel@tonic-gate 
41630Sstevel@tonic-gate 	/* sanity check */
41640Sstevel@tonic-gate 	if (args[0] == NULL)
41650Sstevel@tonic-gate 	{
41660Sstevel@tonic-gate 		*state = SMFIR_REJECT;
41670Sstevel@tonic-gate 		if (MilterLogLevel > 10)
41680Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
41690Sstevel@tonic-gate 		return NULL;
41700Sstevel@tonic-gate 	}
41710Sstevel@tonic-gate 
41720Sstevel@tonic-gate 	/* put together data */
41730Sstevel@tonic-gate 	s = 0;
41740Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41750Sstevel@tonic-gate 		s += strlen(args[i]) + 1;
41760Sstevel@tonic-gate 
41770Sstevel@tonic-gate 	if (s < 0)
41780Sstevel@tonic-gate 	{
41790Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL;
41800Sstevel@tonic-gate 		return NULL;
41810Sstevel@tonic-gate 	}
41820Sstevel@tonic-gate 
41830Sstevel@tonic-gate 	buf = (char *) xalloc(s);
41840Sstevel@tonic-gate 	bp = buf;
41850Sstevel@tonic-gate 	for (i = 0; args[i] != NULL; i++)
41860Sstevel@tonic-gate 	{
41870Sstevel@tonic-gate 		(void) sm_strlcpy(bp, args[i], s - (bp - buf));
41880Sstevel@tonic-gate 		bp += strlen(bp) + 1;
41890Sstevel@tonic-gate 	}
41900Sstevel@tonic-gate 
41910Sstevel@tonic-gate 	if (MilterLogLevel > 14)
41920Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
41930Sstevel@tonic-gate 
41940Sstevel@tonic-gate 	/* send it over */
41953544Sjbeck 	response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros,
41963544Sjbeck 				e, state, "rcpt", rcpt_error);
41970Sstevel@tonic-gate 	sm_free(buf); /* XXX */
41980Sstevel@tonic-gate 	return response;
41990Sstevel@tonic-gate }
42000Sstevel@tonic-gate 
42010Sstevel@tonic-gate /*
42020Sstevel@tonic-gate **  MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
42030Sstevel@tonic-gate **
42040Sstevel@tonic-gate **	Parameters:
42050Sstevel@tonic-gate **		e -- current envelope.
42060Sstevel@tonic-gate **		state -- return state from response.
42070Sstevel@tonic-gate **
42080Sstevel@tonic-gate **	Returns:
42090Sstevel@tonic-gate **		response string (may be NULL)
42100Sstevel@tonic-gate */
42110Sstevel@tonic-gate 
42120Sstevel@tonic-gate char *
42130Sstevel@tonic-gate milter_data_cmd(e, state)
42140Sstevel@tonic-gate 	ENVELOPE *e;
42150Sstevel@tonic-gate 	char *state;
42160Sstevel@tonic-gate {
42170Sstevel@tonic-gate 	if (tTd(64, 10))
42180Sstevel@tonic-gate 		sm_dprintf("milter_data_cmd\n");
42190Sstevel@tonic-gate 
42200Sstevel@tonic-gate 	/* send it over */
42213544Sjbeck 	return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state,
42223544Sjbeck 				"data", false);
42230Sstevel@tonic-gate }
42240Sstevel@tonic-gate 
42250Sstevel@tonic-gate /*
42260Sstevel@tonic-gate **  MILTER_DATA -- send message headers/body and gather final message results
42270Sstevel@tonic-gate **
42280Sstevel@tonic-gate **	Parameters:
42290Sstevel@tonic-gate **		e -- current envelope.
42300Sstevel@tonic-gate **		state -- return state from response.
42310Sstevel@tonic-gate **
42320Sstevel@tonic-gate **	Returns:
42330Sstevel@tonic-gate **		response string (may be NULL)
42340Sstevel@tonic-gate **
42350Sstevel@tonic-gate **	Side effects:
42360Sstevel@tonic-gate **		- Uses e->e_dfp for access to the body
42370Sstevel@tonic-gate **		- Can call the various milter action routines to
42380Sstevel@tonic-gate **		  modify the envelope or message.
42390Sstevel@tonic-gate */
42400Sstevel@tonic-gate 
42410Sstevel@tonic-gate # define MILTER_CHECK_RESULTS() \
42420Sstevel@tonic-gate 	if (*state == SMFIR_ACCEPT || \
42430Sstevel@tonic-gate 	    m->mf_state == SMFS_DONE || \
42440Sstevel@tonic-gate 	    m->mf_state == SMFS_ERROR) \
42450Sstevel@tonic-gate 	{ \
42460Sstevel@tonic-gate 		if (m->mf_state != SMFS_ERROR) \
42470Sstevel@tonic-gate 			m->mf_state = SMFS_DONE; \
42480Sstevel@tonic-gate 		continue;	/* to next filter */ \
42490Sstevel@tonic-gate 	} \
42500Sstevel@tonic-gate 	if (*state != SMFIR_CONTINUE) \
42510Sstevel@tonic-gate 	{ \
42520Sstevel@tonic-gate 		m->mf_state = SMFS_DONE; \
42530Sstevel@tonic-gate 		goto finishup; \
42540Sstevel@tonic-gate 	}
42550Sstevel@tonic-gate 
42560Sstevel@tonic-gate char *
42570Sstevel@tonic-gate milter_data(e, state)
42580Sstevel@tonic-gate 	ENVELOPE *e;
42590Sstevel@tonic-gate 	char *state;
42600Sstevel@tonic-gate {
42610Sstevel@tonic-gate 	bool replbody = false;		/* milter_replbody() called? */
42620Sstevel@tonic-gate 	bool replfailed = false;	/* milter_replbody() failed? */
42630Sstevel@tonic-gate 	bool rewind = false;		/* rewind data file? */
42640Sstevel@tonic-gate 	bool dfopen = false;		/* data file open for writing? */
42650Sstevel@tonic-gate 	bool newfilter;			/* reset on each new filter */
42660Sstevel@tonic-gate 	char rcmd;
42670Sstevel@tonic-gate 	int i;
42680Sstevel@tonic-gate 	int save_errno;
42690Sstevel@tonic-gate 	char *response = NULL;
42700Sstevel@tonic-gate 	time_t eomsent;
42710Sstevel@tonic-gate 	ssize_t rlen;
42720Sstevel@tonic-gate 
42730Sstevel@tonic-gate 	if (tTd(64, 10))
42740Sstevel@tonic-gate 		sm_dprintf("milter_data\n");
42750Sstevel@tonic-gate 
42760Sstevel@tonic-gate 	*state = SMFIR_CONTINUE;
42770Sstevel@tonic-gate 
42780Sstevel@tonic-gate 	/*
42790Sstevel@tonic-gate 	**  XXX: Should actually send body chunks to each filter
42800Sstevel@tonic-gate 	**  a chunk at a time instead of sending the whole body to
42810Sstevel@tonic-gate 	**  each filter in turn.  However, only if the filters don't
42820Sstevel@tonic-gate 	**  change the body.
42830Sstevel@tonic-gate 	*/
42840Sstevel@tonic-gate 
42850Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
42860Sstevel@tonic-gate 	{
42870Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
42880Sstevel@tonic-gate 
42890Sstevel@tonic-gate 		if (*state != SMFIR_CONTINUE &&
42900Sstevel@tonic-gate 		    *state != SMFIR_ACCEPT)
42910Sstevel@tonic-gate 		{
42920Sstevel@tonic-gate 			/*
42930Sstevel@tonic-gate 			**  A previous filter has dealt with the message,
42940Sstevel@tonic-gate 			**  safe to stop processing the filters.
42950Sstevel@tonic-gate 			*/
42960Sstevel@tonic-gate 
42970Sstevel@tonic-gate 			break;
42980Sstevel@tonic-gate 		}
42990Sstevel@tonic-gate 
43000Sstevel@tonic-gate 		/* Now reset state for later evaluation */
43010Sstevel@tonic-gate 		*state = SMFIR_CONTINUE;
43020Sstevel@tonic-gate 		newfilter = true;
43030Sstevel@tonic-gate 
43040Sstevel@tonic-gate 		/* previous problem? */
43050Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
43060Sstevel@tonic-gate 		{
43070Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
43080Sstevel@tonic-gate 			break;
43090Sstevel@tonic-gate 		}
43100Sstevel@tonic-gate 
43110Sstevel@tonic-gate 		/* sanity checks */
43120Sstevel@tonic-gate 		if (m->mf_sock < 0 ||
43130Sstevel@tonic-gate 		    (m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
43140Sstevel@tonic-gate 			continue;
43150Sstevel@tonic-gate 
43160Sstevel@tonic-gate 		m->mf_state = SMFS_INMSG;
43170Sstevel@tonic-gate 
43180Sstevel@tonic-gate 		/* check if filter wants the headers */
43190Sstevel@tonic-gate 		if (!bitset(SMFIP_NOHDRS, m->mf_pflags))
43200Sstevel@tonic-gate 		{
43210Sstevel@tonic-gate 			response = milter_headers(m, e, state);
43220Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43230Sstevel@tonic-gate 		}
43240Sstevel@tonic-gate 
43250Sstevel@tonic-gate 		/* check if filter wants EOH */
43260Sstevel@tonic-gate 		if (!bitset(SMFIP_NOEOH, m->mf_pflags))
43270Sstevel@tonic-gate 		{
43280Sstevel@tonic-gate 			if (tTd(64, 10))
43290Sstevel@tonic-gate 				sm_dprintf("milter_data: eoh\n");
43300Sstevel@tonic-gate 
43313544Sjbeck 			if (MilterEOHMacros[0] != NULL)
43323544Sjbeck 			{
43333544Sjbeck 				milter_send_macros(m, MilterEOHMacros,
43343544Sjbeck 					   SMFIC_EOH, e);
43353544Sjbeck 				MILTER_CHECK_RESULTS();
43363544Sjbeck 			}
43373544Sjbeck 
43380Sstevel@tonic-gate 			/* send it over */
43390Sstevel@tonic-gate 			response = milter_send_command(m, SMFIC_EOH, NULL, 0,
43403544Sjbeck 						       e, state, "eoh");
43410Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43420Sstevel@tonic-gate 		}
43430Sstevel@tonic-gate 
43440Sstevel@tonic-gate 		/* check if filter wants the body */
43450Sstevel@tonic-gate 		if (!bitset(SMFIP_NOBODY, m->mf_pflags) &&
43460Sstevel@tonic-gate 		    e->e_dfp != NULL)
43470Sstevel@tonic-gate 		{
43480Sstevel@tonic-gate 			rewind = true;
43490Sstevel@tonic-gate 			response = milter_body(m, e, state);
43500Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43510Sstevel@tonic-gate 		}
43520Sstevel@tonic-gate 
43530Sstevel@tonic-gate 		if (MilterEOMMacros[0] != NULL)
43540Sstevel@tonic-gate 		{
43550Sstevel@tonic-gate 			milter_send_macros(m, MilterEOMMacros,
43560Sstevel@tonic-gate 					   SMFIC_BODYEOB, e);
43570Sstevel@tonic-gate 			MILTER_CHECK_RESULTS();
43580Sstevel@tonic-gate 		}
43590Sstevel@tonic-gate 
43600Sstevel@tonic-gate 		/* send the final body chunk */
43610Sstevel@tonic-gate 		(void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
43623544Sjbeck 				    m->mf_timeout[SMFTO_WRITE], e, "eom");
43630Sstevel@tonic-gate 
43640Sstevel@tonic-gate 		/* Get time EOM sent for timeout */
43650Sstevel@tonic-gate 		eomsent = curtime();
43660Sstevel@tonic-gate 
43670Sstevel@tonic-gate 		/* deal with the possibility of multiple responses */
43680Sstevel@tonic-gate 		while (*state == SMFIR_CONTINUE)
43690Sstevel@tonic-gate 		{
43700Sstevel@tonic-gate 			/* Check total timeout from EOM to final ACK/NAK */
43710Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_EOM] > 0 &&
43720Sstevel@tonic-gate 			    curtime() - eomsent >= m->mf_timeout[SMFTO_EOM])
43730Sstevel@tonic-gate 			{
43740Sstevel@tonic-gate 				if (tTd(64, 5))
43750Sstevel@tonic-gate 					sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
43760Sstevel@tonic-gate 						m->mf_name);
43770Sstevel@tonic-gate 				if (MilterLogLevel > 0)
43780Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
43790Sstevel@tonic-gate 						  "milter_data(%s): EOM ACK/NAK timeout",
43800Sstevel@tonic-gate 						  m->mf_name);
43810Sstevel@tonic-gate 				milter_error(m, e);
43820Sstevel@tonic-gate 				MILTER_CHECK_ERROR(false, break);
43830Sstevel@tonic-gate 				break;
43840Sstevel@tonic-gate 			}
43850Sstevel@tonic-gate 
43860Sstevel@tonic-gate 			response = milter_read(m, &rcmd, &rlen,
43873544Sjbeck 					       m->mf_timeout[SMFTO_READ], e,
43883544Sjbeck 						"body");
43890Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
43900Sstevel@tonic-gate 				break;
43910Sstevel@tonic-gate 
43920Sstevel@tonic-gate 			if (tTd(64, 10))
43930Sstevel@tonic-gate 				sm_dprintf("milter_data(%s): state %c\n",
43940Sstevel@tonic-gate 					   m->mf_name, (char) rcmd);
43950Sstevel@tonic-gate 
43960Sstevel@tonic-gate 			switch (rcmd)
43970Sstevel@tonic-gate 			{
43980Sstevel@tonic-gate 			  case SMFIR_REPLYCODE:
43990Sstevel@tonic-gate 				MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
44000Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44010Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
44020Sstevel@tonic-gate 						  m->mf_name, response);
44030Sstevel@tonic-gate 				*state = rcmd;
44040Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44050Sstevel@tonic-gate 				break;
44060Sstevel@tonic-gate 
44070Sstevel@tonic-gate 			  case SMFIR_REJECT: /* log msg at end of function */
44080Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44090Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
44100Sstevel@tonic-gate 						  m->mf_name);
44110Sstevel@tonic-gate 				*state = rcmd;
44120Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44130Sstevel@tonic-gate 				break;
44140Sstevel@tonic-gate 
44150Sstevel@tonic-gate 			  case SMFIR_DISCARD:
44160Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44170Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
44180Sstevel@tonic-gate 						  m->mf_name);
44190Sstevel@tonic-gate 				*state = rcmd;
44200Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44210Sstevel@tonic-gate 				break;
44220Sstevel@tonic-gate 
44230Sstevel@tonic-gate 			  case SMFIR_TEMPFAIL:
44240Sstevel@tonic-gate 				if (MilterLogLevel > 12)
44250Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
44260Sstevel@tonic-gate 						  m->mf_name);
44270Sstevel@tonic-gate 				*state = rcmd;
44280Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44290Sstevel@tonic-gate 				break;
44300Sstevel@tonic-gate 
44310Sstevel@tonic-gate 			  case SMFIR_CONTINUE:
44320Sstevel@tonic-gate 			  case SMFIR_ACCEPT:
44330Sstevel@tonic-gate 				/* this filter is done with message */
44340Sstevel@tonic-gate 				if (replfailed)
44350Sstevel@tonic-gate 					*state = SMFIR_TEMPFAIL;
44360Sstevel@tonic-gate 				else
44370Sstevel@tonic-gate 					*state = SMFIR_ACCEPT;
44380Sstevel@tonic-gate 				m->mf_state = SMFS_DONE;
44390Sstevel@tonic-gate 				break;
44400Sstevel@tonic-gate 
44410Sstevel@tonic-gate 			  case SMFIR_PROGRESS:
44420Sstevel@tonic-gate 				break;
44430Sstevel@tonic-gate 
44440Sstevel@tonic-gate 			  case SMFIR_QUARANTINE:
44450Sstevel@tonic-gate 				if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
44460Sstevel@tonic-gate 				{
44470Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44480Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44490Sstevel@tonic-gate 							  "milter_data(%s): lied about quarantining, honoring request anyway",
44500Sstevel@tonic-gate 							  m->mf_name);
44510Sstevel@tonic-gate 				}
44520Sstevel@tonic-gate 				if (response == NULL)
44530Sstevel@tonic-gate 					response = newstr("");
44540Sstevel@tonic-gate 				if (MilterLogLevel > 3)
44550Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
44560Sstevel@tonic-gate 						  "milter=%s, quarantine=%s",
44570Sstevel@tonic-gate 						  m->mf_name, response);
44580Sstevel@tonic-gate 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
44590Sstevel@tonic-gate 								 response);
44600Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
44610Sstevel@tonic-gate 					  macid("{quarantine}"), e->e_quarmsg);
44620Sstevel@tonic-gate 				break;
44630Sstevel@tonic-gate 
44640Sstevel@tonic-gate 			  case SMFIR_ADDHEADER:
44650Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
44660Sstevel@tonic-gate 				{
44670Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44680Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44690Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
44700Sstevel@tonic-gate 							  m->mf_name);
44710Sstevel@tonic-gate 				}
44723544Sjbeck 				milter_addheader(m, response, rlen, e);
44730Sstevel@tonic-gate 				break;
44740Sstevel@tonic-gate 
44750Sstevel@tonic-gate 			  case SMFIR_INSHEADER:
44760Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
44770Sstevel@tonic-gate 				{
44780Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44790Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44800Sstevel@tonic-gate 							  "milter_data(%s): lied about adding headers, honoring request anyway",
44810Sstevel@tonic-gate 							  m->mf_name);
44820Sstevel@tonic-gate 				}
44833544Sjbeck 				milter_insheader(m, response, rlen, e);
44840Sstevel@tonic-gate 				break;
44850Sstevel@tonic-gate 
44860Sstevel@tonic-gate 			  case SMFIR_CHGHEADER:
44870Sstevel@tonic-gate 				if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
44880Sstevel@tonic-gate 				{
44890Sstevel@tonic-gate 					if (MilterLogLevel > 9)
44900Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
44910Sstevel@tonic-gate 							  "milter_data(%s): lied about changing headers, honoring request anyway",
44920Sstevel@tonic-gate 							  m->mf_name);
44930Sstevel@tonic-gate 				}
44943544Sjbeck 				milter_changeheader(m, response, rlen, e);
44953544Sjbeck 				break;
44963544Sjbeck 
44973544Sjbeck 			  case SMFIR_CHGFROM:
44983544Sjbeck 				if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
44993544Sjbeck 				{
45003544Sjbeck 					if (MilterLogLevel > 9)
45013544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
45023544Sjbeck 							  "milter_data(%s) lied about changing sender, honoring request anyway",
45033544Sjbeck 							  m->mf_name);
45043544Sjbeck 				}
45053544Sjbeck 				milter_chgfrom(response, rlen, e);
45060Sstevel@tonic-gate 				break;
45070Sstevel@tonic-gate 
45080Sstevel@tonic-gate 			  case SMFIR_ADDRCPT:
45090Sstevel@tonic-gate 				if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
45100Sstevel@tonic-gate 				{
45110Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45120Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45130Sstevel@tonic-gate 							  "milter_data(%s) lied about adding recipients, honoring request anyway",
45140Sstevel@tonic-gate 							  m->mf_name);
45150Sstevel@tonic-gate 				}
45160Sstevel@tonic-gate 				milter_addrcpt(response, rlen, e);
45170Sstevel@tonic-gate 				break;
45180Sstevel@tonic-gate 
45193544Sjbeck 			  case SMFIR_ADDRCPT_PAR:
45203544Sjbeck 				if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
45213544Sjbeck 				{
45223544Sjbeck 					if (MilterLogLevel > 9)
45233544Sjbeck 						sm_syslog(LOG_WARNING, e->e_id,
45243544Sjbeck 							  "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
45253544Sjbeck 							  m->mf_name);
45263544Sjbeck 				}
45273544Sjbeck 				milter_addrcpt_par(response, rlen, e);
45283544Sjbeck 				break;
45293544Sjbeck 
45300Sstevel@tonic-gate 			  case SMFIR_DELRCPT:
45310Sstevel@tonic-gate 				if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
45320Sstevel@tonic-gate 				{
45330Sstevel@tonic-gate 					if (MilterLogLevel > 9)
45340Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
45350Sstevel@tonic-gate 							  "milter_data(%s): lied about removing recipients, honoring request anyway",
45360Sstevel@tonic-gate 							  m->mf_name);
45370Sstevel@tonic-gate 				}
45380Sstevel@tonic-gate 				milter_delrcpt(response, rlen, e);
45390Sstevel@tonic-gate 				break;
45400Sstevel@tonic-gate 
45410Sstevel@tonic-gate 			  case SMFIR_REPLBODY:
45420Sstevel@tonic-gate 				if (!bitset(SMFIF_MODBODY, m->mf_fflags))
45430Sstevel@tonic-gate 				{
45440Sstevel@tonic-gate 					if (MilterLogLevel > 0)
45450Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
45460Sstevel@tonic-gate 							  "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
45470Sstevel@tonic-gate 							  m->mf_name);
45480Sstevel@tonic-gate 					replfailed = true;
45490Sstevel@tonic-gate 					break;
45500Sstevel@tonic-gate 				}
45510Sstevel@tonic-gate 
45520Sstevel@tonic-gate 				/* already failed in attempt */
45530Sstevel@tonic-gate 				if (replfailed)
45540Sstevel@tonic-gate 					break;
45550Sstevel@tonic-gate 
45560Sstevel@tonic-gate 				if (!dfopen)
45570Sstevel@tonic-gate 				{
45580Sstevel@tonic-gate 					if (milter_reopen_df(e) < 0)
45590Sstevel@tonic-gate 					{
45600Sstevel@tonic-gate 						replfailed = true;
45610Sstevel@tonic-gate 						break;
45620Sstevel@tonic-gate 					}
45630Sstevel@tonic-gate 					dfopen = true;
45640Sstevel@tonic-gate 					rewind = true;
45650Sstevel@tonic-gate 				}
45660Sstevel@tonic-gate 
45670Sstevel@tonic-gate 				if (milter_replbody(response, rlen,
45680Sstevel@tonic-gate 						    newfilter, e) < 0)
45690Sstevel@tonic-gate 					replfailed = true;
45700Sstevel@tonic-gate 				newfilter = false;
45710Sstevel@tonic-gate 				replbody = true;
45720Sstevel@tonic-gate 				break;
45730Sstevel@tonic-gate 
45740Sstevel@tonic-gate 			  default:
45750Sstevel@tonic-gate 				/* Invalid response to command */
45760Sstevel@tonic-gate 				if (MilterLogLevel > 0)
45770Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
45780Sstevel@tonic-gate 						  "milter_data(%s): returned bogus response %c",
45790Sstevel@tonic-gate 						  m->mf_name, rcmd);
45800Sstevel@tonic-gate 				milter_error(m, e);
45810Sstevel@tonic-gate 				break;
45820Sstevel@tonic-gate 			}
45830Sstevel@tonic-gate 			if (rcmd != SMFIR_REPLYCODE && response != NULL)
45840Sstevel@tonic-gate 			{
45850Sstevel@tonic-gate 				sm_free(response); /* XXX */
45860Sstevel@tonic-gate 				response = NULL;
45870Sstevel@tonic-gate 			}
45880Sstevel@tonic-gate 
45890Sstevel@tonic-gate 			if (m->mf_state == SMFS_ERROR)
45900Sstevel@tonic-gate 				break;
45910Sstevel@tonic-gate 		}
45920Sstevel@tonic-gate 
45930Sstevel@tonic-gate 		if (replbody && !replfailed)
45940Sstevel@tonic-gate 		{
45950Sstevel@tonic-gate 			/* flush possible buffered character */
45960Sstevel@tonic-gate 			milter_replbody(NULL, 0, !replbody, e);
45970Sstevel@tonic-gate 			replbody = false;
45980Sstevel@tonic-gate 		}
45990Sstevel@tonic-gate 
46000Sstevel@tonic-gate 		if (m->mf_state == SMFS_ERROR)
46010Sstevel@tonic-gate 		{
46020Sstevel@tonic-gate 			MILTER_CHECK_ERROR(false, continue);
46030Sstevel@tonic-gate 			goto finishup;
46040Sstevel@tonic-gate 		}
46050Sstevel@tonic-gate 	}
46060Sstevel@tonic-gate 
46070Sstevel@tonic-gate finishup:
46080Sstevel@tonic-gate 	/* leave things in the expected state if we touched it */
46090Sstevel@tonic-gate 	if (replfailed)
46100Sstevel@tonic-gate 	{
46110Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46120Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46130Sstevel@tonic-gate 		{
46140Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46150Sstevel@tonic-gate 			SM_FREE_CLR(response);
46160Sstevel@tonic-gate 		}
46170Sstevel@tonic-gate 
46180Sstevel@tonic-gate 		if (dfopen)
46190Sstevel@tonic-gate 		{
46200Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
46210Sstevel@tonic-gate 			e->e_dfp = NULL;
46220Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
46230Sstevel@tonic-gate 			dfopen = false;
46240Sstevel@tonic-gate 		}
46250Sstevel@tonic-gate 		rewind = false;
46260Sstevel@tonic-gate 	}
46270Sstevel@tonic-gate 
46280Sstevel@tonic-gate 	if ((dfopen && milter_reset_df(e) < 0) ||
46290Sstevel@tonic-gate 	    (rewind && bfrewind(e->e_dfp) < 0))
46300Sstevel@tonic-gate 	{
46310Sstevel@tonic-gate 		save_errno = errno;
46320Sstevel@tonic-gate 		ExitStat = EX_IOERR;
46330Sstevel@tonic-gate 
46340Sstevel@tonic-gate 		/*
46350Sstevel@tonic-gate 		**  If filter told us to keep message but we had
46360Sstevel@tonic-gate 		**  an error, we can't really keep it, tempfail it.
46370Sstevel@tonic-gate 		*/
46380Sstevel@tonic-gate 
46390Sstevel@tonic-gate 		if (*state == SMFIR_CONTINUE ||
46400Sstevel@tonic-gate 		    *state == SMFIR_ACCEPT)
46410Sstevel@tonic-gate 		{
46420Sstevel@tonic-gate 			*state = SMFIR_TEMPFAIL;
46430Sstevel@tonic-gate 			SM_FREE_CLR(response);
46440Sstevel@tonic-gate 		}
46450Sstevel@tonic-gate 
46460Sstevel@tonic-gate 		errno = save_errno;
46470Sstevel@tonic-gate 		syserr("milter_data: %s/%cf%s: read error",
46480Sstevel@tonic-gate 		       qid_printqueue(e->e_qgrp, e->e_qdir),
46490Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
46500Sstevel@tonic-gate 	}
46510Sstevel@tonic-gate 
46520Sstevel@tonic-gate 	MILTER_CHECK_DONE_MSG();
46530Sstevel@tonic-gate 	if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
46540Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
46550Sstevel@tonic-gate 	return response;
46560Sstevel@tonic-gate }
46570Sstevel@tonic-gate 
46580Sstevel@tonic-gate /*
46590Sstevel@tonic-gate **  MILTER_UNKNOWN -- send any unrecognized or unimplemented command
46600Sstevel@tonic-gate **			string to milter filters
46610Sstevel@tonic-gate **
46620Sstevel@tonic-gate **	Parameters:
46633544Sjbeck **		smtpcmd -- the string itself.
46640Sstevel@tonic-gate **		e -- current envelope.
46650Sstevel@tonic-gate **		state -- return state from response.
46660Sstevel@tonic-gate **
46670Sstevel@tonic-gate **
46680Sstevel@tonic-gate **	Returns:
46690Sstevel@tonic-gate **		response string (may be NULL)
46700Sstevel@tonic-gate */
46710Sstevel@tonic-gate 
46720Sstevel@tonic-gate char *
46733544Sjbeck milter_unknown(smtpcmd, e, state)
46743544Sjbeck 	char *smtpcmd;
46750Sstevel@tonic-gate 	ENVELOPE *e;
46760Sstevel@tonic-gate 	char *state;
46770Sstevel@tonic-gate {
46780Sstevel@tonic-gate 	if (tTd(64, 10))
46793544Sjbeck 		sm_dprintf("milter_unknown(%s)\n", smtpcmd);
46803544Sjbeck 
46813544Sjbeck 	return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
46823544Sjbeck 				NULL, e, state, "unknown", false);
46830Sstevel@tonic-gate }
46840Sstevel@tonic-gate 
46850Sstevel@tonic-gate /*
46860Sstevel@tonic-gate **  MILTER_QUIT -- informs the filter(s) we are done and closes connection(s)
46870Sstevel@tonic-gate **
46880Sstevel@tonic-gate **	Parameters:
46890Sstevel@tonic-gate **		e -- current envelope.
46900Sstevel@tonic-gate **
46910Sstevel@tonic-gate **	Returns:
46920Sstevel@tonic-gate **		none
46930Sstevel@tonic-gate */
46940Sstevel@tonic-gate 
46950Sstevel@tonic-gate void
46960Sstevel@tonic-gate milter_quit(e)
46970Sstevel@tonic-gate 	ENVELOPE *e;
46980Sstevel@tonic-gate {
46990Sstevel@tonic-gate 	int i;
47000Sstevel@tonic-gate 
47010Sstevel@tonic-gate 	if (tTd(64, 10))
47020Sstevel@tonic-gate 		sm_dprintf("milter_quit(%s)\n", e->e_id);
47030Sstevel@tonic-gate 
47040Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47050Sstevel@tonic-gate 		milter_quit_filter(InputFilters[i], e);
47060Sstevel@tonic-gate }
47073544Sjbeck 
47080Sstevel@tonic-gate /*
47090Sstevel@tonic-gate **  MILTER_ABORT -- informs the filter(s) that we are aborting current message
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_abort(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_abort\n");
47260Sstevel@tonic-gate 
47270Sstevel@tonic-gate 	for (i = 0; InputFilters[i] != NULL; i++)
47280Sstevel@tonic-gate 	{
47290Sstevel@tonic-gate 		struct milter *m = InputFilters[i];
47300Sstevel@tonic-gate 
47310Sstevel@tonic-gate 		/* sanity checks */
47320Sstevel@tonic-gate 		if (m->mf_sock < 0 || m->mf_state != SMFS_INMSG)
47330Sstevel@tonic-gate 			continue;
47340Sstevel@tonic-gate 
47350Sstevel@tonic-gate 		milter_abort_filter(m, e);
47360Sstevel@tonic-gate 	}
47370Sstevel@tonic-gate }
47380Sstevel@tonic-gate #endif /* MILTER */
4739