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