listener.c revision 141858
164562Sgshapiro/*
2141858Sgshapiro *  Copyright (c) 1999-2004 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12141858SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.111 2004/09/20 21:11:15 msk Exp $")
1364562Sgshapiro
1464562Sgshapiro/*
1564562Sgshapiro**  listener.c -- threaded network listener
1664562Sgshapiro*/
1764562Sgshapiro
1864562Sgshapiro#include "libmilter.h"
1990792Sgshapiro#include <sm/errstring.h>
2064562Sgshapiro
21125820Sgshapiro#include <sys/types.h>
22125820Sgshapiro#include <sys/stat.h>
2364562Sgshapiro
24125820Sgshapiro
2564562Sgshapiro# if NETINET || NETINET6
2664562Sgshapiro#  include <arpa/inet.h>
2764562Sgshapiro# endif /* NETINET || NETINET6 */
2890792Sgshapiro
2990792Sgshapirostatic smutex_t L_Mutex;
3098121Sgshapirostatic int L_family;
3198121Sgshapirostatic SOCKADDR_LEN_T L_socksize;
3298121Sgshapirostatic socket_t listenfd = INVALID_SOCKET;
3390792Sgshapiro
34125820Sgshapirostatic socket_t mi_milteropen __P((char *, int, bool, char *));
35141858Sgshapirostatic void *mi_thread_handle_wrapper __P((void *));
3698121Sgshapiro
3790792Sgshapiro/*
3898121Sgshapiro**  MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
3998121Sgshapiro**
40125820Sgshapiro**	Parameters:
4198121Sgshapiro**		conn -- connection description
4298121Sgshapiro**		backlog -- listen backlog
43125820Sgshapiro**		dbg -- debug level
44125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
45132943Sgshapiro**			(UNIX domain sockets only)
4698121Sgshapiro**		smfi -- filter structure to use
4798121Sgshapiro**
48125820Sgshapiro**	Return value:
49125820Sgshapiro**		MI_SUCCESS/MI_FAILURE
5098121Sgshapiro*/
5198121Sgshapiro
5298121Sgshapiroint
53125820Sgshapiromi_opensocket(conn, backlog, dbg, rmsocket, smfi)
5498121Sgshapiro	char *conn;
5598121Sgshapiro	int backlog;
5698121Sgshapiro	int dbg;
57125820Sgshapiro	bool rmsocket;
5898121Sgshapiro	smfiDesc_ptr smfi;
5998121Sgshapiro{
6098121Sgshapiro	if (smfi == NULL || conn == NULL)
6198121Sgshapiro		return MI_FAILURE;
6298121Sgshapiro
6398121Sgshapiro	if (ValidSocket(listenfd))
6498121Sgshapiro		return MI_SUCCESS;
6598121Sgshapiro
6698121Sgshapiro	if (dbg > 0)
6798121Sgshapiro	{
6898121Sgshapiro		smi_log(SMI_LOG_DEBUG,
6998121Sgshapiro			"%s: Opening listen socket on conn %s",
7098121Sgshapiro			smfi->xxfi_name, conn);
7198121Sgshapiro	}
7298121Sgshapiro	(void) smutex_init(&L_Mutex);
7398121Sgshapiro	(void) smutex_lock(&L_Mutex);
74125820Sgshapiro	listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
7598121Sgshapiro	if (!ValidSocket(listenfd))
7698121Sgshapiro	{
7798121Sgshapiro		smi_log(SMI_LOG_FATAL,
7898121Sgshapiro			"%s: Unable to create listening socket on conn %s",
7998121Sgshapiro			smfi->xxfi_name, conn);
8098121Sgshapiro		(void) smutex_unlock(&L_Mutex);
8198121Sgshapiro		return MI_FAILURE;
8298121Sgshapiro	}
83132943Sgshapiro#if !SM_CONF_POLL
84110560Sgshapiro	if (!SM_FD_OK_SELECT(listenfd))
85110560Sgshapiro	{
86110560Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
87110560Sgshapiro			smfi->xxfi_name, listenfd, FD_SETSIZE);
88110560Sgshapiro		(void) smutex_unlock(&L_Mutex);
89110560Sgshapiro		return MI_FAILURE;
90110560Sgshapiro	}
91132943Sgshapiro#endif /* !SM_CONF_POLL */
92141858Sgshapiro	(void) smutex_unlock(&L_Mutex);
9398121Sgshapiro	return MI_SUCCESS;
9498121Sgshapiro}
9598121Sgshapiro
9698121Sgshapiro/*
9764562Sgshapiro**  MI_MILTEROPEN -- setup socket to listen on
9864562Sgshapiro**
9964562Sgshapiro**	Parameters:
10064562Sgshapiro**		conn -- connection description
10164562Sgshapiro**		backlog -- listen backlog
102125820Sgshapiro**		rmsocket -- if true, try to unlink() the socket first
103125820Sgshapiro**			(UNIX domain sockets only)
10473188Sgshapiro**		name -- name for logging
10564562Sgshapiro**
10664562Sgshapiro**	Returns:
10764562Sgshapiro**		socket upon success, error code otherwise.
10890792Sgshapiro**
10990792Sgshapiro**	Side effect:
11090792Sgshapiro**		sets sockpath if UNIX socket.
11164562Sgshapiro*/
11264562Sgshapiro
11390792Sgshapiro#if NETUNIX
11490792Sgshapirostatic char	*sockpath = NULL;
11590792Sgshapiro#endif /* NETUNIX */
11690792Sgshapiro
11766494Sgshapirostatic socket_t
118125820Sgshapiromi_milteropen(conn, backlog, rmsocket, name)
11964562Sgshapiro	char *conn;
12064562Sgshapiro	int backlog;
121125820Sgshapiro	bool rmsocket;
12264562Sgshapiro	char *name;
12364562Sgshapiro{
12466494Sgshapiro	socket_t sock;
12564562Sgshapiro	int sockopt = 1;
12698121Sgshapiro	int fdflags;
12790792Sgshapiro	size_t len = 0;
12864562Sgshapiro	char *p;
12964562Sgshapiro	char *colon;
13064562Sgshapiro	char *at;
13164562Sgshapiro	SOCKADDR addr;
13264562Sgshapiro
13364562Sgshapiro	if (conn == NULL || conn[0] == '\0')
13464562Sgshapiro	{
13564562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
13664562Sgshapiro			name);
13766494Sgshapiro		return INVALID_SOCKET;
13864562Sgshapiro	}
13964562Sgshapiro	(void) memset(&addr, '\0', sizeof addr);
14064562Sgshapiro
14164562Sgshapiro	/* protocol:filename or protocol:port@host */
14264562Sgshapiro	p = conn;
14364562Sgshapiro	colon = strchr(p, ':');
14464562Sgshapiro	if (colon != NULL)
14564562Sgshapiro	{
14664562Sgshapiro		*colon = '\0';
14764562Sgshapiro
14894334Sgshapiro		if (*p == '\0')
14964562Sgshapiro		{
15064562Sgshapiro#if NETUNIX
15164562Sgshapiro			/* default to AF_UNIX */
15294334Sgshapiro			addr.sa.sa_family = AF_UNIX;
15398121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
15464562Sgshapiro#else /* NETUNIX */
15564562Sgshapiro# if NETINET
15664562Sgshapiro			/* default to AF_INET */
15764562Sgshapiro			addr.sa.sa_family = AF_INET;
15898121Sgshapiro			L_socksize = sizeof addr.sin;
15964562Sgshapiro# else /* NETINET */
16064562Sgshapiro#  if NETINET6
16164562Sgshapiro			/* default to AF_INET6 */
16264562Sgshapiro			addr.sa.sa_family = AF_INET6;
16398121Sgshapiro			L_socksize = sizeof addr.sin6;
16464562Sgshapiro#  else /* NETINET6 */
16564562Sgshapiro			/* no protocols available */
16664562Sgshapiro			smi_log(SMI_LOG_ERR,
16764562Sgshapiro				"%s: no valid socket protocols available",
16864562Sgshapiro				name);
16966494Sgshapiro			return INVALID_SOCKET;
17064562Sgshapiro#  endif /* NETINET6 */
17164562Sgshapiro# endif /* NETINET */
17264562Sgshapiro#endif /* NETUNIX */
17364562Sgshapiro		}
17464562Sgshapiro#if NETUNIX
17564562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
17664562Sgshapiro			 strcasecmp(p, "local") == 0)
17764562Sgshapiro		{
17864562Sgshapiro			addr.sa.sa_family = AF_UNIX;
17998121Sgshapiro			L_socksize = sizeof (struct sockaddr_un);
18064562Sgshapiro		}
18164562Sgshapiro#endif /* NETUNIX */
18264562Sgshapiro#if NETINET
18364562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
18464562Sgshapiro		{
18564562Sgshapiro			addr.sa.sa_family = AF_INET;
18698121Sgshapiro			L_socksize = sizeof addr.sin;
18764562Sgshapiro		}
18864562Sgshapiro#endif /* NETINET */
18964562Sgshapiro#if NETINET6
19064562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
19164562Sgshapiro		{
19264562Sgshapiro			addr.sa.sa_family = AF_INET6;
19398121Sgshapiro			L_socksize = sizeof addr.sin6;
19464562Sgshapiro		}
19564562Sgshapiro#endif /* NETINET6 */
19664562Sgshapiro		else
19764562Sgshapiro		{
19864562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
19964562Sgshapiro				name, p);
20066494Sgshapiro			return INVALID_SOCKET;
20164562Sgshapiro		}
20264562Sgshapiro		*colon++ = ':';
20364562Sgshapiro	}
20464562Sgshapiro	else
20564562Sgshapiro	{
20664562Sgshapiro		colon = p;
20764562Sgshapiro#if NETUNIX
20864562Sgshapiro		/* default to AF_UNIX */
20994334Sgshapiro		addr.sa.sa_family = AF_UNIX;
21098121Sgshapiro		L_socksize = sizeof (struct sockaddr_un);
21164562Sgshapiro#else /* NETUNIX */
21264562Sgshapiro# if NETINET
21364562Sgshapiro		/* default to AF_INET */
21464562Sgshapiro		addr.sa.sa_family = AF_INET;
21598121Sgshapiro		L_socksize = sizeof addr.sin;
21664562Sgshapiro# else /* NETINET */
21764562Sgshapiro#  if NETINET6
21864562Sgshapiro		/* default to AF_INET6 */
21964562Sgshapiro		addr.sa.sa_family = AF_INET6;
22098121Sgshapiro		L_socksize = sizeof addr.sin6;
22164562Sgshapiro#  else /* NETINET6 */
22264562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
22364562Sgshapiro			name, p);
22466494Sgshapiro		return INVALID_SOCKET;
22564562Sgshapiro#  endif /* NETINET6 */
22664562Sgshapiro# endif /* NETINET */
22764562Sgshapiro#endif /* NETUNIX */
22864562Sgshapiro	}
22964562Sgshapiro
23064562Sgshapiro#if NETUNIX
23164562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
23264562Sgshapiro	{
23364562Sgshapiro# if 0
23464562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
23564562Sgshapiro# endif /* 0 */
23664562Sgshapiro
23764562Sgshapiro		at = colon;
23890792Sgshapiro		len = strlen(colon) + 1;
23990792Sgshapiro		if (len >= sizeof addr.sunix.sun_path)
24064562Sgshapiro		{
24164562Sgshapiro			errno = EINVAL;
24264562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
24364562Sgshapiro				name, colon);
24466494Sgshapiro			return INVALID_SOCKET;
24564562Sgshapiro		}
24690792Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
24790792Sgshapiro				sizeof addr.sunix.sun_path);
24864562Sgshapiro# if 0
24964562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
25064562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
25164562Sgshapiro
25264562Sgshapiro		/* if not safe, don't create */
25364562Sgshapiro		if (errno != 0)
25464562Sgshapiro		{
25564562Sgshapiro			smi_log(SMI_LOG_ERR,
25664562Sgshapiro				"%s: UNIX socket name %s unsafe",
25764562Sgshapiro				name, colon);
25866494Sgshapiro			return INVALID_SOCKET;
25964562Sgshapiro		}
26064562Sgshapiro# endif /* 0 */
26164562Sgshapiro	}
26264562Sgshapiro#endif /* NETUNIX */
26364562Sgshapiro
26464562Sgshapiro#if NETINET || NETINET6
26564562Sgshapiro	if (
26664562Sgshapiro# if NETINET
26764562Sgshapiro	    addr.sa.sa_family == AF_INET
26864562Sgshapiro# endif /* NETINET */
26964562Sgshapiro# if NETINET && NETINET6
27064562Sgshapiro	    ||
27164562Sgshapiro# endif /* NETINET && NETINET6 */
27264562Sgshapiro# if NETINET6
27364562Sgshapiro	    addr.sa.sa_family == AF_INET6
27464562Sgshapiro# endif /* NETINET6 */
27564562Sgshapiro	   )
27664562Sgshapiro	{
27790792Sgshapiro		unsigned short port;
27864562Sgshapiro
27964562Sgshapiro		/* Parse port@host */
28064562Sgshapiro		at = strchr(colon, '@');
28164562Sgshapiro		if (at == NULL)
28264562Sgshapiro		{
28364562Sgshapiro			switch (addr.sa.sa_family)
28464562Sgshapiro			{
28564562Sgshapiro# if NETINET
28664562Sgshapiro			  case AF_INET:
28764562Sgshapiro				addr.sin.sin_addr.s_addr = INADDR_ANY;
28864562Sgshapiro				break;
28964562Sgshapiro# endif /* NETINET */
29064562Sgshapiro
29164562Sgshapiro# if NETINET6
29264562Sgshapiro			  case AF_INET6:
29364562Sgshapiro				addr.sin6.sin6_addr = in6addr_any;
29464562Sgshapiro				break;
29564562Sgshapiro# endif /* NETINET6 */
29664562Sgshapiro			}
29764562Sgshapiro		}
29864562Sgshapiro		else
29964562Sgshapiro			*at = '\0';
30064562Sgshapiro
30164562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
30290792Sgshapiro			port = htons((unsigned short) atoi(colon));
30364562Sgshapiro		else
30464562Sgshapiro		{
30564562Sgshapiro# ifdef NO_GETSERVBYNAME
30664562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
30764562Sgshapiro				name, colon);
30866494Sgshapiro			return INVALID_SOCKET;
30964562Sgshapiro# else /* NO_GETSERVBYNAME */
31064562Sgshapiro			register struct servent *sp;
31164562Sgshapiro
31264562Sgshapiro			sp = getservbyname(colon, "tcp");
31364562Sgshapiro			if (sp == NULL)
31464562Sgshapiro			{
31564562Sgshapiro				smi_log(SMI_LOG_ERR,
31664562Sgshapiro					"%s: unknown port name %s",
31764562Sgshapiro					name, colon);
31866494Sgshapiro				return INVALID_SOCKET;
31964562Sgshapiro			}
32064562Sgshapiro			port = sp->s_port;
32164562Sgshapiro# endif /* NO_GETSERVBYNAME */
32264562Sgshapiro		}
32364562Sgshapiro		if (at != NULL)
32464562Sgshapiro		{
32564562Sgshapiro			*at++ = '@';
32664562Sgshapiro			if (*at == '[')
32764562Sgshapiro			{
32864562Sgshapiro				char *end;
32964562Sgshapiro
33064562Sgshapiro				end = strchr(at, ']');
33164562Sgshapiro				if (end != NULL)
33264562Sgshapiro				{
33390792Sgshapiro					bool found = false;
33464562Sgshapiro# if NETINET
33564562Sgshapiro					unsigned long hid = INADDR_NONE;
33664562Sgshapiro# endif /* NETINET */
33764562Sgshapiro# if NETINET6
33864562Sgshapiro					struct sockaddr_in6 hid6;
33964562Sgshapiro# endif /* NETINET6 */
34064562Sgshapiro
34164562Sgshapiro					*end = '\0';
34264562Sgshapiro# if NETINET
34364562Sgshapiro					if (addr.sa.sa_family == AF_INET &&
34490792Sgshapiro					    (hid = inet_addr(&at[1])) != INADDR_NONE)
34564562Sgshapiro					{
34664562Sgshapiro						addr.sin.sin_addr.s_addr = hid;
34764562Sgshapiro						addr.sin.sin_port = port;
34890792Sgshapiro						found = true;
34964562Sgshapiro					}
35064562Sgshapiro# endif /* NETINET */
35164562Sgshapiro# if NETINET6
35264562Sgshapiro					(void) memset(&hid6, '\0', sizeof hid6);
35364562Sgshapiro					if (addr.sa.sa_family == AF_INET6 &&
35490792Sgshapiro					    mi_inet_pton(AF_INET6, &at[1],
35590792Sgshapiro							 &hid6.sin6_addr) == 1)
35664562Sgshapiro					{
35764562Sgshapiro						addr.sin6.sin6_addr = hid6.sin6_addr;
35864562Sgshapiro						addr.sin6.sin6_port = port;
35990792Sgshapiro						found = true;
36064562Sgshapiro					}
36164562Sgshapiro# endif /* NETINET6 */
36264562Sgshapiro					*end = ']';
36364562Sgshapiro					if (!found)
36464562Sgshapiro					{
36564562Sgshapiro						smi_log(SMI_LOG_ERR,
36664562Sgshapiro							"%s: Invalid numeric domain spec \"%s\"",
36764562Sgshapiro							name, at);
36866494Sgshapiro						return INVALID_SOCKET;
36964562Sgshapiro					}
37064562Sgshapiro				}
37164562Sgshapiro				else
37264562Sgshapiro				{
37364562Sgshapiro					smi_log(SMI_LOG_ERR,
37464562Sgshapiro						"%s: Invalid numeric domain spec \"%s\"",
37564562Sgshapiro						name, at);
37666494Sgshapiro					return INVALID_SOCKET;
37764562Sgshapiro				}
37864562Sgshapiro			}
37964562Sgshapiro			else
38064562Sgshapiro			{
38171345Sgshapiro				struct hostent *hp = NULL;
38271345Sgshapiro
38364562Sgshapiro				hp = mi_gethostbyname(at, addr.sa.sa_family);
38464562Sgshapiro				if (hp == NULL)
38564562Sgshapiro				{
38664562Sgshapiro					smi_log(SMI_LOG_ERR,
38764562Sgshapiro						"%s: Unknown host name %s",
38864562Sgshapiro						name, at);
38966494Sgshapiro					return INVALID_SOCKET;
39064562Sgshapiro				}
39164562Sgshapiro				addr.sa.sa_family = hp->h_addrtype;
39264562Sgshapiro				switch (hp->h_addrtype)
39364562Sgshapiro				{
39464562Sgshapiro# if NETINET
39564562Sgshapiro				  case AF_INET:
396120256Sgshapiro					(void) memmove(&addr.sin.sin_addr,
397120256Sgshapiro						       hp->h_addr,
398120256Sgshapiro						       INADDRSZ);
39964562Sgshapiro					addr.sin.sin_port = port;
40064562Sgshapiro					break;
40164562Sgshapiro# endif /* NETINET */
40264562Sgshapiro
40364562Sgshapiro# if NETINET6
40464562Sgshapiro				  case AF_INET6:
405120256Sgshapiro					(void) memmove(&addr.sin6.sin6_addr,
406120256Sgshapiro						       hp->h_addr,
407120256Sgshapiro						       IN6ADDRSZ);
40864562Sgshapiro					addr.sin6.sin6_port = port;
40964562Sgshapiro					break;
41064562Sgshapiro# endif /* NETINET6 */
41164562Sgshapiro
41264562Sgshapiro				  default:
41364562Sgshapiro					smi_log(SMI_LOG_ERR,
41464562Sgshapiro						"%s: Unknown protocol for %s (%d)",
41564562Sgshapiro						name, at, hp->h_addrtype);
41666494Sgshapiro					return INVALID_SOCKET;
41764562Sgshapiro				}
41890792Sgshapiro# if NETINET6
41971345Sgshapiro				freehostent(hp);
42090792Sgshapiro# endif /* NETINET6 */
42164562Sgshapiro			}
42264562Sgshapiro		}
42364562Sgshapiro		else
42464562Sgshapiro		{
42564562Sgshapiro			switch (addr.sa.sa_family)
42664562Sgshapiro			{
42764562Sgshapiro# if NETINET
42864562Sgshapiro			  case AF_INET:
42964562Sgshapiro				addr.sin.sin_port = port;
43064562Sgshapiro				break;
43164562Sgshapiro# endif /* NETINET */
43264562Sgshapiro# if NETINET6
43364562Sgshapiro			  case AF_INET6:
43464562Sgshapiro				addr.sin6.sin6_port = port;
43564562Sgshapiro				break;
43664562Sgshapiro# endif /* NETINET6 */
43764562Sgshapiro			}
43864562Sgshapiro		}
43964562Sgshapiro	}
44064562Sgshapiro#endif /* NETINET || NETINET6 */
44164562Sgshapiro
44264562Sgshapiro	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
44364562Sgshapiro	if (!ValidSocket(sock))
44464562Sgshapiro	{
44564562Sgshapiro		smi_log(SMI_LOG_ERR,
44664562Sgshapiro			"%s: Unable to create new socket: %s",
44790792Sgshapiro			name, sm_errstring(errno));
44866494Sgshapiro		return INVALID_SOCKET;
44964562Sgshapiro	}
45064562Sgshapiro
45198121Sgshapiro	if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
45298121Sgshapiro	    fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
45398121Sgshapiro	{
45498121Sgshapiro		smi_log(SMI_LOG_ERR,
45598121Sgshapiro			"%s: Unable to set close-on-exec: %s", name,
45698121Sgshapiro			sm_errstring(errno));
45798121Sgshapiro		(void) closesocket(sock);
45898121Sgshapiro		return INVALID_SOCKET;
45998121Sgshapiro	}
46098121Sgshapiro
46164562Sgshapiro	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
46264562Sgshapiro		       sizeof(sockopt)) == -1)
46364562Sgshapiro	{
46464562Sgshapiro		smi_log(SMI_LOG_ERR,
46590792Sgshapiro			"%s: Unable to setsockopt: %s", name,
46690792Sgshapiro			sm_errstring(errno));
46790792Sgshapiro		(void) closesocket(sock);
46866494Sgshapiro		return INVALID_SOCKET;
46964562Sgshapiro	}
47064562Sgshapiro
471125820Sgshapiro#if NETUNIX
472125820Sgshapiro	if (addr.sa.sa_family == AF_UNIX && rmsocket)
473125820Sgshapiro	{
474125820Sgshapiro		struct stat s;
475125820Sgshapiro
476125820Sgshapiro		if (stat(colon, &s) != 0)
477125820Sgshapiro		{
478125820Sgshapiro			if (errno != ENOENT)
479125820Sgshapiro			{
480125820Sgshapiro				smi_log(SMI_LOG_ERR,
481125820Sgshapiro					"%s: Unable to stat() %s: %s",
482125820Sgshapiro					name, colon, sm_errstring(errno));
483125820Sgshapiro				(void) closesocket(sock);
484125820Sgshapiro				return INVALID_SOCKET;
485125820Sgshapiro			}
486125820Sgshapiro		}
487125820Sgshapiro		else if (!S_ISSOCK(s.st_mode))
488125820Sgshapiro		{
489125820Sgshapiro			smi_log(SMI_LOG_ERR,
490125820Sgshapiro				"%s: %s is not a UNIX domain socket",
491125820Sgshapiro				name, colon);
492125820Sgshapiro			(void) closesocket(sock);
493125820Sgshapiro			return INVALID_SOCKET;
494125820Sgshapiro		}
495125820Sgshapiro		else if (unlink(colon) != 0)
496125820Sgshapiro		{
497125820Sgshapiro			smi_log(SMI_LOG_ERR,
498125820Sgshapiro				"%s: Unable to remove %s: %s",
499125820Sgshapiro				name, colon, sm_errstring(errno));
500125820Sgshapiro			(void) closesocket(sock);
501125820Sgshapiro			return INVALID_SOCKET;
502125820Sgshapiro		}
503125820Sgshapiro	}
504125820Sgshapiro#endif /* NETUNIX */
505125820Sgshapiro
50698121Sgshapiro	if (bind(sock, &addr.sa, L_socksize) < 0)
50764562Sgshapiro	{
50864562Sgshapiro		smi_log(SMI_LOG_ERR,
50964562Sgshapiro			"%s: Unable to bind to port %s: %s",
51090792Sgshapiro			name, conn, sm_errstring(errno));
51190792Sgshapiro		(void) closesocket(sock);
51266494Sgshapiro		return INVALID_SOCKET;
51364562Sgshapiro	}
51464562Sgshapiro
51564562Sgshapiro	if (listen(sock, backlog) < 0)
51664562Sgshapiro	{
51764562Sgshapiro		smi_log(SMI_LOG_ERR,
51890792Sgshapiro			"%s: listen call failed: %s", name,
51990792Sgshapiro			sm_errstring(errno));
52090792Sgshapiro		(void) closesocket(sock);
52166494Sgshapiro		return INVALID_SOCKET;
52264562Sgshapiro	}
52390792Sgshapiro
52490792Sgshapiro#if NETUNIX
52590792Sgshapiro	if (addr.sa.sa_family == AF_UNIX && len > 0)
52690792Sgshapiro	{
52790792Sgshapiro		/*
52890792Sgshapiro		**  Set global variable sockpath so the UNIX socket can be
52990792Sgshapiro		**  unlink()ed at exit.
53090792Sgshapiro		*/
53190792Sgshapiro
53290792Sgshapiro		sockpath = (char *) malloc(len);
53390792Sgshapiro		if (sockpath != NULL)
53490792Sgshapiro			(void) sm_strlcpy(sockpath, colon, len);
53590792Sgshapiro		else
53690792Sgshapiro		{
53790792Sgshapiro			smi_log(SMI_LOG_ERR,
53890792Sgshapiro				"%s: can't malloc(%d) for sockpath: %s",
539110560Sgshapiro				name, (int) len, sm_errstring(errno));
54090792Sgshapiro			(void) closesocket(sock);
54190792Sgshapiro			return INVALID_SOCKET;
54290792Sgshapiro		}
54390792Sgshapiro	}
54490792Sgshapiro#endif /* NETUNIX */
54598121Sgshapiro	L_family = addr.sa.sa_family;
54664562Sgshapiro	return sock;
54764562Sgshapiro}
54890792Sgshapiro/*
54964562Sgshapiro**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
55064562Sgshapiro**
55164562Sgshapiro**	Parameters:
55264562Sgshapiro**		arg -- argument to pass to mi_handle_session()
55364562Sgshapiro**
55464562Sgshapiro**	Returns:
55564562Sgshapiro**		results from mi_handle_session()
55664562Sgshapiro*/
55764562Sgshapiro
558141858Sgshapirostatic void *
55964562Sgshapiromi_thread_handle_wrapper(arg)
56064562Sgshapiro	void *arg;
56164562Sgshapiro{
56264562Sgshapiro	return (void *) mi_handle_session(arg);
56364562Sgshapiro}
56464562Sgshapiro
56590792Sgshapiro/*
56666494Sgshapiro**  MI_CLOSENER -- close listen socket
56764562Sgshapiro**
56890792Sgshapiro**	NOTE: It is assumed that this function is called from a
56990792Sgshapiro**	      function that has a mutex lock (currently mi_stop_milters()).
57090792Sgshapiro**
57166494Sgshapiro**	Parameters:
57266494Sgshapiro**		none.
57366494Sgshapiro**
57466494Sgshapiro**	Returns:
57566494Sgshapiro**		none.
57666494Sgshapiro*/
57766494Sgshapiro
57866494Sgshapirovoid
57966494Sgshapiromi_closener()
58066494Sgshapiro{
58171345Sgshapiro	(void) smutex_lock(&L_Mutex);
58266494Sgshapiro	if (ValidSocket(listenfd))
58366494Sgshapiro	{
58490792Sgshapiro#if NETUNIX
58590792Sgshapiro		bool removable;
58690792Sgshapiro		struct stat sockinfo;
58790792Sgshapiro		struct stat fileinfo;
58890792Sgshapiro
58990792Sgshapiro		removable = sockpath != NULL &&
59090792Sgshapiro			    geteuid() != 0 &&
59190792Sgshapiro			    fstat(listenfd, &sockinfo) == 0 &&
59290792Sgshapiro			    (S_ISFIFO(sockinfo.st_mode)
59390792Sgshapiro# ifdef S_ISSOCK
59490792Sgshapiro			     || S_ISSOCK(sockinfo.st_mode)
59590792Sgshapiro# endif /* S_ISSOCK */
59690792Sgshapiro			    );
59790792Sgshapiro#endif /* NETUNIX */
59890792Sgshapiro
59990792Sgshapiro		(void) closesocket(listenfd);
60066494Sgshapiro		listenfd = INVALID_SOCKET;
60190792Sgshapiro
60290792Sgshapiro#if NETUNIX
60390792Sgshapiro		/* XXX sleep() some time before doing this? */
60490792Sgshapiro		if (sockpath != NULL)
60590792Sgshapiro		{
60690792Sgshapiro			if (removable &&
60790792Sgshapiro			    stat(sockpath, &fileinfo) == 0 &&
60890792Sgshapiro			    ((fileinfo.st_dev == sockinfo.st_dev &&
60990792Sgshapiro			      fileinfo.st_ino == sockinfo.st_ino)
61090792Sgshapiro# ifdef S_ISSOCK
61190792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
61290792Sgshapiro# endif /* S_ISSOCK */
61390792Sgshapiro			    )
61490792Sgshapiro			    &&
61590792Sgshapiro			    (S_ISFIFO(fileinfo.st_mode)
61690792Sgshapiro# ifdef S_ISSOCK
61790792Sgshapiro			     || S_ISSOCK(fileinfo.st_mode)
61890792Sgshapiro# endif /* S_ISSOCK */
61990792Sgshapiro			     ))
62090792Sgshapiro				(void) unlink(sockpath);
62190792Sgshapiro			free(sockpath);
62290792Sgshapiro			sockpath = NULL;
62390792Sgshapiro		}
62490792Sgshapiro#endif /* NETUNIX */
62566494Sgshapiro	}
62671345Sgshapiro	(void) smutex_unlock(&L_Mutex);
62766494Sgshapiro}
62866494Sgshapiro
62990792Sgshapiro/*
63066494Sgshapiro**  MI_LISTENER -- Generic listener harness
63166494Sgshapiro**
63264562Sgshapiro**	Open up listen port
63364562Sgshapiro**	Wait for connections
63464562Sgshapiro**
63564562Sgshapiro**	Parameters:
63664562Sgshapiro**		conn -- connection description
63764562Sgshapiro**		dbg -- debug level
63864562Sgshapiro**		smfi -- filter structure to use
63964562Sgshapiro**		timeout -- timeout for reads/writes
640125820Sgshapiro**		backlog -- listen queue backlog size
64164562Sgshapiro**
64264562Sgshapiro**	Returns:
64364562Sgshapiro**		MI_SUCCESS -- Exited normally
64464562Sgshapiro**			   (session finished or we were told to exit)
64564562Sgshapiro**		MI_FAILURE -- Network initialization failed.
64664562Sgshapiro*/
64764562Sgshapiro
64890792Sgshapiro#if BROKEN_PTHREAD_SLEEP
64973188Sgshapiro
65073188Sgshapiro/*
65173188Sgshapiro**  Solaris 2.6, perhaps others, gets an internal threads library panic
65273188Sgshapiro**  when sleep() is used:
65373188Sgshapiro**
65473188Sgshapiro**  thread_create() failed, returned 11 (EINVAL)
65573188Sgshapiro**  co_enable, thr_create() returned error = 24
65673188Sgshapiro**  libthread panic: co_enable failed (PID: 17793 LWP 1)
65773188Sgshapiro**  stacktrace:
65873188Sgshapiro**	ef526b10
65973188Sgshapiro**	ef52646c
66073188Sgshapiro**	ef534cbc
66173188Sgshapiro**	156a4
66273188Sgshapiro**	14644
66373188Sgshapiro**	1413c
66473188Sgshapiro**	135e0
66573188Sgshapiro**	0
66673188Sgshapiro*/
66773188Sgshapiro
66890792Sgshapiro# define MI_SLEEP(s)							\
66973188Sgshapiro{									\
67073188Sgshapiro	int rs = 0;							\
67173188Sgshapiro	struct timeval st;						\
67273188Sgshapiro									\
67373188Sgshapiro	st.tv_sec = (s);						\
67473188Sgshapiro	st.tv_usec = 0;							\
67573188Sgshapiro	if (st.tv_sec > 0)						\
67673188Sgshapiro	{								\
677102528Sgshapiro		for (;;)						\
678102528Sgshapiro		{							\
679102528Sgshapiro			rs = select(0, NULL, NULL, NULL, &st);		\
680102528Sgshapiro			if (rs < 0 && errno == EINTR)			\
681102528Sgshapiro				continue;				\
682102528Sgshapiro			if (rs != 0)					\
683102528Sgshapiro			{						\
684102528Sgshapiro				smi_log(SMI_LOG_ERR,			\
685110560Sgshapiro					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
686102528Sgshapiro					rs, errno);			\
687102528Sgshapiro			}						\
688110560Sgshapiro			break;						\
689102528Sgshapiro		}							\
69073188Sgshapiro	}								\
69173188Sgshapiro}
69290792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */
69390792Sgshapiro# define MI_SLEEP(s)	sleep((s))
69490792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */
69573188Sgshapiro
69664562Sgshapiroint
69766494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog)
69864562Sgshapiro	char *conn;
69964562Sgshapiro	int dbg;
70064562Sgshapiro	smfiDesc_ptr smfi;
70164562Sgshapiro	time_t timeout;
70266494Sgshapiro	int backlog;
70364562Sgshapiro{
70466494Sgshapiro	socket_t connfd = INVALID_SOCKET;
705132943Sgshapiro#if _FFR_DUP_FD
706132943Sgshapiro	socket_t dupfd = INVALID_SOCKET;
707132943Sgshapiro#endif /* _FFR_DUP_FD */
70864562Sgshapiro	int sockopt = 1;
709120256Sgshapiro	int r, mistop;
71064562Sgshapiro	int ret = MI_SUCCESS;
71190792Sgshapiro	int mcnt = 0;	/* error count for malloc() failures */
71290792Sgshapiro	int tcnt = 0;	/* error count for thread_create() failures */
71390792Sgshapiro	int acnt = 0;	/* error count for accept() failures */
71490792Sgshapiro	int scnt = 0;	/* error count for select() failures */
71577349Sgshapiro	int save_errno = 0;
71664562Sgshapiro	sthread_t thread_id;
71764562Sgshapiro	_SOCK_ADDR cliaddr;
71864562Sgshapiro	SOCKADDR_LEN_T clilen;
71964562Sgshapiro	SMFICTX_PTR ctx;
720111823Sgshapiro	FD_RD_VAR(rds, excs);
72164562Sgshapiro	struct timeval chktime;
72264562Sgshapiro
723125820Sgshapiro	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
72464562Sgshapiro		return MI_FAILURE;
72571345Sgshapiro
72698121Sgshapiro	clilen = L_socksize;
727120256Sgshapiro	while ((mistop = mi_stop()) == MILTER_CONT)
72864562Sgshapiro	{
72971345Sgshapiro		(void) smutex_lock(&L_Mutex);
73071345Sgshapiro		if (!ValidSocket(listenfd))
73171345Sgshapiro		{
732120256Sgshapiro			ret = MI_FAILURE;
733120256Sgshapiro			smi_log(SMI_LOG_ERR,
734120256Sgshapiro				"%s: listenfd=%d corrupted, terminating, errno=%d",
735120256Sgshapiro				smfi->xxfi_name, listenfd, errno);
73671345Sgshapiro			(void) smutex_unlock(&L_Mutex);
73771345Sgshapiro			break;
73871345Sgshapiro		}
73971345Sgshapiro
74064562Sgshapiro		/* select on interface ports */
741111823Sgshapiro		FD_RD_INIT(listenfd, rds, excs);
74264562Sgshapiro		chktime.tv_sec = MI_CHK_TIME;
74364562Sgshapiro		chktime.tv_usec = 0;
744111823Sgshapiro		r = FD_RD_READY(listenfd, rds, excs, &chktime);
74564562Sgshapiro		if (r == 0)		/* timeout */
74671345Sgshapiro		{
74771345Sgshapiro			(void) smutex_unlock(&L_Mutex);
74864562Sgshapiro			continue;	/* just check mi_stop() */
74971345Sgshapiro		}
75064562Sgshapiro		if (r < 0)
75164562Sgshapiro		{
75277349Sgshapiro			save_errno = errno;
75371345Sgshapiro			(void) smutex_unlock(&L_Mutex);
75477349Sgshapiro			if (save_errno == EINTR)
75564562Sgshapiro				continue;
75690792Sgshapiro			scnt++;
75790792Sgshapiro			smi_log(SMI_LOG_ERR,
75890792Sgshapiro				"%s: select() failed (%s), %s",
75990792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
76090792Sgshapiro				scnt >= MAX_FAILS_S ? "abort" : "try again");
76190792Sgshapiro			MI_SLEEP(scnt);
76290792Sgshapiro			if (scnt >= MAX_FAILS_S)
76390792Sgshapiro			{
76490792Sgshapiro				ret = MI_FAILURE;
76590792Sgshapiro				break;
76690792Sgshapiro			}
76790792Sgshapiro			continue;
76864562Sgshapiro		}
769111823Sgshapiro		if (!FD_IS_RD_RDY(listenfd, rds, excs))
77064562Sgshapiro		{
77164562Sgshapiro			/* some error: just stop for now... */
77264562Sgshapiro			ret = MI_FAILURE;
77371345Sgshapiro			(void) smutex_unlock(&L_Mutex);
77490792Sgshapiro			smi_log(SMI_LOG_ERR,
775111823Sgshapiro				"%s: %s() returned exception for socket, abort",
776111823Sgshapiro				smfi->xxfi_name, MI_POLLSELECT);
77764562Sgshapiro			break;
77864562Sgshapiro		}
77990792Sgshapiro		scnt = 0;	/* reset error counter for select() */
78064562Sgshapiro
781120256Sgshapiro		(void) memset(&cliaddr, '\0', sizeof cliaddr);
78264562Sgshapiro		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
78364562Sgshapiro				&clilen);
78477349Sgshapiro		save_errno = errno;
78571345Sgshapiro		(void) smutex_unlock(&L_Mutex);
78664562Sgshapiro
78773188Sgshapiro		/*
78873188Sgshapiro		**  If remote side closes before
78973188Sgshapiro		**  accept() finishes, sockaddr
79073188Sgshapiro		**  might not be fully filled in.
79173188Sgshapiro		*/
79273188Sgshapiro
79373188Sgshapiro		if (ValidSocket(connfd) &&
79473188Sgshapiro		    (clilen == 0 ||
79573188Sgshapiro# ifdef BSD4_4_SOCKADDR
79673188Sgshapiro		     cliaddr.sa.sa_len == 0 ||
79773188Sgshapiro# endif /* BSD4_4_SOCKADDR */
79898121Sgshapiro		     cliaddr.sa.sa_family != L_family))
79973188Sgshapiro		{
80090792Sgshapiro			(void) closesocket(connfd);
80173188Sgshapiro			connfd = INVALID_SOCKET;
80277349Sgshapiro			save_errno = EINVAL;
80373188Sgshapiro		}
80473188Sgshapiro
805132943Sgshapiro#if !SM_CONF_POLL
806110560Sgshapiro		/* check if acceptable for select() */
807110560Sgshapiro		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
808110560Sgshapiro		{
809110560Sgshapiro			(void) closesocket(connfd);
810110560Sgshapiro			connfd = INVALID_SOCKET;
811110560Sgshapiro			save_errno = ERANGE;
812110560Sgshapiro		}
813132943Sgshapiro#endif /* !SM_CONF_POLL */
814110560Sgshapiro
81566494Sgshapiro		if (!ValidSocket(connfd))
81664562Sgshapiro		{
817132943Sgshapiro			if (save_errno == EINTR
818132943Sgshapiro#ifdef EAGAIN
819132943Sgshapiro			    || save_errno == EAGAIN
820132943Sgshapiro#endif /* EAGAIN */
821132943Sgshapiro#ifdef ECONNABORTED
822132943Sgshapiro			    || save_errno == ECONNABORTED
823132943Sgshapiro#endif /* ECONNABORTED */
824132943Sgshapiro#ifdef EMFILE
825132943Sgshapiro			    || save_errno == EMFILE
826132943Sgshapiro#endif /* EMFILE */
827132943Sgshapiro#ifdef ENFILE
828132943Sgshapiro			    || save_errno == ENFILE
829132943Sgshapiro#endif /* ENFILE */
830132943Sgshapiro#ifdef ENOBUFS
831132943Sgshapiro			    || save_errno == ENOBUFS
832132943Sgshapiro#endif /* ENOBUFS */
833132943Sgshapiro#ifdef ENOMEM
834132943Sgshapiro			    || save_errno == ENOMEM
835132943Sgshapiro#endif /* ENOMEM */
836132943Sgshapiro#ifdef ENOSR
837132943Sgshapiro			    || save_errno == ENOSR
838132943Sgshapiro#endif /* ENOSR */
839132943Sgshapiro#ifdef EWOULDBLOCK
840132943Sgshapiro			    || save_errno == EWOULDBLOCK
841132943Sgshapiro#endif /* EWOULDBLOCK */
842132943Sgshapiro			   )
84377349Sgshapiro				continue;
84477349Sgshapiro			acnt++;
84590792Sgshapiro			smi_log(SMI_LOG_ERR,
84690792Sgshapiro				"%s: accept() returned invalid socket (%s), %s",
84790792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
84890792Sgshapiro				acnt >= MAX_FAILS_A ? "abort" : "try again");
84977349Sgshapiro			MI_SLEEP(acnt);
85077349Sgshapiro			if (acnt >= MAX_FAILS_A)
85177349Sgshapiro			{
85277349Sgshapiro				ret = MI_FAILURE;
85377349Sgshapiro				break;
85477349Sgshapiro			}
85564562Sgshapiro			continue;
85664562Sgshapiro		}
85790792Sgshapiro		acnt = 0;	/* reset error counter for accept() */
858132943Sgshapiro#if _FFR_DUP_FD
859132943Sgshapiro		dupfd = fcntl(connfd, F_DUPFD, 256);
860132943Sgshapiro		if (ValidSocket(dupfd)
861132943Sgshapiro# if !SM_CONF_POLL
862132943Sgshapiro		    && SM_FD_OK_SELECT(dupfd)
863132943Sgshapiro# endif /* !SM_CONF_POLL */
864132943Sgshapiro		   )
865132943Sgshapiro		{
866132943Sgshapiro			close(connfd);
867132943Sgshapiro			connfd = dupfd;
868132943Sgshapiro			dupfd = INVALID_SOCKET;
869132943Sgshapiro		}
870132943Sgshapiro#endif /* _FFR_DUP_FD */
87164562Sgshapiro
87264562Sgshapiro		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
87364562Sgshapiro				(void *) &sockopt, sizeof sockopt) < 0)
87464562Sgshapiro		{
87590792Sgshapiro			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)",
87690792Sgshapiro				smfi->xxfi_name, sm_errstring(errno));
87764562Sgshapiro			/* XXX: continue? */
87864562Sgshapiro		}
87964562Sgshapiro		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
88064562Sgshapiro		{
88190792Sgshapiro			(void) closesocket(connfd);
88273188Sgshapiro			mcnt++;
88390792Sgshapiro			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
88490792Sgshapiro				smfi->xxfi_name, sm_errstring(save_errno),
88590792Sgshapiro				mcnt >= MAX_FAILS_M ? "abort" : "try again");
88673188Sgshapiro			MI_SLEEP(mcnt);
88773188Sgshapiro			if (mcnt >= MAX_FAILS_M)
88864562Sgshapiro			{
88964562Sgshapiro				ret = MI_FAILURE;
89064562Sgshapiro				break;
89164562Sgshapiro			}
89264562Sgshapiro			continue;
89364562Sgshapiro		}
89490792Sgshapiro		mcnt = 0;	/* reset error counter for malloc() */
895120256Sgshapiro		(void) memset(ctx, '\0', sizeof *ctx);
89664562Sgshapiro		ctx->ctx_sd = connfd;
89764562Sgshapiro		ctx->ctx_dbg = dbg;
89864562Sgshapiro		ctx->ctx_timeout = timeout;
89964562Sgshapiro		ctx->ctx_smfi = smfi;
90064562Sgshapiro#if 0
90164562Sgshapiro		if (smfi->xxfi_eoh == NULL)
90264562Sgshapiro		if (smfi->xxfi_eom == NULL)
90364562Sgshapiro		if (smfi->xxfi_abort == NULL)
90464562Sgshapiro		if (smfi->xxfi_close == NULL)
90564562Sgshapiro#endif /* 0 */
90664562Sgshapiro		if (smfi->xxfi_connect == NULL)
90764562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOCONNECT;
90864562Sgshapiro		if (smfi->xxfi_helo == NULL)
90964562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHELO;
91064562Sgshapiro		if (smfi->xxfi_envfrom == NULL)
91164562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOMAIL;
91264562Sgshapiro		if (smfi->xxfi_envrcpt == NULL)
91364562Sgshapiro			ctx->ctx_pflags |= SMFIP_NORCPT;
91464562Sgshapiro		if (smfi->xxfi_header == NULL)
91564562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHDRS;
91664562Sgshapiro		if (smfi->xxfi_eoh == NULL)
91764562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOEOH;
91864562Sgshapiro		if (smfi->xxfi_body == NULL)
91964562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOBODY;
92064562Sgshapiro
92164562Sgshapiro		if ((r = thread_create(&thread_id,
92264562Sgshapiro					mi_thread_handle_wrapper,
92371345Sgshapiro					(void *) ctx)) != 0)
92464562Sgshapiro		{
92590792Sgshapiro			tcnt++;
92664562Sgshapiro			smi_log(SMI_LOG_ERR,
92790792Sgshapiro				"%s: thread_create() failed: %d, %s",
92890792Sgshapiro				smfi->xxfi_name,  r,
92990792Sgshapiro				tcnt >= MAX_FAILS_T ? "abort" : "try again");
93073188Sgshapiro			MI_SLEEP(tcnt);
93190792Sgshapiro			(void) closesocket(connfd);
93264562Sgshapiro			free(ctx);
93373188Sgshapiro			if (tcnt >= MAX_FAILS_T)
93464562Sgshapiro			{
93564562Sgshapiro				ret = MI_FAILURE;
93664562Sgshapiro				break;
93764562Sgshapiro			}
93864562Sgshapiro			continue;
93964562Sgshapiro		}
94073188Sgshapiro		tcnt = 0;
94164562Sgshapiro	}
94264562Sgshapiro	if (ret != MI_SUCCESS)
94364562Sgshapiro		mi_stop_milters(MILTER_ABRT);
94471345Sgshapiro	else
945120256Sgshapiro	{
946120256Sgshapiro		if (mistop != MILTER_CONT)
947120256Sgshapiro			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
948120256Sgshapiro				smfi->xxfi_name, mistop);
94971345Sgshapiro		mi_closener();
950120256Sgshapiro	}
95171345Sgshapiro	(void) smutex_destroy(&L_Mutex);
95264562Sgshapiro	return ret;
95364562Sgshapiro}
954