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