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