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