xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 58501)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #include <errno.h>
10 #include <signal.h>
11 #include "sendmail.h"
12 
13 #ifndef lint
14 #ifdef DAEMON
15 static char sccsid[] = "@(#)daemon.c	6.14 (Berkeley) 03/05/93 (with daemon mode)";
16 #else
17 static char sccsid[] = "@(#)daemon.c	6.14 (Berkeley) 03/05/93 (without daemon mode)";
18 #endif
19 #endif /* not lint */
20 
21 #ifdef DAEMON
22 
23 # include <netdb.h>
24 # include <sys/wait.h>
25 # include <sys/time.h>
26 
27 /*
28 **  DAEMON.C -- routines to use when running as a daemon.
29 **
30 **	This entire file is highly dependent on the 4.2 BSD
31 **	interprocess communication primitives.  No attempt has
32 **	been made to make this file portable to Version 7,
33 **	Version 6, MPX files, etc.  If you should try such a
34 **	thing yourself, I recommend chucking the entire file
35 **	and starting from scratch.  Basic semantics are:
36 **
37 **	getrequests()
38 **		Opens a port and initiates a connection.
39 **		Returns in a child.  Must set InChannel and
40 **		OutChannel appropriately.
41 **	clrdaemon()
42 **		Close any open files associated with getting
43 **		the connection; this is used when running the queue,
44 **		etc., to avoid having extra file descriptors during
45 **		the queue run and to avoid confusing the network
46 **		code (if it cares).
47 **	makeconnection(host, port, outfile, infile, usesecureport)
48 **		Make a connection to the named host on the given
49 **		port.  Set *outfile and *infile to the files
50 **		appropriate for communication.  Returns zero on
51 **		success, else an exit status describing the
52 **		error.
53 **	maphostname(map, hbuf, hbufsiz, avp)
54 **		Convert the entry in hbuf into a canonical form.
55 */
56 /*
57 **  GETREQUESTS -- open mail IPC port and get requests.
58 **
59 **	Parameters:
60 **		none.
61 **
62 **	Returns:
63 **		none.
64 **
65 **	Side Effects:
66 **		Waits until some interesting activity occurs.  When
67 **		it does, a child is created to process it, and the
68 **		parent waits for completion.  Return from this
69 **		routine is always in the child.  The file pointers
70 **		"InChannel" and "OutChannel" should be set to point
71 **		to the communication channel.
72 */
73 
74 int	DaemonSocket	= -1;		/* fd describing socket */
75 
76 getrequests()
77 {
78 	int t;
79 	register struct servent *sp;
80 	int on = 1;
81 	bool refusingconnections = TRUE;
82 	FILE *pidf;
83 	struct sockaddr_in srvraddr;
84 	extern void reapchild();
85 
86 	/*
87 	**  Set up the address for the mailer.
88 	*/
89 
90 	sp = getservbyname("smtp", "tcp");
91 	if (sp == NULL)
92 	{
93 		syserr("554 server \"smtp\" unknown");
94 		goto severe;
95 	}
96 	srvraddr.sin_family = AF_INET;
97 	srvraddr.sin_addr.s_addr = INADDR_ANY;
98 	srvraddr.sin_port = sp->s_port;
99 
100 	/*
101 	**  Try to actually open the connection.
102 	*/
103 
104 	if (tTd(15, 1))
105 		printf("getrequests: port 0x%x\n", srvraddr.sin_port);
106 
107 	/* get a socket for the SMTP connection */
108 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
109 	if (DaemonSocket < 0)
110 	{
111 		/* probably another daemon already */
112 		syserr("getrequests: can't create socket");
113 	  severe:
114 # ifdef LOG
115 		if (LogLevel > 0)
116 			syslog(LOG_ALERT, "problem creating SMTP socket");
117 # endif /* LOG */
118 		finis();
119 	}
120 
121 	/* turn on network debugging? */
122 	if (tTd(15, 101))
123 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
124 
125 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
126 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
127 
128 	if (bind(DaemonSocket, (struct sockaddr *)&srvraddr, sizeof srvraddr) < 0)
129 	{
130 		syserr("getrequests: cannot bind");
131 		(void) close(DaemonSocket);
132 		goto severe;
133 	}
134 
135 	(void) signal(SIGCHLD, reapchild);
136 
137 	/* write the pid to the log file for posterity */
138 	pidf = fopen(PidFile, "w");
139 	if (pidf != NULL)
140 	{
141 		fprintf(pidf, "%d\n", getpid());
142 		fclose(pidf);
143 	}
144 
145 
146 	if (tTd(15, 1))
147 		printf("getrequests: %d\n", DaemonSocket);
148 
149 	for (;;)
150 	{
151 		register int pid;
152 		auto int lotherend;
153 		extern bool refuseconnections();
154 
155 		/* see if we are rejecting connections */
156 		CurrentLA = getla();
157 		if (refuseconnections())
158 		{
159 			if (!refusingconnections)
160 			{
161 				/* don't queue so peer will fail quickly */
162 				(void) listen(DaemonSocket, 0);
163 				refusingconnections = TRUE;
164 			}
165 			setproctitle("rejecting connections: load average: %d",
166 				CurrentLA);
167 			sleep(5);
168 			continue;
169 		}
170 
171 		if (refusingconnections)
172 		{
173 			/* start listening again */
174 			if (listen(DaemonSocket, 10) < 0)
175 			{
176 				syserr("getrequests: cannot listen");
177 				(void) close(DaemonSocket);
178 				goto severe;
179 			}
180 			setproctitle("accepting connections");
181 			refusingconnections = FALSE;
182 		}
183 
184 		/* wait for a connection */
185 		do
186 		{
187 			errno = 0;
188 			lotherend = sizeof RealHostAddr;
189 			t = accept(DaemonSocket,
190 			    (struct sockaddr *)&RealHostAddr, &lotherend);
191 		} while (t < 0 && errno == EINTR);
192 		if (t < 0)
193 		{
194 			syserr("getrequests: accept");
195 			sleep(5);
196 			continue;
197 		}
198 
199 		/*
200 		**  Create a subprocess to process the mail.
201 		*/
202 
203 		if (tTd(15, 2))
204 			printf("getrequests: forking (fd = %d)\n", t);
205 
206 		pid = fork();
207 		if (pid < 0)
208 		{
209 			syserr("daemon: cannot fork");
210 			sleep(10);
211 			(void) close(t);
212 			continue;
213 		}
214 
215 		if (pid == 0)
216 		{
217 			extern struct hostent *gethostbyaddr();
218 			register struct hostent *hp;
219 			char buf[MAXNAME];
220 			extern char *inet_ntoa();
221 
222 			/*
223 			**  CHILD -- return to caller.
224 			**	Collect verified idea of sending host.
225 			**	Verify calling user id if possible here.
226 			*/
227 
228 			(void) signal(SIGCHLD, SIG_DFL);
229 
230 			/* determine host name */
231 			hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
232 			if (hp != NULL)
233 				(void) strcpy(buf, hp->h_name);
234 			else
235 			{
236 				/* produce a dotted quad */
237 				(void) sprintf(buf, "[%s]",
238 					inet_ntoa(RealHostAddr.sin_addr));
239 			}
240 
241 #ifdef LOG
242 			if (LogLevel > 10)
243 			{
244 				/* log connection information */
245 				syslog(LOG_INFO, "connect from %s (%s)",
246 					buf, inet_ntoa(RealHostAddr.sin_addr));
247 			}
248 #endif
249 
250 			/* should we check for illegal connection here? XXX */
251 
252 			RealHostName = newstr(buf);
253 
254 			(void) close(DaemonSocket);
255 			InChannel = fdopen(t, "r");
256 			OutChannel = fdopen(dup(t), "w");
257 			if (tTd(15, 2))
258 				printf("getreq: returning\n");
259 # ifdef LOG
260 			if (LogLevel > 11)
261 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
262 # endif /* LOG */
263 			return;
264 		}
265 
266 		/* close the port so that others will hang (for a while) */
267 		(void) close(t);
268 	}
269 	/*NOTREACHED*/
270 }
271 /*
272 **  CLRDAEMON -- reset the daemon connection
273 **
274 **	Parameters:
275 **		none.
276 **
277 **	Returns:
278 **		none.
279 **
280 **	Side Effects:
281 **		releases any resources used by the passive daemon.
282 */
283 
284 clrdaemon()
285 {
286 	if (DaemonSocket >= 0)
287 		(void) close(DaemonSocket);
288 	DaemonSocket = -1;
289 }
290 /*
291 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
292 **
293 **	Parameters:
294 **		host -- the name of the host.
295 **		port -- the port number to connect to.
296 **		mci -- a pointer to the mail connection information
297 **			structure to be filled in.
298 **		usesecureport -- if set, use a low numbered (reserved)
299 **			port to provide some rudimentary authentication.
300 **
301 **	Returns:
302 **		An exit code telling whether the connection could be
303 **			made and if not why not.
304 **
305 **	Side Effects:
306 **		none.
307 */
308 
309 struct sockaddr_in	CurHostAddr;		/* address of current host */
310 
311 int
312 makeconnection(host, port, mci, usesecureport)
313 	char *host;
314 	u_short port;
315 	register MCI *mci;
316 	bool usesecureport;
317 {
318 	register int i, s;
319 	register struct hostent *hp = (struct hostent *)NULL;
320 	struct sockaddr_in addr;
321 	int sav_errno;
322 	extern char *inet_ntoa();
323 #ifdef NAMED_BIND
324 	extern int h_errno;
325 #endif
326 
327 	/*
328 	**  Set up the address for the mailer.
329 	**	Accept "[a.b.c.d]" syntax for host name.
330 	*/
331 
332 #ifdef NAMED_BIND
333 	h_errno = 0;
334 #endif
335 	errno = 0;
336 
337 	if (host[0] == '[')
338 	{
339 		long hid;
340 		register char *p = strchr(host, ']');
341 
342 		if (p != NULL)
343 		{
344 			*p = '\0';
345 			hid = inet_addr(&host[1]);
346 			if (hid == -1)
347 			{
348 				/* try it as a host name (avoid MX lookup) */
349 				hp = gethostbyname(&host[1]);
350 				*p = ']';
351 				goto gothostent;
352 			}
353 			*p = ']';
354 		}
355 		if (p == NULL)
356 		{
357 			usrerr("553 Invalid numeric domain spec \"%s\"", host);
358 			return (EX_NOHOST);
359 		}
360 		addr.sin_addr.s_addr = hid;
361 	}
362 	else
363 	{
364 		hp = gethostbyname(host);
365 gothostent:
366 		if (hp == NULL)
367 		{
368 #ifdef NAMED_BIND
369 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
370 				return (EX_TEMPFAIL);
371 
372 			/* if name server is specified, assume temp fail */
373 			if (errno == ECONNREFUSED && UseNameServer)
374 				return (EX_TEMPFAIL);
375 #endif
376 			return (EX_NOHOST);
377 		}
378 		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
379 		i = 1;
380 	}
381 
382 	/*
383 	**  Determine the port number.
384 	*/
385 
386 	if (port != 0)
387 		addr.sin_port = htons(port);
388 	else
389 	{
390 		register struct servent *sp = getservbyname("smtp", "tcp");
391 
392 		if (sp == NULL)
393 		{
394 			syserr("554 makeconnection: server \"smtp\" unknown");
395 			return (EX_OSERR);
396 		}
397 		addr.sin_port = sp->s_port;
398 	}
399 
400 	/*
401 	**  Try to actually open the connection.
402 	*/
403 
404 	for (;;)
405 	{
406 		if (tTd(16, 1))
407 			printf("makeconnection (%s [%s])\n", host,
408 			    inet_ntoa(addr.sin_addr));
409 
410 		if (usesecureport)
411 		{
412 			int rport = IPPORT_RESERVED - 1;
413 
414 			s = rresvport(&rport);
415 		}
416 		else
417 		{
418 			s = socket(AF_INET, SOCK_STREAM, 0);
419 		}
420 		if (s < 0)
421 		{
422 			sav_errno = errno;
423 			syserr("makeconnection: no socket");
424 			goto failure;
425 		}
426 
427 		if (tTd(16, 1))
428 			printf("makeconnection: fd=%d\n", s);
429 
430 		/* turn on network debugging? */
431 		if (tTd(16, 101))
432 		{
433 			int on = 1;
434 			(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG,
435 					  (char *)&on, sizeof on);
436 		}
437 		if (CurEnv->e_xfp != NULL)
438 			(void) fflush(CurEnv->e_xfp);		/* for debugging */
439 		errno = 0;					/* for debugging */
440 		addr.sin_family = AF_INET;
441 		if (connect(s, (struct sockaddr *) &addr, sizeof addr) >= 0)
442 			break;
443 
444 		/* couldn't connect.... figure out why */
445 		sav_errno = errno;
446 		(void) close(s);
447 		if (hp && hp->h_addr_list[i])
448 		{
449 			if (tTd(16, 1))
450 				printf("Connect failed; trying new address....\n");
451 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
452 					hp->h_length);
453 			continue;
454 		}
455 
456 		/* failure, decide if temporary or not */
457 	failure:
458 		switch (sav_errno)
459 		{
460 		  case EISCONN:
461 		  case ETIMEDOUT:
462 		  case EINPROGRESS:
463 		  case EALREADY:
464 		  case EADDRINUSE:
465 		  case EHOSTDOWN:
466 		  case ENETDOWN:
467 		  case ENETRESET:
468 		  case ENOBUFS:
469 		  case ECONNREFUSED:
470 		  case ECONNRESET:
471 		  case EHOSTUNREACH:
472 		  case ENETUNREACH:
473 #ifdef ENOSR
474 		  case ENOSR:
475 #endif
476 			/* there are others, I'm sure..... */
477 			return (EX_TEMPFAIL);
478 
479 		  case EPERM:
480 			/* why is this happening? */
481 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
482 				addr.sin_addr.s_addr, addr.sin_port);
483 			return (EX_TEMPFAIL);
484 
485 		  default:
486 			{
487 				extern char *errstring();
488 
489 				message("%s", errstring(sav_errno));
490 				return (EX_UNAVAILABLE);
491 			}
492 		}
493 	}
494 
495 	/* connection ok, put it into canonical form */
496 	mci->mci_out = fdopen(s, "w");
497 	mci->mci_in = fdopen(dup(s), "r");
498 
499 	/* save for logging */
500 	CurHostAddr = addr;
501 
502 	return (EX_OK);
503 }
504 /*
505 **  MYHOSTNAME -- return the name of this host.
506 **
507 **	Parameters:
508 **		hostbuf -- a place to return the name of this host.
509 **		size -- the size of hostbuf.
510 **
511 **	Returns:
512 **		A list of aliases for this host.
513 **
514 **	Side Effects:
515 **		Sets the MyIpAddrs buffer to a list of my IP addresses.
516 */
517 
518 struct in_addr	MyIpAddrs[MAXIPADDR + 1];
519 
520 char **
521 myhostname(hostbuf, size)
522 	char hostbuf[];
523 	int size;
524 {
525 	register struct hostent *hp;
526 	extern struct hostent *gethostbyname();
527 
528 	if (gethostname(hostbuf, size) < 0)
529 	{
530 		(void) strcpy(hostbuf, "localhost");
531 	}
532 	hp = gethostbyname(hostbuf);
533 	if (hp != NULL)
534 	{
535 		(void) strncpy(hostbuf, hp->h_name, size - 1);
536 		hostbuf[size - 1] = '\0';
537 
538 		if (hp->h_addrtype == AF_INET && hp->h_length == 4)
539 		{
540 			register int i;
541 
542 			for (i = 0; i < MAXIPADDR; i++)
543 			{
544 				if (hp->h_addr_list[i] == NULL)
545 					break;
546 				MyIpAddrs[i].s_addr = *(u_long *) hp->h_addr_list[i];
547 			}
548 			MyIpAddrs[i].s_addr = 0;
549 		}
550 
551 		return (hp->h_aliases);
552 	}
553 	else
554 		return (NULL);
555 }
556 /*
557 **  GETREALHOSTNAME -- get the real host name asociated with a file descriptor
558 **
559 **	Parameters:
560 **		fd -- the descriptor
561 **
562 **	Returns:
563 **		The host name associated with this descriptor, if it can
564 **			be determined.
565 **		NULL otherwise.
566 **
567 **	Side Effects:
568 **		none
569 */
570 
571 char *
572 getrealhostname(fd)
573 	int fd;
574 {
575 	register struct hostent *hp;
576 	struct sockaddr_in sin;
577 	int sinlen;
578 	char hbuf[MAXNAME];
579 	extern struct hostent *gethostbyaddr();
580 	extern char *inet_ntoa();
581 
582 	if (getsockname(fd, (struct sockaddr *) &sin, &sinlen) < 0 ||
583 	    sinlen <= 0)
584 		return NULL;
585 	hp = gethostbyaddr((char *) &sin.sin_addr, sizeof sin.sin_addr,
586 			   sin.sin_family);
587 	if (hp != NULL)
588 		(void) strcpy(hbuf, hp->h_name);
589 	else
590 		(void) sprintf(hbuf, "[%s]", inet_ntoa(sin.sin_addr));
591 	return hbuf;
592 }
593 /*
594 **  MAPHOSTNAME -- turn a hostname into canonical form
595 **
596 **	Parameters:
597 **		map -- a pointer to this map (unused).
598 **		hbuf -- a buffer containing a hostname.
599 **		hbsize -- the size of hbuf.
600 **		avp -- unused -- for compatibility with other mapping
601 **			functions.
602 **
603 **	Returns:
604 **		The mapping, if found.
605 **		NULL if no mapping found.
606 **
607 **	Side Effects:
608 **		Looks up the host specified in hbuf.  If it is not
609 **		the canonical name for that host, return the canonical
610 **		name.
611 */
612 
613 char *
614 maphostname(map, hbuf, hbsize, avp)
615 	MAP *map;
616 	char *hbuf;
617 	int hbsize;
618 	char **avp;
619 {
620 	register struct hostent *hp;
621 	u_long in_addr;
622 	char *cp;
623 	int i;
624 	struct hostent *gethostbyaddr();
625 
626 	/* allow room for null */
627 	hbsize--;
628 
629 	/*
630 	 * If first character is a bracket, then it is an address
631 	 * lookup.  Address is copied into a temporary buffer to
632 	 * strip the brackets and to preserve hbuf if address is
633 	 * unknown.
634 	 */
635 
636 	if (*hbuf != '[')
637 	{
638 		extern bool getcanonname();
639 
640 		if (getcanonname(hbuf, hbsize))
641 			return hbuf;
642 		else
643 			return NULL;
644 	}
645 	if ((cp = strchr(hbuf, ']')) == NULL)
646 		return (NULL);
647 	*cp = '\0';
648 	in_addr = inet_addr(&hbuf[1]);
649 
650 	/* check to see if this is one of our addresses */
651 	for (i = 0; MyIpAddrs[i].s_addr != 0; i++)
652 	{
653 		if (MyIpAddrs[i].s_addr == in_addr)
654 		{
655 			strncpy(hbuf, MyHostName, hbsize);
656 			hbuf[hbsize] = '\0';
657 			return hbuf;
658 		}
659 	}
660 
661 	/* nope -- ask the name server */
662 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
663 	if (hp == NULL)
664 		return (NULL);
665 
666 	/* found a match -- copy out */
667 	if (strlen(hp->h_name) > hbsize)
668 		hp->h_name[hbsize] = '\0';
669 	(void) strcpy(hbuf, hp->h_name);
670 	return hbuf;
671 }
672 
673 # else /* DAEMON */
674 /* code for systems without sophisticated networking */
675 
676 /*
677 **  MYHOSTNAME -- stub version for case of no daemon code.
678 **
679 **	Can't convert to upper case here because might be a UUCP name.
680 **
681 **	Mark, you can change this to be anything you want......
682 */
683 
684 char **
685 myhostname(hostbuf, size)
686 	char hostbuf[];
687 	int size;
688 {
689 	register FILE *f;
690 
691 	hostbuf[0] = '\0';
692 	f = fopen("/usr/include/whoami", "r");
693 	if (f != NULL)
694 	{
695 		(void) fgets(hostbuf, size, f);
696 		fixcrlf(hostbuf, TRUE);
697 		(void) fclose(f);
698 	}
699 	return (NULL);
700 }
701 /*
702 **  GETREALHOSTNAME -- get the real host name asociated with a file descriptor
703 **
704 **	Parameters:
705 **		fd -- the descriptor
706 **
707 **	Returns:
708 **		The host name associated with this descriptor, if it can
709 **			be determined.
710 **		NULL otherwise.
711 **
712 **	Side Effects:
713 **		none
714 */
715 
716 char *
717 getrealhostname(fd)
718 	int fd;
719 {
720 	return NULL;
721 }
722 /*
723 **  MAPHOSTNAME -- turn a hostname into canonical form
724 **
725 **	Parameters:
726 **		map -- a pointer to the database map.
727 **		hbuf -- a buffer containing a hostname.
728 **		avp -- a pointer to a (cf file defined) argument vector.
729 **
730 **	Returns:
731 **		mapped host name
732 **		FALSE otherwise.
733 **
734 **	Side Effects:
735 **		Looks up the host specified in hbuf.  If it is not
736 **		the canonical name for that host, replace it with
737 **		the canonical name.  If the name is unknown, or it
738 **		is already the canonical name, leave it unchanged.
739 */
740 
741 /*ARGSUSED*/
742 char *
743 maphostname(map, hbuf, hbsize, avp)
744 	MAP *map;
745 	char *hbuf;
746 	int hbsize;
747 	char **avp;
748 {
749 	return NULL;
750 }
751 
752 #endif /* DAEMON */
753