xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision d3640572)
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 
12 # include <errno.h>
13 # include "sendmail.h"
14 
15 # ifndef DAEMON
16 # ifndef lint
17 static char	SccsId[] = "@(#)daemon.c	5.23 (Berkeley) 12/21/87	(w/o daemon mode)";
18 # endif not lint
19 # else
20 
21 # include <netdb.h>
22 # include <sys/signal.h>
23 # include <sys/wait.h>
24 # include <sys/time.h>
25 # include <sys/resource.h>
26 
27 # ifndef lint
28 static char	SccsId[] = "@(#)daemon.c	5.23 (Berkeley) 12/21/87 (with daemon mode)";
29 # endif not lint
30 # endif
31 
32 # ifdef DAEMON
33 /*
34 **  DAEMON.C -- routines to use when running as a daemon.
35 **
36 **	This entire file is highly dependent on the 4.2 BSD
37 **	interprocess communication primitives.  No attempt has
38 **	been made to make this file portable to Version 7,
39 **	Version 6, MPX files, etc.  If you should try such a
40 **	thing yourself, I recommend chucking the entire file
41 **	and starting from scratch.  Basic semantics are:
42 **
43 **	getrequests()
44 **		Opens a port and initiates a connection.
45 **		Returns in a child.  Must set InChannel and
46 **		OutChannel appropriately.
47 **	clrdaemon()
48 **		Close any open files associated with getting
49 **		the connection; this is used when running the queue,
50 **		etc., to avoid having extra file descriptors during
51 **		the queue run and to avoid confusing the network
52 **		code (if it cares).
53 **	makeconnection(host, port, outfile, infile)
54 **		Make a connection to the named host on the given
55 **		port.  Set *outfile and *infile to the files
56 **		appropriate for communication.  Returns zero on
57 **		success, else an exit status describing the
58 **		error.
59 **	maphostname(hbuf, hbufsize)
60 **		Convert the entry in hbuf into a canonical form.  It
61 **		may not be larger than hbufsize.
62 */
63 /*
64 **  GETREQUESTS -- open mail IPC port and get requests.
65 **
66 **	Parameters:
67 **		none.
68 **
69 **	Returns:
70 **		none.
71 **
72 **	Side Effects:
73 **		Waits until some interesting activity occurs.  When
74 **		it does, a child is created to process it, and the
75 **		parent waits for completion.  Return from this
76 **		routine is always in the child.  The file pointers
77 **		"InChannel" and "OutChannel" should be set to point
78 **		to the communication channel.
79 */
80 
81 struct sockaddr_in	SendmailAddress;/* internet address of sendmail */
82 
83 int	DaemonSocket	= -1;		/* fd describing socket */
84 char	*NetName;			/* name of home (local?) network */
85 
86 getrequests()
87 {
88 	int t;
89 	register struct servent *sp;
90 	int on = 1;
91 	extern reapchild();
92 
93 	/*
94 	**  Set up the address for the mailer.
95 	*/
96 
97 	sp = getservbyname("smtp", "tcp");
98 	if (sp == NULL)
99 	{
100 		syserr("server \"smtp\" unknown");
101 		goto severe;
102 	}
103 	SendmailAddress.sin_family = AF_INET;
104 	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
105 	SendmailAddress.sin_port = sp->s_port;
106 
107 	/*
108 	**  Try to actually open the connection.
109 	*/
110 
111 # ifdef DEBUG
112 	if (tTd(15, 1))
113 		printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
114 # endif DEBUG
115 
116 	/* get a socket for the SMTP connection */
117 	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
118 	if (DaemonSocket < 0)
119 	{
120 		/* probably another daemon already */
121 		syserr("getrequests: can't create socket");
122 	  severe:
123 # ifdef LOG
124 		if (LogLevel > 0)
125 			syslog(LOG_ALERT, "cannot get connection");
126 # endif LOG
127 		finis();
128 	}
129 
130 #ifdef DEBUG
131 	/* turn on network debugging? */
132 	if (tTd(15, 15))
133 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
134 #endif DEBUG
135 
136 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
137 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
138 
139 	if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress) < 0)
140 	{
141 		syserr("getrequests: cannot bind");
142 		(void) close(DaemonSocket);
143 		goto severe;
144 	}
145 	if (listen(DaemonSocket, 10) < 0)
146 	{
147 		syserr("getrequests: cannot listen");
148 		(void) close(DaemonSocket);
149 		goto severe;
150 	}
151 
152 	(void) signal(SIGCHLD, reapchild);
153 
154 # ifdef DEBUG
155 	if (tTd(15, 1))
156 		printf("getrequests: %d\n", DaemonSocket);
157 # endif DEBUG
158 
159 	for (;;)
160 	{
161 		register int pid;
162 		auto int lotherend;
163 		struct sockaddr_in otherend;
164 		extern int RefuseLA;
165 
166 		/* see if we are rejecting connections */
167 		while (getla() > RefuseLA)
168 			sleep(5);
169 
170 		/* wait for a connection */
171 		do
172 		{
173 			errno = 0;
174 			lotherend = sizeof otherend;
175 			t = accept(DaemonSocket, &otherend, &lotherend);
176 		} while (t < 0 && errno == EINTR);
177 		if (t < 0)
178 		{
179 			syserr("getrequests: accept");
180 			sleep(5);
181 			continue;
182 		}
183 
184 		/*
185 		**  Create a subprocess to process the mail.
186 		*/
187 
188 # ifdef DEBUG
189 		if (tTd(15, 2))
190 			printf("getrequests: forking (fd = %d)\n", t);
191 # endif DEBUG
192 
193 		pid = fork();
194 		if (pid < 0)
195 		{
196 			syserr("daemon: cannot fork");
197 			sleep(10);
198 			(void) close(t);
199 			continue;
200 		}
201 
202 		if (pid == 0)
203 		{
204 			extern struct hostent *gethostbyaddr();
205 			register struct hostent *hp;
206 			char buf[MAXNAME];
207 
208 			/*
209 			**  CHILD -- return to caller.
210 			**	Collect verified idea of sending host.
211 			**	Verify calling user id if possible here.
212 			*/
213 
214 			(void) signal(SIGCHLD, SIG_DFL);
215 
216 			/* determine host name */
217 			hp = gethostbyaddr((char *) &otherend.sin_addr, sizeof otherend.sin_addr, AF_INET);
218 			if (hp != NULL)
219 			{
220 				(void) strcpy(buf, hp->h_name);
221 				if (NetName != NULL && NetName[0] != '\0' &&
222 				    index(hp->h_name, '.') == NULL)
223 				{
224 					(void) strcat(buf, ".");
225 					(void) strcat(buf, NetName);
226 				}
227 			}
228 			else
229 			{
230 				extern char *inet_ntoa();
231 
232 				/* produce a dotted quad */
233 				(void) sprintf(buf, "[%s]",
234 					inet_ntoa(otherend.sin_addr));
235 			}
236 
237 			/* should we check for illegal connection here? XXX */
238 
239 			RealHostName = newstr(buf);
240 
241 			(void) close(DaemonSocket);
242 			InChannel = fdopen(t, "r");
243 			OutChannel = fdopen(dup(t), "w");
244 # ifdef DEBUG
245 			if (tTd(15, 2))
246 				printf("getreq: returning\n");
247 # endif DEBUG
248 # ifdef LOG
249 			if (LogLevel > 11)
250 				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
251 # endif LOG
252 			return;
253 		}
254 
255 		/* close the port so that others will hang (for a while) */
256 		(void) close(t);
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 int	h_errno;	/*this will go away when code implemented*/
298 
299 makeconnection(host, port, outfile, infile)
300 	char *host;
301 	u_short port;
302 	FILE **outfile;
303 	FILE **infile;
304 {
305 	register int i, s;
306 	register struct hostent *hp = (struct hostent *)NULL;
307 	extern char *inet_ntoa();
308 	int sav_errno;
309 
310 	/*
311 	**  Set up the address for the mailer.
312 	**	Accept "[a.b.c.d]" syntax for host name.
313 	*/
314 
315 	h_errno = 0;
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 		SendmailAddress.sin_addr.s_addr = hid;
335 	}
336 	else
337 	{
338 		hp = gethostbyname(host);
339 		if (hp == NULL)
340 		{
341 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
342 				return (EX_TEMPFAIL);
343 
344 			/*
345 			**  XXX Should look for mail forwarder record here
346 			**  XXX if (h_errno == NO_ADDRESS).
347 			*/
348 
349 			return (EX_NOHOST);
350 		}
351 		bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
352 		i = 1;
353 	}
354 
355 	/*
356 	**  Determine the port number.
357 	*/
358 
359 	if (port != 0)
360 		SendmailAddress.sin_port = htons(port);
361 	else
362 	{
363 		register struct servent *sp = getservbyname("smtp", "tcp");
364 
365 		if (sp == NULL)
366 		{
367 			syserr("makeconnection: server \"smtp\" unknown");
368 			return (EX_OSFILE);
369 		}
370 		SendmailAddress.sin_port = sp->s_port;
371 	}
372 
373 	/*
374 	**  Try to actually open the connection.
375 	*/
376 
377 again:
378 # ifdef DEBUG
379 	if (tTd(16, 1))
380 		printf("makeconnection (%s [%s])\n", host,
381 		    inet_ntoa(SendmailAddress.sin_addr.s_addr));
382 # endif DEBUG
383 
384 	s = socket(AF_INET, SOCK_STREAM, 0);
385 	if (s < 0)
386 	{
387 		syserr("makeconnection: no socket");
388 		sav_errno = errno;
389 		goto failure;
390 	}
391 
392 # ifdef DEBUG
393 	if (tTd(16, 1))
394 		printf("makeconnection: %d\n", s);
395 
396 	/* turn on network debugging? */
397 	if (tTd(16, 14))
398 	{
399 		int on = 1;
400 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
401 	}
402 # endif DEBUG
403 	(void) fflush(CurEnv->e_xfp);			/* for debugging */
404 	errno = 0;					/* for debugging */
405 	SendmailAddress.sin_family = AF_INET;
406 	if (connect(s, &SendmailAddress, sizeof SendmailAddress) < 0)
407 	{
408 		sav_errno = errno;
409 		(void) close(s);
410 		if (hp && hp->h_addr_list[i])
411 		{
412 			bcopy(hp->h_addr_list[i++],
413 			    (char *)&SendmailAddress.sin_addr, hp->h_length);
414 			goto again;
415 		}
416 
417 		/* failure, decide if temporary or not */
418 	failure:
419 		switch (sav_errno)
420 		{
421 		  case EISCONN:
422 		  case ETIMEDOUT:
423 		  case EINPROGRESS:
424 		  case EALREADY:
425 		  case EADDRINUSE:
426 		  case EHOSTDOWN:
427 		  case ENETDOWN:
428 		  case ENETRESET:
429 		  case ENOBUFS:
430 		  case ECONNREFUSED:
431 		  case ECONNRESET:
432 		  case EHOSTUNREACH:
433 		  case ENETUNREACH:
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 				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
441 			return (EX_TEMPFAIL);
442 
443 		  default:
444 			message(Arpa_Info, "%s", errstring(sav_errno));
445 			return (EX_UNAVAILABLE);
446 		}
447 	}
448 
449 	/* connection ok, put it into canonical form */
450 	*outfile = fdopen(s, "w");
451 	*infile = fdopen(s, "r");
452 
453 	return (EX_OK);
454 }
455 /*
456 **  MYHOSTNAME -- return the name of this host.
457 **
458 **	Parameters:
459 **		hostbuf -- a place to return the name of this host.
460 **		size -- the size of hostbuf.
461 **
462 **	Returns:
463 **		A list of aliases for this host.
464 **
465 **	Side Effects:
466 **		none.
467 */
468 
469 char **
470 myhostname(hostbuf, size)
471 	char hostbuf[];
472 	int size;
473 {
474 	extern struct hostent *gethostbyname();
475 	struct hostent *hp;
476 
477 	if (gethostname(hostbuf, size) < 0)
478 	{
479 		(void) strcpy(hostbuf, "localhost");
480 	}
481 	hp = gethostbyname(hostbuf);
482 	if (hp != NULL)
483 	{
484 		(void) strcpy(hostbuf, hp->h_name);
485 		return (hp->h_aliases);
486 	}
487 	else
488 		return (NULL);
489 }
490 /*
491 **  MAPHOSTNAME -- turn a hostname into canonical form
492 **
493 **	Parameters:
494 **		hbuf -- a buffer containing a hostname.
495 **		hbsize -- the size of hbuf.
496 **
497 **	Returns:
498 **		none.
499 **
500 **	Side Effects:
501 **		Looks up the host specified in hbuf.  If it is not
502 **		the canonical name for that host, replace it with
503 **		the canonical name.  If the name is unknown, or it
504 **		is already the canonical name, leave it unchanged.
505 */
506 
507 maphostname(hbuf, hbsize)
508 	char *hbuf;
509 	int hbsize;
510 {
511 	register struct hostent *hp;
512 	extern struct hostent *gethostbyname();
513 
514 	/*
515 	**  If first character is a bracket, then it is an address
516 	**  lookup.  Address is copied into a temporary buffer to
517 	**  strip the brackets and to preserve hbuf if address is
518 	**  unknown.
519 	*/
520 
521 	if (*hbuf == '[')
522 	{
523 		extern struct hostent *gethostbyaddr();
524 		u_long in_addr;
525 		char ptr[256];
526 		char *bptr;
527 
528 		(void) strcpy(ptr, hbuf);
529 		bptr = index(ptr,']');
530 		*bptr = '\0';
531 		in_addr = inet_addr(&ptr[1]);
532 		hp = gethostbyaddr((char *) &in_addr, sizeof(struct in_addr), AF_INET);
533 		if (hp == NULL)
534 			return;
535 	}
536 	else
537 	{
538 		makelower(hbuf);
539 #ifdef MXDOMAIN
540 		getcanonname(hbuf, hbsize);
541 		return;
542 #else MXDOMAIN
543 		hp = gethostbyname(hbuf);
544 #endif
545 	}
546 	if (hp != NULL)
547 	{
548 		int i = strlen(hp->h_name);
549 
550 		if (i >= hbsize)
551 			hp->h_name[hbsize - 1] = '\0';
552 		(void) strcpy(hbuf, hp->h_name);
553 	}
554 }
555 
556 # else DAEMON
557 /* code for systems without sophisticated networking */
558 
559 /*
560 **  MYHOSTNAME -- stub version for case of no daemon code.
561 **
562 **	Can't convert to upper case here because might be a UUCP name.
563 **
564 **	Mark, you can change this to be anything you want......
565 */
566 
567 char **
568 myhostname(hostbuf, size)
569 	char hostbuf[];
570 	int size;
571 {
572 	register FILE *f;
573 
574 	hostbuf[0] = '\0';
575 	f = fopen("/usr/include/whoami", "r");
576 	if (f != NULL)
577 	{
578 		(void) fgets(hostbuf, size, f);
579 		fixcrlf(hostbuf, TRUE);
580 		(void) fclose(f);
581 	}
582 	return (NULL);
583 }
584 /*
585 **  MAPHOSTNAME -- turn a hostname into canonical form
586 **
587 **	Parameters:
588 **		hbuf -- a buffer containing a hostname.
589 **		hbsize -- the size of hbuf.
590 **
591 **	Returns:
592 **		none.
593 **
594 **	Side Effects:
595 **		Looks up the host specified in hbuf.  If it is not
596 **		the canonical name for that host, replace it with
597 **		the canonical name.  If the name is unknown, or it
598 **		is already the canonical name, leave it unchanged.
599 */
600 
601 /*ARGSUSED*/
602 maphostname(hbuf, hbsize)
603 	char *hbuf;
604 	int hbsize;
605 {
606 	return;
607 }
608 
609 #endif DAEMON
610