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