xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 58153)
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.9 (Berkeley) 02/23/93 (with daemon mode)";
16 #else
17 static char sccsid[] = "@(#)daemon.c	6.9 (Berkeley) 02/23/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 	struct sockaddr_in srvraddr;
83 	extern void reapchild();
84 
85 	/*
86 	**  Set up the address for the mailer.
87 	*/
88 
89 	sp = getservbyname("smtp", "tcp");
90 	if (sp == NULL)
91 	{
92 		syserr("554 server \"smtp\" unknown");
93 		goto severe;
94 	}
95 	srvraddr.sin_family = AF_INET;
96 	srvraddr.sin_addr.s_addr = INADDR_ANY;
97 	srvraddr.sin_port = sp->s_port;
98 
99 	/*
100 	**  Try to actually open the connection.
101 	*/
102 
103 	if (tTd(15, 1))
104 		printf("getrequests: port 0x%x\n", srvraddr.sin_port);
105 
106 	/* get a socket for the SMTP connection */
107 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
108 	if (DaemonSocket < 0)
109 	{
110 		/* probably another daemon already */
111 		syserr("getrequests: can't create socket");
112 	  severe:
113 # ifdef LOG
114 		if (LogLevel > 0)
115 			syslog(LOG_ALERT, "problem creating SMTP socket");
116 # endif /* LOG */
117 		finis();
118 	}
119 
120 	/* turn on network debugging? */
121 	if (tTd(15, 101))
122 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
123 
124 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
125 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
126 
127 	if (bind(DaemonSocket, (struct sockaddr *)&srvraddr, sizeof srvraddr) < 0)
128 	{
129 		syserr("getrequests: cannot bind");
130 		(void) close(DaemonSocket);
131 		goto severe;
132 	}
133 
134 	(void) signal(SIGCHLD, reapchild);
135 
136 	if (tTd(15, 1))
137 		printf("getrequests: %d\n", DaemonSocket);
138 
139 	for (;;)
140 	{
141 		register int pid;
142 		auto int lotherend;
143 		extern bool refuseconnections();
144 
145 		/* see if we are rejecting connections */
146 		CurrentLA = getla();
147 		if (refuseconnections())
148 		{
149 			if (!refusingconnections)
150 			{
151 				/* don't queue so peer will fail quickly */
152 				(void) listen(DaemonSocket, 0);
153 				refusingconnections = TRUE;
154 			}
155 			setproctitle("rejecting connections: load average: %d",
156 				CurrentLA);
157 			sleep(5);
158 			continue;
159 		}
160 
161 		if (refusingconnections)
162 		{
163 			/* start listening again */
164 			if (listen(DaemonSocket, 10) < 0)
165 			{
166 				syserr("getrequests: cannot listen");
167 				(void) close(DaemonSocket);
168 				goto severe;
169 			}
170 			setproctitle("accepting connections");
171 			refusingconnections = FALSE;
172 		}
173 
174 		/* wait for a connection */
175 		do
176 		{
177 			errno = 0;
178 			lotherend = sizeof RealHostAddr;
179 			t = accept(DaemonSocket,
180 			    (struct sockaddr *)&RealHostAddr, &lotherend);
181 		} while (t < 0 && errno == EINTR);
182 		if (t < 0)
183 		{
184 			syserr("getrequests: accept");
185 			sleep(5);
186 			continue;
187 		}
188 
189 		/*
190 		**  Create a subprocess to process the mail.
191 		*/
192 
193 		if (tTd(15, 2))
194 			printf("getrequests: forking (fd = %d)\n", t);
195 
196 		pid = fork();
197 		if (pid < 0)
198 		{
199 			syserr("daemon: cannot fork");
200 			sleep(10);
201 			(void) close(t);
202 			continue;
203 		}
204 
205 		if (pid == 0)
206 		{
207 			extern struct hostent *gethostbyaddr();
208 			register struct hostent *hp;
209 			char buf[MAXNAME];
210 			extern char *inet_ntoa();
211 
212 			/*
213 			**  CHILD -- return to caller.
214 			**	Collect verified idea of sending host.
215 			**	Verify calling user id if possible here.
216 			*/
217 
218 			(void) signal(SIGCHLD, SIG_DFL);
219 
220 			/* determine host name */
221 			hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
222 			if (hp != NULL)
223 				(void) strcpy(buf, hp->h_name);
224 			else
225 			{
226 				/* produce a dotted quad */
227 				(void) sprintf(buf, "[%s]",
228 					inet_ntoa(RealHostAddr.sin_addr));
229 			}
230 
231 #ifdef LOG
232 			if (LogLevel > 10)
233 			{
234 				/* log connection information */
235 				syslog(LOG_INFO, "connect from %s (%s)",
236 					buf, inet_ntoa(RealHostAddr.sin_addr));
237 			}
238 #endif
239 
240 			/* should we check for illegal connection here? XXX */
241 
242 			RealHostName = newstr(buf);
243 
244 			(void) close(DaemonSocket);
245 			InChannel = fdopen(t, "r");
246 			OutChannel = fdopen(dup(t), "w");
247 			if (tTd(15, 2))
248 				printf("getreq: returning\n");
249 # ifdef LOG
250 			if (LogLevel > 11)
251 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
252 # endif /* LOG */
253 			return;
254 		}
255 
256 		/* close the port so that others will hang (for a while) */
257 		(void) close(t);
258 	}
259 	/*NOTREACHED*/
260 }
261 /*
262 **  CLRDAEMON -- reset the daemon connection
263 **
264 **	Parameters:
265 **		none.
266 **
267 **	Returns:
268 **		none.
269 **
270 **	Side Effects:
271 **		releases any resources used by the passive daemon.
272 */
273 
274 clrdaemon()
275 {
276 	if (DaemonSocket >= 0)
277 		(void) close(DaemonSocket);
278 	DaemonSocket = -1;
279 }
280 /*
281 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
282 **
283 **	Parameters:
284 **		host -- the name of the host.
285 **		port -- the port number to connect to.
286 **		mci -- a pointer to the mail connection information
287 **			structure to be filled in.
288 **		usesecureport -- if set, use a low numbered (reserved)
289 **			port to provide some rudimentary authentication.
290 **
291 **	Returns:
292 **		An exit code telling whether the connection could be
293 **			made and if not why not.
294 **
295 **	Side Effects:
296 **		none.
297 */
298 
299 int
300 makeconnection(host, port, mci, usesecureport)
301 	char *host;
302 	u_short port;
303 	register MCI *mci;
304 	bool usesecureport;
305 {
306 	register int i, s;
307 	register struct hostent *hp = (struct hostent *)NULL;
308 	struct sockaddr_in addr;
309 	int sav_errno;
310 	extern char *inet_ntoa();
311 #ifdef NAMED_BIND
312 	extern int h_errno;
313 #endif
314 
315 	/*
316 	**  Set up the address for the mailer.
317 	**	Accept "[a.b.c.d]" syntax for host name.
318 	*/
319 
320 #ifdef NAMED_BIND
321 	h_errno = 0;
322 #endif
323 	errno = 0;
324 
325 	if (host[0] == '[')
326 	{
327 		long hid;
328 		register char *p = strchr(host, ']');
329 
330 		if (p != NULL)
331 		{
332 			*p = '\0';
333 			hid = inet_addr(&host[1]);
334 			*p = ']';
335 		}
336 		if (p == NULL || hid == -1)
337 		{
338 			usrerr("553 Invalid numeric domain spec \"%s\"", host);
339 			return (EX_NOHOST);
340 		}
341 		addr.sin_addr.s_addr = hid;
342 	}
343 	else
344 	{
345 		hp = gethostbyname(host);
346 		if (hp == NULL)
347 		{
348 #ifdef NAMED_BIND
349 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
350 				return (EX_TEMPFAIL);
351 
352 			/* if name server is specified, assume temp fail */
353 			if (errno == ECONNREFUSED && UseNameServer)
354 				return (EX_TEMPFAIL);
355 #endif
356 			return (EX_NOHOST);
357 		}
358 		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
359 		i = 1;
360 	}
361 
362 	/*
363 	**  Determine the port number.
364 	*/
365 
366 	if (port != 0)
367 		addr.sin_port = htons(port);
368 	else
369 	{
370 		register struct servent *sp = getservbyname("smtp", "tcp");
371 
372 		if (sp == NULL)
373 		{
374 			syserr("554 makeconnection: server \"smtp\" unknown");
375 			return (EX_OSERR);
376 		}
377 		addr.sin_port = sp->s_port;
378 	}
379 
380 	/*
381 	**  Try to actually open the connection.
382 	*/
383 
384 	for (;;)
385 	{
386 		if (tTd(16, 1))
387 			printf("makeconnection (%s [%s])\n", host,
388 			    inet_ntoa(addr.sin_addr));
389 
390 		if (usesecureport)
391 		{
392 			int rport = IPPORT_RESERVED - 1;
393 
394 			s = rresvport(&rport);
395 		}
396 		else
397 		{
398 			s = socket(AF_INET, SOCK_STREAM, 0);
399 		}
400 		if (s < 0)
401 		{
402 			sav_errno = errno;
403 			syserr("makeconnection: no socket");
404 			goto failure;
405 		}
406 
407 		if (tTd(16, 1))
408 			printf("makeconnection: fd=%d\n", s);
409 
410 		/* turn on network debugging? */
411 		if (tTd(16, 101))
412 		{
413 			int on = 1;
414 			(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG,
415 					  (char *)&on, sizeof on);
416 		}
417 		if (CurEnv->e_xfp != NULL)
418 			(void) fflush(CurEnv->e_xfp);		/* for debugging */
419 		errno = 0;					/* for debugging */
420 		addr.sin_family = AF_INET;
421 		if (connect(s, (struct sockaddr *) &addr, sizeof addr) >= 0)
422 			break;
423 
424 		/* couldn't connect.... figure out why */
425 		sav_errno = errno;
426 		(void) close(s);
427 		if (hp && hp->h_addr_list[i])
428 		{
429 			if (tTd(16, 1))
430 				printf("Connect failed; trying new address....\n");
431 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
432 					hp->h_length);
433 			continue;
434 		}
435 
436 		/* failure, decide if temporary or not */
437 	failure:
438 		switch (sav_errno)
439 		{
440 		  case EISCONN:
441 		  case ETIMEDOUT:
442 		  case EINPROGRESS:
443 		  case EALREADY:
444 		  case EADDRINUSE:
445 		  case EHOSTDOWN:
446 		  case ENETDOWN:
447 		  case ENETRESET:
448 		  case ENOBUFS:
449 		  case ECONNREFUSED:
450 		  case ECONNRESET:
451 		  case EHOSTUNREACH:
452 		  case ENETUNREACH:
453 #ifdef ENOSR
454 		  case ENOSR:
455 #endif
456 			/* there are others, I'm sure..... */
457 			return (EX_TEMPFAIL);
458 
459 		  case EPERM:
460 			/* why is this happening? */
461 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
462 				addr.sin_addr.s_addr, addr.sin_port);
463 			return (EX_TEMPFAIL);
464 
465 		  default:
466 			{
467 				extern char *errstring();
468 
469 				message("%s", errstring(sav_errno));
470 				return (EX_UNAVAILABLE);
471 			}
472 		}
473 	}
474 
475 	/* connection ok, put it into canonical form */
476 	mci->mci_out = fdopen(s, "w");
477 	mci->mci_in = fdopen(dup(s), "r");
478 
479 	return (EX_OK);
480 }
481 /*
482 **  MYHOSTNAME -- return the name of this host.
483 **
484 **	Parameters:
485 **		hostbuf -- a place to return the name of this host.
486 **		size -- the size of hostbuf.
487 **
488 **	Returns:
489 **		A list of aliases for this host.
490 **
491 **	Side Effects:
492 **		Sets the MyIpAddrs buffer to a list of my IP addresses.
493 */
494 
495 struct in_addr	MyIpAddrs[MAXIPADDR + 1];
496 
497 char **
498 myhostname(hostbuf, size)
499 	char hostbuf[];
500 	int size;
501 {
502 	register struct hostent *hp;
503 	extern struct hostent *gethostbyname();
504 
505 	if (gethostname(hostbuf, size) < 0)
506 	{
507 		(void) strcpy(hostbuf, "localhost");
508 	}
509 	hp = gethostbyname(hostbuf);
510 	if (hp != NULL)
511 	{
512 		(void) strncpy(hostbuf, hp->h_name, size - 1);
513 		hostbuf[size - 1] = '\0';
514 
515 		if (hp->h_addrtype == AF_INET && hp->h_length == 4)
516 		{
517 			register int i;
518 
519 			for (i = 0; i < MAXIPADDR; i++)
520 			{
521 				if (hp->h_addr_list[i] == NULL)
522 					break;
523 				MyIpAddrs[i].s_addr = *(u_long *) hp->h_addr_list[i];
524 			}
525 			MyIpAddrs[i].s_addr = 0;
526 		}
527 
528 		return (hp->h_aliases);
529 	}
530 	else
531 		return (NULL);
532 }
533 /*
534 **  MAPHOSTNAME -- turn a hostname into canonical form
535 **
536 **	Parameters:
537 **		map -- a pointer to this map (unused).
538 **		hbuf -- a buffer containing a hostname.
539 **		hbsize -- the size of hbuf.
540 **		avp -- unused -- for compatibility with other mapping
541 **			functions.
542 **
543 **	Returns:
544 **		The mapping, if found.
545 **		NULL if no mapping found.
546 **
547 **	Side Effects:
548 **		Looks up the host specified in hbuf.  If it is not
549 **		the canonical name for that host, return the canonical
550 **		name.
551 */
552 
553 char *
554 maphostname(map, hbuf, hbsize, avp)
555 	MAP *map;
556 	char *hbuf;
557 	int hbsize;
558 	char **avp;
559 {
560 	register struct hostent *hp;
561 	u_long in_addr;
562 	char *cp;
563 	int i;
564 	struct hostent *gethostbyaddr();
565 
566 	/* allow room for null */
567 	hbsize--;
568 
569 	/*
570 	 * If first character is a bracket, then it is an address
571 	 * lookup.  Address is copied into a temporary buffer to
572 	 * strip the brackets and to preserve hbuf if address is
573 	 * unknown.
574 	 */
575 
576 	if (*hbuf != '[')
577 	{
578 		extern bool getcanonname();
579 
580 		if (getcanonname(hbuf, hbsize))
581 			return hbuf;
582 		else
583 			return NULL;
584 	}
585 	if ((cp = strchr(hbuf, ']')) == NULL)
586 		return (NULL);
587 	*cp = '\0';
588 	in_addr = inet_addr(&hbuf[1]);
589 
590 	/* check to see if this is one of our addresses */
591 	for (i = 0; MyIpAddrs[i].s_addr != 0; i++)
592 	{
593 		if (MyIpAddrs[i].s_addr == in_addr)
594 		{
595 			strncpy(hbuf, MyHostName, hbsize);
596 			hbuf[hbsize] = '\0';
597 			return hbuf;
598 		}
599 	}
600 
601 	/* nope -- ask the name server */
602 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
603 	if (hp == NULL)
604 		return (NULL);
605 
606 	/* found a match -- copy out */
607 	if (strlen(hp->h_name) > hbsize)
608 		hp->h_name[hbsize] = '\0';
609 	(void) strcpy(hbuf, hp->h_name);
610 	return hbuf;
611 }
612 
613 # else /* DAEMON */
614 /* code for systems without sophisticated networking */
615 
616 /*
617 **  MYHOSTNAME -- stub version for case of no daemon code.
618 **
619 **	Can't convert to upper case here because might be a UUCP name.
620 **
621 **	Mark, you can change this to be anything you want......
622 */
623 
624 char **
625 myhostname(hostbuf, size)
626 	char hostbuf[];
627 	int size;
628 {
629 	register FILE *f;
630 
631 	hostbuf[0] = '\0';
632 	f = fopen("/usr/include/whoami", "r");
633 	if (f != NULL)
634 	{
635 		(void) fgets(hostbuf, size, f);
636 		fixcrlf(hostbuf, TRUE);
637 		(void) fclose(f);
638 	}
639 	return (NULL);
640 }
641 /*
642 **  MAPHOSTNAME -- turn a hostname into canonical form
643 **
644 **	Parameters:
645 **		map -- a pointer to the database map.
646 **		hbuf -- a buffer containing a hostname.
647 **		avp -- a pointer to a (cf file defined) argument vector.
648 **
649 **	Returns:
650 **		mapped host name
651 **		FALSE otherwise.
652 **
653 **	Side Effects:
654 **		Looks up the host specified in hbuf.  If it is not
655 **		the canonical name for that host, replace it with
656 **		the canonical name.  If the name is unknown, or it
657 **		is already the canonical name, leave it unchanged.
658 */
659 
660 /*ARGSUSED*/
661 char *
662 maphostname(map, hbuf, hbsize, avp)
663 	MAP *map;
664 	char *hbuf;
665 	int hbsize;
666 	char **avp;
667 {
668 	return NULL;
669 }
670 
671 #endif /* DAEMON */
672