xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision 0a334d47)
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.45 (Berkeley) 05/31/92 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	5.45 (Berkeley) 05/31/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 **		mci -- a pointer to the mail connection information
281 **			structure to be filled in.
282 **		usesecureport -- if set, use a low numbered (reserved)
283 **			port to provide some rudimentary authentication.
284 **
285 **	Returns:
286 **		An exit code telling whether the connection could be
287 **			made and if not why not.
288 **
289 **	Side Effects:
290 **		none.
291 */
292 
293 makeconnection(host, port, mci, usesecureport)
294 	char *host;
295 	u_short port;
296 	register MCONINFO *mci;
297 	bool usesecureport;
298 {
299 	register int i, s;
300 	register struct hostent *hp = (struct hostent *)NULL;
301 	struct sockaddr_in addr;
302 	int sav_errno;
303 	extern char *inet_ntoa();
304 #ifdef NAMED_BIND
305 	extern int h_errno;
306 #endif
307 
308 	/*
309 	**  Set up the address for the mailer.
310 	**	Accept "[a.b.c.d]" syntax for host name.
311 	*/
312 
313 #ifdef NAMED_BIND
314 	h_errno = 0;
315 #endif
316 	errno = 0;
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 		addr.sin_addr.s_addr = hid;
335 	}
336 	else
337 	{
338 		hp = gethostbyname(host);
339 		if (hp == NULL)
340 		{
341 #ifdef NAMED_BIND
342 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
343 				return (EX_TEMPFAIL);
344 
345 			/* if name server is specified, assume temp fail */
346 			if (errno == ECONNREFUSED && UseNameServer)
347 				return (EX_TEMPFAIL);
348 #endif
349 
350 			/*
351 			**  XXX Should look for mail forwarder record here
352 			**  XXX if (h_errno == NO_ADDRESS).
353 			*/
354 
355 			return (EX_NOHOST);
356 		}
357 		bcopy(hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
358 		i = 1;
359 	}
360 
361 	/*
362 	**  Determine the port number.
363 	*/
364 
365 	if (port != 0)
366 		addr.sin_port = htons(port);
367 	else
368 	{
369 		register struct servent *sp = getservbyname("smtp", "tcp");
370 
371 		if (sp == NULL)
372 		{
373 			syserr("makeconnection: server \"smtp\" unknown");
374 			return (EX_OSFILE);
375 		}
376 		addr.sin_port = sp->s_port;
377 	}
378 
379 	/*
380 	**  Try to actually open the connection.
381 	*/
382 
383 again:
384 	if (tTd(16, 1))
385 		printf("makeconnection (%s [%s])\n", host,
386 		    inet_ntoa(addr.sin_addr));
387 
388 	if (usesecureport)
389 	{
390 		int rport = IPPORT_RESERVED - 1;
391 
392 		s = rresvport(&rport);
393 	}
394 	else
395 	{
396 		s = socket(AF_INET, SOCK_STREAM, 0);
397 	}
398 	if (s < 0)
399 	{
400 		sav_errno = errno;
401 		syserr("makeconnection: no socket");
402 		goto failure;
403 	}
404 
405 	if (tTd(16, 1))
406 		printf("makeconnection: %d\n", s);
407 
408 	/* turn on network debugging? */
409 	if (tTd(16, 14))
410 	{
411 		int on = 1;
412 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
413 	}
414 	if (CurEnv->e_xfp != NULL)
415 		(void) fflush(CurEnv->e_xfp);		/* for debugging */
416 	errno = 0;					/* for debugging */
417 	addr.sin_family = AF_INET;
418 	if (connect(s, (struct sockaddr *) &addr, sizeof addr) < 0)
419 	{
420 		sav_errno = errno;
421 		(void) close(s);
422 		if (hp && hp->h_addr_list[i])
423 		{
424 			bcopy(hp->h_addr_list[i++], (char *) &addr.sin_addr,
425 					hp->h_length);
426 			goto again;
427 		}
428 
429 		/* failure, decide if temporary or not */
430 	failure:
431 		switch (sav_errno)
432 		{
433 		  case EISCONN:
434 		  case ETIMEDOUT:
435 		  case EINPROGRESS:
436 		  case EALREADY:
437 		  case EADDRINUSE:
438 		  case EHOSTDOWN:
439 		  case ENETDOWN:
440 		  case ENETRESET:
441 		  case ENOBUFS:
442 		  case ECONNREFUSED:
443 		  case ECONNRESET:
444 		  case EHOSTUNREACH:
445 		  case ENETUNREACH:
446 #ifdef ENOSR
447 		  case ENOSR:
448 #endif
449 			/* there are others, I'm sure..... */
450 			return (EX_TEMPFAIL);
451 
452 		  case EPERM:
453 			/* why is this happening? */
454 			syserr("makeconnection: funny failure, addr=%lx, port=%x",
455 				addr.sin_addr.s_addr, addr.sin_port);
456 			return (EX_TEMPFAIL);
457 
458 		  default:
459 			{
460 				extern char *errstring();
461 
462 				message(Arpa_Info, "%s", errstring(sav_errno));
463 				return (EX_UNAVAILABLE);
464 			}
465 		}
466 	}
467 
468 	/* connection ok, put it into canonical form */
469 	mci->mci_out = fdopen(s, "w");
470 	mci->mci_in = fdopen(dup(s), "r");
471 
472 	return (EX_OK);
473 }
474 /*
475 **  MYHOSTNAME -- return the name of this host.
476 **
477 **	Parameters:
478 **		hostbuf -- a place to return the name of this host.
479 **		size -- the size of hostbuf.
480 **
481 **	Returns:
482 **		A list of aliases for this host.
483 **
484 **	Side Effects:
485 **		none.
486 */
487 
488 char **
489 myhostname(hostbuf, size)
490 	char hostbuf[];
491 	int size;
492 {
493 	extern struct hostent *gethostbyname();
494 	struct hostent *hp;
495 
496 	if (gethostname(hostbuf, size) < 0)
497 	{
498 		(void) strcpy(hostbuf, "localhost");
499 	}
500 	hp = gethostbyname(hostbuf);
501 	if (hp != NULL)
502 	{
503 		(void) strcpy(hostbuf, hp->h_name);
504 		return (hp->h_aliases);
505 	}
506 	else
507 		return (NULL);
508 }
509 /*
510 **  MAPHOSTNAME -- turn a hostname into canonical form
511 **
512 **	Parameters:
513 **		hbuf -- a buffer containing a hostname.
514 **		hbsize -- the size of hbuf.
515 **
516 **	Returns:
517 **		The mapping, if found.
518 **		NULL if no mapping found.
519 **
520 **	Side Effects:
521 **		Looks up the host specified in hbuf.  If it is not
522 **		the canonical name for that host, return the canonical
523 **		name.
524 */
525 
526 char *
527 maphostname(hbuf, hbsize, avp)
528 	char *hbuf;
529 	int hbsize;
530 	char **avp;
531 {
532 	register struct hostent *hp;
533 	u_long in_addr;
534 	char ptr[256], *cp;
535 	struct hostent *gethostbyaddr();
536 
537 	/* allow room for trailing dot on correct match */
538 	if (ConfigLevel >= 2)
539 		hbsize--;
540 
541 	/*
542 	 * If first character is a bracket, then it is an address
543 	 * lookup.  Address is copied into a temporary buffer to
544 	 * strip the brackets and to preserve hbuf if address is
545 	 * unknown.
546 	 */
547 
548 	if (*hbuf != '[')
549 	{
550 		if (getcanonname(hbuf, hbsize))
551 		{
552 			/* found a match -- add the trailing dot */
553 			if (ConfigLevel >= 2)
554 				(void) strcat(hbuf, ".");
555 			return hbuf;
556 		}
557 		else
558 			return NULL;
559 	}
560 	if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
561 		return (NULL);
562 	*cp = '\0';
563 	in_addr = inet_addr(&ptr[1]);
564 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
565 	if (hp == NULL)
566 		return (NULL);
567 
568 	/* found a match -- copy and dot terminate */
569 	if (strlen(hp->h_name) >= hbsize)
570 		hp->h_name[hbsize - 1] = '\0';
571 	(void) strcpy(hbuf, hp->h_name);
572 	if (ConfigLevel >= 2)
573 		(void) strcat(hbuf, ".");
574 	return hbuf;
575 }
576 
577 # else DAEMON
578 /* code for systems without sophisticated networking */
579 
580 /*
581 **  MYHOSTNAME -- stub version for case of no daemon code.
582 **
583 **	Can't convert to upper case here because might be a UUCP name.
584 **
585 **	Mark, you can change this to be anything you want......
586 */
587 
588 char **
589 myhostname(hostbuf, size)
590 	char hostbuf[];
591 	int size;
592 {
593 	register FILE *f;
594 
595 	hostbuf[0] = '\0';
596 	f = fopen("/usr/include/whoami", "r");
597 	if (f != NULL)
598 	{
599 		(void) fgets(hostbuf, size, f);
600 		fixcrlf(hostbuf, TRUE);
601 		(void) fclose(f);
602 	}
603 	return (NULL);
604 }
605 /*
606 **  MAPHOSTNAME -- turn a hostname into canonical form
607 **
608 **	Parameters:
609 **		hbuf -- a buffer containing a hostname.
610 **		hbsize -- the size of hbuf.
611 **		avp -- a pointer to a (cf file defined) argument vector.
612 **
613 **	Returns:
614 **		mapped host name
615 **		FALSE otherwise.
616 **
617 **	Side Effects:
618 **		Looks up the host specified in hbuf.  If it is not
619 **		the canonical name for that host, replace it with
620 **		the canonical name.  If the name is unknown, or it
621 **		is already the canonical name, leave it unchanged.
622 */
623 
624 /*ARGSUSED*/
625 char *
626 maphostname(hbuf, hbsize, avp)
627 	char *hbuf;
628 	int hbsize;
629 	char **avp;
630 {
631 	return NULL;
632 }
633 
634 #endif DAEMON
635