listener.c revision 66494
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.11 2000/09/01 00:49:04 ca 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	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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
412static socket_t listenfd = INVALID_SOCKET;
413
414/*
415**  MI_CLOSENER -- close listen socket
416**
417**	Parameters:
418**		none.
419**
420**	Returns:
421**		none.
422*/
423
424void
425mi_closener()
426{
427	if (ValidSocket(listenfd))
428	{
429		(void) close(listenfd);
430		listenfd = INVALID_SOCKET;
431	}
432}
433
434/*
435**  MI_LISTENER -- Generic listener harness
436**
437**	Open up listen port
438**	Wait for connections
439**
440**	Parameters:
441**		conn -- connection description
442**		dbg -- debug level
443**		smfi -- filter structure to use
444**		timeout -- timeout for reads/writes
445**
446**	Returns:
447**		MI_SUCCESS -- Exited normally
448**			   (session finished or we were told to exit)
449**		MI_FAILURE -- Network initialization failed.
450*/
451
452int
453mi_listener(conn, dbg, smfi, timeout, backlog)
454	char *conn;
455	int dbg;
456	smfiDesc_ptr smfi;
457	time_t timeout;
458	int backlog;
459{
460	socket_t connfd = INVALID_SOCKET;
461	int sockopt = 1;
462	int r;
463	int ret = MI_SUCCESS;
464	int cnt_m = 0;
465	int cnt_t = 0;
466	sthread_t thread_id;
467	_SOCK_ADDR cliaddr;
468	SOCKADDR_LEN_T socksize;
469	SOCKADDR_LEN_T clilen;
470	SMFICTX_PTR ctx;
471	fd_set readset, excset;
472	struct timeval chktime;
473
474	if (dbg > 0)
475		smi_log(SMI_LOG_DEBUG,
476			"%s: Opening listen socket on conn %s",
477			smfi->xxfi_name, conn);
478	listenfd = mi_milteropen(conn, backlog, &socksize, smfi->xxfi_name);
479	if (!ValidSocket(listenfd))
480	{
481		smi_log(SMI_LOG_FATAL,
482			"%s: Unable to create listening socket on conn %s",
483			smfi->xxfi_name, conn);
484		return MI_FAILURE;
485	}
486	clilen = socksize;
487	if (listenfd >= FD_SETSIZE)
488	{
489		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
490			smfi->xxfi_name, listenfd, FD_SETSIZE);
491		return MI_FAILURE;
492	}
493
494	while (mi_stop() == MILTER_CONT)
495	{
496		/* select on interface ports */
497		FD_ZERO(&readset);
498		FD_SET((u_int) listenfd, &readset);
499		FD_ZERO(&excset);
500		FD_SET((u_int) listenfd, &excset);
501		chktime.tv_sec = MI_CHK_TIME;
502		chktime.tv_usec = 0;
503		r = select(listenfd + 1, &readset, NULL, &excset, &chktime);
504		if (r == 0)		/* timeout */
505			continue;	/* just check mi_stop() */
506		if (r < 0)
507		{
508			if (errno == EINTR)
509				continue;
510			ret = MI_FAILURE;
511			break;
512		}
513		if (!FD_ISSET(listenfd, &readset))
514		{
515			/* some error: just stop for now... */
516			ret = MI_FAILURE;
517			break;
518		}
519
520		connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
521				&clilen);
522
523		if (!ValidSocket(connfd))
524		{
525			smi_log(SMI_LOG_ERR,
526				"%s: accept() returned invalid socket",
527				smfi->xxfi_name);
528			continue;
529		}
530
531		if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
532				(void *) &sockopt, sizeof sockopt) < 0)
533		{
534			smi_log(SMI_LOG_WARN, "%s: setsockopt() failed",
535				smfi->xxfi_name);
536			/* XXX: continue? */
537		}
538		if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
539		{
540			(void) close(connfd);
541			smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed",
542				smfi->xxfi_name);
543			sleep(++cnt_m);
544			if (cnt_m >= MAX_FAILS_M)
545			{
546				ret = MI_FAILURE;
547				break;
548			}
549			continue;
550		}
551		cnt_m = 0;
552		memset(ctx, '\0', sizeof *ctx);
553		ctx->ctx_sd = connfd;
554		ctx->ctx_dbg = dbg;
555		ctx->ctx_timeout = timeout;
556		ctx->ctx_smfi = smfi;
557#if 0
558		if (smfi->xxfi_eoh == NULL)
559		if (smfi->xxfi_eom == NULL)
560		if (smfi->xxfi_abort == NULL)
561		if (smfi->xxfi_close == NULL)
562#endif /* 0 */
563		if (smfi->xxfi_connect == NULL)
564			ctx->ctx_pflags |= SMFIP_NOCONNECT;
565		if (smfi->xxfi_helo == NULL)
566			ctx->ctx_pflags |= SMFIP_NOHELO;
567		if (smfi->xxfi_envfrom == NULL)
568			ctx->ctx_pflags |= SMFIP_NOMAIL;
569		if (smfi->xxfi_envrcpt == NULL)
570			ctx->ctx_pflags |= SMFIP_NORCPT;
571		if (smfi->xxfi_header == NULL)
572			ctx->ctx_pflags |= SMFIP_NOHDRS;
573		if (smfi->xxfi_eoh == NULL)
574			ctx->ctx_pflags |= SMFIP_NOEOH;
575		if (smfi->xxfi_body == NULL)
576			ctx->ctx_pflags |= SMFIP_NOBODY;
577
578		if ((r = thread_create(&thread_id,
579					mi_thread_handle_wrapper,
580					(void *) ctx)) != MI_SUCCESS)
581		{
582			smi_log(SMI_LOG_ERR,
583				"%s: thread_create() failed: %d",
584				smfi->xxfi_name,  r);
585			sleep(++cnt_t);
586			(void) close(connfd);
587			free(ctx);
588			if (cnt_t >= MAX_FAILS_T)
589			{
590				ret = MI_FAILURE;
591				break;
592			}
593			continue;
594		}
595		cnt_t = 0;
596	}
597	if (ret != MI_SUCCESS)
598		mi_stop_milters(MILTER_ABRT);
599	if (listenfd >= 0)
600		(void) close(listenfd);
601	return ret;
602}
603#endif /* _FFR_MILTER */
604