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