xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 7562ff97)
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.36 (Berkeley) 06/01/90 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	5.36 (Berkeley) 06/01/90 (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 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, &SendmailAddress, sizeof SendmailAddress) < 0)
133 	{
134 		syserr("getrequests: cannot bind");
135 		(void) close(DaemonSocket);
136 		goto severe;
137 	}
138 	if (listen(DaemonSocket, 10) < 0)
139 	{
140 		syserr("getrequests: cannot listen");
141 		(void) close(DaemonSocket);
142 		goto severe;
143 	}
144 
145 	(void) signal(SIGCHLD, reapchild);
146 
147 	if (tTd(15, 1))
148 		printf("getrequests: %d\n", DaemonSocket);
149 
150 	for (;;)
151 	{
152 		register int pid;
153 		auto int lotherend;
154 		extern int RefuseLA;
155 
156 		/* see if we are rejecting connections */
157 		while ((la = getla()) > RefuseLA)
158 		{
159 			setproctitle("rejecting connections: load average: %.2f", (double)la);
160 			sleep(5);
161 		}
162 
163 		/* wait for a connection */
164 		setproctitle("accepting connections");
165 		do
166 		{
167 			errno = 0;
168 			lotherend = sizeof RealHostAddr;
169 			t = accept(DaemonSocket, &RealHostAddr, &lotherend);
170 		} while (t < 0 && errno == EINTR);
171 		if (t < 0)
172 		{
173 			syserr("getrequests: accept");
174 			sleep(5);
175 			continue;
176 		}
177 
178 		/*
179 		**  Create a subprocess to process the mail.
180 		*/
181 
182 		if (tTd(15, 2))
183 			printf("getrequests: forking (fd = %d)\n", t);
184 
185 		pid = fork();
186 		if (pid < 0)
187 		{
188 			syserr("daemon: cannot fork");
189 			sleep(10);
190 			(void) close(t);
191 			continue;
192 		}
193 
194 		if (pid == 0)
195 		{
196 			extern struct hostent *gethostbyaddr();
197 			register struct hostent *hp;
198 			char buf[MAXNAME];
199 
200 			/*
201 			**  CHILD -- return to caller.
202 			**	Collect verified idea of sending host.
203 			**	Verify calling user id if possible here.
204 			*/
205 
206 			(void) signal(SIGCHLD, SIG_DFL);
207 
208 			/* determine host name */
209 			hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
210 			if (hp != NULL)
211 				(void) strcpy(buf, hp->h_name);
212 			else
213 			{
214 				extern char *inet_ntoa();
215 
216 				/* produce a dotted quad */
217 				(void) sprintf(buf, "[%s]",
218 					inet_ntoa(RealHostAddr.sin_addr));
219 			}
220 
221 			/* should we check for illegal connection here? XXX */
222 
223 			RealHostName = newstr(buf);
224 
225 			(void) close(DaemonSocket);
226 			InChannel = fdopen(t, "r");
227 			OutChannel = fdopen(dup(t), "w");
228 			if (tTd(15, 2))
229 				printf("getreq: returning\n");
230 # ifdef LOG
231 			if (LogLevel > 11)
232 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
233 # endif LOG
234 			return;
235 		}
236 
237 		/* close the port so that others will hang (for a while) */
238 		(void) close(t);
239 	}
240 	/*NOTREACHED*/
241 }
242 /*
243 **  CLRDAEMON -- reset the daemon connection
244 **
245 **	Parameters:
246 **		none.
247 **
248 **	Returns:
249 **		none.
250 **
251 **	Side Effects:
252 **		releases any resources used by the passive daemon.
253 */
254 
255 clrdaemon()
256 {
257 	if (DaemonSocket >= 0)
258 		(void) close(DaemonSocket);
259 	DaemonSocket = -1;
260 }
261 /*
262 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
263 **
264 **	Parameters:
265 **		host -- the name of the host.
266 **		port -- the port number to connect to.
267 **		outfile -- a pointer to a place to put the outfile
268 **			descriptor.
269 **		infile -- ditto for infile.
270 **
271 **	Returns:
272 **		An exit code telling whether the connection could be
273 **			made and if not why not.
274 **
275 **	Side Effects:
276 **		none.
277 */
278 
279 makeconnection(host, port, outfile, infile)
280 	char *host;
281 	u_short port;
282 	FILE **outfile;
283 	FILE **infile;
284 {
285 	register int i, s;
286 	register struct hostent *hp = (struct hostent *)NULL;
287 	extern char *inet_ntoa();
288 	int sav_errno;
289 #ifdef NAMED_BIND
290 	extern int h_errno;
291 #endif
292 
293 	/*
294 	**  Set up the address for the mailer.
295 	**	Accept "[a.b.c.d]" syntax for host name.
296 	*/
297 
298 #ifdef NAMED_BIND
299 	h_errno = 0;
300 #endif
301 	errno = 0;
302 
303 	if (host[0] == '[')
304 	{
305 		long hid;
306 		register char *p = index(host, ']');
307 
308 		if (p != NULL)
309 		{
310 			*p = '\0';
311 			hid = inet_addr(&host[1]);
312 			*p = ']';
313 		}
314 		if (p == NULL || hid == -1)
315 		{
316 			usrerr("Invalid numeric domain spec \"%s\"", host);
317 			return (EX_NOHOST);
318 		}
319 		SendmailAddress.sin_addr.s_addr = hid;
320 	}
321 	else
322 	{
323 		hp = gethostbyname(host);
324 		if (hp == NULL)
325 		{
326 #ifdef NAMED_BIND
327 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
328 				return (EX_TEMPFAIL);
329 
330 			/* if name server is specified, assume temp fail */
331 			if (errno == ECONNREFUSED && UseNameServer)
332 				return (EX_TEMPFAIL);
333 #endif
334 
335 			/*
336 			**  XXX Should look for mail forwarder record here
337 			**  XXX if (h_errno == NO_ADDRESS).
338 			*/
339 
340 			return (EX_NOHOST);
341 		}
342 		bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
343 		i = 1;
344 	}
345 
346 	/*
347 	**  Determine the port number.
348 	*/
349 
350 	if (port != 0)
351 		SendmailAddress.sin_port = htons(port);
352 	else
353 	{
354 		register struct servent *sp = getservbyname("smtp", "tcp");
355 
356 		if (sp == NULL)
357 		{
358 			syserr("makeconnection: server \"smtp\" unknown");
359 			return (EX_OSFILE);
360 		}
361 		SendmailAddress.sin_port = sp->s_port;
362 	}
363 
364 	/*
365 	**  Try to actually open the connection.
366 	*/
367 
368 again:
369 	if (tTd(16, 1))
370 		printf("makeconnection (%s [%s])\n", host,
371 		    inet_ntoa(SendmailAddress.sin_addr.s_addr));
372 
373 	s = socket(AF_INET, SOCK_STREAM, 0);
374 	if (s < 0)
375 	{
376 		syserr("makeconnection: no socket");
377 		sav_errno = errno;
378 		goto failure;
379 	}
380 
381 	if (tTd(16, 1))
382 		printf("makeconnection: %d\n", s);
383 
384 	/* turn on network debugging? */
385 	if (tTd(16, 14))
386 	{
387 		int on = 1;
388 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
389 	}
390 	if (CurEnv->e_xfp != NULL)
391 		(void) fflush(CurEnv->e_xfp);		/* for debugging */
392 	errno = 0;					/* for debugging */
393 	SendmailAddress.sin_family = AF_INET;
394 	if (connect(s, &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 /*
484  *  MAPHOSTNAME -- turn a hostname into canonical form
485  *
486  *	Parameters:
487  *		hbuf -- a buffer containing a hostname.
488  *		hbsize -- the size of hbuf.
489  *
490  *	Returns:
491  *		none.
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 maphostname(hbuf, hbsize)
500 	char *hbuf;
501 	int hbsize;
502 {
503 	register struct hostent *hp;
504 	u_long in_addr;
505 	char ptr[256], *cp;
506 	struct hostent *gethostbyaddr();
507 
508 	/*
509 	 * If first character is a bracket, then it is an address
510 	 * lookup.  Address is copied into a temporary buffer to
511 	 * strip the brackets and to preserve hbuf if address is
512 	 * unknown.
513 	 */
514 	if (*hbuf != '[') {
515 		getcanonname(hbuf, hbsize);
516 		return;
517 	}
518 	if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
519 		return;
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;
525 	if (strlen(hp->h_name) >= hbsize)
526 		hp->h_name[hbsize - 1] = '\0';
527 	(void)strcpy(hbuf, hp->h_name);
528 }
529 
530 # else DAEMON
531 /* code for systems without sophisticated networking */
532 
533 /*
534 **  MYHOSTNAME -- stub version for case of no daemon code.
535 **
536 **	Can't convert to upper case here because might be a UUCP name.
537 **
538 **	Mark, you can change this to be anything you want......
539 */
540 
541 char **
542 myhostname(hostbuf, size)
543 	char hostbuf[];
544 	int size;
545 {
546 	register FILE *f;
547 
548 	hostbuf[0] = '\0';
549 	f = fopen("/usr/include/whoami", "r");
550 	if (f != NULL)
551 	{
552 		(void) fgets(hostbuf, size, f);
553 		fixcrlf(hostbuf, TRUE);
554 		(void) fclose(f);
555 	}
556 	return (NULL);
557 }
558 /*
559 **  MAPHOSTNAME -- turn a hostname into canonical form
560 **
561 **	Parameters:
562 **		hbuf -- a buffer containing a hostname.
563 **		hbsize -- the size of hbuf.
564 **
565 **	Returns:
566 **		none.
567 **
568 **	Side Effects:
569 **		Looks up the host specified in hbuf.  If it is not
570 **		the canonical name for that host, replace it with
571 **		the canonical name.  If the name is unknown, or it
572 **		is already the canonical name, leave it unchanged.
573 */
574 
575 /*ARGSUSED*/
576 maphostname(hbuf, hbsize)
577 	char *hbuf;
578 	int hbsize;
579 {
580 	return;
581 }
582 
583 #endif DAEMON
584