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