listener.c revision 141858
1/*
2 *  Copyright (c) 1999-2004 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.111 2004/09/20 21:11:15 msk 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 (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
462		       sizeof(sockopt)) == -1)
463	{
464		smi_log(SMI_LOG_ERR,
465			"%s: Unable to setsockopt: %s", name,
466			sm_errstring(errno));
467		(void) closesocket(sock);
468		return INVALID_SOCKET;
469	}
470
471#if NETUNIX
472	if (addr.sa.sa_family == AF_UNIX && rmsocket)
473	{
474		struct stat s;
475
476		if (stat(colon, &s) != 0)
477		{
478			if (errno != ENOENT)
479			{
480				smi_log(SMI_LOG_ERR,
481					"%s: Unable to stat() %s: %s",
482					name, colon, sm_errstring(errno));
483				(void) closesocket(sock);
484				return INVALID_SOCKET;
485			}
486		}
487		else if (!S_ISSOCK(s.st_mode))
488		{
489			smi_log(SMI_LOG_ERR,
490				"%s: %s is not a UNIX domain socket",
491				name, colon);
492			(void) closesocket(sock);
493			return INVALID_SOCKET;
494		}
495		else if (unlink(colon) != 0)
496		{
497			smi_log(SMI_LOG_ERR,
498				"%s: Unable to remove %s: %s",
499				name, colon, sm_errstring(errno));
500			(void) closesocket(sock);
501			return INVALID_SOCKET;
502		}
503	}
504#endif /* NETUNIX */
505
506	if (bind(sock, &addr.sa, L_socksize) < 0)
507	{
508		smi_log(SMI_LOG_ERR,
509			"%s: Unable to bind to port %s: %s",
510			name, conn, sm_errstring(errno));
511		(void) closesocket(sock);
512		return INVALID_SOCKET;
513	}
514
515	if (listen(sock, backlog) < 0)
516	{
517		smi_log(SMI_LOG_ERR,
518			"%s: listen call failed: %s", name,
519			sm_errstring(errno));
520		(void) closesocket(sock);
521		return INVALID_SOCKET;
522	}
523
524#if NETUNIX
525	if (addr.sa.sa_family == AF_UNIX && len > 0)
526	{
527		/*
528		**  Set global variable sockpath so the UNIX socket can be
529		**  unlink()ed at exit.
530		*/
531
532		sockpath = (char *) malloc(len);
533		if (sockpath != NULL)
534			(void) sm_strlcpy(sockpath, colon, len);
535		else
536		{
537			smi_log(SMI_LOG_ERR,
538				"%s: can't malloc(%d) for sockpath: %s",
539				name, (int) len, sm_errstring(errno));
540			(void) closesocket(sock);
541			return INVALID_SOCKET;
542		}
543	}
544#endif /* NETUNIX */
545	L_family = addr.sa.sa_family;
546	return sock;
547}
548/*
549**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
550**
551**	Parameters:
552**		arg -- argument to pass to mi_handle_session()
553**
554**	Returns:
555**		results from mi_handle_session()
556*/
557
558static void *
559mi_thread_handle_wrapper(arg)
560	void *arg;
561{
562	return (void *) mi_handle_session(arg);
563}
564
565/*
566**  MI_CLOSENER -- close listen socket
567**
568**	NOTE: It is assumed that this function is called from a
569**	      function that has a mutex lock (currently mi_stop_milters()).
570**
571**	Parameters:
572**		none.
573**
574**	Returns:
575**		none.
576*/
577
578void
579mi_closener()
580{
581	(void) smutex_lock(&L_Mutex);
582	if (ValidSocket(listenfd))
583	{
584#if NETUNIX
585		bool removable;
586		struct stat sockinfo;
587		struct stat fileinfo;
588
589		removable = sockpath != NULL &&
590			    geteuid() != 0 &&
591			    fstat(listenfd, &sockinfo) == 0 &&
592			    (S_ISFIFO(sockinfo.st_mode)
593# ifdef S_ISSOCK
594			     || S_ISSOCK(sockinfo.st_mode)
595# endif /* S_ISSOCK */
596			    );
597#endif /* NETUNIX */
598
599		(void) closesocket(listenfd);
600		listenfd = INVALID_SOCKET;
601
602#if NETUNIX
603		/* XXX sleep() some time before doing this? */
604		if (sockpath != NULL)
605		{
606			if (removable &&
607			    stat(sockpath, &fileinfo) == 0 &&
608			    ((fileinfo.st_dev == sockinfo.st_dev &&
609			      fileinfo.st_ino == sockinfo.st_ino)
610# ifdef S_ISSOCK
611			     || S_ISSOCK(fileinfo.st_mode)
612# endif /* S_ISSOCK */
613			    )
614			    &&
615			    (S_ISFIFO(fileinfo.st_mode)
616# ifdef S_ISSOCK
617			     || S_ISSOCK(fileinfo.st_mode)
618# endif /* S_ISSOCK */
619			     ))
620				(void) unlink(sockpath);
621			free(sockpath);
622			sockpath = NULL;
623		}
624#endif /* NETUNIX */
625	}
626	(void) smutex_unlock(&L_Mutex);
627}
628
629/*
630**  MI_LISTENER -- Generic listener harness
631**
632**	Open up listen port
633**	Wait for connections
634**
635**	Parameters:
636**		conn -- connection description
637**		dbg -- debug level
638**		smfi -- filter structure to use
639**		timeout -- timeout for reads/writes
640**		backlog -- listen queue backlog size
641**
642**	Returns:
643**		MI_SUCCESS -- Exited normally
644**			   (session finished or we were told to exit)
645**		MI_FAILURE -- Network initialization failed.
646*/
647
648#if BROKEN_PTHREAD_SLEEP
649
650/*
651**  Solaris 2.6, perhaps others, gets an internal threads library panic
652**  when sleep() is used:
653**
654**  thread_create() failed, returned 11 (EINVAL)
655**  co_enable, thr_create() returned error = 24
656**  libthread panic: co_enable failed (PID: 17793 LWP 1)
657**  stacktrace:
658**	ef526b10
659**	ef52646c
660**	ef534cbc
661**	156a4
662**	14644
663**	1413c
664**	135e0
665**	0
666*/
667
668# define MI_SLEEP(s)							\
669{									\
670	int rs = 0;							\
671	struct timeval st;						\
672									\
673	st.tv_sec = (s);						\
674	st.tv_usec = 0;							\
675	if (st.tv_sec > 0)						\
676	{								\
677		for (;;)						\
678		{							\
679			rs = select(0, NULL, NULL, NULL, &st);		\
680			if (rs < 0 && errno == EINTR)			\
681				continue;				\
682			if (rs != 0)					\
683			{						\
684				smi_log(SMI_LOG_ERR,			\
685					"MI_SLEEP(): select() returned non-zero result %d, errno = %d",	\
686					rs, errno);			\
687			}						\
688			break;						\
689		}							\
690	}								\
691}
692#else /* BROKEN_PTHREAD_SLEEP */
693# define MI_SLEEP(s)	sleep((s))
694#endif /* BROKEN_PTHREAD_SLEEP */
695
696int
697mi_listener(conn, dbg, smfi, timeout, backlog)
698	char *conn;
699	int dbg;
700	smfiDesc_ptr smfi;
701	time_t timeout;
702	int backlog;
703{
704	socket_t connfd = INVALID_SOCKET;
705#if _FFR_DUP_FD
706	socket_t dupfd = INVALID_SOCKET;
707#endif /* _FFR_DUP_FD */
708	int sockopt = 1;
709	int r, mistop;
710	int ret = MI_SUCCESS;
711	int mcnt = 0;	/* error count for malloc() failures */
712	int tcnt = 0;	/* error count for thread_create() failures */
713	int acnt = 0;	/* error count for accept() failures */
714	int scnt = 0;	/* error count for select() failures */
715	int save_errno = 0;
716	sthread_t thread_id;
717	_SOCK_ADDR cliaddr;
718	SOCKADDR_LEN_T clilen;
719	SMFICTX_PTR ctx;
720	FD_RD_VAR(rds, excs);
721	struct timeval chktime;
722
723	if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
724		return MI_FAILURE;
725
726	clilen = L_socksize;
727	while ((mistop = mi_stop()) == MILTER_CONT)
728	{
729		(void) smutex_lock(&L_Mutex);
730		if (!ValidSocket(listenfd))
731		{
732			ret = MI_FAILURE;
733			smi_log(SMI_LOG_ERR,
734				"%s: listenfd=%d corrupted, terminating, errno=%d",
735				smfi->xxfi_name, listenfd, errno);
736			(void) smutex_unlock(&L_Mutex);
737			break;
738		}
739
740		/* select on interface ports */
741		FD_RD_INIT(listenfd, rds, excs);
742		chktime.tv_sec = MI_CHK_TIME;
743		chktime.tv_usec = 0;
744		r = FD_RD_READY(listenfd, rds, excs, &chktime);
745		if (r == 0)		/* timeout */
746		{
747			(void) smutex_unlock(&L_Mutex);
748			continue;	/* just check mi_stop() */
749		}
750		if (r < 0)
751		{
752			save_errno = errno;
753			(void) smutex_unlock(&L_Mutex);
754			if (save_errno == EINTR)
755				continue;
756			scnt++;
757			smi_log(SMI_LOG_ERR,
758				"%s: select() failed (%s), %s",
759				smfi->xxfi_name, sm_errstring(save_errno),
760				scnt >= MAX_FAILS_S ? "abort" : "try again");
761			MI_SLEEP(scnt);
762			if (scnt >= MAX_FAILS_S)
763			{
764				ret = MI_FAILURE;
765				break;
766			}
767			continue;
768		}
769		if (!FD_IS_RD_RDY(listenfd, rds, excs))
770		{
771			/* some error: just stop for now... */
772			ret = MI_FAILURE;
773			(void) smutex_unlock(&L_Mutex);
774			smi_log(SMI_LOG_ERR,
775				"%s: %s() returned exception for socket, abort",
776				smfi->xxfi_name, MI_POLLSELECT);
777			break;
778		}
779		scnt = 0;	/* reset error counter for select() */
780
781		(void) memset(&cliaddr, '\0', sizeof cliaddr);
782		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
783				&clilen);
784		save_errno = errno;
785		(void) smutex_unlock(&L_Mutex);
786
787		/*
788		**  If remote side closes before
789		**  accept() finishes, sockaddr
790		**  might not be fully filled in.
791		*/
792
793		if (ValidSocket(connfd) &&
794		    (clilen == 0 ||
795# ifdef BSD4_4_SOCKADDR
796		     cliaddr.sa.sa_len == 0 ||
797# endif /* BSD4_4_SOCKADDR */
798		     cliaddr.sa.sa_family != L_family))
799		{
800			(void) closesocket(connfd);
801			connfd = INVALID_SOCKET;
802			save_errno = EINVAL;
803		}
804
805#if !SM_CONF_POLL
806		/* check if acceptable for select() */
807		if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
808		{
809			(void) closesocket(connfd);
810			connfd = INVALID_SOCKET;
811			save_errno = ERANGE;
812		}
813#endif /* !SM_CONF_POLL */
814
815		if (!ValidSocket(connfd))
816		{
817			if (save_errno == EINTR
818#ifdef EAGAIN
819			    || save_errno == EAGAIN
820#endif /* EAGAIN */
821#ifdef ECONNABORTED
822			    || save_errno == ECONNABORTED
823#endif /* ECONNABORTED */
824#ifdef EMFILE
825			    || save_errno == EMFILE
826#endif /* EMFILE */
827#ifdef ENFILE
828			    || save_errno == ENFILE
829#endif /* ENFILE */
830#ifdef ENOBUFS
831			    || save_errno == ENOBUFS
832#endif /* ENOBUFS */
833#ifdef ENOMEM
834			    || save_errno == ENOMEM
835#endif /* ENOMEM */
836#ifdef ENOSR
837			    || save_errno == ENOSR
838#endif /* ENOSR */
839#ifdef EWOULDBLOCK
840			    || save_errno == EWOULDBLOCK
841#endif /* EWOULDBLOCK */
842			   )
843				continue;
844			acnt++;
845			smi_log(SMI_LOG_ERR,
846				"%s: accept() returned invalid socket (%s), %s",
847				smfi->xxfi_name, sm_errstring(save_errno),
848				acnt >= MAX_FAILS_A ? "abort" : "try again");
849			MI_SLEEP(acnt);
850			if (acnt >= MAX_FAILS_A)
851			{
852				ret = MI_FAILURE;
853				break;
854			}
855			continue;
856		}
857		acnt = 0;	/* reset error counter for accept() */
858#if _FFR_DUP_FD
859		dupfd = fcntl(connfd, F_DUPFD, 256);
860		if (ValidSocket(dupfd)
861# if !SM_CONF_POLL
862		    && SM_FD_OK_SELECT(dupfd)
863# endif /* !SM_CONF_POLL */
864		   )
865		{
866			close(connfd);
867			connfd = dupfd;
868			dupfd = INVALID_SOCKET;
869		}
870#endif /* _FFR_DUP_FD */
871
872		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
873				(void *) &sockopt, sizeof sockopt) < 0)
874		{
875			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)",
876				smfi->xxfi_name, sm_errstring(errno));
877			/* XXX: continue? */
878		}
879		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
880		{
881			(void) closesocket(connfd);
882			mcnt++;
883			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
884				smfi->xxfi_name, sm_errstring(save_errno),
885				mcnt >= MAX_FAILS_M ? "abort" : "try again");
886			MI_SLEEP(mcnt);
887			if (mcnt >= MAX_FAILS_M)
888			{
889				ret = MI_FAILURE;
890				break;
891			}
892			continue;
893		}
894		mcnt = 0;	/* reset error counter for malloc() */
895		(void) memset(ctx, '\0', sizeof *ctx);
896		ctx->ctx_sd = connfd;
897		ctx->ctx_dbg = dbg;
898		ctx->ctx_timeout = timeout;
899		ctx->ctx_smfi = smfi;
900#if 0
901		if (smfi->xxfi_eoh == NULL)
902		if (smfi->xxfi_eom == NULL)
903		if (smfi->xxfi_abort == NULL)
904		if (smfi->xxfi_close == NULL)
905#endif /* 0 */
906		if (smfi->xxfi_connect == NULL)
907			ctx->ctx_pflags |= SMFIP_NOCONNECT;
908		if (smfi->xxfi_helo == NULL)
909			ctx->ctx_pflags |= SMFIP_NOHELO;
910		if (smfi->xxfi_envfrom == NULL)
911			ctx->ctx_pflags |= SMFIP_NOMAIL;
912		if (smfi->xxfi_envrcpt == NULL)
913			ctx->ctx_pflags |= SMFIP_NORCPT;
914		if (smfi->xxfi_header == NULL)
915			ctx->ctx_pflags |= SMFIP_NOHDRS;
916		if (smfi->xxfi_eoh == NULL)
917			ctx->ctx_pflags |= SMFIP_NOEOH;
918		if (smfi->xxfi_body == NULL)
919			ctx->ctx_pflags |= SMFIP_NOBODY;
920
921		if ((r = thread_create(&thread_id,
922					mi_thread_handle_wrapper,
923					(void *) ctx)) != 0)
924		{
925			tcnt++;
926			smi_log(SMI_LOG_ERR,
927				"%s: thread_create() failed: %d, %s",
928				smfi->xxfi_name,  r,
929				tcnt >= MAX_FAILS_T ? "abort" : "try again");
930			MI_SLEEP(tcnt);
931			(void) closesocket(connfd);
932			free(ctx);
933			if (tcnt >= MAX_FAILS_T)
934			{
935				ret = MI_FAILURE;
936				break;
937			}
938			continue;
939		}
940		tcnt = 0;
941	}
942	if (ret != MI_SUCCESS)
943		mi_stop_milters(MILTER_ABRT);
944	else
945	{
946		if (mistop != MILTER_CONT)
947			smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
948				smfi->xxfi_name, mistop);
949		mi_closener();
950	}
951	(void) smutex_destroy(&L_Mutex);
952	return ret;
953}
954