xref: /csrg-svn/usr.sbin/sendmail/src/daemon.c (revision 57977)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #include <errno.h>
10 #include "sendmail.h"
11 
12 #ifndef lint
13 #ifdef DAEMON
14 static char sccsid[] = "@(#)daemon.c	6.5 (Berkeley) 02/14/93 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	6.5 (Berkeley) 02/14/93 (without daemon mode)";
17 #endif
18 #endif /* not lint */
19 
20 #ifdef DAEMON
21 
22 # include <netdb.h>
23 # include <sys/signal.h>
24 # include <sys/wait.h>
25 # include <sys/time.h>
26 # include <sys/resource.h>
27 
28 /*
29 **  DAEMON.C -- routines to use when running as a daemon.
30 **
31 **	This entire file is highly dependent on the 4.2 BSD
32 **	interprocess communication primitives.  No attempt has
33 **	been made to make this file portable to Version 7,
34 **	Version 6, MPX files, etc.  If you should try such a
35 **	thing yourself, I recommend chucking the entire file
36 **	and starting from scratch.  Basic semantics are:
37 **
38 **	getrequests()
39 **		Opens a port and initiates a connection.
40 **		Returns in a child.  Must set InChannel and
41 **		OutChannel appropriately.
42 **	clrdaemon()
43 **		Close any open files associated with getting
44 **		the connection; this is used when running the queue,
45 **		etc., to avoid having extra file descriptors during
46 **		the queue run and to avoid confusing the network
47 **		code (if it cares).
48 **	makeconnection(host, port, outfile, infile, usesecureport)
49 **		Make a connection to the named host on the given
50 **		port.  Set *outfile and *infile to the files
51 **		appropriate for communication.  Returns zero on
52 **		success, else an exit status describing the
53 **		error.
54 **	maphostname(map, hbuf, hbufsiz, avp)
55 **		Convert the entry in hbuf into a canonical form.
56 */
57 /*
58 **  GETREQUESTS -- open mail IPC port and get requests.
59 **
60 **	Parameters:
61 **		none.
62 **
63 **	Returns:
64 **		none.
65 **
66 **	Side Effects:
67 **		Waits until some interesting activity occurs.  When
68 **		it does, a child is created to process it, and the
69 **		parent waits for completion.  Return from this
70 **		routine is always in the child.  The file pointers
71 **		"InChannel" and "OutChannel" should be set to point
72 **		to the communication channel.
73 */
74 
75 int	DaemonSocket	= -1;		/* fd describing socket */
76 
77 getrequests()
78 {
79 	int t;
80 	register struct servent *sp;
81 	int on = 1;
82 	bool refusingconnections = TRUE;
83 	struct sockaddr_in srvraddr;
84 	extern void reapchild();
85 
86 	/*
87 	**  Set up the address for the mailer.
88 	*/
89 
90 	sp = getservbyname("smtp", "tcp");
91 	if (sp == NULL)
92 	{
93 		syserr("server \"smtp\" unknown");
94 		goto severe;
95 	}
96 	srvraddr.sin_family = AF_INET;
97 	srvraddr.sin_addr.s_addr = INADDR_ANY;
98 	srvraddr.sin_port = sp->s_port;
99 
100 	/*
101 	**  Try to actually open the connection.
102 	*/
103 
104 	if (tTd(15, 1))
105 		printf("getrequests: port 0x%x\n", srvraddr.sin_port);
106 
107 	/* get a socket for the SMTP connection */
108 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 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_ALERT, "problem creating SMTP socket");
117 # endif /* LOG */
118 		finis();
119 	}
120 
121 	/* turn on network debugging? */
122 	if (tTd(15, 101))
123 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
124 
125 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
126 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
127 
128 	if (bind(DaemonSocket, (struct sockaddr *)&srvraddr, sizeof srvraddr) < 0)
129 	{
130 		syserr("getrequests: cannot bind");
131 		(void) close(DaemonSocket);
132 		goto severe;
133 	}
134 
135 	(void) signal(SIGCHLD, reapchild);
136 
137 	if (tTd(15, 1))
138 		printf("getrequests: %d\n", DaemonSocket);
139 
140 	for (;;)
141 	{
142 		register int pid;
143 		auto int lotherend;
144 		extern bool refuseconnections();
145 
146 		/* see if we are rejecting connections */
147 		CurrentLA = getla();
148 		if (refuseconnections())
149 		{
150 			if (!refusingconnections)
151 			{
152 				/* don't queue so peer will fail quickly */
153 				(void) listen(DaemonSocket, 0);
154 				refusingconnections = TRUE;
155 			}
156 			setproctitle("rejecting connections: load average: %d",
157 				CurrentLA);
158 			sleep(5);
159 			continue;
160 		}
161 
162 		if (refusingconnections)
163 		{
164 			/* start listening again */
165 			if (listen(DaemonSocket, 10) < 0)
166 			{
167 				syserr("getrequests: cannot listen");
168 				(void) close(DaemonSocket);
169 				goto severe;
170 			}
171 			setproctitle("accepting connections");
172 			refusingconnections = FALSE;
173 		}
174 
175 		/* wait for a connection */
176 		do
177 		{
178 			errno = 0;
179 			lotherend = sizeof RealHostAddr;
180 			t = accept(DaemonSocket,
181 			    (struct sockaddr *)&RealHostAddr, &lotherend);
182 		} while (t < 0 && errno == EINTR);
183 		if (t < 0)
184 		{
185 			syserr("getrequests: accept");
186 			sleep(5);
187 			continue;
188 		}
189 
190 		/*
191 		**  Create a subprocess to process the mail.
192 		*/
193 
194 		if (tTd(15, 2))
195 			printf("getrequests: forking (fd = %d)\n", t);
196 
197 		pid = fork();
198 		if (pid < 0)
199 		{
200 			syserr("daemon: cannot fork");
201 			sleep(10);
202 			(void) close(t);
203 			continue;
204 		}
205 
206 		if (pid == 0)
207 		{
208 			extern struct hostent *gethostbyaddr();
209 			register struct hostent *hp;
210 			char buf[MAXNAME];
211 			extern char *inet_ntoa();
212 
213 			/*
214 			**  CHILD -- return to caller.
215 			**	Collect verified idea of sending host.
216 			**	Verify calling user id if possible here.
217 			*/
218 
219 			(void) signal(SIGCHLD, SIG_DFL);
220 
221 			/* determine host name */
222 			hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
223 			if (hp != NULL)
224 				(void) strcpy(buf, hp->h_name);
225 			else
226 			{
227 				/* produce a dotted quad */
228 				(void) sprintf(buf, "[%s]",
229 					inet_ntoa(RealHostAddr.sin_addr));
230 			}
231 
232 #ifdef LOG
233 			if (LogLevel > 10)
234 			{
235 				/* log connection information */
236 				syslog(LOG_INFO, "connect from %s (%s)",
237 					buf, inet_ntoa(RealHostAddr.sin_addr));
238 			}
239 #endif
240 
241 			/* should we check for illegal connection here? XXX */
242 
243 			RealHostName = newstr(buf);
244 
245 			(void) close(DaemonSocket);
246 			InChannel = fdopen(t, "r");
247 			OutChannel = fdopen(dup(t), "w");
248 			if (tTd(15, 2))
249 				printf("getreq: returning\n");
250 # ifdef LOG
251 			if (LogLevel > 11)
252 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
253 # endif /* LOG */
254 			return;
255 		}
256 
257 		/* close the port so that others will hang (for a while) */
258 		(void) close(t);
259 	}
260 	/*NOTREACHED*/
261 }
262 /*
263 **  CLRDAEMON -- reset the daemon connection
264 **
265 **	Parameters:
266 **		none.
267 **
268 **	Returns:
269 **		none.
270 **
271 **	Side Effects:
272 **		releases any resources used by the passive daemon.
273 */
274 
275 clrdaemon()
276 {
277 	if (DaemonSocket >= 0)
278 		(void) close(DaemonSocket);
279 	DaemonSocket = -1;
280 }
281 /*
282 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
283 **
284 **	Parameters:
285 **		host -- the name of the host.
286 **		port -- the port number to connect to.
287 **		mci -- a pointer to the mail connection information
288 **			structure to be filled in.
289 **		usesecureport -- if set, use a low numbered (reserved)
290 **			port to provide some rudimentary authentication.
291 **
292 **	Returns:
293 **		An exit code telling whether the connection could be
294 **			made and if not why not.
295 **
296 **	Side Effects:
297 **		none.
298 */
299 
300 int
301 makeconnection(host, port, mci, usesecureport)
302 	char *host;
303 	u_short port;
304 	register MCI *mci;
305 	bool usesecureport;
306 {
307 	register int i, s;
308 	register struct hostent *hp = (struct hostent *)NULL;
309 	struct sockaddr_in addr;
310 	int sav_errno;
311 	extern char *inet_ntoa();
312 #ifdef NAMED_BIND
313 	extern int h_errno;
314 #endif
315 
316 	/*
317 	**  Set up the address for the mailer.
318 	**	Accept "[a.b.c.d]" syntax for host name.
319 	*/
320 
321 #ifdef NAMED_BIND
322 	h_errno = 0;
323 #endif
324 	errno = 0;
325 
326 	if (host[0] == '[')
327 	{
328 		long hid;
329 		register char *p = strchr(host, ']');
330 
331 		if (p != NULL)
332 		{
333 			*p = '\0';
334 			hid = inet_addr(&host[1]);
335 			*p = ']';
336 		}
337 		if (p == NULL || hid == -1)
338 		{
339 			usrerr("Invalid numeric domain spec \"%s\"", host);
340 			return (EX_NOHOST);
341 		}
342 		addr.sin_addr.s_addr = hid;
343 	}
344 	else
345 	{
346 		hp = gethostbyname(host);
347 		if (hp == NULL)
348 		{
349 #ifdef NAMED_BIND
350 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
351 				return (EX_TEMPFAIL);
352 
353 			/* if name server is specified, assume temp fail */
354 			if (errno == ECONNREFUSED && UseNameServer)
355 				return (EX_TEMPFAIL);
356 #endif
357 
358 			/*
359 			**  XXX Should look for mail forwarder record here
360 			**  XXX if (h_errno == NO_ADDRESS).
361 			*/
362 
363 			return (EX_NOHOST);
364 		}
365 		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
366 		i = 1;
367 	}
368 
369 	/*
370 	**  Determine the port number.
371 	*/
372 
373 	if (port != 0)
374 		addr.sin_port = htons(port);
375 	else
376 	{
377 		register struct servent *sp = getservbyname("smtp", "tcp");
378 
379 		if (sp == NULL)
380 		{
381 			syserr("makeconnection: server \"smtp\" unknown");
382 			return (EX_OSERR);
383 		}
384 		addr.sin_port = sp->s_port;
385 	}
386 
387 	/*
388 	**  Try to actually open the connection.
389 	*/
390 
391 	for (;;)
392 	{
393 		if (tTd(16, 1))
394 			printf("makeconnection (%s [%s])\n", host,
395 			    inet_ntoa(addr.sin_addr));
396 
397 		if (usesecureport)
398 		{
399 			int rport = IPPORT_RESERVED - 1;
400 
401 			s = rresvport(&rport);
402 		}
403 		else
404 		{
405 			s = socket(AF_INET, SOCK_STREAM, 0);
406 		}
407 		if (s < 0)
408 		{
409 			sav_errno = errno;
410 			syserr("makeconnection: no socket");
411 			goto failure;
412 		}
413 
414 		if (tTd(16, 1))
415 			printf("makeconnection: fd=%d\n", s);
416 
417 		/* turn on network debugging? */
418 		if (tTd(16, 101))
419 		{
420 			int on = 1;
421 			(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG,
422 					  (char *)&on, sizeof on);
423 		}
424 		if (CurEnv->e_xfp != NULL)
425 			(void) fflush(CurEnv->e_xfp);		/* for debugging */
426 		errno = 0;					/* for debugging */
427 		addr.sin_family = AF_INET;
428 		if (connect(s, (struct sockaddr *) &addr, sizeof addr) >= 0)
429 			break;
430 
431 		/* couldn't connect.... figure out why */
432 		sav_errno = errno;
433 		(void) close(s);
434 		if (hp && hp->h_addr_list[i])
435 		{
436 			if (tTd(16, 1))
437 				printf("Connect failed; trying new address....\n");
438 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
439 					hp->h_length);
440 			continue;
441 		}
442 
443 		/* failure, decide if temporary or not */
444 	failure:
445 		switch (sav_errno)
446 		{
447 		  case EISCONN:
448 		  case ETIMEDOUT:
449 		  case EINPROGRESS:
450 		  case EALREADY:
451 		  case EADDRINUSE:
452 		  case EHOSTDOWN:
453 		  case ENETDOWN:
454 		  case ENETRESET:
455 		  case ENOBUFS:
456 		  case ECONNREFUSED:
457 		  case ECONNRESET:
458 		  case EHOSTUNREACH:
459 		  case ENETUNREACH:
460 #ifdef ENOSR
461 		  case ENOSR:
462 #endif
463 			/* there are others, I'm sure..... */
464 			return (EX_TEMPFAIL);
465 
466 		  case EPERM:
467 			/* why is this happening? */
468 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
469 				addr.sin_addr.s_addr, addr.sin_port);
470 			return (EX_TEMPFAIL);
471 
472 		  default:
473 			{
474 				extern char *errstring();
475 
476 				message(Arpa_Info, "%s", errstring(sav_errno));
477 				return (EX_UNAVAILABLE);
478 			}
479 		}
480 	}
481 
482 	/* connection ok, put it into canonical form */
483 	mci->mci_out = fdopen(s, "w");
484 	mci->mci_in = fdopen(dup(s), "r");
485 
486 	return (EX_OK);
487 }
488 /*
489 **  MYHOSTNAME -- return the name of this host.
490 **
491 **	Parameters:
492 **		hostbuf -- a place to return the name of this host.
493 **		size -- the size of hostbuf.
494 **
495 **	Returns:
496 **		A list of aliases for this host.
497 **
498 **	Side Effects:
499 **		none.
500 */
501 
502 char **
503 myhostname(hostbuf, size)
504 	char hostbuf[];
505 	int size;
506 {
507 	extern struct hostent *gethostbyname();
508 	struct hostent *hp;
509 
510 	if (gethostname(hostbuf, size) < 0)
511 	{
512 		(void) strcpy(hostbuf, "localhost");
513 	}
514 	hp = gethostbyname(hostbuf);
515 	if (hp != NULL)
516 	{
517 		(void) strcpy(hostbuf, hp->h_name);
518 		return (hp->h_aliases);
519 	}
520 	else
521 		return (NULL);
522 }
523 /*
524 **  MAPHOSTNAME -- turn a hostname into canonical form
525 **
526 **	Parameters:
527 **		map -- a pointer to this map (unused).
528 **		hbuf -- a buffer containing a hostname.
529 **		hbsize -- the size of hbuf.
530 **		avp -- unused -- for compatibility with other mapping
531 **			functions.
532 **
533 **	Returns:
534 **		The mapping, if found.
535 **		NULL if no mapping found.
536 **
537 **	Side Effects:
538 **		Looks up the host specified in hbuf.  If it is not
539 **		the canonical name for that host, return the canonical
540 **		name.
541 */
542 
543 char *
544 maphostname(map, hbuf, hbsize, avp)
545 	MAP *map;
546 	char *hbuf;
547 	int hbsize;
548 	char **avp;
549 {
550 	register struct hostent *hp;
551 	u_long in_addr;
552 	char *cp;
553 	struct hostent *gethostbyaddr();
554 
555 	/* allow room for null */
556 	hbsize--;
557 
558 	/*
559 	 * If first character is a bracket, then it is an address
560 	 * lookup.  Address is copied into a temporary buffer to
561 	 * strip the brackets and to preserve hbuf if address is
562 	 * unknown.
563 	 */
564 
565 	if (*hbuf != '[')
566 	{
567 		extern bool getcanonname();
568 
569 		if (getcanonname(hbuf, hbsize))
570 			return hbuf;
571 		else
572 			return NULL;
573 	}
574 	if ((cp = strchr(hbuf, ']')) == NULL)
575 		return (NULL);
576 	*cp = '\0';
577 	in_addr = inet_addr(&hbuf[1]);
578 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
579 	if (hp == NULL)
580 		return (NULL);
581 
582 	/* found a match -- copy and dot terminate */
583 	if (strlen(hp->h_name) > hbsize)
584 		hp->h_name[hbsize] = '\0';
585 	(void) strcpy(hbuf, hp->h_name);
586 	return hbuf;
587 }
588 
589 # else /* DAEMON */
590 /* code for systems without sophisticated networking */
591 
592 /*
593 **  MYHOSTNAME -- stub version for case of no daemon code.
594 **
595 **	Can't convert to upper case here because might be a UUCP name.
596 **
597 **	Mark, you can change this to be anything you want......
598 */
599 
600 char **
601 myhostname(hostbuf, size)
602 	char hostbuf[];
603 	int size;
604 {
605 	register FILE *f;
606 
607 	hostbuf[0] = '\0';
608 	f = fopen("/usr/include/whoami", "r");
609 	if (f != NULL)
610 	{
611 		(void) fgets(hostbuf, size, f);
612 		fixcrlf(hostbuf, TRUE);
613 		(void) fclose(f);
614 	}
615 	return (NULL);
616 }
617 /*
618 **  MAPHOSTNAME -- turn a hostname into canonical form
619 **
620 **	Parameters:
621 **		map -- a pointer to the database map.
622 **		hbuf -- a buffer containing a hostname.
623 **		avp -- a pointer to a (cf file defined) argument vector.
624 **
625 **	Returns:
626 **		mapped host name
627 **		FALSE otherwise.
628 **
629 **	Side Effects:
630 **		Looks up the host specified in hbuf.  If it is not
631 **		the canonical name for that host, replace it with
632 **		the canonical name.  If the name is unknown, or it
633 **		is already the canonical name, leave it unchanged.
634 */
635 
636 /*ARGSUSED*/
637 char *
638 maphostname(map, hbuf, hbsize, avp)
639 	MAP *map;
640 	char *hbuf;
641 	int hbsize;
642 	char **avp;
643 {
644 	return NULL;
645 }
646 
647 #endif /* DAEMON */
648