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