xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 55019)
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.47 (Berkeley) 07/12/92 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	5.47 (Berkeley) 07/12/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 			/* should we check for illegal connection here? XXX */
235 
236 			RealHostName = newstr(buf);
237 
238 			(void) close(DaemonSocket);
239 			InChannel = fdopen(t, "r");
240 			OutChannel = fdopen(dup(t), "w");
241 			if (tTd(15, 2))
242 				printf("getreq: returning\n");
243 # ifdef LOG
244 			if (LogLevel > 11)
245 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
246 # endif LOG
247 			return;
248 		}
249 
250 		/* close the port so that others will hang (for a while) */
251 		(void) close(t);
252 	}
253 	/*NOTREACHED*/
254 }
255 /*
256 **  CLRDAEMON -- reset the daemon connection
257 **
258 **	Parameters:
259 **		none.
260 **
261 **	Returns:
262 **		none.
263 **
264 **	Side Effects:
265 **		releases any resources used by the passive daemon.
266 */
267 
268 clrdaemon()
269 {
270 	if (DaemonSocket >= 0)
271 		(void) close(DaemonSocket);
272 	DaemonSocket = -1;
273 }
274 /*
275 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
276 **
277 **	Parameters:
278 **		host -- the name of the host.
279 **		port -- the port number to connect to.
280 **		mci -- a pointer to the mail connection information
281 **			structure to be filled in.
282 **		usesecureport -- if set, use a low numbered (reserved)
283 **			port to provide some rudimentary authentication.
284 **
285 **	Returns:
286 **		An exit code telling whether the connection could be
287 **			made and if not why not.
288 **
289 **	Side Effects:
290 **		none.
291 */
292 
293 int
294 makeconnection(host, port, mci, usesecureport)
295 	char *host;
296 	u_short port;
297 	register MCI *mci;
298 	bool usesecureport;
299 {
300 	register int i, s;
301 	register struct hostent *hp = (struct hostent *)NULL;
302 	struct sockaddr_in addr;
303 	int sav_errno;
304 	extern char *inet_ntoa();
305 #ifdef NAMED_BIND
306 	extern int h_errno;
307 #endif
308 
309 	/*
310 	**  Set up the address for the mailer.
311 	**	Accept "[a.b.c.d]" syntax for host name.
312 	*/
313 
314 #ifdef NAMED_BIND
315 	h_errno = 0;
316 #endif
317 	errno = 0;
318 
319 	if (host[0] == '[')
320 	{
321 		long hid;
322 		register char *p = index(host, ']');
323 
324 		if (p != NULL)
325 		{
326 			*p = '\0';
327 			hid = inet_addr(&host[1]);
328 			*p = ']';
329 		}
330 		if (p == NULL || hid == -1)
331 		{
332 			usrerr("Invalid numeric domain spec \"%s\"", host);
333 			return (EX_NOHOST);
334 		}
335 		addr.sin_addr.s_addr = hid;
336 	}
337 	else
338 	{
339 		hp = gethostbyname(host);
340 		if (hp == NULL)
341 		{
342 #ifdef NAMED_BIND
343 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
344 				return (EX_TEMPFAIL);
345 
346 			/* if name server is specified, assume temp fail */
347 			if (errno == ECONNREFUSED && UseNameServer)
348 				return (EX_TEMPFAIL);
349 #endif
350 
351 			/*
352 			**  XXX Should look for mail forwarder record here
353 			**  XXX if (h_errno == NO_ADDRESS).
354 			*/
355 
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("makeconnection: server \"smtp\" unknown");
375 			return (EX_OSFILE);
376 		}
377 		addr.sin_port = sp->s_port;
378 	}
379 
380 	/*
381 	**  Try to actually open the connection.
382 	*/
383 
384 again:
385 	if (tTd(16, 1))
386 		printf("makeconnection (%s [%s])\n", host,
387 		    inet_ntoa(addr.sin_addr));
388 
389 	if (usesecureport)
390 	{
391 		int rport = IPPORT_RESERVED - 1;
392 
393 		s = rresvport(&rport);
394 	}
395 	else
396 	{
397 		s = socket(AF_INET, SOCK_STREAM, 0);
398 	}
399 	if (s < 0)
400 	{
401 		sav_errno = errno;
402 		syserr("makeconnection: no socket");
403 		goto failure;
404 	}
405 
406 	if (tTd(16, 1))
407 		printf("makeconnection: fd=%d\n", s);
408 
409 	/* turn on network debugging? */
410 	if (tTd(16, 14))
411 	{
412 		int on = 1;
413 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
414 	}
415 	if (CurEnv->e_xfp != NULL)
416 		(void) fflush(CurEnv->e_xfp);		/* for debugging */
417 	errno = 0;					/* for debugging */
418 	addr.sin_family = AF_INET;
419 	if (connect(s, (struct sockaddr *) &addr, sizeof addr) < 0)
420 	{
421 		sav_errno = errno;
422 		(void) close(s);
423 		if (hp && hp->h_addr_list[i])
424 		{
425 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
426 					hp->h_length);
427 			goto again;
428 		}
429 
430 		/* failure, decide if temporary or not */
431 	failure:
432 		switch (sav_errno)
433 		{
434 		  case EISCONN:
435 		  case ETIMEDOUT:
436 		  case EINPROGRESS:
437 		  case EALREADY:
438 		  case EADDRINUSE:
439 		  case EHOSTDOWN:
440 		  case ENETDOWN:
441 		  case ENETRESET:
442 		  case ENOBUFS:
443 		  case ECONNREFUSED:
444 		  case ECONNRESET:
445 		  case EHOSTUNREACH:
446 		  case ENETUNREACH:
447 #ifdef ENOSR
448 		  case ENOSR:
449 #endif
450 			/* there are others, I'm sure..... */
451 			return (EX_TEMPFAIL);
452 
453 		  case EPERM:
454 			/* why is this happening? */
455 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
456 				addr.sin_addr.s_addr, addr.sin_port);
457 			return (EX_TEMPFAIL);
458 
459 		  default:
460 			{
461 				extern char *errstring();
462 
463 				message(Arpa_Info, "%s", errstring(sav_errno));
464 				return (EX_UNAVAILABLE);
465 			}
466 		}
467 	}
468 
469 	/* connection ok, put it into canonical form */
470 	mci->mci_out = fdopen(s, "w");
471 	mci->mci_in = fdopen(dup(s), "r");
472 
473 	return (EX_OK);
474 }
475 /*
476 **  MYHOSTNAME -- return the name of this host.
477 **
478 **	Parameters:
479 **		hostbuf -- a place to return the name of this host.
480 **		size -- the size of hostbuf.
481 **
482 **	Returns:
483 **		A list of aliases for this host.
484 **
485 **	Side Effects:
486 **		none.
487 */
488 
489 char **
490 myhostname(hostbuf, size)
491 	char hostbuf[];
492 	int size;
493 {
494 	extern struct hostent *gethostbyname();
495 	struct hostent *hp;
496 
497 	if (gethostname(hostbuf, size) < 0)
498 	{
499 		(void) strcpy(hostbuf, "localhost");
500 	}
501 	hp = gethostbyname(hostbuf);
502 	if (hp != NULL)
503 	{
504 		(void) strcpy(hostbuf, hp->h_name);
505 		return (hp->h_aliases);
506 	}
507 	else
508 		return (NULL);
509 }
510 /*
511 **  MAPHOSTNAME -- turn a hostname into canonical form
512 **
513 **	Parameters:
514 **		hbuf -- a buffer containing a hostname.
515 **		hbsize -- the size of hbuf.
516 **		avp -- unused -- for compatibility with other mapping
517 **			functions.
518 **
519 **	Returns:
520 **		The mapping, if found.
521 **		NULL if no mapping found.
522 **
523 **	Side Effects:
524 **		Looks up the host specified in hbuf.  If it is not
525 **		the canonical name for that host, return the canonical
526 **		name.
527 */
528 
529 char *
530 maphostname(hbuf, hbsize, avp)
531 	char *hbuf;
532 	int hbsize;
533 	char **avp;
534 {
535 	register struct hostent *hp;
536 	u_long in_addr;
537 	char ptr[256], *cp;
538 	struct hostent *gethostbyaddr();
539 
540 	/* allow room for trailing dot on correct match */
541 	if (ConfigLevel >= 2)
542 		hbsize--;
543 
544 	/*
545 	 * If first character is a bracket, then it is an address
546 	 * lookup.  Address is copied into a temporary buffer to
547 	 * strip the brackets and to preserve hbuf if address is
548 	 * unknown.
549 	 */
550 
551 	if (*hbuf != '[')
552 	{
553 		extern bool getcanonname();
554 
555 		if (getcanonname(hbuf, hbsize))
556 		{
557 			/* found a match -- add the trailing dot */
558 			if (ConfigLevel >= 2)
559 				(void) strcat(hbuf, ".");
560 			return hbuf;
561 		}
562 		else
563 			return NULL;
564 	}
565 	if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
566 		return (NULL);
567 	*cp = '\0';
568 	in_addr = inet_addr(&ptr[1]);
569 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
570 	if (hp == NULL)
571 		return (NULL);
572 
573 	/* found a match -- copy and dot terminate */
574 	if (strlen(hp->h_name) >= hbsize)
575 		hp->h_name[hbsize - 1] = '\0';
576 	(void) strcpy(hbuf, hp->h_name);
577 	if (ConfigLevel >= 2)
578 		(void) strcat(hbuf, ".");
579 	return hbuf;
580 }
581 
582 # else DAEMON
583 /* code for systems without sophisticated networking */
584 
585 /*
586 **  MYHOSTNAME -- stub version for case of no daemon code.
587 **
588 **	Can't convert to upper case here because might be a UUCP name.
589 **
590 **	Mark, you can change this to be anything you want......
591 */
592 
593 char **
594 myhostname(hostbuf, size)
595 	char hostbuf[];
596 	int size;
597 {
598 	register FILE *f;
599 
600 	hostbuf[0] = '\0';
601 	f = fopen("/usr/include/whoami", "r");
602 	if (f != NULL)
603 	{
604 		(void) fgets(hostbuf, size, f);
605 		fixcrlf(hostbuf, TRUE);
606 		(void) fclose(f);
607 	}
608 	return (NULL);
609 }
610 /*
611 **  MAPHOSTNAME -- turn a hostname into canonical form
612 **
613 **	Parameters:
614 **		hbuf -- a buffer containing a hostname.
615 **		hbsize -- the size of hbuf.
616 **		avp -- a pointer to a (cf file defined) argument vector.
617 **
618 **	Returns:
619 **		mapped host name
620 **		FALSE otherwise.
621 **
622 **	Side Effects:
623 **		Looks up the host specified in hbuf.  If it is not
624 **		the canonical name for that host, replace it with
625 **		the canonical name.  If the name is unknown, or it
626 **		is already the canonical name, leave it unchanged.
627 */
628 
629 /*ARGSUSED*/
630 char *
631 maphostname(hbuf, hbsize, avp)
632 	char *hbuf;
633 	int hbsize;
634 	char **avp;
635 {
636 	return NULL;
637 }
638 
639 #endif DAEMON
640