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