listener.c revision 157001
1/*
2 *  Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: listener.c,v 8.115 2006/01/24 00:48:39 ca Exp $")
13
14/*
15**  listener.c -- threaded network listener
16*/
17
18#include "libmilter.h"
19#include <sm/errstring.h>
20
21#include <sys/types.h>
22#include <sys/stat.h>
23
24
25# if NETINET || NETINET6
26#  include <arpa/inet.h>
27# endif /* NETINET || NETINET6 */
28
29static smutex_t L_Mutex;
30static int L_family;
31static SOCKADDR_LEN_T L_socksize;
32static socket_t listenfd = INVALID_SOCKET;
33
34static socket_t mi_milteropen __P((char *, int, bool, char *));
35static void *mi_thread_handle_wrapper __P((void *));
36
37/*
38**  MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
39**
40**	Parameters:
41**		conn -- connection description
42**		backlog -- listen backlog
43**		dbg -- debug level
44**		rmsocket -- if true, try to unlink() the socket first
45**			(UNIX domain sockets only)
46**		smfi -- filter structure to use
47**
48**	Return value:
49**		MI_SUCCESS/MI_FAILURE
50*/
51
52int
53mi_opensocket(conn, backlog, dbg, rmsocket, smfi)
54	char *conn;
55	int backlog;
56	int dbg;
57	bool rmsocket;
58	smfiDesc_ptr smfi;
59{
60	if (smfi == NULL || conn == NULL)
61		return MI_FAILURE;
62
63	if (ValidSocket(listenfd))
64		return MI_SUCCESS;
65
66	if (dbg > 0)
67	{
68		smi_log(SMI_LOG_DEBUG,
69			"%s: Opening listen socket on conn %s",
70			smfi->xxfi_name, conn);
71	}
72	(void) smutex_init(&L_Mutex);
73	(void) smutex_lock(&L_Mutex);
74	listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
75	if (!ValidSocket(listenfd))
76	{
77		smi_log(SMI_LOG_FATAL,
78			"%s: Unable to create listening socket on conn %s",
79			smfi->xxfi_name, conn);
80		(void) smutex_unlock(&L_Mutex);
81		return MI_FAILURE;
82	}
83#if !SM_CONF_POLL
84	if (!SM_FD_OK_SELECT(listenfd))
85	{
86		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
87			smfi->xxfi_name, listenfd, FD_SETSIZE);
88		(void) smutex_unlock(&L_Mutex);
89		return MI_FAILURE;
90	}
91#endif /* !SM_CONF_POLL */
92	(void) smutex_unlock(&L_Mutex);
93	return MI_SUCCESS;
94}
95
96/*
97**  MI_MILTEROPEN -- setup socket to listen on
98**
99**	Parameters:
100**		conn -- connection description
101**		backlog -- listen backlog
102**		rmsocket -- if true, try to unlink() the socket first
103**			(UNIX domain sockets only)
104**		name -- name for logging
105**
106**	Returns:
107**		socket upon success, error code otherwise.
108**
109**	Side effect:
110**		sets sockpath if UNIX socket.
111*/
112
113#if NETUNIX
114static char	*sockpath = NULL;
115#endif /* NETUNIX */
116
117static socket_t
118mi_milteropen(conn, backlog, rmsocket, name)
119	char *conn;
120	int backlog;
121	bool rmsocket;
122	char *name;
123{
124	socket_t sock;
125	int sockopt = 1;
126	int fdflags;
127	size_t len = 0;
128	char *p;
129	char *colon;
130	char *at;
131	SOCKADDR addr;
132
133	if (conn == NULL || conn[0] == '\0')
134	{
135		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
136			name);
137		return INVALID_SOCKET;
138	}
139	(void) memset(&addr, '\0', sizeof addr);
140
141	/* protocol:filename or protocol:port@host */
142	p = conn;
143	colon = strchr(p, ':');
144	if (colon != NULL)
145	{
146		*colon = '\0';
147
148		if (*p == '\0')
149		{
150#if NETUNIX
151			/* default to AF_UNIX */
152			addr.sa.sa_family = AF_UNIX;
153			L_socksize = sizeof (struct sockaddr_un);
154#else /* NETUNIX */
155# if NETINET
156			/* default to AF_INET */
157			addr.sa.sa_family = AF_INET;
158			L_socksize = sizeof addr.sin;
159# else /* NETINET */
160#  if NETINET6
161			/* default to AF_INET6 */
162			addr.sa.sa_family = AF_INET6;
163			L_socksize = sizeof addr.sin6;
164#  else /* NETINET6 */
165			/* no protocols available */
166			smi_log(SMI_LOG_ERR,
167				"%s: no valid socket protocols available",
168				name);
169			return INVALID_SOCKET;
170#  endif /* NETINET6 */
171# endif /* NETINET */
172#endif /* NETUNIX */
173		}
174#if NETUNIX
175		else if (strcasecmp(p, "unix") == 0 ||
176			 strcasecmp(p, "local") == 0)
177		{
178			addr.sa.sa_family = AF_UNIX;
179			L_socksize = sizeof (struct sockaddr_un);
180		}
181#endif /* NETUNIX */
182#if NETINET
183		else if (strcasecmp(p, "inet") == 0)
184		{
185			addr.sa.sa_family = AF_INET;
186			L_socksize = sizeof addr.sin;
187		}
188#endif /* NETINET */
189#if NETINET6
190		else if (strcasecmp(p, "inet6") == 0)
191		{
192			addr.sa.sa_family = AF_INET6;
193			L_socksize = sizeof addr.sin6;
194		}
195#endif /* NETINET6 */
196		else
197		{
198			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
199				name, p);
200			return INVALID_SOCKET;
201		}
202		*colon++ = ':';
203	}
204	else
205	{
206		colon = p;
207#if NETUNIX
208		/* default to AF_UNIX */
209		addr.sa.sa_family = AF_UNIX;
210		L_socksize = sizeof (struct sockaddr_un);
211#else /* NETUNIX */
212# if NETINET
213		/* default to AF_INET */
214		addr.sa.sa_family = AF_INET;
215		L_socksize = sizeof addr.sin;
216# else /* NETINET */
217#  if NETINET6
218		/* default to AF_INET6 */
219		addr.sa.sa_family = AF_INET6;
220		L_socksize = sizeof addr.sin6;
221#  else /* NETINET6 */
222		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
223			name, p);
224		return INVALID_SOCKET;
225#  endif /* NETINET6 */
226# endif /* NETINET */
227#endif /* NETUNIX */
228	}
229
230#if NETUNIX
231	if (addr.sa.sa_family == AF_UNIX)
232	{
233# if 0
234		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
235# endif /* 0 */
236
237		at = colon;
238		len = strlen(colon) + 1;
239		if (len >= sizeof addr.sunix.sun_path)
240		{
241			errno = EINVAL;
242			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
243				name, colon);
244			return INVALID_SOCKET;
245		}
246		(void) sm_strlcpy(addr.sunix.sun_path, colon,
247				sizeof addr.sunix.sun_path);
248# if 0
249		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
250				 S_IRUSR|S_IWUSR, NULL);
251
252		/* if not safe, don't create */
253		if (errno != 0)
254		{
255			smi_log(SMI_LOG_ERR,
256				"%s: UNIX socket name %s unsafe",
257				name, colon);
258			return INVALID_SOCKET;
259		}
260# endif /* 0 */
261	}
262#endif /* NETUNIX */
263
264#if NETINET || NETINET6
265	if (
266# if NETINET
267	    addr.sa.sa_family == AF_INET
268# endif /* NETINET */
269# if NETINET && NETINET6
270	    ||
271# endif /* NETINET && NETINET6 */
272# if NETINET6
273	    addr.sa.sa_family == AF_INET6
274# endif /* NETINET6 */
275	   )
276	{
277		unsigned short port;
278
279		/* Parse port@host */
280		at = strchr(colon, '@');
281		if (at == NULL)
282		{
283			switch (addr.sa.sa_family)
284			{
285# if NETINET
286			  case AF_INET:
287				addr.sin.sin_addr.s_addr = INADDR_ANY;
288				break;
289# endif /* NETINET */
290
291# if NETINET6
292			  case AF_INET6:
293				addr.sin6.sin6_addr = in6addr_any;
294				break;
295# endif /* NETINET6 */
296			}
297		}
298		else
299			*at = '\0';
300
301		if (isascii(*colon) && isdigit(*colon))
302			port = htons((unsigned short) atoi(colon));
303		else
304		{
305# ifdef NO_GETSERVBYNAME
306			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
307				name, colon);
308			return INVALID_SOCKET;
309# else /* NO_GETSERVBYNAME */
310			register struct servent *sp;
311
312			sp = getservbyname(colon, "tcp");
313			if (sp == NULL)
314			{
315				smi_log(SMI_LOG_ERR,
316					"%s: unknown port name %s",
317					name, colon);
318				return INVALID_SOCKET;
319			}
320			port = sp->s_port;
321# endif /* NO_GETSERVBYNAME */
322		}
323		if (at != NULL)
324		{
325			*at++ = '@';
326			if (*at == '[')
327			{
328				char *end;
329
330				end = strchr(at, ']');
331				if (end != NULL)
332				{
333					bool found = false;
334# if NETINET
335					unsigned long hid = INADDR_NONE;
336# endif /* NETINET */
337# if NETINET6
338					struct sockaddr_in6 hid6;
339# endif /* NETINET6 */
340
341					*end = '\0';
342# if NETINET
343					if (addr.sa.sa_family == AF_INET &&
344					    (hid = inet_addr(&at[1])) != INADDR_NONE)
345					{
346						addr.sin.sin_addr.s_addr = hid;
347						addr.sin.sin_port = port;
348						found = true;
349					}
350# endif /* NETINET */
351# if NETINET6
352					(void) memset(&hid6, '\0', sizeof hid6);
353					if (addr.sa.sa_family == AF_INET6 &&
354					    mi_inet_pton(AF_INET6, &at[1],
355							 &hid6.sin6_addr) == 1)
356					{
357						addr.sin6.sin6_addr = hid6.sin6_addr;
358						addr.sin6.sin6_port = port;
359						found = true;
360					}
361# endif /* NETINET6 */
362					*end = ']';
363					if (!found)
364					{
365						smi_log(SMI_LOG_ERR,
366							"%s: Invalid numeric domain spec \"%s\"",
367							name, at);
368						return INVALID_SOCKET;
369					}
370				}
371				else
372				{
373					smi_log(SMI_LOG_ERR,
374						"%s: Invalid numeric domain spec \"%s\"",
375						name, at);
376					return INVALID_SOCKET;
377				}
378			}
379			else
380			{
381				struct hostent *hp = NULL;
382
383				hp = mi_gethostbyname(at, addr.sa.sa_family);
384				if (hp == NULL)
385				{
386					smi_log(SMI_LOG_ERR,
387						"%s: Unknown host name %s",
388						name, at);
389					return INVALID_SOCKET;
390				}
391				addr.sa.sa_family = hp->h_addrtype;
392				switch (hp->h_addrtype)
393				{
394# if NETINET
395				  case AF_INET:
396					(void) memmove(&addr.sin.sin_addr,
397						       hp->h_addr,
398						       INADDRSZ);
399					addr.sin.sin_port = port;
400					break;
401# endif /* NETINET */
402
403# if NETINET6
404				  case AF_INET6:
405					(void) memmove(&addr.sin6.sin6_addr,
406						       hp->h_addr,
407						       IN6ADDRSZ);
408					addr.sin6.sin6_port = port;
409					break;
410# endif /* NETINET6 */
411
412				  default:
413					smi_log(SMI_LOG_ERR,
414						"%s: Unknown protocol for %s (%d)",
415						name, at, hp->h_addrtype);
416					return INVALID_SOCKET;
417				}
418# if NETINET6
419				freehostent(hp);
420# endif /* NETINET6 */
421			}
422		}
423		else
424		{
425			switch (addr.sa.sa_family)
426			{
427# if NETINET
428			  case AF_INET:
429				addr.sin.sin_port = port;
430				break;
431# endif /* NETINET */
432# if NETINET6
433			  case AF_INET6:
434				addr.sin6.sin6_port = port;
435				break;
436# endif /* NETINET6 */
437			}
438		}
439	}
440#endif /* NETINET || NETINET6 */
441
442	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
443	if (!ValidSocket(sock))
444	{
445		smi_log(SMI_LOG_ERR,
446			"%s: Unable to create new socket: %s",
447			name, sm_errstring(errno));
448		return INVALID_SOCKET;
449	}
450
451	if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
452	    fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
453	{
454		smi_log(SMI_LOG_ERR,
455			"%s: Unable to set close-on-exec: %s", name,
456			sm_errstring(errno));
457		(void) closesocket(sock);
458		return INVALID_SOCKET;
459	}
460
461	if (
462#if NETUNIX
463	    addr.sa.sa_family != AF_UNIX &&
464#endif /* NETUNIX */
465	    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
466		       sizeof(sockopt)) == -1)
467	{
468		smi_log(SMI_LOG_ERR,
469			"%s: set reuseaddr failed (%s)", name,
470			sm_errstring(errno));
471		(void) closesocket(sock);
472		return INVALID_SOCKET;
473	}
474
475#if NETUNIX
476	if (addr.sa.sa_family == AF_UNIX && rmsocket)
477	{
478		struct stat s;
479
480		if (stat(colon, &s) != 0)
481		{
482			if (errno != ENOENT)
483			{
484				smi_log(SMI_LOG_ERR,
485					"%s: Unable to stat() %s: %s",
486					name, colon, sm_errstring(errno));
487				(void) closesocket(sock);
488				return INVALID_SOCKET;
489			}
490		}
491		else if (!S_ISSOCK(s.st_mode))
492		{
493			smi_log(SMI_LOG_ERR,
494				"%s: %s is not a UNIX domain socket",
495				name, colon);
496			(void) closesocket(sock);
497			return INVALID_SOCKET;
498		}
499		else if (unlink(colon) != 0)
500		{
501			smi_log(SMI_LOG_ERR,
502				"%s: Unable to remove %s: %s",
503				name, colon, sm_errstring(errno));
504			(void) closesocket(sock);
505			return INVALID_SOCKET;
506		}
507	}
508#endif /* NETUNIX */
509
510	if (bind(sock, &addr.sa, L_socksize) < 0)
511	{
512		smi_log(SMI_LOG_ERR,
513			"%s: Unable to bind to port %s: %s",
514			name, conn, sm_errstring(errno));
515		(void) closesocket(sock);
516		return INVALID_SOCKET;
517	}
518
519	if (listen(sock, backlog) < 0)
520	{
521		smi_log(SMI_LOG_ERR,
522			"%s: listen call failed: %s", name,
523			sm_errstring(errno));
524		(void) closesocket(sock);
525		return INVALID_SOCKET;
526	}
527
528#if NETUNIX
529	if (addr.sa.sa_family == AF_UNIX && len > 0)
530	{
531		/*
532		**  Set global variable sockpath so the UNIX socket can be
533		**  unlink()ed at exit.
534		*/
535
536		sockpath = (char *) malloc(len);
537		if (sockpath != NULL)
538			(void) sm_strlcpy(sockpath, colon, len);
539		else
540		{
541			smi_log(SMI_LOG_ERR,
542				"%s: can't malloc(%d) for sockpath: %s",
543				name, (int) len, sm_errstring(errno));
544			(void) closesocket(sock);
545			return INVALID_SOCKET;
546		}
547	}
548#endif /* NETUNIX */
549	L_family = addr.sa.sa_family;
550	return sock;
551}
552/*
553**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
554**
555**	Parameters:
556**		arg -- argument to pass to mi_handle_session()
557**
558**	Returns:
559**		results from mi_handle_session()
560*/
561
562static void *
563mi_thread_handle_wrapper(arg)
564	void *arg;
565{
566	return (void *) mi_handle_session(arg);
567}
568
569/*
570**  MI_CLOSENER -- close listen socket
571**
572**	Parameters:
573**		none.
574**
575**	Returns:
576**		none.
577*/
578
579void
580mi_closener()
581{
582	(void) smutex_lock(&L_Mutex);
583	if (ValidSocket(listenfd))
584	{
585#if NETUNIX
586		bool removable;
587		struct stat sockinfo;
588		struct stat fileinfo;
589
590		removable = sockpath != NULL &&
591			    geteuid() != 0 &&
592			    fstat(listenfd, &sockinfo) == 0 &&
593			    (S_ISFIFO(sockinfo.st_mode)
594# ifdef S_ISSOCK
595			     || S_ISSOCK(sockinfo.st_mode)
596# endif /* S_ISSOCK */
597			    );
598#endif /* NETUNIX */
599
600		(void) closesocket(listenfd);
601		listenfd = INVALID_SOCKET;
602
603#if NETUNIX
604		/* XXX sleep() some time before doing this? */
605		if (sockpath != NULL)
606		{
607			if (removable &&
608			    stat(sockpath, &fileinfo) == 0 &&
609			    ((fileinfo.st_dev == sockinfo.st_dev &&
610			      fileinfo.st_ino == sockinfo.st_ino)
611# ifdef S_ISSOCK
612			     || S_ISSOCK(fileinfo.st_mode)
613# endif /* S_ISSOCK */
614			    )
615			    &&
616			    (S_ISFIFO(fileinfo.st_mode)
617# ifdef S_ISSOCK
618			     || S_ISSOCK(fileinfo.st_mode)
619# endif /* S_ISSOCK */
620			     ))
621				(void) unlink(sockpath);
622			free(sockpath);
623			sockpath = NULL;
624		}
625#endif /* NETUNIX */
626	}
627	(void) smutex_unlock(&L_Mutex);
628}
629
630/*
631**  MI_LISTENER -- Generic listener harness
632**
633**	Open up listen port
634**	Wait for connections
635**
636**	Parameters:
637**		conn -- connection description
638**		dbg -- debug level
639**		smfi -- filter structure to use
640**		timeout -- timeout for reads/writes
641**		backlog -- listen queue backlog size
642**
643**	Returns:
644**		MI_SUCCESS -- Exited normally
645**			   (session finished or we were told to exit)
646**		MI_FAILURE -- Network initialization failed.
647*/
648
649#if BROKEN_PTHREAD_SLEEP
650
651/*
652**  Solaris 2.6, perhaps others, gets an internal threads library panic
653**  when sleep() is used:
654**
655**  thread_create() failed, returned 11 (EINVAL)
656**  co_enable, thr_create() returned error = 24
657**  libthread panic: co_enable failed (PID: 17793 LWP 1)
658**  stacktrace:
659**	ef526b10
660**	ef52646c
661**	ef534cbc
662**	156a4
663**	14644
664**	1413c
665**	135e0
666**	0
667*/
668
669# define MI_SLEEP(s)							\
670{									\
671	int rs = 0;							\
672	struct timeval st;						\
673									\
674	st.tv_sec = (s);						\
675	st.tv_usec = 0;							\
676	if (st.tv_sec > 0)						\
677	{								\
678		for (;;)						\
679		{							\
680			rs = select(0, NULL, NULL, NULL, &st);		\
681			if (rs < 0 && errno == EINTR)			\
682				continue;				\
683			if (rs != 0)					\
684			{						\
685				smi_log(SMI_LOG_ERR,			\
686					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
687					rs, errno);			\
688			}						\
689			break;						\
690		}							\
691	}								\
692}
693#else /* BROKEN_PTHREAD_SLEEP */
694# define MI_SLEEP(s)	sleep((s))
695#endif /* BROKEN_PTHREAD_SLEEP */
696
697int
698mi_listener(conn, dbg, smfi, timeout, backlog)
699	char *conn;
700	int dbg;
701	smfiDesc_ptr smfi;
702	time_t timeout;
703	int backlog;
704{
705	socket_t connfd = INVALID_SOCKET;
706#if _FFR_DUP_FD
707	socket_t dupfd = INVALID_SOCKET;
708#endif /* _FFR_DUP_FD */
709	int sockopt = 1;
710	int r, mistop;
711	int ret = MI_SUCCESS;
712	int mcnt = 0;	/* error count for malloc() failures */
713	int tcnt = 0;	/* error count for thread_create() failures */
714	int acnt = 0;	/* error count for accept() failures */
715	int scnt = 0;	/* error count for select() failures */
716	int save_errno = 0;
717	sthread_t thread_id;
718	_SOCK_ADDR cliaddr;
719	SOCKADDR_LEN_T clilen;
720	SMFICTX_PTR ctx;
721	FD_RD_VAR(rds, excs);
722	struct timeval chktime;
723
724	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
725		return MI_FAILURE;
726
727	clilen = L_socksize;
728	while ((mistop = mi_stop()) == MILTER_CONT)
729	{
730		(void) smutex_lock(&L_Mutex);
731		if (!ValidSocket(listenfd))
732		{
733			ret = MI_FAILURE;
734			smi_log(SMI_LOG_ERR,
735				"%s: listenfd=%d corrupted, terminating, errno=%d",
736				smfi->xxfi_name, listenfd, errno);
737			(void) smutex_unlock(&L_Mutex);
738			break;
739		}
740
741		/* select on interface ports */
742		FD_RD_INIT(listenfd, rds, excs);
743		chktime.tv_sec = MI_CHK_TIME;
744		chktime.tv_usec = 0;
745		r = FD_RD_READY(listenfd, rds, excs, &chktime);
746		if (r == 0)		/* timeout */
747		{
748			(void) smutex_unlock(&L_Mutex);
749			continue;	/* just check mi_stop() */
750		}
751		if (r < 0)
752		{
753			save_errno = errno;
754			(void) smutex_unlock(&L_Mutex);
755			if (save_errno == EINTR)
756				continue;
757			scnt++;
758			smi_log(SMI_LOG_ERR,
759				"%s: select() failed (%s), %s",
760				smfi->xxfi_name, sm_errstring(save_errno),
761				scnt >= MAX_FAILS_S ? "abort" : "try again");
762			MI_SLEEP(scnt);
763			if (scnt >= MAX_FAILS_S)
764			{
765				ret = MI_FAILURE;
766				break;
767			}
768			continue;
769		}
770		if (!FD_IS_RD_RDY(listenfd, rds, excs))
771		{
772			/* some error: just stop for now... */
773			ret = MI_FAILURE;
774			(void) smutex_unlock(&L_Mutex);
775			smi_log(SMI_LOG_ERR,
776				"%s: %s() returned exception for socket, abort",
777				smfi->xxfi_name, MI_POLLSELECT);
778			break;
779		}
780		scnt = 0;	/* reset error counter for select() */
781
782		(void) memset(&cliaddr, '\0', sizeof cliaddr);
783		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
784				&clilen);
785		save_errno = errno;
786		(void) smutex_unlock(&L_Mutex);
787
788		/*
789		**  If remote side closes before
790		**  accept() finishes, sockaddr
791		**  might not be fully filled in.
792		*/
793
794		if (ValidSocket(connfd) &&
795		    (clilen == 0 ||
796# ifdef BSD4_4_SOCKADDR
797		     cliaddr.sa.sa_len == 0 ||
798# endif /* BSD4_4_SOCKADDR */
799		     cliaddr.sa.sa_family != L_family))
800		{
801			(void) closesocket(connfd);
802			connfd = INVALID_SOCKET;
803			save_errno = EINVAL;
804		}
805
806#if !SM_CONF_POLL
807		/* check if acceptable for select() */
808		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
809		{
810			(void) closesocket(connfd);
811			connfd = INVALID_SOCKET;
812			save_errno = ERANGE;
813		}
814#endif /* !SM_CONF_POLL */
815
816		if (!ValidSocket(connfd))
817		{
818			if (save_errno == EINTR
819#ifdef EAGAIN
820			    || save_errno == EAGAIN
821#endif /* EAGAIN */
822#ifdef ECONNABORTED
823			    || save_errno == ECONNABORTED
824#endif /* ECONNABORTED */
825#ifdef EMFILE
826			    || save_errno == EMFILE
827#endif /* EMFILE */
828#ifdef ENFILE
829			    || save_errno == ENFILE
830#endif /* ENFILE */
831#ifdef ENOBUFS
832			    || save_errno == ENOBUFS
833#endif /* ENOBUFS */
834#ifdef ENOMEM
835			    || save_errno == ENOMEM
836#endif /* ENOMEM */
837#ifdef ENOSR
838			    || save_errno == ENOSR
839#endif /* ENOSR */
840#ifdef EWOULDBLOCK
841			    || save_errno == EWOULDBLOCK
842#endif /* EWOULDBLOCK */
843			   )
844				continue;
845			acnt++;
846			smi_log(SMI_LOG_ERR,
847				"%s: accept() returned invalid socket (%s), %s",
848				smfi->xxfi_name, sm_errstring(save_errno),
849				acnt >= MAX_FAILS_A ? "abort" : "try again");
850			MI_SLEEP(acnt);
851			if (acnt >= MAX_FAILS_A)
852			{
853				ret = MI_FAILURE;
854				break;
855			}
856			continue;
857		}
858		acnt = 0;	/* reset error counter for accept() */
859#if _FFR_DUP_FD
860		dupfd = fcntl(connfd, F_DUPFD, 256);
861		if (ValidSocket(dupfd)
862# if !SM_CONF_POLL
863		    && SM_FD_OK_SELECT(dupfd)
864# endif /* !SM_CONF_POLL */
865		   )
866		{
867			close(connfd);
868			connfd = dupfd;
869			dupfd = INVALID_SOCKET;
870		}
871#endif /* _FFR_DUP_FD */
872
873		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
874				(void *) &sockopt, sizeof sockopt) < 0)
875		{
876			smi_log(SMI_LOG_WARN,
877				"%s: set keepalive failed (%s)",
878				smfi->xxfi_name, sm_errstring(errno));
879			/* XXX: continue? */
880		}
881		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
882		{
883			(void) closesocket(connfd);
884			mcnt++;
885			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
886				smfi->xxfi_name, sm_errstring(save_errno),
887				mcnt >= MAX_FAILS_M ? "abort" : "try again");
888			MI_SLEEP(mcnt);
889			if (mcnt >= MAX_FAILS_M)
890			{
891				ret = MI_FAILURE;
892				break;
893			}
894			continue;
895		}
896		mcnt = 0;	/* reset error counter for malloc() */
897		(void) memset(ctx, '\0', sizeof *ctx);
898		ctx->ctx_sd = connfd;
899		ctx->ctx_dbg = dbg;
900		ctx->ctx_timeout = timeout;
901		ctx->ctx_smfi = smfi;
902#if 0
903		if (smfi->xxfi_eoh == NULL)
904		if (smfi->xxfi_eom == NULL)
905		if (smfi->xxfi_abort == NULL)
906		if (smfi->xxfi_close == NULL)
907#endif /* 0 */
908		if (smfi->xxfi_connect == NULL)
909			ctx->ctx_pflags |= SMFIP_NOCONNECT;
910		if (smfi->xxfi_helo == NULL)
911			ctx->ctx_pflags |= SMFIP_NOHELO;
912		if (smfi->xxfi_envfrom == NULL)
913			ctx->ctx_pflags |= SMFIP_NOMAIL;
914		if (smfi->xxfi_envrcpt == NULL)
915			ctx->ctx_pflags |= SMFIP_NORCPT;
916		if (smfi->xxfi_header == NULL)
917			ctx->ctx_pflags |= SMFIP_NOHDRS;
918		if (smfi->xxfi_eoh == NULL)
919			ctx->ctx_pflags |= SMFIP_NOEOH;
920		if (smfi->xxfi_body == NULL)
921			ctx->ctx_pflags |= SMFIP_NOBODY;
922
923		if ((r = thread_create(&thread_id,
924					mi_thread_handle_wrapper,
925					(void *) ctx)) != 0)
926		{
927			tcnt++;
928			smi_log(SMI_LOG_ERR,
929				"%s: thread_create() failed: %d, %s",
930				smfi->xxfi_name,  r,
931				tcnt >= MAX_FAILS_T ? "abort" : "try again");
932			MI_SLEEP(tcnt);
933			(void) closesocket(connfd);
934			free(ctx);
935			if (tcnt >= MAX_FAILS_T)
936			{
937				ret = MI_FAILURE;
938				break;
939			}
940			continue;
941		}
942		tcnt = 0;
943	}
944	if (ret != MI_SUCCESS)
945		mi_stop_milters(MILTER_ABRT);
946	else
947	{
948		if (mistop != MILTER_CONT)
949			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
950				smfi->xxfi_name, mistop);
951		mi_closener();
952	}
953	(void) smutex_destroy(&L_Mutex);
954	return ret;
955}
956