xref: /illumos-gate/usr/src/cmd/sendmail/src/milter.c (revision e9af4bc0)
17c478bd9Sstevel@tonic-gate /*
2*e9af4bc0SJohn Beck  * Copyright (c) 1999-2009 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
67c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
77c478bd9Sstevel@tonic-gate  * the sendmail distribution.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate #include <sendmail.h>
127c478bd9Sstevel@tonic-gate 
13*e9af4bc0SJohn Beck SM_RCSID("@(#)$Id: milter.c,v 8.277 2009/11/06 00:57:06 ca Exp $")
147c478bd9Sstevel@tonic-gate 
157c478bd9Sstevel@tonic-gate #if MILTER
16058561cbSjbeck # include <sm/sendmail.h>
177c478bd9Sstevel@tonic-gate # include <libmilter/mfapi.h>
187c478bd9Sstevel@tonic-gate # include <libmilter/mfdef.h>
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate # include <errno.h>
2149218d4fSjbeck # include <sm/time.h>
227c478bd9Sstevel@tonic-gate # include <sys/uio.h>
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
257c478bd9Sstevel@tonic-gate #  include <arpa/inet.h>
26058561cbSjbeck #  if MILTER_NO_NAGLE
277c478bd9Sstevel@tonic-gate #   include <netinet/tcp.h>
28058561cbSjbeck #  endif /* MILTER_NO_NAGLE */
297c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate # include <sm/fdset.h>
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate static void	milter_connect_timeout __P((int));
347c478bd9Sstevel@tonic-gate static void	milter_error __P((struct milter *, ENVELOPE *));
357c478bd9Sstevel@tonic-gate static int	milter_open __P((struct milter *, bool, ENVELOPE *));
367c478bd9Sstevel@tonic-gate static void	milter_parse_timeouts __P((char *, struct milter *));
37058561cbSjbeck static char	*milter_sysread __P((struct milter *, char *, ssize_t, time_t,
38058561cbSjbeck 			ENVELOPE *, const char *));
39058561cbSjbeck static char	*milter_read __P((struct milter *, char *, ssize_t *, time_t,
40058561cbSjbeck 			ENVELOPE *, const char *));
41058561cbSjbeck static char	*milter_write __P((struct milter *, int, char *, ssize_t,
42058561cbSjbeck 			time_t, ENVELOPE *, const char *));
43058561cbSjbeck static char	*milter_send_command __P((struct milter *, int, void *,
44058561cbSjbeck 			ssize_t, ENVELOPE *, char *, const char *));
45058561cbSjbeck static char	*milter_command __P((int, void *, ssize_t, char **,
46058561cbSjbeck 			ENVELOPE *, char *, const char *, bool));
47058561cbSjbeck static char	*milter_body __P((struct milter *, ENVELOPE *, char *));
48058561cbSjbeck static int	milter_reopen_df __P((ENVELOPE *));
49058561cbSjbeck static int	milter_reset_df __P((ENVELOPE *));
50058561cbSjbeck static void	milter_quit_filter __P((struct milter *, ENVELOPE *));
51058561cbSjbeck static void	milter_abort_filter __P((struct milter *, ENVELOPE *));
52058561cbSjbeck static void	milter_send_macros __P((struct milter *, char **, int,
53058561cbSjbeck 			ENVELOPE *));
547800901eSjbeck static int	milter_negotiate __P((struct milter *, ENVELOPE *,
557800901eSjbeck 			milters_T *));
56058561cbSjbeck static void	milter_per_connection_check __P((ENVELOPE *));
57058561cbSjbeck static char	*milter_headers __P((struct milter *, ENVELOPE *, char *));
58058561cbSjbeck static void	milter_addheader __P((struct milter *, char *, ssize_t,
59058561cbSjbeck 			ENVELOPE *));
60058561cbSjbeck static void	milter_insheader __P((struct milter *, char *, ssize_t,
61058561cbSjbeck 			ENVELOPE *));
62058561cbSjbeck static void	milter_changeheader __P((struct milter *, char *, ssize_t,
63058561cbSjbeck 			ENVELOPE *));
64058561cbSjbeck static void	milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
65058561cbSjbeck static void	milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
66058561cbSjbeck static void	milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
67058561cbSjbeck static void	milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
68058561cbSjbeck static int	milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
69058561cbSjbeck static int	milter_set_macros __P((char *, char **, char *, int));
70058561cbSjbeck 
71058561cbSjbeck 
72058561cbSjbeck /* milter states */
73058561cbSjbeck # define SMFS_CLOSED		'C'	/* closed for all further actions */
74058561cbSjbeck # define SMFS_OPEN		'O'	/* connected to remote milter filter */
75058561cbSjbeck # define SMFS_INMSG		'M'	/* currently servicing a message */
76058561cbSjbeck # define SMFS_DONE		'D'	/* done with current message */
77058561cbSjbeck # define SMFS_CLOSABLE		'Q'	/* done with current connection */
78058561cbSjbeck # define SMFS_ERROR		'E'	/* error state */
79058561cbSjbeck # define SMFS_READY		'R'	/* ready for action */
80058561cbSjbeck # define SMFS_SKIP		'S'	/* skip body */
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate static char *MilterConnectMacros[MAXFILTERMACROS + 1];
837c478bd9Sstevel@tonic-gate static char *MilterHeloMacros[MAXFILTERMACROS + 1];
847c478bd9Sstevel@tonic-gate static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
857c478bd9Sstevel@tonic-gate static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
867c478bd9Sstevel@tonic-gate static char *MilterDataMacros[MAXFILTERMACROS + 1];
877c478bd9Sstevel@tonic-gate static char *MilterEOMMacros[MAXFILTERMACROS + 1];
88058561cbSjbeck static char *MilterEOHMacros[MAXFILTERMACROS + 1];
897c478bd9Sstevel@tonic-gate static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate # define MILTER_CHECK_DONE_MSG() \
927c478bd9Sstevel@tonic-gate 	if (*state == SMFIR_REPLYCODE || \
937c478bd9Sstevel@tonic-gate 	    *state == SMFIR_REJECT || \
947c478bd9Sstevel@tonic-gate 	    *state == SMFIR_DISCARD || \
957c478bd9Sstevel@tonic-gate 	    *state == SMFIR_TEMPFAIL) \
967c478bd9Sstevel@tonic-gate 	{ \
977c478bd9Sstevel@tonic-gate 		/* Abort the filters to let them know we are done with msg */ \
987c478bd9Sstevel@tonic-gate 		milter_abort(e); \
997c478bd9Sstevel@tonic-gate 	}
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate # define MILTER_CHECK_ERROR(initial, action) \
1027c478bd9Sstevel@tonic-gate 	if (!initial && tTd(71, 100)) \
1037c478bd9Sstevel@tonic-gate 	{ \
1047c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1057c478bd9Sstevel@tonic-gate 		{ \
1067c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1077c478bd9Sstevel@tonic-gate 							 "filter failure"); \
1087c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1097c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
1107c478bd9Sstevel@tonic-gate 		} \
1117c478bd9Sstevel@tonic-gate 	} \
1127c478bd9Sstevel@tonic-gate 	else if (tTd(71, 101)) \
1137c478bd9Sstevel@tonic-gate 	{ \
1147c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg == NULL) \
1157c478bd9Sstevel@tonic-gate 		{ \
1167c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1177c478bd9Sstevel@tonic-gate 							 "filter failure"); \
1187c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1197c478bd9Sstevel@tonic-gate 				  e->e_quarmsg); \
1207c478bd9Sstevel@tonic-gate 		} \
1217c478bd9Sstevel@tonic-gate 	} \
1227c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
1237c478bd9Sstevel@tonic-gate 		*state = SMFIR_TEMPFAIL; \
1247c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
1257c478bd9Sstevel@tonic-gate 		*state = SMFIR_SHUTDOWN; \
1267c478bd9Sstevel@tonic-gate 	else if (bitnset(SMF_REJECT, m->mf_flags)) \
1277c478bd9Sstevel@tonic-gate 		*state = SMFIR_REJECT; \
1287c478bd9Sstevel@tonic-gate 	else \
1297c478bd9Sstevel@tonic-gate 		action;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate # define MILTER_CHECK_REPLYCODE(default) \
1327c478bd9Sstevel@tonic-gate 	if (response == NULL || \
1337c478bd9Sstevel@tonic-gate 	    strlen(response) + 1 != (size_t) rlen || \
1347c478bd9Sstevel@tonic-gate 	    rlen < 3 || \
1357c478bd9Sstevel@tonic-gate 	    (response[0] != '4' && response[0] != '5') || \
1367c478bd9Sstevel@tonic-gate 	    !isascii(response[1]) || !isdigit(response[1]) || \
1377c478bd9Sstevel@tonic-gate 	    !isascii(response[2]) || !isdigit(response[2])) \
1387c478bd9Sstevel@tonic-gate 	{ \
1397c478bd9Sstevel@tonic-gate 		if (response != NULL) \
1407c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */ \
1417c478bd9Sstevel@tonic-gate 		response = newstr(default); \
1427c478bd9Sstevel@tonic-gate 	} \
1437c478bd9Sstevel@tonic-gate 	else \
1447c478bd9Sstevel@tonic-gate 	{ \
1457c478bd9Sstevel@tonic-gate 		char *ptr = response; \
1467c478bd9Sstevel@tonic-gate  \
1477c478bd9Sstevel@tonic-gate 		/* Check for unprotected %'s in the string */ \
1487c478bd9Sstevel@tonic-gate 		while (*ptr != '\0') \
1497c478bd9Sstevel@tonic-gate 		{ \
1507c478bd9Sstevel@tonic-gate 			if (*ptr == '%' && *++ptr != '%') \
1517c478bd9Sstevel@tonic-gate 			{ \
1527c478bd9Sstevel@tonic-gate 				sm_free(response); /* XXX */ \
1537c478bd9Sstevel@tonic-gate 				response = newstr(default); \
1547c478bd9Sstevel@tonic-gate 				break; \
1557c478bd9Sstevel@tonic-gate 			} \
1567c478bd9Sstevel@tonic-gate 			ptr++; \
1577c478bd9Sstevel@tonic-gate 		} \
1587c478bd9Sstevel@tonic-gate 	}
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate # define MILTER_DF_ERROR(msg) \
1617c478bd9Sstevel@tonic-gate { \
1627c478bd9Sstevel@tonic-gate 	int save_errno = errno; \
1637c478bd9Sstevel@tonic-gate  \
1647c478bd9Sstevel@tonic-gate 	if (tTd(64, 5)) \
1657c478bd9Sstevel@tonic-gate 	{ \
1667c478bd9Sstevel@tonic-gate 		sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
1677c478bd9Sstevel@tonic-gate 		sm_dprintf("\n"); \
1687c478bd9Sstevel@tonic-gate 	} \
1697c478bd9Sstevel@tonic-gate 	if (MilterLogLevel > 0) \
1707c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
1717c478bd9Sstevel@tonic-gate 	if (SuperSafe == SAFE_REALLY) \
1727c478bd9Sstevel@tonic-gate 	{ \
1737c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL) \
1747c478bd9Sstevel@tonic-gate 		{ \
1757c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
1767c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL; \
1777c478bd9Sstevel@tonic-gate 		} \
1787c478bd9Sstevel@tonic-gate 		e->e_flags &= ~EF_HAS_DF; \
1797c478bd9Sstevel@tonic-gate 	} \
1807c478bd9Sstevel@tonic-gate 	errno = save_errno; \
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate /*
1847c478bd9Sstevel@tonic-gate **  MILTER_TIMEOUT -- make sure socket is ready in time
1857c478bd9Sstevel@tonic-gate **
1867c478bd9Sstevel@tonic-gate **	Parameters:
1877c478bd9Sstevel@tonic-gate **		routine -- routine name for debug/logging
1887c478bd9Sstevel@tonic-gate **		secs -- number of seconds in timeout
1897c478bd9Sstevel@tonic-gate **		write -- waiting to read or write?
1907c478bd9Sstevel@tonic-gate **		started -- whether this is part of a previous sequence
1917c478bd9Sstevel@tonic-gate **
1927c478bd9Sstevel@tonic-gate **	Assumes 'm' is a milter structure for the current socket.
1937c478bd9Sstevel@tonic-gate */
1947c478bd9Sstevel@tonic-gate 
195058561cbSjbeck # define MILTER_TIMEOUT(routine, secs, write, started, function) \
1967c478bd9Sstevel@tonic-gate { \
1977c478bd9Sstevel@tonic-gate 	int ret; \
1987c478bd9Sstevel@tonic-gate 	int save_errno; \
1997c478bd9Sstevel@tonic-gate 	fd_set fds; \
2007c478bd9Sstevel@tonic-gate 	struct timeval tv; \
2017c478bd9Sstevel@tonic-gate  \
2027c478bd9Sstevel@tonic-gate 	if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
2037c478bd9Sstevel@tonic-gate 	{ \
2047c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2057c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
2067c478bd9Sstevel@tonic-gate 				   (routine), m->mf_name, m->mf_sock, \
2077c478bd9Sstevel@tonic-gate 				   SM_FD_SETSIZE); \
2087c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2097c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2107c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
2117c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), m->mf_sock, \
2127c478bd9Sstevel@tonic-gate 				  SM_FD_SETSIZE); \
2137c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2147c478bd9Sstevel@tonic-gate 		return NULL; \
2157c478bd9Sstevel@tonic-gate 	} \
2167c478bd9Sstevel@tonic-gate  \
2177c478bd9Sstevel@tonic-gate 	do \
2187c478bd9Sstevel@tonic-gate 	{ \
2197c478bd9Sstevel@tonic-gate 		FD_ZERO(&fds); \
2207c478bd9Sstevel@tonic-gate 		SM_FD_SET(m->mf_sock, &fds); \
2217c478bd9Sstevel@tonic-gate 		tv.tv_sec = (secs); \
2227c478bd9Sstevel@tonic-gate 		tv.tv_usec = 0; \
2237c478bd9Sstevel@tonic-gate 		ret = select(m->mf_sock + 1, \
2247c478bd9Sstevel@tonic-gate 			     (write) ? NULL : &fds, \
2257c478bd9Sstevel@tonic-gate 			     (write) ? &fds : NULL, \
2267c478bd9Sstevel@tonic-gate 			     NULL, &tv); \
2277c478bd9Sstevel@tonic-gate 	} while (ret < 0 && errno == EINTR); \
2287c478bd9Sstevel@tonic-gate  \
2297c478bd9Sstevel@tonic-gate 	switch (ret) \
2307c478bd9Sstevel@tonic-gate 	{ \
2317c478bd9Sstevel@tonic-gate 	  case 0: \
2327c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
233058561cbSjbeck 			sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
234058561cbSjbeck 				(routine), m->mf_name, (function)); \
2357c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2367c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
237058561cbSjbeck 				  "Milter (%s): timeout %s data %s, where=%s", \
238058561cbSjbeck 				  m->mf_name, \
2397c478bd9Sstevel@tonic-gate 				  started ? "during" : "before", \
240058561cbSjbeck 				  (routine), (function)); \
2417c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2427c478bd9Sstevel@tonic-gate 		return NULL; \
2437c478bd9Sstevel@tonic-gate  \
2447c478bd9Sstevel@tonic-gate 	  case -1: \
2457c478bd9Sstevel@tonic-gate 		save_errno = errno; \
2467c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2477c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
2487c478bd9Sstevel@tonic-gate 				   m->mf_name, sm_errstring(save_errno)); \
2497c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2507c478bd9Sstevel@tonic-gate 		{ \
2517c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2527c478bd9Sstevel@tonic-gate 				  "Milter (%s): select(%s): %s", \
2537c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine), \
2547c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno)); \
2557c478bd9Sstevel@tonic-gate 		} \
2567c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2577c478bd9Sstevel@tonic-gate 		return NULL; \
2587c478bd9Sstevel@tonic-gate  \
2597c478bd9Sstevel@tonic-gate 	  default: \
2607c478bd9Sstevel@tonic-gate 		if (SM_FD_ISSET(m->mf_sock, &fds)) \
2617c478bd9Sstevel@tonic-gate 			break; \
2627c478bd9Sstevel@tonic-gate 		if (tTd(64, 5)) \
2637c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_%s(%s): socket not ready\n", \
2647c478bd9Sstevel@tonic-gate 				(routine), m->mf_name); \
2657c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0) \
2667c478bd9Sstevel@tonic-gate 		{ \
2677c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id, \
2687c478bd9Sstevel@tonic-gate 				  "Milter (%s): socket(%s) not ready", \
2697c478bd9Sstevel@tonic-gate 				  m->mf_name, (routine)); \
2707c478bd9Sstevel@tonic-gate 		} \
2717c478bd9Sstevel@tonic-gate 		milter_error(m, e); \
2727c478bd9Sstevel@tonic-gate 		return NULL; \
2737c478bd9Sstevel@tonic-gate 	} \
2747c478bd9Sstevel@tonic-gate }
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate /*
2777c478bd9Sstevel@tonic-gate **  Low level functions
2787c478bd9Sstevel@tonic-gate */
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate /*
2817c478bd9Sstevel@tonic-gate **  MILTER_READ -- read from a remote milter filter
2827c478bd9Sstevel@tonic-gate **
2837c478bd9Sstevel@tonic-gate **	Parameters:
2847c478bd9Sstevel@tonic-gate **		m -- milter to read from.
2857c478bd9Sstevel@tonic-gate **		cmd -- return param for command read.
2867c478bd9Sstevel@tonic-gate **		rlen -- return length of response string.
2877c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
2887c478bd9Sstevel@tonic-gate **		e -- current envelope.
2897c478bd9Sstevel@tonic-gate **
2907c478bd9Sstevel@tonic-gate **	Returns:
2917c478bd9Sstevel@tonic-gate **		response string (may be NULL)
2927c478bd9Sstevel@tonic-gate */
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate static char *
milter_sysread(m,buf,sz,to,e,where)295058561cbSjbeck milter_sysread(m, buf, sz, to, e, where)
2967c478bd9Sstevel@tonic-gate 	struct milter *m;
2977c478bd9Sstevel@tonic-gate 	char *buf;
2987c478bd9Sstevel@tonic-gate 	ssize_t sz;
2997c478bd9Sstevel@tonic-gate 	time_t to;
3007c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
301058561cbSjbeck 	const char *where;
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
3047c478bd9Sstevel@tonic-gate 	ssize_t len, curl;
3057c478bd9Sstevel@tonic-gate 	bool started = false;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	curl = 0;
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	if (to > 0)
3107c478bd9Sstevel@tonic-gate 		readstart = curtime();
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	for (;;)
3137c478bd9Sstevel@tonic-gate 	{
3147c478bd9Sstevel@tonic-gate 		if (to > 0)
3157c478bd9Sstevel@tonic-gate 		{
3167c478bd9Sstevel@tonic-gate 			time_t now;
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 			now = curtime();
3197c478bd9Sstevel@tonic-gate 			if (now - readstart >= to)
3207c478bd9Sstevel@tonic-gate 			{
3217c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
322058561cbSjbeck 					sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
323058561cbSjbeck 						  m->mf_name,
3247c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
325058561cbSjbeck 						  where);
3267c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
3277c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
328058561cbSjbeck 						  "Milter (%s): timeout %s data read in %s",
329058561cbSjbeck 						  m->mf_name,
3307c478bd9Sstevel@tonic-gate 						  started ? "during" : "before",
331058561cbSjbeck 						  where);
3327c478bd9Sstevel@tonic-gate 				milter_error(m, e);
3337c478bd9Sstevel@tonic-gate 				return NULL;
3347c478bd9Sstevel@tonic-gate 			}
3357c478bd9Sstevel@tonic-gate 			to -= now - readstart;
3367c478bd9Sstevel@tonic-gate 			readstart = now;
337058561cbSjbeck 			MILTER_TIMEOUT("read", to, false, started, where);
3387c478bd9Sstevel@tonic-gate 		}
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 		len = read(m->mf_sock, buf + curl, sz - curl);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 		if (len < 0)
3437c478bd9Sstevel@tonic-gate 		{
3447c478bd9Sstevel@tonic-gate 			int save_errno = errno;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
347058561cbSjbeck 				sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
3487c478bd9Sstevel@tonic-gate 					m->mf_name, (long) len,
3497c478bd9Sstevel@tonic-gate 					sm_errstring(save_errno));
3507c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
3517c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
3527c478bd9Sstevel@tonic-gate 					  "Milter (%s): read returned %ld: %s",
3537c478bd9Sstevel@tonic-gate 					  m->mf_name, (long) len,
3547c478bd9Sstevel@tonic-gate 					  sm_errstring(save_errno));
3557c478bd9Sstevel@tonic-gate 			milter_error(m, e);
3567c478bd9Sstevel@tonic-gate 			return NULL;
3577c478bd9Sstevel@tonic-gate 		}
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 		started = true;
3607c478bd9Sstevel@tonic-gate 		curl += len;
3617c478bd9Sstevel@tonic-gate 		if (len == 0 || curl >= sz)
3627c478bd9Sstevel@tonic-gate 			break;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	if (curl != sz)
3677c478bd9Sstevel@tonic-gate 	{
3687c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
369058561cbSjbeck 			sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
3707c478bd9Sstevel@tonic-gate 				m->mf_name, (long) curl, (long) sz);
3717c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
3727c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
373058561cbSjbeck 				  "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
3747c478bd9Sstevel@tonic-gate 				  m->mf_name, (long) curl, (long) sz);
3757c478bd9Sstevel@tonic-gate 		milter_error(m, e);
3767c478bd9Sstevel@tonic-gate 		return NULL;
3777c478bd9Sstevel@tonic-gate 	}
3787c478bd9Sstevel@tonic-gate 	return buf;
3797c478bd9Sstevel@tonic-gate }
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate static char *
milter_read(m,cmd,rlen,to,e,where)382058561cbSjbeck milter_read(m, cmd, rlen, to, e, where)
3837c478bd9Sstevel@tonic-gate 	struct milter *m;
3847c478bd9Sstevel@tonic-gate 	char *cmd;
3857c478bd9Sstevel@tonic-gate 	ssize_t *rlen;
3867c478bd9Sstevel@tonic-gate 	time_t to;
3877c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
388058561cbSjbeck 	const char *where;
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	time_t readstart = 0;
3917c478bd9Sstevel@tonic-gate 	ssize_t expl;
3927c478bd9Sstevel@tonic-gate 	mi_int32 i;
393058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
3947c478bd9Sstevel@tonic-gate 	int cork = 0;
395058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
3967c478bd9Sstevel@tonic-gate 	char *buf;
3977c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
4007c478bd9Sstevel@tonic-gate 	{
4017c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4027c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
403058561cbSjbeck 				  "milter_read(%s): socket closed, where=%s",
404058561cbSjbeck 				  m->mf_name, where);
4057c478bd9Sstevel@tonic-gate 		milter_error(m, e);
4067c478bd9Sstevel@tonic-gate 		return NULL;
4077c478bd9Sstevel@tonic-gate 	}
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	*rlen = 0;
4107c478bd9Sstevel@tonic-gate 	*cmd = '\0';
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	if (to > 0)
4137c478bd9Sstevel@tonic-gate 		readstart = curtime();
4147c478bd9Sstevel@tonic-gate 
415058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4167c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4177c478bd9Sstevel@tonic-gate 		   sizeof(cork));
418058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4197c478bd9Sstevel@tonic-gate 
420058561cbSjbeck 	if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
4217c478bd9Sstevel@tonic-gate 		return NULL;
4227c478bd9Sstevel@tonic-gate 
423058561cbSjbeck # if MILTER_NO_NAGLE && defined(TCP_CORK)
4247c478bd9Sstevel@tonic-gate 	cork = 1;
4257c478bd9Sstevel@tonic-gate 	setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
4267c478bd9Sstevel@tonic-gate 		   sizeof(cork));
427058561cbSjbeck # endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/* reset timeout */
4307c478bd9Sstevel@tonic-gate 	if (to > 0)
4317c478bd9Sstevel@tonic-gate 	{
4327c478bd9Sstevel@tonic-gate 		time_t now;
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 		now = curtime();
4357c478bd9Sstevel@tonic-gate 		if (now - readstart >= to)
4367c478bd9Sstevel@tonic-gate 		{
4377c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
438058561cbSjbeck 				sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
439058561cbSjbeck 					m->mf_name, where);
4407c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
4417c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
442058561cbSjbeck 					  "Milter read(%s): timeout before data read, where=%s",
443058561cbSjbeck 					  m->mf_name, where);
4447c478bd9Sstevel@tonic-gate 			milter_error(m, e);
4457c478bd9Sstevel@tonic-gate 			return NULL;
4467c478bd9Sstevel@tonic-gate 		}
4477c478bd9Sstevel@tonic-gate 		to -= now - readstart;
4487c478bd9Sstevel@tonic-gate 	}
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	*cmd = data[MILTER_LEN_BYTES];
4517c478bd9Sstevel@tonic-gate 	data[MILTER_LEN_BYTES] = '\0';
4527c478bd9Sstevel@tonic-gate 	(void) memcpy(&i, data, MILTER_LEN_BYTES);
4537c478bd9Sstevel@tonic-gate 	expl = ntohl(i) - 1;
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	if (tTd(64, 25))
4567c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): expecting %ld bytes\n",
4577c478bd9Sstevel@tonic-gate 			m->mf_name, (long) expl);
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	if (expl < 0)
4607c478bd9Sstevel@tonic-gate 	{
4617c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
462058561cbSjbeck 			sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
463058561cbSjbeck 				m->mf_name, (long) expl, where);
4647c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
4657c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
466058561cbSjbeck 				  "milter_read(%s): read size %ld out of range, where=%s",
467058561cbSjbeck 				  m->mf_name, (long) expl, where);
4687c478bd9Sstevel@tonic-gate 		milter_error(m, e);
4697c478bd9Sstevel@tonic-gate 		return NULL;
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	if (expl == 0)
4737c478bd9Sstevel@tonic-gate 		return NULL;
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 	buf = (char *) xalloc(expl);
4767c478bd9Sstevel@tonic-gate 
477058561cbSjbeck 	if (milter_sysread(m, buf, expl, to, e, where) == NULL)
4787c478bd9Sstevel@tonic-gate 	{
4797c478bd9Sstevel@tonic-gate 		sm_free(buf); /* XXX */
4807c478bd9Sstevel@tonic-gate 		return NULL;
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	if (tTd(64, 50))
4847c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_read(%s): Returning %*s\n",
4857c478bd9Sstevel@tonic-gate 			m->mf_name, (int) expl, buf);
4867c478bd9Sstevel@tonic-gate 	*rlen = expl;
4877c478bd9Sstevel@tonic-gate 	return buf;
4887c478bd9Sstevel@tonic-gate }
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate /*
4917c478bd9Sstevel@tonic-gate **  MILTER_WRITE -- write to a remote milter filter
4927c478bd9Sstevel@tonic-gate **
4937c478bd9Sstevel@tonic-gate **	Parameters:
4947c478bd9Sstevel@tonic-gate **		m -- milter to read from.
4957c478bd9Sstevel@tonic-gate **		cmd -- command to send.
4967c478bd9Sstevel@tonic-gate **		buf -- optional command data.
4977c478bd9Sstevel@tonic-gate **		len -- length of buf.
4987c478bd9Sstevel@tonic-gate **		to -- timeout in seconds.
4997c478bd9Sstevel@tonic-gate **		e -- current envelope.
5007c478bd9Sstevel@tonic-gate **
5017c478bd9Sstevel@tonic-gate **	Returns:
5027c478bd9Sstevel@tonic-gate **		buf if successful, NULL otherwise
5037c478bd9Sstevel@tonic-gate **		Not actually used anywhere but function prototype
5047c478bd9Sstevel@tonic-gate **			must match milter_read()
5057c478bd9Sstevel@tonic-gate */
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate static char *
milter_write(m,cmd,buf,len,to,e,where)508058561cbSjbeck milter_write(m, cmd, buf, len, to, e, where)
5097c478bd9Sstevel@tonic-gate 	struct milter *m;
510058561cbSjbeck 	int cmd;
5117c478bd9Sstevel@tonic-gate 	char *buf;
5127c478bd9Sstevel@tonic-gate 	ssize_t len;
5137c478bd9Sstevel@tonic-gate 	time_t to;
5147c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
515058561cbSjbeck 	const char *where;
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate 	ssize_t sl, i;
5187c478bd9Sstevel@tonic-gate 	int num_vectors;
5197c478bd9Sstevel@tonic-gate 	mi_int32 nl;
520058561cbSjbeck 	char command = (char) cmd;
5217c478bd9Sstevel@tonic-gate 	char data[MILTER_LEN_BYTES + 1];
5227c478bd9Sstevel@tonic-gate 	bool started = false;
5237c478bd9Sstevel@tonic-gate 	struct iovec vector[2];
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	/*
5267c478bd9Sstevel@tonic-gate 	**  At most two buffers will be written, though
5277c478bd9Sstevel@tonic-gate 	**  only one may actually be used (see num_vectors).
5287c478bd9Sstevel@tonic-gate 	**  The first is the size/command and the second is the command data.
5297c478bd9Sstevel@tonic-gate 	*/
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	if (len < 0 || len > MilterMaxDataSize)
5327c478bd9Sstevel@tonic-gate 	{
5337c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
534*e9af4bc0SJohn Beck 		{
535*e9af4bc0SJohn Beck 			sm_dprintf("milter_write(%s): length %ld out of range, cmd=%c\n",
536*e9af4bc0SJohn Beck 				m->mf_name, (long) len, command);
537*e9af4bc0SJohn Beck 			sm_dprintf("milter_write(%s): buf=%s\n",
538*e9af4bc0SJohn Beck 				m->mf_name, str2prt(buf));
539*e9af4bc0SJohn Beck 		}
5407c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5417c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
542*e9af4bc0SJohn Beck 				  "milter_write(%s): length %ld out of range, cmd=%c",
543*e9af4bc0SJohn Beck 				  m->mf_name, (long) len, command);
5447c478bd9Sstevel@tonic-gate 		milter_error(m, e);
5457c478bd9Sstevel@tonic-gate 		return NULL;
5467c478bd9Sstevel@tonic-gate 	}
5477c478bd9Sstevel@tonic-gate 	if (m->mf_sock < 0)
5487c478bd9Sstevel@tonic-gate 	{
5497c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
5507c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
5517c478bd9Sstevel@tonic-gate 				  "milter_write(%s): socket closed",
5527c478bd9Sstevel@tonic-gate 				  m->mf_name);
5537c478bd9Sstevel@tonic-gate 		milter_error(m, e);
5547c478bd9Sstevel@tonic-gate 		return NULL;
5557c478bd9Sstevel@tonic-gate 	}
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	if (tTd(64, 20))
5587c478bd9Sstevel@tonic-gate 		sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
559058561cbSjbeck 			   m->mf_name, command, (long) len);
5607c478bd9Sstevel@tonic-gate 
561058561cbSjbeck 	nl = htonl(len + 1);	/* add 1 for the command char */
5627c478bd9Sstevel@tonic-gate 	(void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
563058561cbSjbeck 	data[MILTER_LEN_BYTES] = command;
5647c478bd9Sstevel@tonic-gate 	sl = MILTER_LEN_BYTES + 1;
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	/* set up the vector for the size / command */
5677c478bd9Sstevel@tonic-gate 	vector[0].iov_base = (void *) data;
5687c478bd9Sstevel@tonic-gate 	vector[0].iov_len  = sl;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	/*
5717c478bd9Sstevel@tonic-gate 	**  Determine if there is command data.  If so, there will be two
5727c478bd9Sstevel@tonic-gate 	**  vectors.  If not, there will be only one.  The vectors are set
5737c478bd9Sstevel@tonic-gate 	**  up here and 'num_vectors' and 'sl' are set appropriately.
5747c478bd9Sstevel@tonic-gate 	*/
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	/* NOTE:  len<0 has already been checked for.  Pedantic */
5777c478bd9Sstevel@tonic-gate 	if (len <= 0 || buf == NULL)
5787c478bd9Sstevel@tonic-gate 	{
5797c478bd9Sstevel@tonic-gate 		/* There is no command data -- only a size / command data */
5807c478bd9Sstevel@tonic-gate 		num_vectors = 1;
5817c478bd9Sstevel@tonic-gate 	}
5827c478bd9Sstevel@tonic-gate 	else
5837c478bd9Sstevel@tonic-gate 	{
5847c478bd9Sstevel@tonic-gate 		/*
5857c478bd9Sstevel@tonic-gate 		**  There is both size / command and command data.
5867c478bd9Sstevel@tonic-gate 		**  Set up the vector for the command data.
5877c478bd9Sstevel@tonic-gate 		*/
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 		num_vectors = 2;
5907c478bd9Sstevel@tonic-gate 		sl += len;
5917c478bd9Sstevel@tonic-gate 		vector[1].iov_base = (void *) buf;
5927c478bd9Sstevel@tonic-gate 		vector[1].iov_len  = len;
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		if (tTd(64, 50))
5957c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): Sending %*s\n",
5967c478bd9Sstevel@tonic-gate 				   m->mf_name, (int) len, buf);
5977c478bd9Sstevel@tonic-gate 	}
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	if (to > 0)
600058561cbSjbeck 		MILTER_TIMEOUT("write", to, true, started, where);
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	/* write the vector(s) */
6037c478bd9Sstevel@tonic-gate 	i = writev(m->mf_sock, vector, num_vectors);
6047c478bd9Sstevel@tonic-gate 	if (i != sl)
6057c478bd9Sstevel@tonic-gate 	{
6067c478bd9Sstevel@tonic-gate 		int save_errno = errno;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
6097c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
610058561cbSjbeck 				   m->mf_name, command, (long) i, (long) sl,
6117c478bd9Sstevel@tonic-gate 				   sm_errstring(save_errno));
6127c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 0)
6137c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6147c478bd9Sstevel@tonic-gate 				  "Milter (%s): write(%c) returned %ld, expected %ld: %s",
615058561cbSjbeck 				  m->mf_name, command, (long) i, (long) sl,
6167c478bd9Sstevel@tonic-gate 				  sm_errstring(save_errno));
6177c478bd9Sstevel@tonic-gate 		milter_error(m, e);
6187c478bd9Sstevel@tonic-gate 		return NULL;
6197c478bd9Sstevel@tonic-gate 	}
6207c478bd9Sstevel@tonic-gate 	return buf;
6217c478bd9Sstevel@tonic-gate }
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate /*
6247c478bd9Sstevel@tonic-gate **  Utility functions
6257c478bd9Sstevel@tonic-gate */
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate /*
6287c478bd9Sstevel@tonic-gate **  MILTER_OPEN -- connect to remote milter filter
6297c478bd9Sstevel@tonic-gate **
6307c478bd9Sstevel@tonic-gate **	Parameters:
6317c478bd9Sstevel@tonic-gate **		m -- milter to connect to.
6327c478bd9Sstevel@tonic-gate **		parseonly -- parse but don't connect.
6337c478bd9Sstevel@tonic-gate **		e -- current envelope.
6347c478bd9Sstevel@tonic-gate **
6357c478bd9Sstevel@tonic-gate **	Returns:
6367c478bd9Sstevel@tonic-gate **		connected socket if successful && !parseonly,
6377c478bd9Sstevel@tonic-gate **		0 upon parse success if parseonly,
6387c478bd9Sstevel@tonic-gate **		-1 otherwise.
6397c478bd9Sstevel@tonic-gate */
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate static jmp_buf	MilterConnectTimeout;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate static int
milter_open(m,parseonly,e)6447c478bd9Sstevel@tonic-gate milter_open(m, parseonly, e)
6457c478bd9Sstevel@tonic-gate 	struct milter *m;
6467c478bd9Sstevel@tonic-gate 	bool parseonly;
6477c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
6487c478bd9Sstevel@tonic-gate {
6497c478bd9Sstevel@tonic-gate 	int sock = 0;
6507c478bd9Sstevel@tonic-gate 	SOCKADDR_LEN_T addrlen = 0;
6517c478bd9Sstevel@tonic-gate 	int addrno = 0;
6527c478bd9Sstevel@tonic-gate 	int save_errno;
6537c478bd9Sstevel@tonic-gate 	char *p;
6547c478bd9Sstevel@tonic-gate 	char *colon;
6557c478bd9Sstevel@tonic-gate 	char *at;
6567c478bd9Sstevel@tonic-gate 	struct hostent *hp = NULL;
6577c478bd9Sstevel@tonic-gate 	SOCKADDR addr;
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	if (m->mf_conn == NULL || m->mf_conn[0] == '\0')
6607c478bd9Sstevel@tonic-gate 	{
6617c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
6627c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: empty or missing socket information\n",
6637c478bd9Sstevel@tonic-gate 				   m->mf_name);
6647c478bd9Sstevel@tonic-gate 		if (parseonly)
6657c478bd9Sstevel@tonic-gate 			syserr("X%s: empty or missing socket information",
6667c478bd9Sstevel@tonic-gate 			       m->mf_name);
6677c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
6687c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6697c478bd9Sstevel@tonic-gate 				  "Milter (%s): empty or missing socket information",
6707c478bd9Sstevel@tonic-gate 				  m->mf_name);
6717c478bd9Sstevel@tonic-gate 		milter_error(m, e);
6727c478bd9Sstevel@tonic-gate 		return -1;
6737c478bd9Sstevel@tonic-gate 	}
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	/* protocol:filename or protocol:port@host */
676058561cbSjbeck 	memset(&addr, '\0', sizeof(addr));
6777c478bd9Sstevel@tonic-gate 	p = m->mf_conn;
6787c478bd9Sstevel@tonic-gate 	colon = strchr(p, ':');
6797c478bd9Sstevel@tonic-gate 	if (colon != NULL)
6807c478bd9Sstevel@tonic-gate 	{
6817c478bd9Sstevel@tonic-gate 		*colon = '\0';
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 		if (*p == '\0')
6847c478bd9Sstevel@tonic-gate 		{
6857c478bd9Sstevel@tonic-gate # if NETUNIX
6867c478bd9Sstevel@tonic-gate 			/* default to AF_UNIX */
6877c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
6887c478bd9Sstevel@tonic-gate # else /* NETUNIX */
6897c478bd9Sstevel@tonic-gate #  if NETINET
6907c478bd9Sstevel@tonic-gate 			/* default to AF_INET */
6917c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
6927c478bd9Sstevel@tonic-gate #  else /* NETINET */
6937c478bd9Sstevel@tonic-gate #   if NETINET6
6947c478bd9Sstevel@tonic-gate 			/* default to AF_INET6 */
6957c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
6967c478bd9Sstevel@tonic-gate #   else /* NETINET6 */
6977c478bd9Sstevel@tonic-gate 			/* no protocols available */
6987c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
6997c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7007c478bd9Sstevel@tonic-gate 					  "Milter (%s): no valid socket protocols available",
7017c478bd9Sstevel@tonic-gate 					  m->mf_name);
7027c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7037c478bd9Sstevel@tonic-gate 			return -1;
7047c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
7057c478bd9Sstevel@tonic-gate #  endif /* NETINET */
7067c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
7077c478bd9Sstevel@tonic-gate 		}
7087c478bd9Sstevel@tonic-gate # if NETUNIX
7097c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "unix") == 0 ||
7107c478bd9Sstevel@tonic-gate 			 sm_strcasecmp(p, "local") == 0)
7117c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_UNIX;
7127c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
7137c478bd9Sstevel@tonic-gate # if NETINET
7147c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet") == 0)
7157c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET;
7167c478bd9Sstevel@tonic-gate # endif /* NETINET */
7177c478bd9Sstevel@tonic-gate # if NETINET6
7187c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(p, "inet6") == 0)
7197c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = AF_INET6;
7207c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
7217c478bd9Sstevel@tonic-gate 		else
7227c478bd9Sstevel@tonic-gate 		{
7237c478bd9Sstevel@tonic-gate # ifdef EPROTONOSUPPORT
7247c478bd9Sstevel@tonic-gate 			errno = EPROTONOSUPPORT;
7257c478bd9Sstevel@tonic-gate # else /* EPROTONOSUPPORT */
7267c478bd9Sstevel@tonic-gate 			errno = EINVAL;
7277c478bd9Sstevel@tonic-gate # endif /* EPROTONOSUPPORT */
7287c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7297c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: unknown socket type %s\n",
7307c478bd9Sstevel@tonic-gate 					m->mf_name, p);
7317c478bd9Sstevel@tonic-gate 			if (parseonly)
7327c478bd9Sstevel@tonic-gate 				syserr("X%s: unknown socket type %s",
7337c478bd9Sstevel@tonic-gate 				       m->mf_name, p);
7347c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7357c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7367c478bd9Sstevel@tonic-gate 					  "Milter (%s): unknown socket type %s",
7377c478bd9Sstevel@tonic-gate 					  m->mf_name, p);
7387c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7397c478bd9Sstevel@tonic-gate 			return -1;
7407c478bd9Sstevel@tonic-gate 		}
7417c478bd9Sstevel@tonic-gate 		*colon++ = ':';
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate 	else
7447c478bd9Sstevel@tonic-gate 	{
7457c478bd9Sstevel@tonic-gate 		/* default to AF_UNIX */
7467c478bd9Sstevel@tonic-gate 		addr.sa.sa_family = AF_UNIX;
7477c478bd9Sstevel@tonic-gate 		colon = p;
7487c478bd9Sstevel@tonic-gate 	}
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate # if NETUNIX
7517c478bd9Sstevel@tonic-gate 	if (addr.sa.sa_family == AF_UNIX)
7527c478bd9Sstevel@tonic-gate 	{
7537c478bd9Sstevel@tonic-gate 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 		at = colon;
756058561cbSjbeck 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7577c478bd9Sstevel@tonic-gate 		{
7587c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7597c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s too long\n",
7607c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
7617c478bd9Sstevel@tonic-gate 			errno = EINVAL;
7627c478bd9Sstevel@tonic-gate 			if (parseonly)
7637c478bd9Sstevel@tonic-gate 				syserr("X%s: local socket name %s too long",
7647c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
7657c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
7667c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
7677c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s too long",
7687c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
7697c478bd9Sstevel@tonic-gate 			milter_error(m, e);
7707c478bd9Sstevel@tonic-gate 			return -1;
7717c478bd9Sstevel@tonic-gate 		}
7727c478bd9Sstevel@tonic-gate 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7737c478bd9Sstevel@tonic-gate 				 S_IRUSR|S_IWUSR, NULL);
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		/* if just parsing .cf file, socket doesn't need to exist */
7767c478bd9Sstevel@tonic-gate 		if (parseonly && errno == ENOENT)
7777c478bd9Sstevel@tonic-gate 		{
7787c478bd9Sstevel@tonic-gate 			if (OpMode == MD_DAEMON ||
7797c478bd9Sstevel@tonic-gate 			    OpMode == MD_FGDAEMON)
7807c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
7817c478bd9Sstevel@tonic-gate 						     "WARNING: X%s: local socket name %s missing\n",
7827c478bd9Sstevel@tonic-gate 						     m->mf_name, colon);
7837c478bd9Sstevel@tonic-gate 		}
7847c478bd9Sstevel@tonic-gate 		else if (errno != 0)
7857c478bd9Sstevel@tonic-gate 		{
7867c478bd9Sstevel@tonic-gate 			/* if not safe, don't create */
7877c478bd9Sstevel@tonic-gate 			save_errno = errno;
7887c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
7897c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: local socket name %s unsafe\n",
7907c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
7917c478bd9Sstevel@tonic-gate 			errno = save_errno;
7927c478bd9Sstevel@tonic-gate 			if (parseonly)
7937c478bd9Sstevel@tonic-gate 			{
7947c478bd9Sstevel@tonic-gate 				if (OpMode == MD_DAEMON ||
7957c478bd9Sstevel@tonic-gate 				    OpMode == MD_FGDAEMON ||
7967c478bd9Sstevel@tonic-gate 				    OpMode == MD_SMTP)
7977c478bd9Sstevel@tonic-gate 					syserr("X%s: local socket name %s unsafe",
7987c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
7997c478bd9Sstevel@tonic-gate 			}
8007c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8017c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8027c478bd9Sstevel@tonic-gate 					  "Milter (%s): local socket name %s unsafe",
8037c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8047c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8057c478bd9Sstevel@tonic-gate 			return -1;
8067c478bd9Sstevel@tonic-gate 		}
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
809058561cbSjbeck 			       sizeof(addr.sunix.sun_path));
810058561cbSjbeck 		addrlen = sizeof(struct sockaddr_un);
8117c478bd9Sstevel@tonic-gate 	}
8127c478bd9Sstevel@tonic-gate 	else
8137c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
8147c478bd9Sstevel@tonic-gate # if NETINET || NETINET6
8157c478bd9Sstevel@tonic-gate 	if (false
8167c478bd9Sstevel@tonic-gate #  if NETINET
8177c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET
8187c478bd9Sstevel@tonic-gate #  endif /* NETINET */
8197c478bd9Sstevel@tonic-gate #  if NETINET6
8207c478bd9Sstevel@tonic-gate 		 || addr.sa.sa_family == AF_INET6
8217c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
8227c478bd9Sstevel@tonic-gate 		 )
8237c478bd9Sstevel@tonic-gate 	{
8247c478bd9Sstevel@tonic-gate 		unsigned short port;
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 		/* Parse port@host */
8277c478bd9Sstevel@tonic-gate 		at = strchr(colon, '@');
8287c478bd9Sstevel@tonic-gate 		if (at == NULL)
8297c478bd9Sstevel@tonic-gate 		{
8307c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
8317c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: bad address %s (expected port@host)\n",
8327c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
8337c478bd9Sstevel@tonic-gate 			if (parseonly)
8347c478bd9Sstevel@tonic-gate 				syserr("X%s: bad address %s (expected port@host)",
8357c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
8367c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8377c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8387c478bd9Sstevel@tonic-gate 					  "Milter (%s): bad address %s (expected port@host)",
8397c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8407c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8417c478bd9Sstevel@tonic-gate 			return -1;
8427c478bd9Sstevel@tonic-gate 		}
8437c478bd9Sstevel@tonic-gate 		*at = '\0';
8447c478bd9Sstevel@tonic-gate 		if (isascii(*colon) && isdigit(*colon))
8457c478bd9Sstevel@tonic-gate 			port = htons((unsigned short) atoi(colon));
8467c478bd9Sstevel@tonic-gate 		else
8477c478bd9Sstevel@tonic-gate 		{
8487c478bd9Sstevel@tonic-gate #  ifdef NO_GETSERVBYNAME
8497c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
8507c478bd9Sstevel@tonic-gate 				sm_dprintf("X%s: invalid port number %s\n",
8517c478bd9Sstevel@tonic-gate 					m->mf_name, colon);
8527c478bd9Sstevel@tonic-gate 			if (parseonly)
8537c478bd9Sstevel@tonic-gate 				syserr("X%s: invalid port number %s",
8547c478bd9Sstevel@tonic-gate 				       m->mf_name, colon);
8557c478bd9Sstevel@tonic-gate 			else if (MilterLogLevel > 0)
8567c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
8577c478bd9Sstevel@tonic-gate 					  "Milter (%s): invalid port number %s",
8587c478bd9Sstevel@tonic-gate 					  m->mf_name, colon);
8597c478bd9Sstevel@tonic-gate 			milter_error(m, e);
8607c478bd9Sstevel@tonic-gate 			return -1;
8617c478bd9Sstevel@tonic-gate #  else /* NO_GETSERVBYNAME */
862058561cbSjbeck 			struct servent *sp;
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 			sp = getservbyname(colon, "tcp");
8657c478bd9Sstevel@tonic-gate 			if (sp == NULL)
8667c478bd9Sstevel@tonic-gate 			{
8677c478bd9Sstevel@tonic-gate 				save_errno = errno;
8687c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
8697c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: unknown port name %s\n",
8707c478bd9Sstevel@tonic-gate 						m->mf_name, colon);
8717c478bd9Sstevel@tonic-gate 				errno = save_errno;
8727c478bd9Sstevel@tonic-gate 				if (parseonly)
8737c478bd9Sstevel@tonic-gate 					syserr("X%s: unknown port name %s",
8747c478bd9Sstevel@tonic-gate 					       m->mf_name, colon);
8757c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
8767c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
8777c478bd9Sstevel@tonic-gate 						  "Milter (%s): unknown port name %s",
8787c478bd9Sstevel@tonic-gate 						  m->mf_name, colon);
8797c478bd9Sstevel@tonic-gate 				milter_error(m, e);
8807c478bd9Sstevel@tonic-gate 				return -1;
8817c478bd9Sstevel@tonic-gate 			}
8827c478bd9Sstevel@tonic-gate 			port = sp->s_port;
8837c478bd9Sstevel@tonic-gate #  endif /* NO_GETSERVBYNAME */
8847c478bd9Sstevel@tonic-gate 		}
8857c478bd9Sstevel@tonic-gate 		*at++ = '@';
8867c478bd9Sstevel@tonic-gate 		if (*at == '[')
8877c478bd9Sstevel@tonic-gate 		{
8887c478bd9Sstevel@tonic-gate 			char *end;
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 			end = strchr(at, ']');
8917c478bd9Sstevel@tonic-gate 			if (end != NULL)
8927c478bd9Sstevel@tonic-gate 			{
8937c478bd9Sstevel@tonic-gate 				bool found = false;
8947c478bd9Sstevel@tonic-gate #  if NETINET
8957c478bd9Sstevel@tonic-gate 				unsigned long hid = INADDR_NONE;
8967c478bd9Sstevel@tonic-gate #  endif /* NETINET */
8977c478bd9Sstevel@tonic-gate #  if NETINET6
8987c478bd9Sstevel@tonic-gate 				struct sockaddr_in6 hid6;
8997c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 				*end = '\0';
9027c478bd9Sstevel@tonic-gate #  if NETINET
9037c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET &&
9047c478bd9Sstevel@tonic-gate 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
9057c478bd9Sstevel@tonic-gate 				{
9067c478bd9Sstevel@tonic-gate 					addr.sin.sin_addr.s_addr = hid;
9077c478bd9Sstevel@tonic-gate 					addr.sin.sin_port = port;
9087c478bd9Sstevel@tonic-gate 					found = true;
9097c478bd9Sstevel@tonic-gate 				}
9107c478bd9Sstevel@tonic-gate #  endif /* NETINET */
9117c478bd9Sstevel@tonic-gate #  if NETINET6
912058561cbSjbeck 				(void) memset(&hid6, '\0', sizeof(hid6));
9137c478bd9Sstevel@tonic-gate 				if (addr.sa.sa_family == AF_INET6 &&
9147c478bd9Sstevel@tonic-gate 				    anynet_pton(AF_INET6, &at[1],
9157c478bd9Sstevel@tonic-gate 						&hid6.sin6_addr) == 1)
9167c478bd9Sstevel@tonic-gate 				{
9177c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_addr = hid6.sin6_addr;
9187c478bd9Sstevel@tonic-gate 					addr.sin6.sin6_port = port;
9197c478bd9Sstevel@tonic-gate 					found = true;
9207c478bd9Sstevel@tonic-gate 				}
9217c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9227c478bd9Sstevel@tonic-gate 				*end = ']';
9237c478bd9Sstevel@tonic-gate 				if (!found)
9247c478bd9Sstevel@tonic-gate 				{
9257c478bd9Sstevel@tonic-gate 					if (tTd(64, 5))
9267c478bd9Sstevel@tonic-gate 						sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9277c478bd9Sstevel@tonic-gate 							m->mf_name, at);
9287c478bd9Sstevel@tonic-gate 					if (parseonly)
9297c478bd9Sstevel@tonic-gate 						syserr("X%s: Invalid numeric domain spec \"%s\"",
9307c478bd9Sstevel@tonic-gate 						       m->mf_name, at);
9317c478bd9Sstevel@tonic-gate 					else if (MilterLogLevel > 0)
9327c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9337c478bd9Sstevel@tonic-gate 							  "Milter (%s): Invalid numeric domain spec \"%s\"",
9347c478bd9Sstevel@tonic-gate 							  m->mf_name, at);
9357c478bd9Sstevel@tonic-gate 					milter_error(m, e);
9367c478bd9Sstevel@tonic-gate 					return -1;
9377c478bd9Sstevel@tonic-gate 				}
9387c478bd9Sstevel@tonic-gate 			}
9397c478bd9Sstevel@tonic-gate 			else
9407c478bd9Sstevel@tonic-gate 			{
9417c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
9427c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
9437c478bd9Sstevel@tonic-gate 						m->mf_name, at);
9447c478bd9Sstevel@tonic-gate 				if (parseonly)
9457c478bd9Sstevel@tonic-gate 					syserr("X%s: Invalid numeric domain spec \"%s\"",
9467c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
9477c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9487c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9497c478bd9Sstevel@tonic-gate 						  "Milter (%s): Invalid numeric domain spec \"%s\"",
9507c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
9517c478bd9Sstevel@tonic-gate 				milter_error(m, e);
9527c478bd9Sstevel@tonic-gate 				return -1;
9537c478bd9Sstevel@tonic-gate 			}
9547c478bd9Sstevel@tonic-gate 		}
9557c478bd9Sstevel@tonic-gate 		else
9567c478bd9Sstevel@tonic-gate 		{
9577c478bd9Sstevel@tonic-gate 			hp = sm_gethostbyname(at, addr.sa.sa_family);
9587c478bd9Sstevel@tonic-gate 			if (hp == NULL)
9597c478bd9Sstevel@tonic-gate 			{
9607c478bd9Sstevel@tonic-gate 				save_errno = errno;
9617c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
9627c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown host name %s\n",
9637c478bd9Sstevel@tonic-gate 						   m->mf_name, at);
9647c478bd9Sstevel@tonic-gate 				errno = save_errno;
9657c478bd9Sstevel@tonic-gate 				if (parseonly)
9667c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown host name %s",
9677c478bd9Sstevel@tonic-gate 					       m->mf_name, at);
9687c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
9697c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
9707c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown host name %s",
9717c478bd9Sstevel@tonic-gate 						  m->mf_name, at);
9727c478bd9Sstevel@tonic-gate 				milter_error(m, e);
9737c478bd9Sstevel@tonic-gate 				return -1;
9747c478bd9Sstevel@tonic-gate 			}
9757c478bd9Sstevel@tonic-gate 			addr.sa.sa_family = hp->h_addrtype;
9767c478bd9Sstevel@tonic-gate 			switch (hp->h_addrtype)
9777c478bd9Sstevel@tonic-gate 			{
9787c478bd9Sstevel@tonic-gate #  if NETINET
9797c478bd9Sstevel@tonic-gate 			  case AF_INET:
9807c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
9817c478bd9Sstevel@tonic-gate 					hp->h_addr, INADDRSZ);
9827c478bd9Sstevel@tonic-gate 				addr.sin.sin_port = port;
983058561cbSjbeck 				addrlen = sizeof(struct sockaddr_in);
9847c478bd9Sstevel@tonic-gate 				addrno = 1;
9857c478bd9Sstevel@tonic-gate 				break;
9867c478bd9Sstevel@tonic-gate #  endif /* NETINET */
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate #  if NETINET6
9897c478bd9Sstevel@tonic-gate 			  case AF_INET6:
9907c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
9917c478bd9Sstevel@tonic-gate 					hp->h_addr, IN6ADDRSZ);
9927c478bd9Sstevel@tonic-gate 				addr.sin6.sin6_port = port;
993058561cbSjbeck 				addrlen = sizeof(struct sockaddr_in6);
9947c478bd9Sstevel@tonic-gate 				addrno = 1;
9957c478bd9Sstevel@tonic-gate 				break;
9967c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
9977c478bd9Sstevel@tonic-gate 
9987c478bd9Sstevel@tonic-gate 			  default:
9997c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
10007c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
10017c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
10027c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
10037c478bd9Sstevel@tonic-gate 				if (parseonly)
10047c478bd9Sstevel@tonic-gate 					syserr("X%s: Unknown protocol for %s (%d)",
10057c478bd9Sstevel@tonic-gate 					       m->mf_name, at, hp->h_addrtype);
10067c478bd9Sstevel@tonic-gate 				else if (MilterLogLevel > 0)
10077c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
10087c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
10097c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
10107c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
10117c478bd9Sstevel@tonic-gate 				milter_error(m, e);
10127c478bd9Sstevel@tonic-gate #  if NETINET6
10137c478bd9Sstevel@tonic-gate 				freehostent(hp);
10147c478bd9Sstevel@tonic-gate #  endif /* NETINET6 */
10157c478bd9Sstevel@tonic-gate 				return -1;
10167c478bd9Sstevel@tonic-gate 			}
10177c478bd9Sstevel@tonic-gate 		}
10187c478bd9Sstevel@tonic-gate 	}
10197c478bd9Sstevel@tonic-gate 	else
10207c478bd9Sstevel@tonic-gate # endif /* NETINET || NETINET6 */
10217c478bd9Sstevel@tonic-gate 	{
10227c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
10237c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: unknown socket protocol\n",
10247c478bd9Sstevel@tonic-gate 				   m->mf_name);
10257c478bd9Sstevel@tonic-gate 		if (parseonly)
10267c478bd9Sstevel@tonic-gate 			syserr("X%s: unknown socket protocol", m->mf_name);
10277c478bd9Sstevel@tonic-gate 		else if (MilterLogLevel > 0)
10287c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
10297c478bd9Sstevel@tonic-gate 				  "Milter (%s): unknown socket protocol",
10307c478bd9Sstevel@tonic-gate 				  m->mf_name);
10317c478bd9Sstevel@tonic-gate 		milter_error(m, e);
10327c478bd9Sstevel@tonic-gate 		return -1;
10337c478bd9Sstevel@tonic-gate 	}
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 	/* just parsing through? */
10367c478bd9Sstevel@tonic-gate 	if (parseonly)
10377c478bd9Sstevel@tonic-gate 	{
10387c478bd9Sstevel@tonic-gate 		m->mf_state = SMFS_READY;
10397c478bd9Sstevel@tonic-gate # if NETINET6
10407c478bd9Sstevel@tonic-gate 		if (hp != NULL)
10417c478bd9Sstevel@tonic-gate 			freehostent(hp);
10427c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10437c478bd9Sstevel@tonic-gate 		return 0;
10447c478bd9Sstevel@tonic-gate 	}
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	/* sanity check */
10477c478bd9Sstevel@tonic-gate 	if (m->mf_state != SMFS_READY &&
10487c478bd9Sstevel@tonic-gate 	    m->mf_state != SMFS_CLOSED)
10497c478bd9Sstevel@tonic-gate 	{
10507c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
10517c478bd9Sstevel@tonic-gate 		if (tTd(64, 1))
10527c478bd9Sstevel@tonic-gate 			sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
10537c478bd9Sstevel@tonic-gate 				   m->mf_name, (char) m->mf_state);
10547c478bd9Sstevel@tonic-gate 		milter_error(m, e);
10557c478bd9Sstevel@tonic-gate # if NETINET6
10567c478bd9Sstevel@tonic-gate 		if (hp != NULL)
10577c478bd9Sstevel@tonic-gate 			freehostent(hp);
10587c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10597c478bd9Sstevel@tonic-gate 		return -1;
10607c478bd9Sstevel@tonic-gate 	}
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	/* nope, actually connecting */
10637c478bd9Sstevel@tonic-gate 	for (;;)
10647c478bd9Sstevel@tonic-gate 	{
10657c478bd9Sstevel@tonic-gate 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
10667c478bd9Sstevel@tonic-gate 		if (sock < 0)
10677c478bd9Sstevel@tonic-gate 		{
10687c478bd9Sstevel@tonic-gate 			save_errno = errno;
10697c478bd9Sstevel@tonic-gate 			if (tTd(64, 5))
10707c478bd9Sstevel@tonic-gate 				sm_dprintf("Milter (%s): error creating socket: %s\n",
10717c478bd9Sstevel@tonic-gate 					   m->mf_name,
10727c478bd9Sstevel@tonic-gate 					   sm_errstring(save_errno));
10737c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 0)
10747c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
10757c478bd9Sstevel@tonic-gate 					  "Milter (%s): error creating socket: %s",
10767c478bd9Sstevel@tonic-gate 					  m->mf_name, sm_errstring(save_errno));
10777c478bd9Sstevel@tonic-gate 			milter_error(m, e);
10787c478bd9Sstevel@tonic-gate # if NETINET6
10797c478bd9Sstevel@tonic-gate 			if (hp != NULL)
10807c478bd9Sstevel@tonic-gate 				freehostent(hp);
10817c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
10827c478bd9Sstevel@tonic-gate 			return -1;
10837c478bd9Sstevel@tonic-gate 		}
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 		if (setjmp(MilterConnectTimeout) == 0)
10867c478bd9Sstevel@tonic-gate 		{
10877c478bd9Sstevel@tonic-gate 			SM_EVENT *ev = NULL;
10887c478bd9Sstevel@tonic-gate 			int i;
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 			if (m->mf_timeout[SMFTO_CONNECT] > 0)
10917c478bd9Sstevel@tonic-gate 				ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
10927c478bd9Sstevel@tonic-gate 						 milter_connect_timeout, 0);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 			i = connect(sock, (struct sockaddr *) &addr, addrlen);
10957c478bd9Sstevel@tonic-gate 			save_errno = errno;
10967c478bd9Sstevel@tonic-gate 			if (ev != NULL)
10977c478bd9Sstevel@tonic-gate 				sm_clrevent(ev);
10987c478bd9Sstevel@tonic-gate 			errno = save_errno;
10997c478bd9Sstevel@tonic-gate 			if (i >= 0)
11007c478bd9Sstevel@tonic-gate 				break;
11017c478bd9Sstevel@tonic-gate 		}
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 		/* couldn't connect.... try next address */
11047c478bd9Sstevel@tonic-gate 		save_errno = errno;
11057c478bd9Sstevel@tonic-gate 		p = CurHostName;
11067c478bd9Sstevel@tonic-gate 		CurHostName = at;
11077c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
11087c478bd9Sstevel@tonic-gate 			sm_dprintf("milter_open (%s): open %s failed: %s\n",
11097c478bd9Sstevel@tonic-gate 				   m->mf_name, at, sm_errstring(save_errno));
11107c478bd9Sstevel@tonic-gate 		if (MilterLogLevel > 13)
11117c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
11127c478bd9Sstevel@tonic-gate 				  "Milter (%s): open %s failed: %s",
11137c478bd9Sstevel@tonic-gate 				  m->mf_name, at, sm_errstring(save_errno));
11147c478bd9Sstevel@tonic-gate 		CurHostName = p;
11157c478bd9Sstevel@tonic-gate 		(void) close(sock);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 		/* try next address */
11187c478bd9Sstevel@tonic-gate 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
11197c478bd9Sstevel@tonic-gate 		{
11207c478bd9Sstevel@tonic-gate 			switch (addr.sa.sa_family)
11217c478bd9Sstevel@tonic-gate 			{
11227c478bd9Sstevel@tonic-gate # if NETINET
11237c478bd9Sstevel@tonic-gate 			  case AF_INET:
11247c478bd9Sstevel@tonic-gate 				memmove(&addr.sin.sin_addr,
11257c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11267c478bd9Sstevel@tonic-gate 					INADDRSZ);
11277c478bd9Sstevel@tonic-gate 				break;
11287c478bd9Sstevel@tonic-gate # endif /* NETINET */
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate # if NETINET6
11317c478bd9Sstevel@tonic-gate 			  case AF_INET6:
11327c478bd9Sstevel@tonic-gate 				memmove(&addr.sin6.sin6_addr,
11337c478bd9Sstevel@tonic-gate 					hp->h_addr_list[addrno++],
11347c478bd9Sstevel@tonic-gate 					IN6ADDRSZ);
11357c478bd9Sstevel@tonic-gate 				break;
11367c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 			  default:
11397c478bd9Sstevel@tonic-gate 				if (tTd(64, 5))
11407c478bd9Sstevel@tonic-gate 					sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
11417c478bd9Sstevel@tonic-gate 						   m->mf_name, at,
11427c478bd9Sstevel@tonic-gate 						   hp->h_addrtype);
11437c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 0)
11447c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
11457c478bd9Sstevel@tonic-gate 						  "Milter (%s): Unknown protocol for %s (%d)",
11467c478bd9Sstevel@tonic-gate 						  m->mf_name, at,
11477c478bd9Sstevel@tonic-gate 						  hp->h_addrtype);
11487c478bd9Sstevel@tonic-gate 				milter_error(m, e);
11497c478bd9Sstevel@tonic-gate # if NETINET6
11507c478bd9Sstevel@tonic-gate 				freehostent(hp);
11517c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
11527c478bd9Sstevel@tonic-gate 				return -1;
11537c478bd9Sstevel@tonic-gate 			}
11547c478bd9Sstevel@tonic-gate 			continue;
11557c478bd9Sstevel@tonic-gate 		}
11567c478bd9Sstevel@tonic-gate 		p = CurHostName;
11577c478bd9Sstevel@tonic-gate 		CurHostName = at;
11587c478bd9Sstevel@tonic-gate 		if (tTd(64, 5))
11597c478bd9Sstevel@tonic-gate 			sm_dprintf("X%s: error connecting to filter: %s\n",
11607c478bd9Sstevel@tonic-gate