xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 444eaf03)
17aa493c5Seric # include <errno.h>
27fa39d90Seric # include "sendmail.h"
37fa39d90Seric 
4d0a9e852Seric #ifndef DAEMON
5*444eaf03Seric SCCSID(@(#)daemon.c	3.48		02/08/83	(w/o daemon mode));
6d0a9e852Seric #else
7d0a9e852Seric 
8d0a9e852Seric #include <sys/socket.h>
91c71e510Seric #include <netinet/in.h>
101c71e510Seric #include <netdb.h>
112554d49fSeric #include <wait.h>
12d0a9e852Seric 
13*444eaf03Seric SCCSID(@(#)daemon.c	3.48		02/08/83	(with daemon mode));
147fa39d90Seric 
157fa39d90Seric /*
167fa39d90Seric **  DAEMON.C -- routines to use when running as a daemon.
1747b12ae1Seric **
1847b12ae1Seric **	This entire file is highly dependent on the 4.2 BSD
1947b12ae1Seric **	interprocess communication primitives.  No attempt has
2047b12ae1Seric **	been made to make this file portable to Version 7,
2147b12ae1Seric **	Version 6, MPX files, etc.  If you should try such a
2247b12ae1Seric **	thing yourself, I recommend chucking the entire file
2347b12ae1Seric **	and starting from scratch.  Basic semantics are:
2447b12ae1Seric **
2547b12ae1Seric **	getrequests()
2647b12ae1Seric **		Opens a port and initiates a connection.
2747b12ae1Seric **		Returns in a child.  Must set InChannel and
2847b12ae1Seric **		OutChannel appropriately.
29b7d7afcbSeric **	clrdaemon()
30b7d7afcbSeric **		Close any open files associated with getting
31b7d7afcbSeric **		the connection; this is used when running the queue,
32b7d7afcbSeric **		etc., to avoid having extra file descriptors during
33b7d7afcbSeric **		the queue run and to avoid confusing the network
34b7d7afcbSeric **		code (if it cares).
3547b12ae1Seric **	makeconnection(host, port, outfile, infile)
3647b12ae1Seric **		Make a connection to the named host on the given
3747b12ae1Seric **		port.  Set *outfile and *infile to the files
3847b12ae1Seric **		appropriate for communication.  Returns zero on
3947b12ae1Seric **		success, else an exit status describing the
4047b12ae1Seric **		error.
4147b12ae1Seric **
4247b12ae1Seric **	The semantics of both of these should be clean.
437fa39d90Seric */
447fa39d90Seric /*
457fa39d90Seric **  GETREQUESTS -- open mail IPC port and get requests.
467fa39d90Seric **
477fa39d90Seric **	Parameters:
487fa39d90Seric **		none.
497fa39d90Seric **
507fa39d90Seric **	Returns:
517fa39d90Seric **		none.
527fa39d90Seric **
537fa39d90Seric **	Side Effects:
547fa39d90Seric **		Waits until some interesting activity occurs.  When
557fa39d90Seric **		it does, a child is created to process it, and the
567fa39d90Seric **		parent waits for completion.  Return from this
57147303b1Seric **		routine is always in the child.  The file pointers
58147303b1Seric **		"InChannel" and "OutChannel" should be set to point
59147303b1Seric **		to the communication channel.
607fa39d90Seric */
617fa39d90Seric 
62b7d7afcbSeric struct sockaddr_in	SendmailAddress;/* internet address of sendmail */
63b7d7afcbSeric int	DaemonSocket = -1;		/* fd describing socket */
6495639a73Seric int	MaxConnections = 4;		/* maximum simultaneous sendmails */
651c71e510Seric 
667fa39d90Seric getrequests()
677fa39d90Seric {
681c71e510Seric 	int t;
692554d49fSeric 	union wait status;
702554d49fSeric 	int numconnections = 0;
711c71e510Seric 	register struct servent *sp;
72eb889047Seric 
73a8268164Seric 	/*
741c71e510Seric 	**  Set up the address for the mailer.
75eb889047Seric 	*/
76eb889047Seric 
771c71e510Seric 	sp = getservbyname("smtp", "tcp");
781c71e510Seric 	if (sp == NULL)
79d0a9e852Seric 	{
801c71e510Seric 		syserr("server \"smtp\" unknown");
81a1961f2aSeric 		goto severe;
821c71e510Seric 	}
831c71e510Seric 	SendmailAddress.sin_family = AF_INET;
841c71e510Seric 	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
85f43fa3c2Ssam 	SendmailAddress.sin_port = sp->s_port;
861c71e510Seric 
871c71e510Seric 	/*
881c71e510Seric 	**  Try to actually open the connection.
891c71e510Seric 	*/
901c71e510Seric 
911c71e510Seric # ifdef DEBUG
921c71e510Seric 	if (tTd(15, 1))
931c71e510Seric 		printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
941c71e510Seric # endif DEBUG
951c71e510Seric 
961c71e510Seric 	/* get a socket for the SMTP connection */
97b7d7afcbSeric 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0);
98b7d7afcbSeric 	if (DaemonSocket < 0)
991c71e510Seric 	{
1001c71e510Seric 		/* probably another daemon already */
1011c71e510Seric 		syserr("getrequests: can't create socket");
1021c71e510Seric 	  severe:
103b0ba8827Seric # ifdef LOG
104b0ba8827Seric 		if (LogLevel > 0)
105b0ba8827Seric 			syslog(LOG_SALERT, "cannot get connection");
106b0ba8827Seric # endif LOG
10747b12ae1Seric 		finis();
108d0a9e852Seric 	}
1091b6e4a15Seric 
1101b6e4a15Seric #ifdef DEBUG
1111b6e4a15Seric 	/* turn on network debugging? */
1121b6e4a15Seric 	if (tTd(15, 15))
1131b6e4a15Seric 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, 0, 0);
1141b6e4a15Seric #endif DEBUG
1151b6e4a15Seric 
116b7d7afcbSeric 	if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
1171c71e510Seric 	{
1181c71e510Seric 		syserr("getrequests: cannot bind");
119b7d7afcbSeric 		(void) close(DaemonSocket);
1201c71e510Seric 		goto severe;
1211c71e510Seric 	}
122b7d7afcbSeric 	listen(DaemonSocket, 10);
1231c71e510Seric 
1241c71e510Seric # ifdef DEBUG
1251c71e510Seric 	if (tTd(15, 1))
126b7d7afcbSeric 		printf("getrequests: %d\n", DaemonSocket);
1271c71e510Seric # endif DEBUG
1281c71e510Seric 
1291c71e510Seric 	for (;;)
1301c71e510Seric 	{
1311c71e510Seric 		/* wait for a connection */
1321c71e510Seric 		register int pid;
1331c71e510Seric 
1341c71e510Seric 		do
1351c71e510Seric 		{
1361c71e510Seric 			auto int lotherend;
1371c71e510Seric 			struct sockaddr otherend;
1381c71e510Seric 
1391c71e510Seric 			errno = 0;
1401c71e510Seric 			lotherend = sizeof otherend;
141b7d7afcbSeric 			t = accept(DaemonSocket, &otherend, &lotherend, 0);
1421c71e510Seric 		} while (t < 0 && errno == EINTR);
1431c71e510Seric 		if (t < 0)
1441c71e510Seric 		{
1451c71e510Seric 			syserr("getrequests: accept");
1461c71e510Seric 			sleep(5);
1471c71e510Seric 			continue;
1481c71e510Seric 		}
149d0a9e852Seric 
150d0a9e852Seric 		/*
151d0a9e852Seric 		**  Create a subprocess to process the mail.
152d0a9e852Seric 		*/
153d0a9e852Seric 
154d0a9e852Seric # ifdef DEBUG
15561e4310fSeric 		if (tTd(15, 2))
1561c71e510Seric 			printf("getrequests: forking (fd = %d)\n", t);
157d0a9e852Seric # endif DEBUG
158eb889047Seric 
159a8268164Seric 		pid = fork();
160a8268164Seric 		if (pid < 0)
161a8268164Seric 		{
162a8268164Seric 			syserr("daemon: cannot fork");
163a8268164Seric 			sleep(10);
1641c71e510Seric 			(void) close(t);
165a8268164Seric 			continue;
166a8268164Seric 		}
167a8268164Seric 
168a8268164Seric 		if (pid == 0)
169a8268164Seric 		{
170a8268164Seric 			/*
171a8268164Seric 			**  CHILD -- return to caller.
172a8268164Seric 			**	Verify calling user id if possible here.
173a8268164Seric 			*/
174a8268164Seric 
175b7d7afcbSeric 			(void) close(DaemonSocket);
1761c71e510Seric 			InChannel = fdopen(t, "r");
1771c71e510Seric 			OutChannel = fdopen(t, "w");
178d0a9e852Seric # ifdef DEBUG
17961e4310fSeric 			if (tTd(15, 2))
180d0a9e852Seric 				printf("getreq: returning\n");
181d0a9e852Seric # endif DEBUG
182252c8a22Seric # ifdef LOG
183252c8a22Seric 			if (LogLevel > 11)
184252c8a22Seric 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
185252c8a22Seric # endif LOG
186a8268164Seric 			return;
187a8268164Seric 		}
188a8268164Seric 
189a8268164Seric 		/*
190a8268164Seric 		**  PARENT -- wait for child to terminate.
191a8268164Seric 		**	Perhaps we should allow concurrent processing?
192a8268164Seric 		*/
193a8268164Seric 
194d0a9e852Seric # ifdef DEBUG
19561e4310fSeric 		if (tTd(15, 2))
196d0a9e852Seric 		{
197d0a9e852Seric 			sleep(2);
198d0a9e852Seric 			printf("getreq: parent waiting\n");
199d0a9e852Seric 		}
200d0a9e852Seric # endif DEBUG
201d0a9e852Seric 
2022554d49fSeric 		/* close the port so that others will hang (for a while) */
2031c71e510Seric 		(void) close(t);
2042554d49fSeric 
2052554d49fSeric 		/* pick up old zombies; implement load limiting */
2062554d49fSeric 		numconnections++;
20795639a73Seric 		while (wait3(&status, numconnections < MaxConnections ? WNOHANG : 0, 0) > 0)
2082554d49fSeric 			numconnections--;
209a8268164Seric 	}
210147303b1Seric 	/*NOTREACHED*/
2117fa39d90Seric }
212d0a9e852Seric /*
213b7d7afcbSeric **  CLRDAEMON -- reset the daemon connection
214b7d7afcbSeric **
215b7d7afcbSeric **	Parameters:
216b7d7afcbSeric **		none.
217b7d7afcbSeric **
218b7d7afcbSeric **	Returns:
219b7d7afcbSeric **		none.
220b7d7afcbSeric **
221b7d7afcbSeric **	Side Effects:
222b7d7afcbSeric **		releases any resources used by the passive daemon.
223b7d7afcbSeric */
224b7d7afcbSeric 
225b7d7afcbSeric clrdaemon()
226b7d7afcbSeric {
227b7d7afcbSeric 	if (DaemonSocket >= 0)
228b7d7afcbSeric 		(void) close(DaemonSocket);
229b7d7afcbSeric 	DaemonSocket = -1;
230b7d7afcbSeric }
231b7d7afcbSeric /*
2327aa493c5Seric **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
2337aa493c5Seric **
2347aa493c5Seric **	Parameters:
2357aa493c5Seric **		host -- the name of the host.
23648ff0a9dSeric **		port -- the port number to connect to.
2377aa493c5Seric **		outfile -- a pointer to a place to put the outfile
2387aa493c5Seric **			descriptor.
2397aa493c5Seric **		infile -- ditto for infile.
2407aa493c5Seric **
2417aa493c5Seric **	Returns:
2427aa493c5Seric **		An exit code telling whether the connection could be
2437aa493c5Seric **			made and if not why not.
2447aa493c5Seric **
2457aa493c5Seric **	Side Effects:
2467aa493c5Seric **		none.
2477aa493c5Seric */
2487aa493c5Seric 
24948ff0a9dSeric makeconnection(host, port, outfile, infile)
2507aa493c5Seric 	char *host;
251210215eaSeric 	u_short port;
2527aa493c5Seric 	FILE **outfile;
2537aa493c5Seric 	FILE **infile;
2547aa493c5Seric {
2557aa493c5Seric 	register int s;
2567aa493c5Seric 
2577aa493c5Seric 	/*
2587aa493c5Seric 	**  Set up the address for the mailer.
25971096d12Seric 	**	Accept "[a.b.c.d]" syntax for host name.
2607aa493c5Seric 	*/
2617aa493c5Seric 
26271096d12Seric 	if (host[0] == '[')
26371096d12Seric 	{
26471096d12Seric 		long hid = 0;
26571096d12Seric 		int i, j;
26671096d12Seric 		register char *p = host;
26771096d12Seric 
26871096d12Seric 		for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--)
26971096d12Seric 		{
27071096d12Seric 			j = 0;
27171096d12Seric 			while (isdigit(*++p))
27271096d12Seric 				j = j * 10 + (*p - '0');
27371096d12Seric 			if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0)
27471096d12Seric 				break;
27571096d12Seric 			hid |= j << ((3 - i) * 8);
27671096d12Seric 		}
27771096d12Seric 		if (i >= 0 || *p != ']' || *++p != '\0')
27871096d12Seric 		{
27971096d12Seric 			usrerr("Invalid numeric domain spec \"%s\"", host);
28071096d12Seric 			return (EX_NOHOST);
28171096d12Seric 		}
28271096d12Seric 		SendmailAddress.sin_addr.s_addr = hid;
28371096d12Seric 	}
2841c71e510Seric 	else
2851c71e510Seric 	{
2861c71e510Seric 		register struct hostent *hp = gethostbyname(host);
2871c71e510Seric 
2881c71e510Seric 		if (hp == 0)
2897aa493c5Seric 			return (EX_NOHOST);
2901c71e510Seric 		bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
2911c71e510Seric 	}
2921c71e510Seric 
2931c71e510Seric 	/*
2941c71e510Seric 	**  Determine the port number.
2951c71e510Seric 	*/
2961c71e510Seric 
297fd7c0790Seric 	if (port != 0)
298fd7c0790Seric 		SendmailAddress.sin_port = htons(port);
299fd7c0790Seric 	else
3001c71e510Seric 	{
3011c71e510Seric 		register struct servent *sp = getservbyname("smtp", "tcp");
3021c71e510Seric 
3031c71e510Seric 		if (sp == NULL)
3041c71e510Seric 		{
3051c71e510Seric 			syserr("makeconnection: server \"smtp\" unknown");
3061c71e510Seric 			return (EX_OSFILE);
3071c71e510Seric 		}
308fd7c0790Seric 		SendmailAddress.sin_port = sp->s_port;
3091c71e510Seric 	}
3107aa493c5Seric 
3117aa493c5Seric 	/*
3127aa493c5Seric 	**  Try to actually open the connection.
3137aa493c5Seric 	*/
3147aa493c5Seric 
3157aa493c5Seric # ifdef DEBUG
31661e4310fSeric 	if (tTd(16, 1))
3177aa493c5Seric 		printf("makeconnection (%s)\n", host);
3187aa493c5Seric # endif DEBUG
3197aa493c5Seric 
32010d42158Seric 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
3217aa493c5Seric 	if (s < 0)
3227aa493c5Seric 	{
3237aa493c5Seric 		syserr("makeconnection: no socket");
3247aa493c5Seric 		goto failure;
3257aa493c5Seric 	}
3267aa493c5Seric 
3277aa493c5Seric # ifdef DEBUG
32861e4310fSeric 	if (tTd(16, 1))
3297aa493c5Seric 		printf("makeconnection: %d\n", s);
3301b6e4a15Seric 
3311b6e4a15Seric 	/* turn on network debugging? */
3321b6e4a15Seric 	if (tTd(16, 14))
3331b6e4a15Seric 		(void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0);
3347aa493c5Seric # endif DEBUG
335877a6142Seric 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
3361c71e510Seric 	SendmailAddress.sin_family = AF_INET;
337da8e67a0Seric 	/* bind(s, &SendmailAddress, sizeof SendmailAddress, 0); */
33810d42158Seric 	if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0)
3397aa493c5Seric 	{
3407aa493c5Seric 		/* failure, decide if temporary or not */
3417aa493c5Seric 	failure:
3427aa493c5Seric 		switch (errno)
3437aa493c5Seric 		{
3447aa493c5Seric 		  case EISCONN:
3457aa493c5Seric 		  case ETIMEDOUT:
346292668b9Seric 		  case EINPROGRESS:
347292668b9Seric 		  case EALREADY:
348292668b9Seric 		  case EADDRINUSE:
34936bd23a8Seric 		  case EHOSTDOWN:
350292668b9Seric 		  case ENETDOWN:
351292668b9Seric 		  case ENETRESET:
352292668b9Seric 		  case ENOBUFS:
353a1645df8Seric 		  case ECONNREFUSED:
354cae8261aSeric 		  case EHOSTUNREACH:
355dca8e1f7Seric 		  case ENETUNREACH:
3567aa493c5Seric 			/* there are others, I'm sure..... */
3577aa493c5Seric 			return (EX_TEMPFAIL);
3587aa493c5Seric 
3597aa493c5Seric 		  default:
3607aa493c5Seric 			return (EX_UNAVAILABLE);
3617aa493c5Seric 		}
3627aa493c5Seric 	}
3637aa493c5Seric 
3647aa493c5Seric 	/* connection ok, put it into canonical form */
3657aa493c5Seric 	*outfile = fdopen(s, "w");
3667aa493c5Seric 	*infile = fdopen(s, "r");
3677aa493c5Seric 
368dca8e1f7Seric 	return (EX_OK);
3697aa493c5Seric }
370*444eaf03Seric /*
371*444eaf03Seric **  MYHOSTNAME -- return the name of this host.
372*444eaf03Seric **
373*444eaf03Seric **	Parameters:
374*444eaf03Seric **		hostbuf -- a place to return the name of this host.
375*444eaf03Seric **
376*444eaf03Seric **	Returns:
377*444eaf03Seric **		A list of aliases for this host.
378*444eaf03Seric **
379*444eaf03Seric **	Side Effects:
380*444eaf03Seric **		none.
381*444eaf03Seric */
382*444eaf03Seric 
383*444eaf03Seric char **
384*444eaf03Seric myhostname(hostbuf)
385*444eaf03Seric 	char hostbuf[];
386*444eaf03Seric {
387*444eaf03Seric 	extern struct hostent *gethostbyname();
388*444eaf03Seric 	struct hostent *hent;
389*444eaf03Seric 
390*444eaf03Seric 	gethostname(hostbuf, sizeof hostbuf);
391*444eaf03Seric 	hent = gethostbyname(hostbuf);
392*444eaf03Seric 	if (hent != NULL)
393*444eaf03Seric 		return (hent->h_aliases);
394*444eaf03Seric 	else
395*444eaf03Seric 		return (NULL);
396*444eaf03Seric }
397*444eaf03Seric 
398*444eaf03Seric # else DAEMON
399*444eaf03Seric 
400*444eaf03Seric /*
401*444eaf03Seric **  MYHOSTNAME -- stub version for case of no daemon code.
402*444eaf03Seric */
403*444eaf03Seric 
404*444eaf03Seric char **
405*444eaf03Seric myhostname(hostbuf)
406*444eaf03Seric 	char hostbuf[];
407*444eaf03Seric {
408*444eaf03Seric 	register FILE *f;
409*444eaf03Seric 
410*444eaf03Seric 	hostbuf[0] = '\0';
411*444eaf03Seric 	f = fopen("/usr/include/whoami", "r");
412*444eaf03Seric 	if (f != NULL)
413*444eaf03Seric 	{
414*444eaf03Seric 		(void) fgets(hostbuf, sizeof hostbuf, f);
415*444eaf03Seric 		fixcrlf(hostbuf, TRUE);
416*444eaf03Seric 		(void) fclose(f);
417*444eaf03Seric 	}
418*444eaf03Seric 	return (NULL);
419*444eaf03Seric }
420d0a9e852Seric 
421d0a9e852Seric #endif DAEMON
422