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