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