xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 753853ba)
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	5.43 (Berkeley) 03/20/92 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	5.43 (Berkeley) 03/20/92 (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(hbuf, hbufsize)
55 **		Convert the entry in hbuf into a canonical form.  It
56 **		may not be larger than hbufsize.
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 int	DaemonSocket	= -1;		/* fd describing socket */
77 
78 getrequests()
79 {
80 	int t;
81 	register struct servent *sp;
82 	int on = 1;
83 	bool refusingconnections = TRUE;
84 	struct sockaddr_in srvraddr;
85 	extern void reapchild();
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 	srvraddr.sin_family = AF_INET;
98 	srvraddr.sin_addr.s_addr = INADDR_ANY;
99 	srvraddr.sin_port = sp->s_port;
100 
101 	/*
102 	**  Try to actually open the connection.
103 	*/
104 
105 	if (tTd(15, 1))
106 		printf("getrequests: port 0x%x\n", srvraddr.sin_port);
107 
108 	/* get a socket for the SMTP connection */
109 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
110 	if (DaemonSocket < 0)
111 	{
112 		/* probably another daemon already */
113 		syserr("getrequests: can't create socket");
114 	  severe:
115 # ifdef LOG
116 		if (LogLevel > 0)
117 			syslog(LOG_ALERT, "cannot get connection");
118 # endif LOG
119 		finis();
120 	}
121 
122 	/* turn on network debugging? */
123 	if (tTd(15, 15))
124 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
125 
126 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
127 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
128 
129 	if (bind(DaemonSocket, (struct sockaddr *)&srvraddr, sizeof srvraddr) < 0)
130 	{
131 		syserr("getrequests: cannot bind");
132 		(void) close(DaemonSocket);
133 		goto severe;
134 	}
135 
136 	(void) signal(SIGCHLD, reapchild);
137 
138 	if (tTd(15, 1))
139 		printf("getrequests: %d\n", DaemonSocket);
140 
141 	for (;;)
142 	{
143 		register int pid;
144 		auto int lotherend;
145 		extern bool refuseconnections();
146 
147 		/* see if we are rejecting connections */
148 		CurrentLA = getla();
149 		if (refuseconnections())
150 		{
151 			if (!refusingconnections)
152 			{
153 				/* don't queue so peer will fail quickly */
154 				(void) listen(DaemonSocket, 0);
155 				refusingconnections = TRUE;
156 			}
157 			setproctitle("rejecting connections: load average: %.2f",
158 				(double)CurrentLA);
159 			sleep(5);
160 			continue;
161 		}
162 
163 		if (refusingconnections)
164 		{
165 			/* start listening again */
166 			if (listen(DaemonSocket, 10) < 0)
167 			{
168 				syserr("getrequests: cannot listen");
169 				(void) close(DaemonSocket);
170 				goto severe;
171 			}
172 			setproctitle("accepting connections");
173 			refusingconnections = FALSE;
174 		}
175 
176 		/* wait for a connection */
177 		do
178 		{
179 			errno = 0;
180 			lotherend = sizeof RealHostAddr;
181 			t = accept(DaemonSocket,
182 			    (struct sockaddr *)&RealHostAddr, &lotherend);
183 		} while (t < 0 && errno == EINTR);
184 		if (t < 0)
185 		{
186 			syserr("getrequests: accept");
187 			sleep(5);
188 			continue;
189 		}
190 
191 		/*
192 		**  Create a subprocess to process the mail.
193 		*/
194 
195 		if (tTd(15, 2))
196 			printf("getrequests: forking (fd = %d)\n", t);
197 
198 		pid = fork();
199 		if (pid < 0)
200 		{
201 			syserr("daemon: cannot fork");
202 			sleep(10);
203 			(void) close(t);
204 			continue;
205 		}
206 
207 		if (pid == 0)
208 		{
209 			extern struct hostent *gethostbyaddr();
210 			register struct hostent *hp;
211 			char buf[MAXNAME];
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 				extern char *inet_ntoa();
228 
229 				/* produce a dotted quad */
230 				(void) sprintf(buf, "[%s]",
231 					inet_ntoa(RealHostAddr.sin_addr));
232 			}
233 
234 			/* should we check for illegal connection here? XXX */
235 
236 			RealHostName = newstr(buf);
237 
238 			(void) close(DaemonSocket);
239 			InChannel = fdopen(t, "r");
240 			OutChannel = fdopen(dup(t), "w");
241 			if (tTd(15, 2))
242 				printf("getreq: returning\n");
243 # ifdef LOG
244 			if (LogLevel > 11)
245 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
246 # endif LOG
247 			return;
248 		}
249 
250 		/* close the port so that others will hang (for a while) */
251 		(void) close(t);
252 	}
253 	/*NOTREACHED*/
254 }
255 /*
256 **  CLRDAEMON -- reset the daemon connection
257 **
258 **	Parameters:
259 **		none.
260 **
261 **	Returns:
262 **		none.
263 **
264 **	Side Effects:
265 **		releases any resources used by the passive daemon.
266 */
267 
268 clrdaemon()
269 {
270 	if (DaemonSocket >= 0)
271 		(void) close(DaemonSocket);
272 	DaemonSocket = -1;
273 }
274 /*
275 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
276 **
277 **	Parameters:
278 **		host -- the name of the host.
279 **		port -- the port number to connect to.
280 **		outfile -- a pointer to a place to put the outfile
281 **			descriptor.
282 **		infile -- ditto for infile.
283 **		usesecureport -- if set, use a low numbered (reserved)
284 **			port to provide some rudimentary authentication.
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, usesecureport)
295 	char *host;
296 	u_short port;
297 	FILE **outfile;
298 	FILE **infile;
299 	bool usesecureport;
300 {
301 	register int i, s;
302 	register struct hostent *hp = (struct hostent *)NULL;
303 	struct sockaddr_in addr;
304 	int sav_errno;
305 	extern char *inet_ntoa();
306 #ifdef NAMED_BIND
307 	extern int h_errno;
308 #endif
309 
310 	/*
311 	**  Set up the address for the mailer.
312 	**	Accept "[a.b.c.d]" syntax for host name.
313 	*/
314 
315 #ifdef NAMED_BIND
316 	h_errno = 0;
317 #endif
318 	errno = 0;
319 
320 	if (host[0] == '[')
321 	{
322 		long hid;
323 		register char *p = index(host, ']');
324 
325 		if (p != NULL)
326 		{
327 			*p = '\0';
328 			hid = inet_addr(&host[1]);
329 			*p = ']';
330 		}
331 		if (p == NULL || hid == -1)
332 		{
333 			usrerr("Invalid numeric domain spec \"%s\"", host);
334 			return (EX_NOHOST);
335 		}
336 		addr.sin_addr.s_addr = hid;
337 	}
338 	else
339 	{
340 		hp = gethostbyname(host);
341 		if (hp == NULL)
342 		{
343 #ifdef NAMED_BIND
344 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
345 				return (EX_TEMPFAIL);
346 
347 			/* if name server is specified, assume temp fail */
348 			if (errno == ECONNREFUSED && UseNameServer)
349 				return (EX_TEMPFAIL);
350 #endif
351 
352 			/*
353 			**  XXX Should look for mail forwarder record here
354 			**  XXX if (h_errno == NO_ADDRESS).
355 			*/
356 
357 			return (EX_NOHOST);
358 		}
359 		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
360 		i = 1;
361 	}
362 
363 	/*
364 	**  Determine the port number.
365 	*/
366 
367 	if (port != 0)
368 		addr.sin_port = htons(port);
369 	else
370 	{
371 		register struct servent *sp = getservbyname("smtp", "tcp");
372 
373 		if (sp == NULL)
374 		{
375 			syserr("makeconnection: server \"smtp\" unknown");
376 			return (EX_OSFILE);
377 		}
378 		addr.sin_port = sp->s_port;
379 	}
380 
381 	/*
382 	**  Try to actually open the connection.
383 	*/
384 
385 again:
386 	if (tTd(16, 1))
387 		printf("makeconnection (%s [%s])\n", host,
388 		    inet_ntoa(addr.sin_addr));
389 
390 	if (usesecureport)
391 	{
392 		int rport = IPPORT_RESERVED - 1;
393 
394 		s = rresvport(&rport);
395 	}
396 	else
397 	{
398 		s = socket(AF_INET, SOCK_STREAM, 0);
399 	}
400 	if (s < 0)
401 	{
402 		sav_errno = errno;
403 		syserr("makeconnection: no socket");
404 		goto failure;
405 	}
406 
407 	if (tTd(16, 1))
408 		printf("makeconnection: %d\n", s);
409 
410 	/* turn on network debugging? */
411 	if (tTd(16, 14))
412 	{
413 		int on = 1;
414 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
415 	}
416 	if (CurEnv->e_xfp != NULL)
417 		(void) fflush(CurEnv->e_xfp);		/* for debugging */
418 	errno = 0;					/* for debugging */
419 	addr.sin_family = AF_INET;
420 	if (connect(s, (struct sockaddr *) &addr, sizeof addr) < 0)
421 	{
422 		sav_errno = errno;
423 		(void) close(s);
424 		if (hp && hp->h_addr_list[i])
425 		{
426 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
427 					hp->h_length);
428 			goto again;
429 		}
430 
431 		/* failure, decide if temporary or not */
432 	failure:
433 		switch (sav_errno)
434 		{
435 		  case EISCONN:
436 		  case ETIMEDOUT:
437 		  case EINPROGRESS:
438 		  case EALREADY:
439 		  case EADDRINUSE:
440 		  case EHOSTDOWN:
441 		  case ENETDOWN:
442 		  case ENETRESET:
443 		  case ENOBUFS:
444 		  case ECONNREFUSED:
445 		  case ECONNRESET:
446 		  case EHOSTUNREACH:
447 		  case ENETUNREACH:
448 #ifdef ENOSR
449 		  case ENOSR:
450 #endif
451 			/* there are others, I'm sure..... */
452 			return (EX_TEMPFAIL);
453 
454 		  case EPERM:
455 			/* why is this happening? */
456 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
457 				addr.sin_addr.s_addr, addr.sin_port);
458 			return (EX_TEMPFAIL);
459 
460 		  default:
461 			{
462 				extern char *errstring();
463 
464 				message(Arpa_Info, "%s", errstring(sav_errno));
465 				return (EX_UNAVAILABLE);
466 			}
467 		}
468 	}
469 
470 	/* connection ok, put it into canonical form */
471 	*outfile = fdopen(s, "w");
472 	*infile = fdopen(dup(s), "r");
473 
474 	return (EX_OK);
475 }
476 /*
477 **  MYHOSTNAME -- return the name of this host.
478 **
479 **	Parameters:
480 **		hostbuf -- a place to return the name of this host.
481 **		size -- the size of hostbuf.
482 **
483 **	Returns:
484 **		A list of aliases for this host.
485 **
486 **	Side Effects:
487 **		none.
488 */
489 
490 char **
491 myhostname(hostbuf, size)
492 	char hostbuf[];
493 	int size;
494 {
495 	extern struct hostent *gethostbyname();
496 	struct hostent *hp;
497 
498 	if (gethostname(hostbuf, size) < 0)
499 	{
500 		(void) strcpy(hostbuf, "localhost");
501 	}
502 	hp = gethostbyname(hostbuf);
503 	if (hp != NULL)
504 	{
505 		(void) strcpy(hostbuf, hp->h_name);
506 		return (hp->h_aliases);
507 	}
508 	else
509 		return (NULL);
510 }
511 /*
512  *  MAPHOSTNAME -- turn a hostname into canonical form
513  *
514  *	Parameters:
515  *		hbuf -- a buffer containing a hostname.
516  *		hbsize -- the size of hbuf.
517  *
518  *	Returns:
519  *		TRUE if the host name was mapped.
520  *		FALSE otherwise.
521  *
522  *	Side Effects:
523  *		Looks up the host specified in hbuf.  If it is not
524  *		the canonical name for that host, replace it with
525  *		the canonical name.  If the name is unknown, or it
526  *		is already the canonical name, leave it unchanged.
527  */
528 
529 bool
530 maphostname(hbuf, hbsize)
531 	char *hbuf;
532 	int hbsize;
533 {
534 	register struct hostent *hp;
535 	u_long in_addr;
536 	char ptr[256], *cp;
537 	struct hostent *gethostbyaddr();
538 
539 	/*
540 	 * If first character is a bracket, then it is an address
541 	 * lookup.  Address is copied into a temporary buffer to
542 	 * strip the brackets and to preserve hbuf if address is
543 	 * unknown.
544 	 */
545 	if (*hbuf != '[')
546 		return (getcanonname(hbuf, hbsize));
547 	if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
548 		return (FALSE);
549 	*cp = '\0';
550 	in_addr = inet_addr(&ptr[1]);
551 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
552 	if (hp == NULL)
553 		return (FALSE);
554 	if (strlen(hp->h_name) >= hbsize)
555 		hp->h_name[hbsize - 1] = '\0';
556 	(void)strcpy(hbuf, hp->h_name);
557 	return (TRUE);
558 }
559 
560 # else DAEMON
561 /* code for systems without sophisticated networking */
562 
563 /*
564 **  MYHOSTNAME -- stub version for case of no daemon code.
565 **
566 **	Can't convert to upper case here because might be a UUCP name.
567 **
568 **	Mark, you can change this to be anything you want......
569 */
570 
571 char **
572 myhostname(hostbuf, size)
573 	char hostbuf[];
574 	int size;
575 {
576 	register FILE *f;
577 
578 	hostbuf[0] = '\0';
579 	f = fopen("/usr/include/whoami", "r");
580 	if (f != NULL)
581 	{
582 		(void) fgets(hostbuf, size, f);
583 		fixcrlf(hostbuf, TRUE);
584 		(void) fclose(f);
585 	}
586 	return (NULL);
587 }
588 /*
589 **  MAPHOSTNAME -- turn a hostname into canonical form
590 **
591 **	Parameters:
592 **		hbuf -- a buffer containing a hostname.
593 **		hbsize -- the size of hbuf.
594 **
595 **	Returns:
596 **		TRUE if the hostname was mapped.
597 **		FALSE otherwise.
598 **
599 **	Side Effects:
600 **		Looks up the host specified in hbuf.  If it is not
601 **		the canonical name for that host, replace it with
602 **		the canonical name.  If the name is unknown, or it
603 **		is already the canonical name, leave it unchanged.
604 */
605 
606 /*ARGSUSED*/
607 bool
608 maphostname(hbuf, hbsize)
609 	char *hbuf;
610 	int hbsize;
611 {
612 	return (FALSE);
613 }
614 
615 #endif DAEMON
616