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