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