xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 24876)
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.8 (Berkeley) 09/17/85	(w/o daemon mode)";
18 # endif not lint
19 # else
20 
21 # include <sys/socket.h>
22 # include <netinet/in.h>
23 # include <netdb.h>
24 # include <sys/signal.h>
25 # include <sys/wait.h>
26 # include <sys/time.h>
27 # include <sys/resource.h>
28 
29 # ifndef lint
30 static char	SccsId[] = "@(#)daemon.c	5.8 (Berkeley) 09/17/85 (with daemon mode)";
31 # endif not lint
32 
33 /*
34 **  DAEMON.C -- routines to use when running as a daemon.
35 **
36 **	This entire file is highly dependent on the 4.2 BSD
37 **	interprocess communication primitives.  No attempt has
38 **	been made to make this file portable to Version 7,
39 **	Version 6, MPX files, etc.  If you should try such a
40 **	thing yourself, I recommend chucking the entire file
41 **	and starting from scratch.  Basic semantics are:
42 **
43 **	getrequests()
44 **		Opens a port and initiates a connection.
45 **		Returns in a child.  Must set InChannel and
46 **		OutChannel appropriately.
47 **	clrdaemon()
48 **		Close any open files associated with getting
49 **		the connection; this is used when running the queue,
50 **		etc., to avoid having extra file descriptors during
51 **		the queue run and to avoid confusing the network
52 **		code (if it cares).
53 **	makeconnection(host, port, outfile, infile)
54 **		Make a connection to the named host on the given
55 **		port.  Set *outfile and *infile to the files
56 **		appropriate for communication.  Returns zero on
57 **		success, else an exit status describing the
58 **		error.
59 **
60 **	The semantics of both of these should be clean.
61 */
62 /*
63 **  GETREQUESTS -- open mail IPC port and get requests.
64 **
65 **	Parameters:
66 **		none.
67 **
68 **	Returns:
69 **		none.
70 **
71 **	Side Effects:
72 **		Waits until some interesting activity occurs.  When
73 **		it does, a child is created to process it, and the
74 **		parent waits for completion.  Return from this
75 **		routine is always in the child.  The file pointers
76 **		"InChannel" and "OutChannel" should be set to point
77 **		to the communication channel.
78 */
79 
80 struct sockaddr_in	SendmailAddress;/* internet address of sendmail */
81 
82 int	DaemonSocket	= -1;		/* fd describing socket */
83 char	*NetName;			/* name of home (local?) network */
84 
85 getrequests()
86 {
87 	int t;
88 	register struct servent *sp;
89 	extern reapchild();
90 
91 	/*
92 	**  Set up the address for the mailer.
93 	*/
94 
95 	sp = getservbyname("smtp", "tcp");
96 	if (sp == NULL)
97 	{
98 		syserr("server \"smtp\" unknown");
99 		goto severe;
100 	}
101 	SendmailAddress.sin_family = AF_INET;
102 	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
103 	SendmailAddress.sin_port = sp->s_port;
104 
105 	/*
106 	**  Try to actually open the connection.
107 	*/
108 
109 # ifdef DEBUG
110 	if (tTd(15, 1))
111 		printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
112 # endif DEBUG
113 
114 	/* get a socket for the SMTP connection */
115 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
116 	if (DaemonSocket < 0)
117 	{
118 		/* probably another daemon already */
119 		syserr("getrequests: can't create socket");
120 	  severe:
121 # ifdef LOG
122 		if (LogLevel > 0)
123 			syslog(LOG_ALERT, "cannot get connection");
124 # endif LOG
125 		finis();
126 	}
127 
128 #ifdef DEBUG
129 	/* turn on network debugging? */
130 	if (tTd(15, 15))
131 	{
132 		int on = 1;
133 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
134 	}
135 #endif DEBUG
136 
137 	if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress) < 0)
138 	{
139 		syserr("getrequests: cannot bind");
140 		(void) close(DaemonSocket);
141 		goto severe;
142 	}
143 	if (listen(DaemonSocket, 10) < 0)
144 	{
145 		syserr("getrequests: cannot listen");
146 		(void) close(DaemonSocket);
147 		goto severe;
148 	}
149 
150 	signal(SIGCHLD, reapchild);
151 
152 # ifdef DEBUG
153 	if (tTd(15, 1))
154 		printf("getrequests: %d\n", DaemonSocket);
155 # endif DEBUG
156 
157 	for (;;)
158 	{
159 		register int pid;
160 		auto int lotherend;
161 		struct sockaddr_in otherend;
162 		extern int RefuseLA;
163 
164 		/* see if we are rejecting connections */
165 		while (getla() > RefuseLA)
166 			sleep(5);
167 
168 		/* wait for a connection */
169 		do
170 		{
171 			errno = 0;
172 			lotherend = sizeof otherend;
173 			t = accept(DaemonSocket, &otherend, &lotherend);
174 		} while (t < 0 && errno == EINTR);
175 		if (t < 0)
176 		{
177 			syserr("getrequests: accept");
178 			sleep(5);
179 			continue;
180 		}
181 
182 		/*
183 		**  Create a subprocess to process the mail.
184 		*/
185 
186 # ifdef DEBUG
187 		if (tTd(15, 2))
188 			printf("getrequests: forking (fd = %d)\n", t);
189 # endif DEBUG
190 
191 		pid = fork();
192 		if (pid < 0)
193 		{
194 			syserr("daemon: cannot fork");
195 			sleep(10);
196 			(void) close(t);
197 			continue;
198 		}
199 
200 		if (pid == 0)
201 		{
202 			extern struct hostent *gethostbyaddr();
203 			register struct hostent *hp;
204 			extern char *RealHostName;	/* srvrsmtp.c */
205 			char buf[MAXNAME];
206 
207 			/*
208 			**  CHILD -- return to caller.
209 			**	Collect verified idea of sending host.
210 			**	Verify calling user id if possible here.
211 			*/
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 makeconnection(host, port, outfile, infile)
315 	char *host;
316 	u_short port;
317 	FILE **outfile;
318 	FILE **infile;
319 {
320 	register int s;
321 
322 	/*
323 	**  Set up the address for the mailer.
324 	**	Accept "[a.b.c.d]" syntax for host name.
325 	*/
326 
327 	if (host[0] == '[')
328 	{
329 		long hid;
330 		register char *p = index(host, ']');
331 
332 		if (p != NULL)
333 		{
334 			*p = '\0';
335 			hid = inet_addr(&host[1]);
336 			*p = ']';
337 		}
338 		if (p == NULL || hid == -1)
339 		{
340 			usrerr("Invalid numeric domain spec \"%s\"", host);
341 			return (EX_NOHOST);
342 		}
343 		SendmailAddress.sin_addr.s_addr = hid;
344 	}
345 	else
346 	{
347 		register struct hostent *hp = gethostbyname(host);
348 
349 		if (errno == ETIMEDOUT)
350 		{
351 			CurEnv->e_flags &= ~EF_FATALERRS;
352 			return (EX_TEMPFAIL);
353 		}
354 		if (hp == NULL)
355 			return (EX_NOHOST);
356 		bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
357 	}
358 
359 	/*
360 	**  Determine the port number.
361 	*/
362 
363 	if (port != 0)
364 		SendmailAddress.sin_port = htons(port);
365 	else
366 	{
367 		register struct servent *sp = getservbyname("smtp", "tcp");
368 
369 		if (sp == NULL)
370 		{
371 			syserr("makeconnection: server \"smtp\" unknown");
372 			return (EX_OSFILE);
373 		}
374 		SendmailAddress.sin_port = sp->s_port;
375 	}
376 
377 	/*
378 	**  Try to actually open the connection.
379 	*/
380 
381 # ifdef DEBUG
382 	if (tTd(16, 1))
383 		printf("makeconnection (%s)\n", host);
384 # endif DEBUG
385 
386 	s = socket(AF_INET, SOCK_STREAM, 0);
387 	if (s < 0)
388 	{
389 		syserr("makeconnection: no socket");
390 		goto failure;
391 	}
392 
393 # ifdef DEBUG
394 	if (tTd(16, 1))
395 		printf("makeconnection: %d\n", s);
396 
397 	/* turn on network debugging? */
398 	if (tTd(16, 14))
399 	{
400 		int on = 1;
401 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
402 	}
403 # endif DEBUG
404 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
405 	errno = 0;					/* for debugging */
406 	SendmailAddress.sin_family = AF_INET;
407 	if (connect(s, &SendmailAddress, sizeof SendmailAddress) < 0)
408 	{
409 		/* failure, decide if temporary or not */
410 	failure:
411 		switch (errno)
412 		{
413 		  case EISCONN:
414 		  case ETIMEDOUT:
415 		  case EINPROGRESS:
416 		  case EALREADY:
417 		  case EADDRINUSE:
418 		  case EHOSTDOWN:
419 		  case ENETDOWN:
420 		  case ENETRESET:
421 		  case ENOBUFS:
422 		  case ECONNREFUSED:
423 		  case ECONNRESET:
424 		  case EHOSTUNREACH:
425 		  case ENETUNREACH:
426 			/* there are others, I'm sure..... */
427 			CurEnv->e_flags &= ~EF_FATALERRS;
428 			return (EX_TEMPFAIL);
429 
430 		  case EPERM:
431 			/* why is this happening? */
432 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
433 				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
434 			return (EX_TEMPFAIL);
435 
436 		  default:
437 			return (EX_UNAVAILABLE);
438 		}
439 	}
440 
441 	/* connection ok, put it into canonical form */
442 	*outfile = fdopen(s, "w");
443 	*infile = fdopen(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 **		none.
491 **
492 **	Side Effects:
493 **		Looks up the host specified in hbuf.  If it is not
494 **		the canonical name for that host, replace it with
495 **		the canonical name.  If the name is unknown, or it
496 **		is already the canonical name, leave it unchanged.
497 */
498 
499 maphostname(hbuf, hbsize)
500 	char *hbuf;
501 	int hbsize;
502 {
503 	register struct hostent *hp;
504 	extern struct hostent *gethostbyname();
505 
506 	makelower(hbuf);
507 	hp = gethostbyname(hbuf);
508 	if (hp != NULL)
509 	{
510 		int i = strlen(hp->h_name);
511 
512 		if (i >= hbsize)
513 			hp->h_name[--i] = '\0';
514 		(void) strcpy(hbuf, hp->h_name);
515 	}
516 }
517 
518 # else DAEMON
519 /* code for systems without sophisticated networking */
520 
521 /*
522 **  MYHOSTNAME -- stub version for case of no daemon code.
523 **
524 **	Can't convert to upper case here because might be a UUCP name.
525 **
526 **	Mark, you can change this to be anything you want......
527 */
528 
529 char **
530 myhostname(hostbuf, size)
531 	char hostbuf[];
532 	int size;
533 {
534 	register FILE *f;
535 
536 	hostbuf[0] = '\0';
537 	f = fopen("/usr/include/whoami", "r");
538 	if (f != NULL)
539 	{
540 		(void) fgets(hostbuf, size, f);
541 		fixcrlf(hostbuf, TRUE);
542 		(void) fclose(f);
543 	}
544 	return (NULL);
545 }
546 /*
547 **  MAPHOSTNAME -- turn a hostname into canonical form
548 **
549 **	Parameters:
550 **		hbuf -- a buffer containing a hostname.
551 **		hbsize -- the size of hbuf.
552 **
553 **	Returns:
554 **		none.
555 **
556 **	Side Effects:
557 **		Looks up the host specified in hbuf.  If it is not
558 **		the canonical name for that host, replace it with
559 **		the canonical name.  If the name is unknown, or it
560 **		is already the canonical name, leave it unchanged.
561 */
562 
563 /*ARGSUSED*/
564 maphostname(hbuf, hbsize)
565 	char *hbuf;
566 	int hbsize;
567 {
568 	return;
569 }
570 
571 
572 #endif DAEMON
573