listener.c revision 77349
164562Sgshapiro/*
273188Sgshapiro *  Copyright (c) 1999-2001 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
1164562Sgshapiro#ifndef lint
1277349Sgshapirostatic char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.22 2001/05/16 17:15:58 ca Exp $";
1364562Sgshapiro#endif /* ! lint */
1464562Sgshapiro
1564562Sgshapiro#if _FFR_MILTER
1664562Sgshapiro/*
1764562Sgshapiro**  listener.c -- threaded network listener
1864562Sgshapiro*/
1964562Sgshapiro
2064562Sgshapiro#include "libmilter.h"
2164562Sgshapiro
2264562Sgshapiro
2364562Sgshapiro# if NETINET || NETINET6
2464562Sgshapiro#  include <arpa/inet.h>
2564562Sgshapiro# endif /* NETINET || NETINET6 */
2664562Sgshapiro/*
2764562Sgshapiro**  MI_MILTEROPEN -- setup socket to listen on
2864562Sgshapiro**
2964562Sgshapiro**	Parameters:
3064562Sgshapiro**		conn -- connection description
3164562Sgshapiro**		backlog -- listen backlog
3264562Sgshapiro**		socksize -- socksize of created socket
3373188Sgshapiro**		family -- family of created socket
3473188Sgshapiro**		name -- name for logging
3564562Sgshapiro**
3664562Sgshapiro**	Returns:
3764562Sgshapiro**		socket upon success, error code otherwise.
3864562Sgshapiro*/
3964562Sgshapiro
4066494Sgshapirostatic socket_t
4173188Sgshapiromi_milteropen(conn, backlog, socksize, family, name)
4264562Sgshapiro	char *conn;
4364562Sgshapiro	int backlog;
4464562Sgshapiro	SOCKADDR_LEN_T *socksize;
4573188Sgshapiro	int *family;
4664562Sgshapiro	char *name;
4764562Sgshapiro{
4866494Sgshapiro	socket_t sock;
4964562Sgshapiro	int sockopt = 1;
5064562Sgshapiro	char *p;
5164562Sgshapiro	char *colon;
5264562Sgshapiro	char *at;
5364562Sgshapiro	SOCKADDR addr;
5464562Sgshapiro
5564562Sgshapiro	if (conn == NULL || conn[0] == '\0')
5664562Sgshapiro	{
5764562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
5864562Sgshapiro			name);
5966494Sgshapiro		return INVALID_SOCKET;
6064562Sgshapiro	}
6164562Sgshapiro	(void) memset(&addr, '\0', sizeof addr);
6264562Sgshapiro
6364562Sgshapiro	/* protocol:filename or protocol:port@host */
6464562Sgshapiro	p = conn;
6564562Sgshapiro	colon = strchr(p, ':');
6664562Sgshapiro	if (colon != NULL)
6764562Sgshapiro	{
6864562Sgshapiro		*colon = '\0';
6964562Sgshapiro
7064562Sgshapiro 		if (*p == '\0')
7164562Sgshapiro		{
7264562Sgshapiro#if NETUNIX
7364562Sgshapiro			/* default to AF_UNIX */
7464562Sgshapiro 			addr.sa.sa_family = AF_UNIX;
7564562Sgshapiro			*socksize = sizeof (struct sockaddr_un);
7664562Sgshapiro#else /* NETUNIX */
7764562Sgshapiro# if NETINET
7864562Sgshapiro			/* default to AF_INET */
7964562Sgshapiro			addr.sa.sa_family = AF_INET;
8064562Sgshapiro			*socksize = sizeof addr.sin;
8164562Sgshapiro# else /* NETINET */
8264562Sgshapiro#  if NETINET6
8364562Sgshapiro			/* default to AF_INET6 */
8464562Sgshapiro			addr.sa.sa_family = AF_INET6;
8564562Sgshapiro			*socksize = sizeof addr.sin6;
8664562Sgshapiro#  else /* NETINET6 */
8764562Sgshapiro			/* no protocols available */
8864562Sgshapiro			smi_log(SMI_LOG_ERR,
8964562Sgshapiro				"%s: no valid socket protocols available",
9064562Sgshapiro				name);
9166494Sgshapiro			return INVALID_SOCKET;
9264562Sgshapiro#  endif /* NETINET6 */
9364562Sgshapiro# endif /* NETINET */
9464562Sgshapiro#endif /* NETUNIX */
9564562Sgshapiro		}
9664562Sgshapiro#if NETUNIX
9764562Sgshapiro		else if (strcasecmp(p, "unix") == 0 ||
9864562Sgshapiro			 strcasecmp(p, "local") == 0)
9964562Sgshapiro		{
10064562Sgshapiro			addr.sa.sa_family = AF_UNIX;
10164562Sgshapiro			*socksize = sizeof (struct sockaddr_un);
10264562Sgshapiro		}
10364562Sgshapiro#endif /* NETUNIX */
10464562Sgshapiro#if NETINET
10564562Sgshapiro		else if (strcasecmp(p, "inet") == 0)
10664562Sgshapiro		{
10764562Sgshapiro			addr.sa.sa_family = AF_INET;
10864562Sgshapiro			*socksize = sizeof addr.sin;
10964562Sgshapiro		}
11064562Sgshapiro#endif /* NETINET */
11164562Sgshapiro#if NETINET6
11264562Sgshapiro		else if (strcasecmp(p, "inet6") == 0)
11364562Sgshapiro		{
11464562Sgshapiro			addr.sa.sa_family = AF_INET6;
11564562Sgshapiro			*socksize = sizeof addr.sin6;
11664562Sgshapiro		}
11764562Sgshapiro#endif /* NETINET6 */
11864562Sgshapiro		else
11964562Sgshapiro		{
12064562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
12164562Sgshapiro				name, p);
12266494Sgshapiro			return INVALID_SOCKET;
12364562Sgshapiro		}
12464562Sgshapiro		*colon++ = ':';
12564562Sgshapiro	}
12664562Sgshapiro	else
12764562Sgshapiro	{
12864562Sgshapiro		colon = p;
12964562Sgshapiro#if NETUNIX
13064562Sgshapiro		/* default to AF_UNIX */
13164562Sgshapiro 		addr.sa.sa_family = AF_UNIX;
13264562Sgshapiro		*socksize = sizeof (struct sockaddr_un);
13364562Sgshapiro#else /* NETUNIX */
13464562Sgshapiro# if NETINET
13564562Sgshapiro		/* default to AF_INET */
13664562Sgshapiro		addr.sa.sa_family = AF_INET;
13764562Sgshapiro		*socksize = sizeof addr.sin;
13864562Sgshapiro# else /* NETINET */
13964562Sgshapiro#  if NETINET6
14064562Sgshapiro		/* default to AF_INET6 */
14164562Sgshapiro		addr.sa.sa_family = AF_INET6;
14264562Sgshapiro		*socksize = sizeof addr.sin6;
14364562Sgshapiro#  else /* NETINET6 */
14464562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
14564562Sgshapiro			name, p);
14666494Sgshapiro		return INVALID_SOCKET;
14764562Sgshapiro#  endif /* NETINET6 */
14864562Sgshapiro# endif /* NETINET */
14964562Sgshapiro#endif /* NETUNIX */
15064562Sgshapiro	}
15164562Sgshapiro
15264562Sgshapiro#if NETUNIX
15364562Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
15464562Sgshapiro	{
15564562Sgshapiro# if 0
15664562Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
15764562Sgshapiro# endif /* 0 */
15864562Sgshapiro
15964562Sgshapiro		at = colon;
16064562Sgshapiro		if (strlcpy(addr.sunix.sun_path, colon,
16164562Sgshapiro			    sizeof addr.sunix.sun_path) >=
16264562Sgshapiro		    sizeof addr.sunix.sun_path)
16364562Sgshapiro		{
16464562Sgshapiro			errno = EINVAL;
16564562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
16664562Sgshapiro				name, colon);
16766494Sgshapiro			return INVALID_SOCKET;
16864562Sgshapiro		}
16964562Sgshapiro# if 0
17064562Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
17164562Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
17264562Sgshapiro
17364562Sgshapiro		/* if not safe, don't create */
17464562Sgshapiro		if (errno != 0)
17564562Sgshapiro		{
17664562Sgshapiro			smi_log(SMI_LOG_ERR,
17764562Sgshapiro				"%s: UNIX socket name %s unsafe",
17864562Sgshapiro				name, colon);
17966494Sgshapiro			return INVALID_SOCKET;
18064562Sgshapiro		}
18164562Sgshapiro# endif /* 0 */
18264562Sgshapiro
18364562Sgshapiro	}
18464562Sgshapiro#endif /* NETUNIX */
18564562Sgshapiro
18664562Sgshapiro#if NETINET || NETINET6
18764562Sgshapiro	if (
18864562Sgshapiro# if NETINET
18964562Sgshapiro	    addr.sa.sa_family == AF_INET
19064562Sgshapiro# endif /* NETINET */
19164562Sgshapiro# if NETINET && NETINET6
19264562Sgshapiro	    ||
19364562Sgshapiro# endif /* NETINET && NETINET6 */
19464562Sgshapiro# if NETINET6
19564562Sgshapiro	    addr.sa.sa_family == AF_INET6
19664562Sgshapiro# endif /* NETINET6 */
19764562Sgshapiro	   )
19864562Sgshapiro	{
19964562Sgshapiro		u_short port;
20064562Sgshapiro
20164562Sgshapiro		/* Parse port@host */
20264562Sgshapiro		at = strchr(colon, '@');
20364562Sgshapiro		if (at == NULL)
20464562Sgshapiro		{
20564562Sgshapiro			switch (addr.sa.sa_family)
20664562Sgshapiro			{
20764562Sgshapiro# if NETINET
20864562Sgshapiro			  case AF_INET:
20964562Sgshapiro				addr.sin.sin_addr.s_addr = INADDR_ANY;
21064562Sgshapiro				break;
21164562Sgshapiro# endif /* NETINET */
21264562Sgshapiro
21364562Sgshapiro# if NETINET6
21464562Sgshapiro			  case AF_INET6:
21564562Sgshapiro				addr.sin6.sin6_addr = in6addr_any;
21664562Sgshapiro				break;
21764562Sgshapiro# endif /* NETINET6 */
21864562Sgshapiro			}
21964562Sgshapiro		}
22064562Sgshapiro		else
22164562Sgshapiro			*at = '\0';
22264562Sgshapiro
22364562Sgshapiro		if (isascii(*colon) && isdigit(*colon))
22464562Sgshapiro			port = htons((u_short) atoi(colon));
22564562Sgshapiro		else
22664562Sgshapiro		{
22764562Sgshapiro# ifdef NO_GETSERVBYNAME
22864562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
22964562Sgshapiro				name, colon);
23066494Sgshapiro			return INVALID_SOCKET;
23164562Sgshapiro# else /* NO_GETSERVBYNAME */
23264562Sgshapiro			register struct servent *sp;
23364562Sgshapiro
23464562Sgshapiro			sp = getservbyname(colon, "tcp");
23564562Sgshapiro			if (sp == NULL)
23664562Sgshapiro			{
23764562Sgshapiro				smi_log(SMI_LOG_ERR,
23864562Sgshapiro					"%s: unknown port name %s",
23964562Sgshapiro					name, colon);
24066494Sgshapiro				return INVALID_SOCKET;
24164562Sgshapiro			}
24264562Sgshapiro			port = sp->s_port;
24364562Sgshapiro# endif /* NO_GETSERVBYNAME */
24464562Sgshapiro		}
24564562Sgshapiro		if (at != NULL)
24664562Sgshapiro		{
24764562Sgshapiro			*at++ = '@';
24864562Sgshapiro			if (*at == '[')
24964562Sgshapiro			{
25064562Sgshapiro				char *end;
25164562Sgshapiro
25264562Sgshapiro				end = strchr(at, ']');
25364562Sgshapiro				if (end != NULL)
25464562Sgshapiro				{
25564562Sgshapiro					bool found = FALSE;
25664562Sgshapiro# if NETINET
25764562Sgshapiro					unsigned long hid = INADDR_NONE;
25864562Sgshapiro# endif /* NETINET */
25964562Sgshapiro# if NETINET6
26064562Sgshapiro					struct sockaddr_in6 hid6;
26164562Sgshapiro# endif /* NETINET6 */
26264562Sgshapiro
26364562Sgshapiro					*end = '\0';
26464562Sgshapiro# if NETINET
26564562Sgshapiro					if (addr.sa.sa_family == AF_INET &&
26664562Sgshapiro					    (hid = inet_addr(&at[1])) !=
26764562Sgshapiro					    INADDR_NONE)
26864562Sgshapiro					{
26964562Sgshapiro						addr.sin.sin_addr.s_addr = hid;
27064562Sgshapiro						addr.sin.sin_port = port;
27164562Sgshapiro						found = TRUE;
27264562Sgshapiro					}
27364562Sgshapiro# endif /* NETINET */
27464562Sgshapiro# if NETINET6
27564562Sgshapiro					(void) memset(&hid6, '\0', sizeof hid6);
27664562Sgshapiro					if (addr.sa.sa_family == AF_INET6 &&
27764562Sgshapiro					    inet_pton(AF_INET6, &at[1],
27864562Sgshapiro						      &hid6.sin6_addr) == 1)
27964562Sgshapiro					{
28064562Sgshapiro						addr.sin6.sin6_addr = hid6.sin6_addr;
28164562Sgshapiro						addr.sin6.sin6_port = port;
28264562Sgshapiro						found = TRUE;
28364562Sgshapiro					}
28464562Sgshapiro# endif /* NETINET6 */
28564562Sgshapiro					*end = ']';
28664562Sgshapiro					if (!found)
28764562Sgshapiro					{
28864562Sgshapiro						smi_log(SMI_LOG_ERR,
28964562Sgshapiro							"%s: Invalid numeric domain spec \"%s\"",
29064562Sgshapiro							name, at);
29166494Sgshapiro						return INVALID_SOCKET;
29264562Sgshapiro					}
29364562Sgshapiro				}
29464562Sgshapiro				else
29564562Sgshapiro				{
29664562Sgshapiro					smi_log(SMI_LOG_ERR,
29764562Sgshapiro						"%s: Invalid numeric domain spec \"%s\"",
29864562Sgshapiro						name, at);
29966494Sgshapiro					return INVALID_SOCKET;
30064562Sgshapiro				}
30164562Sgshapiro			}
30264562Sgshapiro			else
30364562Sgshapiro			{
30471345Sgshapiro				struct hostent *hp = NULL;
30571345Sgshapiro
30664562Sgshapiro				hp = mi_gethostbyname(at, addr.sa.sa_family);
30764562Sgshapiro				if (hp == NULL)
30864562Sgshapiro				{
30964562Sgshapiro					smi_log(SMI_LOG_ERR,
31064562Sgshapiro						"%s: Unknown host name %s",
31164562Sgshapiro						name, at);
31266494Sgshapiro					return INVALID_SOCKET;
31364562Sgshapiro				}
31464562Sgshapiro				addr.sa.sa_family = hp->h_addrtype;
31564562Sgshapiro				switch (hp->h_addrtype)
31664562Sgshapiro				{
31764562Sgshapiro# if NETINET
31864562Sgshapiro				  case AF_INET:
31964562Sgshapiro					memmove(&addr.sin.sin_addr,
32064562Sgshapiro						hp->h_addr,
32164562Sgshapiro						INADDRSZ);
32264562Sgshapiro					addr.sin.sin_port = port;
32364562Sgshapiro					break;
32464562Sgshapiro# endif /* NETINET */
32564562Sgshapiro
32664562Sgshapiro# if NETINET6
32764562Sgshapiro				  case AF_INET6:
32864562Sgshapiro					memmove(&addr.sin6.sin6_addr,
32964562Sgshapiro						hp->h_addr,
33064562Sgshapiro						IN6ADDRSZ);
33164562Sgshapiro					addr.sin6.sin6_port = port;
33264562Sgshapiro					break;
33364562Sgshapiro# endif /* NETINET6 */
33464562Sgshapiro
33564562Sgshapiro				  default:
33664562Sgshapiro					smi_log(SMI_LOG_ERR,
33764562Sgshapiro						"%s: Unknown protocol for %s (%d)",
33864562Sgshapiro						name, at, hp->h_addrtype);
33966494Sgshapiro					return INVALID_SOCKET;
34064562Sgshapiro				}
34171345Sgshapiro# if _FFR_FREEHOSTENT && NETINET6
34271345Sgshapiro				freehostent(hp);
34371345Sgshapiro# endif /* _FFR_FREEHOSTENT && NETINET6 */
34464562Sgshapiro			}
34564562Sgshapiro		}
34664562Sgshapiro		else
34764562Sgshapiro		{
34864562Sgshapiro			switch (addr.sa.sa_family)
34964562Sgshapiro			{
35064562Sgshapiro# if NETINET
35164562Sgshapiro			  case AF_INET:
35264562Sgshapiro				addr.sin.sin_port = port;
35364562Sgshapiro				break;
35464562Sgshapiro# endif /* NETINET */
35564562Sgshapiro# if NETINET6
35664562Sgshapiro			  case AF_INET6:
35764562Sgshapiro				addr.sin6.sin6_port = port;
35864562Sgshapiro				break;
35964562Sgshapiro# endif /* NETINET6 */
36064562Sgshapiro			}
36164562Sgshapiro		}
36264562Sgshapiro	}
36364562Sgshapiro#endif /* NETINET || NETINET6 */
36464562Sgshapiro
36564562Sgshapiro	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
36664562Sgshapiro	if (!ValidSocket(sock))
36764562Sgshapiro	{
36864562Sgshapiro		smi_log(SMI_LOG_ERR,
36964562Sgshapiro			"%s: Unable to create new socket: %s",
37064562Sgshapiro			name, strerror(errno));
37166494Sgshapiro		return INVALID_SOCKET;
37264562Sgshapiro	}
37364562Sgshapiro
37464562Sgshapiro	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
37564562Sgshapiro		       sizeof(sockopt)) == -1)
37664562Sgshapiro	{
37764562Sgshapiro		smi_log(SMI_LOG_ERR,
37864562Sgshapiro			"%s: Unable to setsockopt: %s", name, strerror(errno));
37964562Sgshapiro		(void) close(sock);
38066494Sgshapiro		return INVALID_SOCKET;
38164562Sgshapiro	}
38264562Sgshapiro
38364562Sgshapiro	if (bind(sock, &addr.sa, *socksize) < 0)
38464562Sgshapiro	{
38564562Sgshapiro		smi_log(SMI_LOG_ERR,
38664562Sgshapiro			"%s: Unable to bind to port %s: %s",
38764562Sgshapiro			name, conn, strerror(errno));
38864562Sgshapiro		(void) close(sock);
38966494Sgshapiro		return INVALID_SOCKET;
39064562Sgshapiro	}
39164562Sgshapiro
39264562Sgshapiro	if (listen(sock, backlog) < 0)
39364562Sgshapiro	{
39464562Sgshapiro		smi_log(SMI_LOG_ERR,
39564562Sgshapiro			"%s: listen call failed: %s", name, strerror(errno));
39664562Sgshapiro		(void) close(sock);
39766494Sgshapiro		return INVALID_SOCKET;
39864562Sgshapiro	}
39973188Sgshapiro	*family = addr.sa.sa_family;
40064562Sgshapiro	return sock;
40164562Sgshapiro}
40264562Sgshapiro/*
40364562Sgshapiro**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
40464562Sgshapiro**
40564562Sgshapiro**	Parameters:
40664562Sgshapiro**		arg -- argument to pass to mi_handle_session()
40764562Sgshapiro**
40864562Sgshapiro**	Returns:
40964562Sgshapiro**		results from mi_handle_session()
41064562Sgshapiro*/
41164562Sgshapiro
41264562Sgshapirovoid *
41364562Sgshapiromi_thread_handle_wrapper(arg)
41464562Sgshapiro	void *arg;
41564562Sgshapiro{
41664562Sgshapiro	return (void *) mi_handle_session(arg);
41764562Sgshapiro}
41864562Sgshapiro
41966494Sgshapirostatic socket_t listenfd = INVALID_SOCKET;
42066494Sgshapiro
42171345Sgshapirostatic smutex_t L_Mutex;
42271345Sgshapiro
42364562Sgshapiro/*
42466494Sgshapiro**  MI_CLOSENER -- close listen socket
42564562Sgshapiro**
42666494Sgshapiro**	Parameters:
42766494Sgshapiro**		none.
42866494Sgshapiro**
42966494Sgshapiro**	Returns:
43066494Sgshapiro**		none.
43166494Sgshapiro*/
43266494Sgshapiro
43366494Sgshapirovoid
43466494Sgshapiromi_closener()
43566494Sgshapiro{
43671345Sgshapiro	(void) smutex_lock(&L_Mutex);
43766494Sgshapiro	if (ValidSocket(listenfd))
43866494Sgshapiro	{
43966494Sgshapiro		(void) close(listenfd);
44066494Sgshapiro		listenfd = INVALID_SOCKET;
44166494Sgshapiro	}
44271345Sgshapiro	(void) smutex_unlock(&L_Mutex);
44366494Sgshapiro}
44466494Sgshapiro
44566494Sgshapiro/*
44666494Sgshapiro**  MI_LISTENER -- Generic listener harness
44766494Sgshapiro**
44864562Sgshapiro**	Open up listen port
44964562Sgshapiro**	Wait for connections
45064562Sgshapiro**
45164562Sgshapiro**	Parameters:
45264562Sgshapiro**		conn -- connection description
45364562Sgshapiro**		dbg -- debug level
45464562Sgshapiro**		smfi -- filter structure to use
45564562Sgshapiro**		timeout -- timeout for reads/writes
45664562Sgshapiro**
45764562Sgshapiro**	Returns:
45864562Sgshapiro**		MI_SUCCESS -- Exited normally
45964562Sgshapiro**			   (session finished or we were told to exit)
46064562Sgshapiro**		MI_FAILURE -- Network initialization failed.
46164562Sgshapiro*/
46264562Sgshapiro
46373188Sgshapiro# if BROKEN_PTHREAD_SLEEP
46473188Sgshapiro
46573188Sgshapiro/*
46673188Sgshapiro**  Solaris 2.6, perhaps others, gets an internal threads library panic
46773188Sgshapiro**  when sleep() is used:
46873188Sgshapiro**
46973188Sgshapiro**  thread_create() failed, returned 11 (EINVAL)
47073188Sgshapiro**  co_enable, thr_create() returned error = 24
47173188Sgshapiro**  libthread panic: co_enable failed (PID: 17793 LWP 1)
47273188Sgshapiro**  stacktrace:
47373188Sgshapiro**	ef526b10
47473188Sgshapiro**	ef52646c
47573188Sgshapiro**	ef534cbc
47673188Sgshapiro**	156a4
47773188Sgshapiro**	14644
47873188Sgshapiro**	1413c
47973188Sgshapiro**	135e0
48073188Sgshapiro**	0
48173188Sgshapiro*/
48273188Sgshapiro
48373188Sgshapiro#  define MI_SLEEP(s)							\
48473188Sgshapiro{									\
48573188Sgshapiro	int rs = 0;							\
48673188Sgshapiro	struct timeval st;						\
48773188Sgshapiro									\
48873188Sgshapiro	st.tv_sec = (s);						\
48973188Sgshapiro	st.tv_usec = 0;							\
49073188Sgshapiro	if (st.tv_sec > 0)						\
49173188Sgshapiro		rs = select(0, NULL, NULL, NULL, &st);			\
49273188Sgshapiro	if (rs != 0)							\
49373188Sgshapiro	{								\
49473188Sgshapiro		smi_log(SMI_LOG_ERR,					\
49573188Sgshapiro			"MI_SLEEP(): select() returned non-zero result %d, errno = %d",								\
49673188Sgshapiro			rs, errno);					\
49773188Sgshapiro	}								\
49873188Sgshapiro}
49973188Sgshapiro# else /* BROKEN_PTHREAD_SLEEP */
50073188Sgshapiro#  define MI_SLEEP(s)	sleep((s))
50173188Sgshapiro# endif /* BROKEN_PTHREAD_SLEEP */
50273188Sgshapiro
50364562Sgshapiroint
50466494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog)
50564562Sgshapiro	char *conn;
50664562Sgshapiro	int dbg;
50764562Sgshapiro	smfiDesc_ptr smfi;
50864562Sgshapiro	time_t timeout;
50966494Sgshapiro	int backlog;
51064562Sgshapiro{
51166494Sgshapiro	socket_t connfd = INVALID_SOCKET;
51273188Sgshapiro	int family = AF_UNSPEC;
51364562Sgshapiro	int sockopt = 1;
51464562Sgshapiro	int r;
51564562Sgshapiro	int ret = MI_SUCCESS;
51673188Sgshapiro	int mcnt = 0;
51773188Sgshapiro	int tcnt = 0;
51877349Sgshapiro	int acnt = 0;
51977349Sgshapiro	int save_errno = 0;
52064562Sgshapiro	sthread_t thread_id;
52164562Sgshapiro	_SOCK_ADDR cliaddr;
52264562Sgshapiro	SOCKADDR_LEN_T socksize;
52364562Sgshapiro	SOCKADDR_LEN_T clilen;
52464562Sgshapiro	SMFICTX_PTR ctx;
52564562Sgshapiro	fd_set readset, excset;
52664562Sgshapiro	struct timeval chktime;
52764562Sgshapiro
52864562Sgshapiro	if (dbg > 0)
52964562Sgshapiro		smi_log(SMI_LOG_DEBUG,
53064562Sgshapiro			"%s: Opening listen socket on conn %s",
53164562Sgshapiro			smfi->xxfi_name, conn);
53271345Sgshapiro	(void) smutex_init(&L_Mutex);
53371345Sgshapiro	(void) smutex_lock(&L_Mutex);
53473188Sgshapiro	listenfd = mi_milteropen(conn, backlog, &socksize, &family,
53573188Sgshapiro				 smfi->xxfi_name);
53666494Sgshapiro	if (!ValidSocket(listenfd))
53764562Sgshapiro	{
53864562Sgshapiro		smi_log(SMI_LOG_FATAL,
53964562Sgshapiro			"%s: Unable to create listening socket on conn %s",
54064562Sgshapiro			smfi->xxfi_name, conn);
54171345Sgshapiro		(void) smutex_unlock(&L_Mutex);
54264562Sgshapiro		return MI_FAILURE;
54364562Sgshapiro	}
54464562Sgshapiro	clilen = socksize;
54571345Sgshapiro
54664562Sgshapiro	if (listenfd >= FD_SETSIZE)
54764562Sgshapiro	{
54864562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
54964562Sgshapiro			smfi->xxfi_name, listenfd, FD_SETSIZE);
55071345Sgshapiro		(void) smutex_unlock(&L_Mutex);
55164562Sgshapiro		return MI_FAILURE;
55264562Sgshapiro	}
55371345Sgshapiro	(void) smutex_unlock(&L_Mutex);
55464562Sgshapiro
55564562Sgshapiro	while (mi_stop() == MILTER_CONT)
55664562Sgshapiro	{
55771345Sgshapiro		(void) smutex_lock(&L_Mutex);
55871345Sgshapiro		if (!ValidSocket(listenfd))
55971345Sgshapiro		{
56071345Sgshapiro			(void) smutex_unlock(&L_Mutex);
56171345Sgshapiro			break;
56271345Sgshapiro		}
56371345Sgshapiro
56464562Sgshapiro		/* select on interface ports */
56564562Sgshapiro		FD_ZERO(&readset);
56671345Sgshapiro		FD_ZERO(&excset);
56764562Sgshapiro		FD_SET((u_int) listenfd, &readset);
56864562Sgshapiro		FD_SET((u_int) listenfd, &excset);
56964562Sgshapiro		chktime.tv_sec = MI_CHK_TIME;
57064562Sgshapiro		chktime.tv_usec = 0;
57164562Sgshapiro		r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
57264562Sgshapiro		if (r == 0)		/* timeout */
57371345Sgshapiro		{
57471345Sgshapiro			(void) smutex_unlock(&L_Mutex);
57564562Sgshapiro			continue;	/* just check mi_stop() */
57671345Sgshapiro		}
57764562Sgshapiro		if (r < 0)
57864562Sgshapiro		{
57977349Sgshapiro			save_errno = errno;
58071345Sgshapiro			(void) smutex_unlock(&L_Mutex);
58177349Sgshapiro			if (save_errno == EINTR)
58264562Sgshapiro				continue;
58364562Sgshapiro			ret = MI_FAILURE;
58464562Sgshapiro			break;
58564562Sgshapiro		}
58664562Sgshapiro		if (!FD_ISSET(listenfd, &readset))
58764562Sgshapiro		{
58864562Sgshapiro			/* some error: just stop for now... */
58964562Sgshapiro			ret = MI_FAILURE;
59071345Sgshapiro			(void) smutex_unlock(&L_Mutex);
59164562Sgshapiro			break;
59264562Sgshapiro		}
59364562Sgshapiro
59473188Sgshapiro		memset(&cliaddr, '\0', sizeof cliaddr);
59564562Sgshapiro		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
59664562Sgshapiro				&clilen);
59777349Sgshapiro		save_errno = errno;
59871345Sgshapiro		(void) smutex_unlock(&L_Mutex);
59964562Sgshapiro
60073188Sgshapiro		/*
60173188Sgshapiro		**  If remote side closes before
60273188Sgshapiro		**  accept() finishes, sockaddr
60373188Sgshapiro		**  might not be fully filled in.
60473188Sgshapiro		*/
60573188Sgshapiro
60673188Sgshapiro		if (ValidSocket(connfd) &&
60773188Sgshapiro		    (clilen == 0 ||
60873188Sgshapiro# ifdef BSD4_4_SOCKADDR
60973188Sgshapiro		     cliaddr.sa.sa_len == 0 ||
61073188Sgshapiro# endif /* BSD4_4_SOCKADDR */
61173188Sgshapiro		     cliaddr.sa.sa_family != family))
61273188Sgshapiro		{
61373188Sgshapiro			(void) close(connfd);
61473188Sgshapiro			connfd = INVALID_SOCKET;
61577349Sgshapiro			save_errno = EINVAL;
61673188Sgshapiro		}
61773188Sgshapiro
61866494Sgshapiro		if (!ValidSocket(connfd))
61964562Sgshapiro		{
62064562Sgshapiro			smi_log(SMI_LOG_ERR,
62177349Sgshapiro				"%s: accept() returned invalid socket (%s)",
62277349Sgshapiro				smfi->xxfi_name, strerror(save_errno));
62377349Sgshapiro			if (save_errno == EINTR)
62477349Sgshapiro				continue;
62577349Sgshapiro			acnt++;
62677349Sgshapiro			MI_SLEEP(acnt);
62777349Sgshapiro			if (acnt >= MAX_FAILS_A)
62877349Sgshapiro			{
62977349Sgshapiro				ret = MI_FAILURE;
63077349Sgshapiro				break;
63177349Sgshapiro			}
63264562Sgshapiro			continue;
63364562Sgshapiro		}
63464562Sgshapiro
63564562Sgshapiro		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
63664562Sgshapiro				(void *) &sockopt, sizeof sockopt) < 0)
63764562Sgshapiro		{
63864562Sgshapiro			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed",
63964562Sgshapiro				smfi->xxfi_name);
64064562Sgshapiro			/* XXX: continue? */
64164562Sgshapiro		}
64264562Sgshapiro		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
64364562Sgshapiro		{
64464562Sgshapiro			(void) close(connfd);
64564562Sgshapiro			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
64664562Sgshapiro				smfi->xxfi_name);
64773188Sgshapiro			mcnt++;
64873188Sgshapiro			MI_SLEEP(mcnt);
64973188Sgshapiro			if (mcnt >= MAX_FAILS_M)
65064562Sgshapiro			{
65164562Sgshapiro				ret = MI_FAILURE;
65264562Sgshapiro				break;
65364562Sgshapiro			}
65464562Sgshapiro			continue;
65564562Sgshapiro		}
65673188Sgshapiro		mcnt = 0;
65777349Sgshapiro		acnt = 0;
65864562Sgshapiro		memset(ctx, '\0', sizeof *ctx);
65964562Sgshapiro		ctx->ctx_sd = connfd;
66064562Sgshapiro		ctx->ctx_dbg = dbg;
66164562Sgshapiro		ctx->ctx_timeout = timeout;
66264562Sgshapiro		ctx->ctx_smfi = smfi;
66364562Sgshapiro#if 0
66464562Sgshapiro		if (smfi->xxfi_eoh == NULL)
66564562Sgshapiro		if (smfi->xxfi_eom == NULL)
66664562Sgshapiro		if (smfi->xxfi_abort == NULL)
66764562Sgshapiro		if (smfi->xxfi_close == NULL)
66864562Sgshapiro#endif /* 0 */
66964562Sgshapiro		if (smfi->xxfi_connect == NULL)
67064562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOCONNECT;
67164562Sgshapiro		if (smfi->xxfi_helo == NULL)
67264562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHELO;
67364562Sgshapiro		if (smfi->xxfi_envfrom == NULL)
67464562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOMAIL;
67564562Sgshapiro		if (smfi->xxfi_envrcpt == NULL)
67664562Sgshapiro			ctx->ctx_pflags |= SMFIP_NORCPT;
67764562Sgshapiro		if (smfi->xxfi_header == NULL)
67864562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOHDRS;
67964562Sgshapiro		if (smfi->xxfi_eoh == NULL)
68064562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOEOH;
68164562Sgshapiro		if (smfi->xxfi_body == NULL)
68264562Sgshapiro			ctx->ctx_pflags |= SMFIP_NOBODY;
68364562Sgshapiro
68464562Sgshapiro		if ((r = thread_create(&thread_id,
68564562Sgshapiro					mi_thread_handle_wrapper,
68671345Sgshapiro					(void *) ctx)) != 0)
68764562Sgshapiro		{
68864562Sgshapiro			smi_log(SMI_LOG_ERR,
68964562Sgshapiro				"%s: thread_create() failed: %d",
69064562Sgshapiro				smfi->xxfi_name,  r);
69173188Sgshapiro			tcnt++;
69273188Sgshapiro			MI_SLEEP(tcnt);
69364562Sgshapiro			(void) close(connfd);
69464562Sgshapiro			free(ctx);
69573188Sgshapiro			if (tcnt >= MAX_FAILS_T)
69664562Sgshapiro			{
69764562Sgshapiro				ret = MI_FAILURE;
69864562Sgshapiro				break;
69964562Sgshapiro			}
70064562Sgshapiro			continue;
70164562Sgshapiro		}
70273188Sgshapiro		tcnt = 0;
70364562Sgshapiro	}
70464562Sgshapiro	if (ret != MI_SUCCESS)
70564562Sgshapiro		mi_stop_milters(MILTER_ABRT);
70671345Sgshapiro	else
70771345Sgshapiro		mi_closener();
70871345Sgshapiro	(void) smutex_destroy(&L_Mutex);
70964562Sgshapiro	return ret;
71064562Sgshapiro}
71164562Sgshapiro#endif /* _FFR_MILTER */
712