listener.c revision 71345
1/*
2 *  Copyright (c) 1999-2000 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#ifndef lint
12static char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.18 2000/12/29 19:44:28 gshapiro Exp $";
13#endif /* ! lint */
14
15#if _FFR_MILTER
16/*
17**  listener.c -- threaded network listener
18*/
19
20#include "libmilter.h"
21
22
23# if NETINET || NETINET6
24#  include <arpa/inet.h>
25# endif /* NETINET || NETINET6 */
26/*
27**  MI_MILTEROPEN -- setup socket to listen on
28**
29**	Parameters:
30**		conn -- connection description
31**		backlog -- listen backlog
32**		socksize -- socksize of created socket
33**
34**	Returns:
35**		socket upon success, error code otherwise.
36*/
37
38static socket_t
39mi_milteropen(conn, backlog, socksize, name)
40	char *conn;
41	int backlog;
42	SOCKADDR_LEN_T *socksize;
43	char *name;
44{
45	socket_t sock;
46	int sockopt = 1;
47	char *p;
48	char *colon;
49	char *at;
50	SOCKADDR addr;
51
52	if (conn == NULL || conn[0] == '\0')
53	{
54		smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
55			name);
56		return INVALID_SOCKET;
57	}
58	(void) memset(&addr, '\0', sizeof addr);
59
60	/* protocol:filename or protocol:port@host */
61	p = conn;
62	colon = strchr(p, ':');
63	if (colon != NULL)
64	{
65		*colon = '\0';
66
67 		if (*p == '\0')
68		{
69#if NETUNIX
70			/* default to AF_UNIX */
71 			addr.sa.sa_family = AF_UNIX;
72			*socksize = sizeof (struct sockaddr_un);
73#else /* NETUNIX */
74# if NETINET
75			/* default to AF_INET */
76			addr.sa.sa_family = AF_INET;
77			*socksize = sizeof addr.sin;
78# else /* NETINET */
79#  if NETINET6
80			/* default to AF_INET6 */
81			addr.sa.sa_family = AF_INET6;
82			*socksize = sizeof addr.sin6;
83#  else /* NETINET6 */
84			/* no protocols available */
85			smi_log(SMI_LOG_ERR,
86				"%s: no valid socket protocols available",
87				name);
88			return INVALID_SOCKET;
89#  endif /* NETINET6 */
90# endif /* NETINET */
91#endif /* NETUNIX */
92		}
93#if NETUNIX
94		else if (strcasecmp(p, "unix") == 0 ||
95			 strcasecmp(p, "local") == 0)
96		{
97			addr.sa.sa_family = AF_UNIX;
98			*socksize = sizeof (struct sockaddr_un);
99		}
100#endif /* NETUNIX */
101#if NETINET
102		else if (strcasecmp(p, "inet") == 0)
103		{
104			addr.sa.sa_family = AF_INET;
105			*socksize = sizeof addr.sin;
106		}
107#endif /* NETINET */
108#if NETINET6
109		else if (strcasecmp(p, "inet6") == 0)
110		{
111			addr.sa.sa_family = AF_INET6;
112			*socksize = sizeof addr.sin6;
113		}
114#endif /* NETINET6 */
115		else
116		{
117			smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
118				name, p);
119			return INVALID_SOCKET;
120		}
121		*colon++ = ':';
122	}
123	else
124	{
125		colon = p;
126#if NETUNIX
127		/* default to AF_UNIX */
128 		addr.sa.sa_family = AF_UNIX;
129		*socksize = sizeof (struct sockaddr_un);
130#else /* NETUNIX */
131# if NETINET
132		/* default to AF_INET */
133		addr.sa.sa_family = AF_INET;
134		*socksize = sizeof addr.sin;
135# else /* NETINET */
136#  if NETINET6
137		/* default to AF_INET6 */
138		addr.sa.sa_family = AF_INET6;
139		*socksize = sizeof addr.sin6;
140#  else /* NETINET6 */
141		smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
142			name, p);
143		return INVALID_SOCKET;
144#  endif /* NETINET6 */
145# endif /* NETINET */
146#endif /* NETUNIX */
147	}
148
149#if NETUNIX
150	if (addr.sa.sa_family == AF_UNIX)
151	{
152# if 0
153		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
154# endif /* 0 */
155
156		at = colon;
157		if (strlcpy(addr.sunix.sun_path, colon,
158			    sizeof addr.sunix.sun_path) >=
159		    sizeof addr.sunix.sun_path)
160		{
161			errno = EINVAL;
162			smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
163				name, colon);
164			return INVALID_SOCKET;
165		}
166# if 0
167		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
168				 S_IRUSR|S_IWUSR, NULL);
169
170		/* if not safe, don't create */
171		if (errno != 0)
172		{
173			smi_log(SMI_LOG_ERR,
174				"%s: UNIX socket name %s unsafe",
175				name, colon);
176			return INVALID_SOCKET;
177		}
178# endif /* 0 */
179
180	}
181#endif /* NETUNIX */
182
183#if NETINET || NETINET6
184	if (
185# if NETINET
186	    addr.sa.sa_family == AF_INET
187# endif /* NETINET */
188# if NETINET && NETINET6
189	    ||
190# endif /* NETINET && NETINET6 */
191# if NETINET6
192	    addr.sa.sa_family == AF_INET6
193# endif /* NETINET6 */
194	   )
195	{
196		u_short port;
197
198		/* Parse port@host */
199		at = strchr(colon, '@');
200		if (at == NULL)
201		{
202			switch (addr.sa.sa_family)
203			{
204# if NETINET
205			  case AF_INET:
206				addr.sin.sin_addr.s_addr = INADDR_ANY;
207				break;
208# endif /* NETINET */
209
210# if NETINET6
211			  case AF_INET6:
212				addr.sin6.sin6_addr = in6addr_any;
213				break;
214# endif /* NETINET6 */
215			}
216		}
217		else
218			*at = '\0';
219
220		if (isascii(*colon) && isdigit(*colon))
221			port = htons((u_short) atoi(colon));
222		else
223		{
224# ifdef NO_GETSERVBYNAME
225			smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
226				name, colon);
227			return INVALID_SOCKET;
228# else /* NO_GETSERVBYNAME */
229			register struct servent *sp;
230
231			sp = getservbyname(colon, "tcp");
232			if (sp == NULL)
233			{
234				smi_log(SMI_LOG_ERR,
235					"%s: unknown port name %s",
236					name, colon);
237				return INVALID_SOCKET;
238			}
239			port = sp->s_port;
240# endif /* NO_GETSERVBYNAME */
241		}
242		if (at != NULL)
243		{
244			*at++ = '@';
245			if (*at == '[')
246			{
247				char *end;
248
249				end = strchr(at, ']');
250				if (end != NULL)
251				{
252					bool found = FALSE;
253# if NETINET
254					unsigned long hid = INADDR_NONE;
255# endif /* NETINET */
256# if NETINET6
257					struct sockaddr_in6 hid6;
258# endif /* NETINET6 */
259
260					*end = '\0';
261# if NETINET
262					if (addr.sa.sa_family == AF_INET &&
263					    (hid = inet_addr(&at[1])) !=
264					    INADDR_NONE)
265					{
266						addr.sin.sin_addr.s_addr = hid;
267						addr.sin.sin_port = port;
268						found = TRUE;
269					}
270# endif /* NETINET */
271# if NETINET6
272					(void) memset(&hid6, '\0', sizeof hid6);
273					if (addr.sa.sa_family == AF_INET6 &&
274					    inet_pton(AF_INET6, &at[1],
275						      &hid6.sin6_addr) == 1)
276					{
277						addr.sin6.sin6_addr = hid6.sin6_addr;
278						addr.sin6.sin6_port = port;
279						found = TRUE;
280					}
281# endif /* NETINET6 */
282					*end = ']';
283					if (!found)
284					{
285						smi_log(SMI_LOG_ERR,
286							"%s: Invalid numeric domain spec \"%s\"",
287							name, at);
288						return INVALID_SOCKET;
289					}
290				}
291				else
292				{
293					smi_log(SMI_LOG_ERR,
294						"%s: Invalid numeric domain spec \"%s\"",
295						name, at);
296					return INVALID_SOCKET;
297				}
298			}
299			else
300			{
301				struct hostent *hp = NULL;
302
303				hp = mi_gethostbyname(at, addr.sa.sa_family);
304				if (hp == NULL)
305				{
306					smi_log(SMI_LOG_ERR,
307						"%s: Unknown host name %s",
308						name, at);
309					return INVALID_SOCKET;
310				}
311				addr.sa.sa_family = hp->h_addrtype;
312				switch (hp->h_addrtype)
313				{
314# if NETINET
315				  case AF_INET:
316					memmove(&addr.sin.sin_addr,
317						hp->h_addr,
318						INADDRSZ);
319					addr.sin.sin_port = port;
320					break;
321# endif /* NETINET */
322
323# if NETINET6
324				  case AF_INET6:
325					memmove(&addr.sin6.sin6_addr,
326						hp->h_addr,
327						IN6ADDRSZ);
328					addr.sin6.sin6_port = port;
329					break;
330# endif /* NETINET6 */
331
332				  default:
333					smi_log(SMI_LOG_ERR,
334						"%s: Unknown protocol for %s (%d)",
335						name, at, hp->h_addrtype);
336					return INVALID_SOCKET;
337				}
338# if _FFR_FREEHOSTENT && NETINET6
339				freehostent(hp);
340# endif /* _FFR_FREEHOSTENT && NETINET6 */
341			}
342		}
343		else
344		{
345			switch (addr.sa.sa_family)
346			{
347# if NETINET
348			  case AF_INET:
349				addr.sin.sin_port = port;
350				break;
351# endif /* NETINET */
352# if NETINET6
353			  case AF_INET6:
354				addr.sin6.sin6_port = port;
355				break;
356# endif /* NETINET6 */
357			}
358		}
359	}
360#endif /* NETINET || NETINET6 */
361
362	sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
363	if (!ValidSocket(sock))
364	{
365		smi_log(SMI_LOG_ERR,
366			"%s: Unable to create new socket: %s",
367			name, strerror(errno));
368		return INVALID_SOCKET;
369	}
370
371	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
372		       sizeof(sockopt)) == -1)
373	{
374		smi_log(SMI_LOG_ERR,
375			"%s: Unable to setsockopt: %s", name, strerror(errno));
376		(void) close(sock);
377		return INVALID_SOCKET;
378	}
379
380	if (bind(sock, &addr.sa, *socksize) < 0)
381	{
382		smi_log(SMI_LOG_ERR,
383			"%s: Unable to bind to port %s: %s",
384			name, conn, strerror(errno));
385		(void) close(sock);
386		return INVALID_SOCKET;
387	}
388
389	if (listen(sock, backlog) < 0)
390	{
391		smi_log(SMI_LOG_ERR,
392			"%s: listen call failed: %s", name, strerror(errno));
393		(void) close(sock);
394		return INVALID_SOCKET;
395	}
396
397	return sock;
398}
399/*
400**  MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
401**
402**	Parameters:
403**		arg -- argument to pass to mi_handle_session()
404**
405**	Returns:
406**		results from mi_handle_session()
407*/
408
409void *
410mi_thread_handle_wrapper(arg)
411	void *arg;
412{
413	return (void *) mi_handle_session(arg);
414}
415
416static socket_t listenfd = INVALID_SOCKET;
417
418static smutex_t L_Mutex;
419
420/*
421**  MI_CLOSENER -- close listen socket
422**
423**	Parameters:
424**		none.
425**
426**	Returns:
427**		none.
428*/
429
430void
431mi_closener()
432{
433	(void) smutex_lock(&L_Mutex);
434	if (ValidSocket(listenfd))
435	{
436		(void) close(listenfd);
437		listenfd = INVALID_SOCKET;
438	}
439	(void) smutex_unlock(&L_Mutex);
440}
441
442/*
443**  MI_LISTENER -- Generic listener harness
444**
445**	Open up listen port
446**	Wait for connections
447**
448**	Parameters:
449**		conn -- connection description
450**		dbg -- debug level
451**		smfi -- filter structure to use
452**		timeout -- timeout for reads/writes
453**
454**	Returns:
455**		MI_SUCCESS -- Exited normally
456**			   (session finished or we were told to exit)
457**		MI_FAILURE -- Network initialization failed.
458*/
459
460int
461mi_listener(conn, dbg, smfi, timeout, backlog)
462	char *conn;
463	int dbg;
464	smfiDesc_ptr smfi;
465	time_t timeout;
466	int backlog;
467{
468	socket_t connfd = INVALID_SOCKET;
469	int sockopt = 1;
470	int r;
471	int ret = MI_SUCCESS;
472	int cnt_m = 0;
473	int cnt_t = 0;
474	sthread_t thread_id;
475	_SOCK_ADDR cliaddr;
476	SOCKADDR_LEN_T socksize;
477	SOCKADDR_LEN_T clilen;
478	SMFICTX_PTR ctx;
479	fd_set readset, excset;
480	struct timeval chktime;
481
482	if (dbg > 0)
483		smi_log(SMI_LOG_DEBUG,
484			"%s: Opening listen socket on conn %s",
485			smfi->xxfi_name, conn);
486	(void) smutex_init(&L_Mutex);
487	(void) smutex_lock(&L_Mutex);
488	listenfd = mi_milteropen(conn, backlog, &socksize, smfi->xxfi_name);
489	if (!ValidSocket(listenfd))
490	{
491		smi_log(SMI_LOG_FATAL,
492			"%s: Unable to create listening socket on conn %s",
493			smfi->xxfi_name, conn);
494		(void) smutex_unlock(&L_Mutex);
495		return MI_FAILURE;
496	}
497	clilen = socksize;
498
499	if (listenfd >= FD_SETSIZE)
500	{
501		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
502			smfi->xxfi_name, listenfd, FD_SETSIZE);
503		(void) smutex_unlock(&L_Mutex);
504		return MI_FAILURE;
505	}
506	(void) smutex_unlock(&L_Mutex);
507
508	while (mi_stop() == MILTER_CONT)
509	{
510		(void) smutex_lock(&L_Mutex);
511		if (!ValidSocket(listenfd))
512		{
513			(void) smutex_unlock(&L_Mutex);
514			break;
515		}
516
517		/* select on interface ports */
518		FD_ZERO(&readset);
519		FD_ZERO(&excset);
520		FD_SET((u_int) listenfd, &readset);
521		FD_SET((u_int) listenfd, &excset);
522		chktime.tv_sec = MI_CHK_TIME;
523		chktime.tv_usec = 0;
524		r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
525		if (r == 0)		/* timeout */
526		{
527			(void) smutex_unlock(&L_Mutex);
528			continue;	/* just check mi_stop() */
529		}
530		if (r < 0)
531		{
532			int err = errno;
533
534			(void) smutex_unlock(&L_Mutex);
535			if (err == EINTR)
536				continue;
537			ret = MI_FAILURE;
538			break;
539		}
540		if (!FD_ISSET(listenfd, &readset))
541		{
542			/* some error: just stop for now... */
543			ret = MI_FAILURE;
544			(void) smutex_unlock(&L_Mutex);
545			break;
546		}
547
548		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
549				&clilen);
550		(void) smutex_unlock(&L_Mutex);
551
552		if (!ValidSocket(connfd))
553		{
554			smi_log(SMI_LOG_ERR,
555				"%s: accept() returned invalid socket",
556				smfi->xxfi_name);
557			continue;
558		}
559
560		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
561				(void *) &sockopt, sizeof sockopt) < 0)
562		{
563			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed",
564				smfi->xxfi_name);
565			/* XXX: continue? */
566		}
567		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
568		{
569			(void) close(connfd);
570			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
571				smfi->xxfi_name);
572			sleep(++cnt_m);
573			if (cnt_m >= MAX_FAILS_M)
574			{
575				ret = MI_FAILURE;
576				break;
577			}
578			continue;
579		}
580		cnt_m = 0;
581		memset(ctx, '\0', sizeof *ctx);
582		ctx->ctx_sd = connfd;
583		ctx->ctx_dbg = dbg;
584		ctx->ctx_timeout = timeout;
585		ctx->ctx_smfi = smfi;
586#if 0
587		if (smfi->xxfi_eoh == NULL)
588		if (smfi->xxfi_eom == NULL)
589		if (smfi->xxfi_abort == NULL)
590		if (smfi->xxfi_close == NULL)
591#endif /* 0 */
592		if (smfi->xxfi_connect == NULL)
593			ctx->ctx_pflags |= SMFIP_NOCONNECT;
594		if (smfi->xxfi_helo == NULL)
595			ctx->ctx_pflags |= SMFIP_NOHELO;
596		if (smfi->xxfi_envfrom == NULL)
597			ctx->ctx_pflags |= SMFIP_NOMAIL;
598		if (smfi->xxfi_envrcpt == NULL)
599			ctx->ctx_pflags |= SMFIP_NORCPT;
600		if (smfi->xxfi_header == NULL)
601			ctx->ctx_pflags |= SMFIP_NOHDRS;
602		if (smfi->xxfi_eoh == NULL)
603			ctx->ctx_pflags |= SMFIP_NOEOH;
604		if (smfi->xxfi_body == NULL)
605			ctx->ctx_pflags |= SMFIP_NOBODY;
606
607		if ((r = thread_create(&thread_id,
608					mi_thread_handle_wrapper,
609					(void *) ctx)) != 0)
610		{
611			smi_log(SMI_LOG_ERR,
612				"%s: thread_create() failed: %d",
613				smfi->xxfi_name,  r);
614			sleep(++cnt_t);
615			(void) close(connfd);
616			free(ctx);
617			if (cnt_t >= MAX_FAILS_T)
618			{
619				ret = MI_FAILURE;
620				break;
621			}
622			continue;
623		}
624		cnt_t = 0;
625	}
626	if (ret != MI_SUCCESS)
627		mi_stop_milters(MILTER_ABRT);
628	else
629		mi_closener();
630	(void) smutex_destroy(&L_Mutex);
631	return ret;
632}
633#endif /* _FFR_MILTER */
634