xref: /original-bsd/usr.sbin/sendmail/src/daemon.c (revision f3f8e977)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  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	8.43 (Berkeley) 04/16/94 (with daemon mode)";
15 #else
16 static char sccsid[] = "@(#)daemon.c	8.43 (Berkeley) 04/16/94 (without daemon mode)";
17 #endif
18 #endif /* not lint */
19 
20 #ifdef DAEMON
21 
22 # include <netdb.h>
23 # include <arpa/inet.h>
24 
25 #if NAMED_BIND
26 # include <arpa/nameser.h>
27 # include <resolv.h>
28 #endif
29 
30 /*
31 **  DAEMON.C -- routines to use when running as a daemon.
32 **
33 **	This entire file is highly dependent on the 4.2 BSD
34 **	interprocess communication primitives.  No attempt has
35 **	been made to make this file portable to Version 7,
36 **	Version 6, MPX files, etc.  If you should try such a
37 **	thing yourself, I recommend chucking the entire file
38 **	and starting from scratch.  Basic semantics are:
39 **
40 **	getrequests()
41 **		Opens a port and initiates a connection.
42 **		Returns in a child.  Must set InChannel and
43 **		OutChannel appropriately.
44 **	clrdaemon()
45 **		Close any open files associated with getting
46 **		the connection; this is used when running the queue,
47 **		etc., to avoid having extra file descriptors during
48 **		the queue run and to avoid confusing the network
49 **		code (if it cares).
50 **	makeconnection(host, port, outfile, infile, usesecureport)
51 **		Make a connection to the named host on the given
52 **		port.  Set *outfile and *infile to the files
53 **		appropriate for communication.  Returns zero on
54 **		success, else an exit status describing the
55 **		error.
56 **	host_map_lookup(map, hbuf, avp, pstat)
57 **		Convert the entry in hbuf into a canonical form.
58 */
59 /*
60 **  GETREQUESTS -- open mail IPC port and get requests.
61 **
62 **	Parameters:
63 **		none.
64 **
65 **	Returns:
66 **		none.
67 **
68 **	Side Effects:
69 **		Waits until some interesting activity occurs.  When
70 **		it does, a child is created to process it, and the
71 **		parent waits for completion.  Return from this
72 **		routine is always in the child.  The file pointers
73 **		"InChannel" and "OutChannel" should be set to point
74 **		to the communication channel.
75 */
76 
77 int		DaemonSocket	= -1;		/* fd describing socket */
78 SOCKADDR	DaemonAddr;			/* socket for incoming */
79 int		ListenQueueSize = 10;		/* size of listen queue */
80 int		TcpRcvBufferSize = 0;		/* size of TCP receive buffer */
81 int		TcpSndBufferSize = 0;		/* size of TCP send buffer */
82 
83 getrequests()
84 {
85 	int t;
86 	int on = 1;
87 	bool refusingconnections = TRUE;
88 	FILE *pidf;
89 	int socksize;
90 #ifdef XDEBUG
91 	bool j_has_dot;
92 #endif
93 	extern void reapchild();
94 
95 	/*
96 	**  Set up the address for the mailer.
97 	*/
98 
99 	if (DaemonAddr.sin.sin_family == 0)
100 		DaemonAddr.sin.sin_family = AF_INET;
101 	if (DaemonAddr.sin.sin_addr.s_addr == 0)
102 		DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY;
103 	if (DaemonAddr.sin.sin_port == 0)
104 	{
105 		register struct servent *sp;
106 
107 		sp = getservbyname("smtp", "tcp");
108 		if (sp == NULL)
109 		{
110 			syserr("554 service \"smtp\" unknown");
111 			DaemonAddr.sin.sin_port = htons(25);
112 		}
113 		else
114 			DaemonAddr.sin.sin_port = sp->s_port;
115 	}
116 
117 	/*
118 	**  Try to actually open the connection.
119 	*/
120 
121 	if (tTd(15, 1))
122 		printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port);
123 
124 	/* get a socket for the SMTP connection */
125 	DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
126 	if (DaemonSocket < 0)
127 	{
128 		/* probably another daemon already */
129 		syserr("getrequests: can't create socket");
130 	  severe:
131 # ifdef LOG
132 		if (LogLevel > 0)
133 			syslog(LOG_ALERT, "problem creating SMTP socket");
134 # endif /* LOG */
135 		finis();
136 	}
137 
138 	/* turn on network debugging? */
139 	if (tTd(15, 101))
140 		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
141 
142 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
143 	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
144 
145 #ifdef SO_RCVBUF
146 	if (TcpRcvBufferSize > 0)
147 	{
148 		if (setsockopt(DaemonSocket, SOL_SOCKET, SO_RCVBUF,
149 			       (char *) &TcpRcvBufferSize,
150 			       sizeof(TcpRcvBufferSize)) < 0)
151 			syserr("getrequests: setsockopt(SO_RCVBUF)");
152 	}
153 #endif
154 
155 	switch (DaemonAddr.sa.sa_family)
156 	{
157 # ifdef NETINET
158 	  case AF_INET:
159 		socksize = sizeof DaemonAddr.sin;
160 		break;
161 # endif
162 
163 # ifdef NETISO
164 	  case AF_ISO:
165 		socksize = sizeof DaemonAddr.siso;
166 		break;
167 # endif
168 
169 	  default:
170 		socksize = sizeof DaemonAddr;
171 		break;
172 	}
173 
174 	if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0)
175 	{
176 		syserr("getrequests: cannot bind");
177 		(void) close(DaemonSocket);
178 		goto severe;
179 	}
180 
181 	(void) setsignal(SIGCHLD, reapchild);
182 
183 	/* write the pid to the log file for posterity */
184 	pidf = fopen(PidFile, "w");
185 	if (pidf != NULL)
186 	{
187 		extern char *CommandLineArgs;
188 
189 		/* write the process id on line 1 */
190 		fprintf(pidf, "%d\n", getpid());
191 
192 		/* line 2 contains all command line flags */
193 		fprintf(pidf, "%s\n", CommandLineArgs);
194 
195 		/* flush and close */
196 		fclose(pidf);
197 	}
198 
199 #ifdef XDEBUG
200 	{
201 		char jbuf[MAXHOSTNAMELEN];
202 
203 		expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
204 		j_has_dot = strchr(jbuf, '.') != NULL;
205 	}
206 #endif
207 
208 	if (tTd(15, 1))
209 		printf("getrequests: %d\n", DaemonSocket);
210 
211 	for (;;)
212 	{
213 		register int pid;
214 		auto int lotherend;
215 		extern bool refuseconnections();
216 
217 		/* see if we are rejecting connections */
218 		CurrentLA = getla();
219 		if (refuseconnections())
220 		{
221 			if (!refusingconnections)
222 			{
223 				/* don't queue so peer will fail quickly */
224 				(void) listen(DaemonSocket, 0);
225 				refusingconnections = TRUE;
226 			}
227 			setproctitle("rejecting connections: load average: %d",
228 				CurrentLA);
229 			sleep(5);
230 			continue;
231 		}
232 
233 		if (refusingconnections)
234 		{
235 			/* start listening again */
236 			if (listen(DaemonSocket, ListenQueueSize) < 0)
237 			{
238 				syserr("getrequests: cannot listen");
239 				(void) close(DaemonSocket);
240 				goto severe;
241 			}
242 			setproctitle("accepting connections");
243 			refusingconnections = FALSE;
244 		}
245 
246 #ifdef XDEBUG
247 		/* check for disaster */
248 		{
249 			register STAB *s;
250 			char jbuf[MAXHOSTNAMELEN];
251 
252 			expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
253 			if ((s = stab(jbuf, ST_CLASS, ST_FIND)) == NULL ||
254 			    !bitnset('w', s->s_class))
255 			{
256 				dumpstate("daemon lost $j");
257 				syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog");
258 				abort();
259 			}
260 			else if (j_has_dot && strchr(jbuf, '.') == NULL)
261 			{
262 				dumpstate("daemon $j lost dot");
263 				syslog(LOG_ALERT, "daemon process $j lost dot; see syslog");
264 				abort();
265 			}
266 		}
267 #endif
268 
269 		/* wait for a connection */
270 		do
271 		{
272 			errno = 0;
273 			lotherend = socksize;
274 			t = accept(DaemonSocket,
275 			    (struct sockaddr *)&RealHostAddr, &lotherend);
276 		} while (t < 0 && errno == EINTR);
277 		if (t < 0)
278 		{
279 			syserr("getrequests: accept");
280 			sleep(5);
281 			continue;
282 		}
283 
284 		/*
285 		**  Create a subprocess to process the mail.
286 		*/
287 
288 		if (tTd(15, 2))
289 			printf("getrequests: forking (fd = %d)\n", t);
290 
291 		pid = fork();
292 		if (pid < 0)
293 		{
294 			syserr("daemon: cannot fork");
295 			sleep(10);
296 			(void) close(t);
297 			continue;
298 		}
299 
300 		if (pid == 0)
301 		{
302 			char *p;
303 			extern char *hostnamebyanyaddr();
304 
305 			/*
306 			**  CHILD -- return to caller.
307 			**	Collect verified idea of sending host.
308 			**	Verify calling user id if possible here.
309 			*/
310 
311 			(void) setsignal(SIGCHLD, SIG_DFL);
312 			DisConnected = FALSE;
313 
314 			setproctitle("startup with %s",
315 				anynet_ntoa(&RealHostAddr));
316 
317 			/* determine host name */
318 			p = hostnamebyanyaddr(&RealHostAddr);
319 			RealHostName = newstr(p);
320 			setproctitle("startup with %s", p);
321 
322 #ifdef LOG
323 			if (LogLevel > 11)
324 			{
325 				/* log connection information */
326 				syslog(LOG_INFO, "connect from %s (%s)",
327 					RealHostName, anynet_ntoa(&RealHostAddr));
328 			}
329 #endif
330 
331 			(void) close(DaemonSocket);
332 			if ((InChannel = fdopen(t, "r")) == NULL ||
333 			    (t = dup(t)) < 0 ||
334 			    (OutChannel = fdopen(t, "w")) == NULL)
335 			{
336 				syserr("cannot open SMTP server channel, fd=%d", t);
337 				exit(0);
338 			}
339 
340 			/* should we check for illegal connection here? XXX */
341 #ifdef XLA
342 			if (!xla_host_ok(RealHostName))
343 			{
344 				message("421 Too many SMTP sessions for this host");
345 				exit(0);
346 			}
347 #endif
348 
349 			if (tTd(15, 2))
350 				printf("getreq: returning\n");
351 			return;
352 		}
353 
354 		/* close the port so that others will hang (for a while) */
355 		(void) close(t);
356 	}
357 	/*NOTREACHED*/
358 }
359 /*
360 **  CLRDAEMON -- reset the daemon connection
361 **
362 **	Parameters:
363 **		none.
364 **
365 **	Returns:
366 **		none.
367 **
368 **	Side Effects:
369 **		releases any resources used by the passive daemon.
370 */
371 
372 clrdaemon()
373 {
374 	if (DaemonSocket >= 0)
375 		(void) close(DaemonSocket);
376 	DaemonSocket = -1;
377 }
378 /*
379 **  SETDAEMONOPTIONS -- set options for running the daemon
380 **
381 **	Parameters:
382 **		p -- the options line.
383 **
384 **	Returns:
385 **		none.
386 */
387 
388 setdaemonoptions(p)
389 	register char *p;
390 {
391 	if (DaemonAddr.sa.sa_family == AF_UNSPEC)
392 		DaemonAddr.sa.sa_family = AF_INET;
393 
394 	while (p != NULL)
395 	{
396 		register char *f;
397 		register char *v;
398 
399 		while (isascii(*p) && isspace(*p))
400 			p++;
401 		if (*p == '\0')
402 			break;
403 		f = p;
404 		p = strchr(p, ',');
405 		if (p != NULL)
406 			*p++ = '\0';
407 		v = strchr(f, '=');
408 		if (v == NULL)
409 			continue;
410 		while (isascii(*++v) && isspace(*v))
411 			continue;
412 
413 		switch (*f)
414 		{
415 		  case 'F':		/* address family */
416 			if (isascii(*v) && isdigit(*v))
417 				DaemonAddr.sa.sa_family = atoi(v);
418 #ifdef NETINET
419 			else if (strcasecmp(v, "inet") == 0)
420 				DaemonAddr.sa.sa_family = AF_INET;
421 #endif
422 #ifdef NETISO
423 			else if (strcasecmp(v, "iso") == 0)
424 				DaemonAddr.sa.sa_family = AF_ISO;
425 #endif
426 #ifdef NETNS
427 			else if (strcasecmp(v, "ns") == 0)
428 				DaemonAddr.sa.sa_family = AF_NS;
429 #endif
430 #ifdef NETX25
431 			else if (strcasecmp(v, "x.25") == 0)
432 				DaemonAddr.sa.sa_family = AF_CCITT;
433 #endif
434 			else
435 				syserr("554 Unknown address family %s in Family=option", v);
436 			break;
437 
438 		  case 'A':		/* address */
439 			switch (DaemonAddr.sa.sa_family)
440 			{
441 #ifdef NETINET
442 			  case AF_INET:
443 				if (isascii(*v) && isdigit(*v))
444 					DaemonAddr.sin.sin_addr.s_addr = inet_network(v);
445 				else
446 				{
447 					register struct netent *np;
448 
449 					np = getnetbyname(v);
450 					if (np == NULL)
451 						syserr("554 network \"%s\" unknown", v);
452 					else
453 						DaemonAddr.sin.sin_addr.s_addr = np->n_net;
454 				}
455 				break;
456 #endif
457 
458 			  default:
459 				syserr("554 Address= option unsupported for family %d",
460 					DaemonAddr.sa.sa_family);
461 				break;
462 			}
463 			break;
464 
465 		  case 'P':		/* port */
466 			switch (DaemonAddr.sa.sa_family)
467 			{
468 				short port;
469 
470 #ifdef NETINET
471 			  case AF_INET:
472 				if (isascii(*v) && isdigit(*v))
473 					DaemonAddr.sin.sin_port = htons(atoi(v));
474 				else
475 				{
476 					register struct servent *sp;
477 
478 					sp = getservbyname(v, "tcp");
479 					if (sp == NULL)
480 						syserr("554 service \"%s\" unknown", v);
481 					else
482 						DaemonAddr.sin.sin_port = sp->s_port;
483 				}
484 				break;
485 #endif
486 
487 #ifdef NETISO
488 			  case AF_ISO:
489 				/* assume two byte transport selector */
490 				if (isascii(*v) && isdigit(*v))
491 					port = htons(atoi(v));
492 				else
493 				{
494 					register struct servent *sp;
495 
496 					sp = getservbyname(v, "tcp");
497 					if (sp == NULL)
498 						syserr("554 service \"%s\" unknown", v);
499 					else
500 						port = sp->s_port;
501 				}
502 				bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2);
503 				break;
504 #endif
505 
506 			  default:
507 				syserr("554 Port= option unsupported for family %d",
508 					DaemonAddr.sa.sa_family);
509 				break;
510 			}
511 			break;
512 
513 		  case 'L':		/* listen queue size */
514 			ListenQueueSize = atoi(v);
515 			break;
516 
517 		  case 'S':		/* send buffer size */
518 			TcpSndBufferSize = atoi(v);
519 			break;
520 
521 		  case 'R':		/* receive buffer size */
522 			TcpRcvBufferSize = atoi(v);
523 			break;
524 		}
525 	}
526 }
527 /*
528 **  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
529 **
530 **	Parameters:
531 **		host -- the name of the host.
532 **		port -- the port number to connect to.
533 **		mci -- a pointer to the mail connection information
534 **			structure to be filled in.
535 **		usesecureport -- if set, use a low numbered (reserved)
536 **			port to provide some rudimentary authentication.
537 **
538 **	Returns:
539 **		An exit code telling whether the connection could be
540 **			made and if not why not.
541 **
542 **	Side Effects:
543 **		none.
544 */
545 
546 SOCKADDR	CurHostAddr;		/* address of current host */
547 
548 int
549 makeconnection(host, port, mci, usesecureport)
550 	char *host;
551 	u_short port;
552 	register MCI *mci;
553 	bool usesecureport;
554 {
555 	register int i, s;
556 	register struct hostent *hp = (struct hostent *)NULL;
557 	SOCKADDR addr;
558 	int sav_errno;
559 	int addrlen;
560 #if NAMED_BIND
561 	extern int h_errno;
562 #endif
563 
564 	/*
565 	**  Set up the address for the mailer.
566 	**	Accept "[a.b.c.d]" syntax for host name.
567 	*/
568 
569 #if NAMED_BIND
570 	h_errno = 0;
571 #endif
572 	errno = 0;
573 	bzero(&CurHostAddr, sizeof CurHostAddr);
574 	SmtpPhase = mci->mci_phase = "initial connection";
575 	CurHostName = host;
576 
577 	if (host[0] == '[')
578 	{
579 		long hid;
580 		register char *p = strchr(host, ']');
581 
582 		if (p != NULL)
583 		{
584 			*p = '\0';
585 #ifdef NETINET
586 			hid = inet_addr(&host[1]);
587 			if (hid == -1)
588 #endif
589 			{
590 				/* try it as a host name (avoid MX lookup) */
591 				hp = gethostbyname(&host[1]);
592 				if (hp == NULL && p[-1] == '.')
593 				{
594 					p[-1] = '\0';
595 					hp = gethostbyname(&host[1]);
596 					p[-1] = '.';
597 				}
598 				*p = ']';
599 				goto gothostent;
600 			}
601 			*p = ']';
602 		}
603 		if (p == NULL)
604 		{
605 			usrerr("553 Invalid numeric domain spec \"%s\"", host);
606 			return (EX_NOHOST);
607 		}
608 #ifdef NETINET
609 		addr.sin.sin_family = AF_INET;		/*XXX*/
610 		addr.sin.sin_addr.s_addr = hid;
611 #endif
612 	}
613 	else
614 	{
615 		register char *p = &host[strlen(host) - 1];
616 
617 		hp = gethostbyname(host);
618 		if (hp == NULL && *p == '.')
619 		{
620 			*p = '\0';
621 			hp = gethostbyname(host);
622 			*p = '.';
623 		}
624 gothostent:
625 		if (hp == NULL)
626 		{
627 #if NAMED_BIND
628 			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
629 				return (EX_TEMPFAIL);
630 
631 			/* if name server is specified, assume temp fail */
632 			if (errno == ECONNREFUSED && UseNameServer)
633 				return (EX_TEMPFAIL);
634 #endif
635 			return (EX_NOHOST);
636 		}
637 		addr.sa.sa_family = hp->h_addrtype;
638 		switch (hp->h_addrtype)
639 		{
640 #ifdef NETINET
641 		  case AF_INET:
642 			bcopy(hp->h_addr,
643 				&addr.sin.sin_addr,
644 				sizeof addr.sin.sin_addr);
645 			break;
646 #endif
647 
648 		  default:
649 			bcopy(hp->h_addr,
650 				addr.sa.sa_data,
651 				hp->h_length);
652 			break;
653 		}
654 		i = 1;
655 	}
656 
657 	/*
658 	**  Determine the port number.
659 	*/
660 
661 	if (port != 0)
662 		port = htons(port);
663 	else
664 	{
665 		register struct servent *sp = getservbyname("smtp", "tcp");
666 
667 		if (sp == NULL)
668 		{
669 			syserr("554 makeconnection: service \"smtp\" unknown");
670 			port = htons(25);
671 		}
672 		else
673 			port = sp->s_port;
674 	}
675 
676 	switch (addr.sa.sa_family)
677 	{
678 #ifdef NETINET
679 	  case AF_INET:
680 		addr.sin.sin_port = port;
681 		addrlen = sizeof (struct sockaddr_in);
682 		break;
683 #endif
684 
685 #ifdef NETISO
686 	  case AF_ISO:
687 		/* assume two byte transport selector */
688 		bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2);
689 		addrlen = sizeof (struct sockaddr_iso);
690 		break;
691 #endif
692 
693 	  default:
694 		syserr("Can't connect to address family %d", addr.sa.sa_family);
695 		return (EX_NOHOST);
696 	}
697 
698 	/*
699 	**  Try to actually open the connection.
700 	*/
701 
702 #ifdef XLA
703 	/* if too many connections, don't bother trying */
704 	if (!xla_noqueue_ok(host))
705 		return EX_TEMPFAIL;
706 #endif
707 
708 	for (;;)
709 	{
710 		if (tTd(16, 1))
711 			printf("makeconnection (%s [%s])\n",
712 				host, anynet_ntoa(&addr));
713 
714 		/* save for logging */
715 		CurHostAddr = addr;
716 
717 		if (usesecureport)
718 		{
719 			int rport = IPPORT_RESERVED - 1;
720 
721 			s = rresvport(&rport);
722 		}
723 		else
724 		{
725 			s = socket(AF_INET, SOCK_STREAM, 0);
726 		}
727 		if (s < 0)
728 		{
729 			sav_errno = errno;
730 			syserr("makeconnection: no socket");
731 			goto failure;
732 		}
733 
734 #ifdef SO_SNDBUF
735 		if (TcpSndBufferSize > 0)
736 		{
737 			if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
738 				       (char *) &TcpSndBufferSize,
739 				       sizeof(TcpSndBufferSize)) < 0)
740 				syserr("makeconnection: setsockopt(SO_SNDBUF)");
741 		}
742 #endif
743 
744 		if (tTd(16, 1))
745 			printf("makeconnection: fd=%d\n", s);
746 
747 		/* turn on network debugging? */
748 		if (tTd(16, 101))
749 		{
750 			int on = 1;
751 			(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG,
752 					  (char *)&on, sizeof on);
753 		}
754 		if (CurEnv->e_xfp != NULL)
755 			(void) fflush(CurEnv->e_xfp);		/* for debugging */
756 		errno = 0;					/* for debugging */
757 		if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0)
758 			break;
759 
760 		/* couldn't connect.... figure out why */
761 		sav_errno = errno;
762 		(void) close(s);
763 		if (hp && hp->h_addr_list[i])
764 		{
765 			if (tTd(16, 1))
766 				printf("Connect failed (%s); trying new address....\n",
767 					errstring(sav_errno));
768 			switch (addr.sa.sa_family)
769 			{
770 #ifdef NETINET
771 			  case AF_INET:
772 				bcopy(hp->h_addr_list[i++],
773 				      &addr.sin.sin_addr,
774 				      sizeof addr.sin.sin_addr);
775 				break;
776 #endif
777 
778 			  default:
779 				bcopy(hp->h_addr_list[i++],
780 					addr.sa.sa_data,
781 					hp->h_length);
782 				break;
783 			}
784 			continue;
785 		}
786 
787 		/* failure, decide if temporary or not */
788 	failure:
789 #ifdef XLA
790 		xla_host_end(host);
791 #endif
792 		if (transienterror(sav_errno))
793 			return EX_TEMPFAIL;
794 		else
795 		{
796 			message("%s", errstring(sav_errno));
797 			return (EX_UNAVAILABLE);
798 		}
799 	}
800 
801 	/* connection ok, put it into canonical form */
802 	if ((mci->mci_out = fdopen(s, "w")) == NULL ||
803 	    (s = dup(s)) < 0 ||
804 	    (mci->mci_in = fdopen(s, "r")) == NULL)
805 	{
806 		syserr("cannot open SMTP client channel, fd=%d", s);
807 		return EX_TEMPFAIL;
808 	}
809 
810 	return (EX_OK);
811 }
812 /*
813 **  MYHOSTNAME -- return the name of this host.
814 **
815 **	Parameters:
816 **		hostbuf -- a place to return the name of this host.
817 **		size -- the size of hostbuf.
818 **
819 **	Returns:
820 **		A list of aliases for this host.
821 **
822 **	Side Effects:
823 **		Adds numeric codes to $=w.
824 */
825 
826 char **
827 myhostname(hostbuf, size)
828 	char hostbuf[];
829 	int size;
830 {
831 	register struct hostent *hp;
832 	extern struct hostent *gethostbyname();
833 
834 	if (gethostname(hostbuf, size) < 0)
835 	{
836 		(void) strcpy(hostbuf, "localhost");
837 	}
838 	hp = gethostbyname(hostbuf);
839 	if (hp != NULL)
840 	{
841 		(void) strncpy(hostbuf, hp->h_name, size - 1);
842 		hostbuf[size - 1] = '\0';
843 #ifdef NAMED_BIND
844 		/* if still no dot, try DNS directly (i.e., avoid NIS) */
845 		if (strchr(hostbuf, '.') == NULL)
846 		{
847 			extern bool getcanonname();
848 
849 			(void) getcanonname(hostbuf, size, TRUE);
850 		}
851 #endif
852 
853 		if (hp->h_addrtype == AF_INET && hp->h_length == 4)
854 		{
855 			register int i;
856 
857 			for (i = 0; hp->h_addr_list[i] != NULL; i++)
858 			{
859 				char ipbuf[100];
860 
861 				sprintf(ipbuf, "[%s]",
862 					inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
863 				setclass('w', ipbuf);
864 			}
865 		}
866 
867 		return (hp->h_aliases);
868 	}
869 	else
870 		return (NULL);
871 }
872 /*
873 **  GETAUTHINFO -- get the real host name asociated with a file descriptor
874 **
875 **	Uses RFC1413 protocol to try to get info from the other end.
876 **
877 **	Parameters:
878 **		fd -- the descriptor
879 **
880 **	Returns:
881 **		The user@host information associated with this descriptor.
882 */
883 
884 #if IDENTPROTO
885 
886 static jmp_buf	CtxAuthTimeout;
887 
888 static
889 authtimeout()
890 {
891 	longjmp(CtxAuthTimeout, 1);
892 }
893 
894 #endif
895 
896 char *
897 getauthinfo(fd)
898 	int fd;
899 {
900 	int falen;
901 	register char *p;
902 #if IDENTPROTO
903 	SOCKADDR la;
904 	int lalen;
905 	register struct servent *sp;
906 	int s;
907 	int i;
908 	EVENT *ev;
909 #endif
910 	static char hbuf[MAXNAME * 2 + 2];
911 	extern char *hostnamebyanyaddr();
912 	extern char RealUserName[];			/* main.c */
913 
914 	falen = sizeof RealHostAddr;
915 	if (getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 ||
916 	    RealHostAddr.sa.sa_family == 0)
917 	{
918 		(void) sprintf(hbuf, "%s@localhost", RealUserName);
919 		if (tTd(9, 1))
920 			printf("getauthinfo: %s\n", hbuf);
921 		return hbuf;
922 	}
923 
924 	if (RealHostName == NULL)
925 	{
926 		/* translate that to a host name */
927 		RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
928 	}
929 
930 #if IDENTPROTO
931 	if (TimeOuts.to_ident == 0)
932 		goto noident;
933 
934 	lalen = sizeof la;
935 	if (RealHostAddr.sa.sa_family != AF_INET ||
936 	    getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 ||
937 	    la.sa.sa_family != AF_INET)
938 	{
939 		/* no ident info */
940 		goto noident;
941 	}
942 
943 	/* create ident query */
944 	(void) sprintf(hbuf, "%d,%d\r\n",
945 		ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port));
946 
947 	/* create local address */
948 	la.sin.sin_port = 0;
949 
950 	/* create foreign address */
951 	sp = getservbyname("auth", "tcp");
952 	if (sp != NULL)
953 		RealHostAddr.sin.sin_port = sp->s_port;
954 	else
955 		RealHostAddr.sin.sin_port = htons(113);
956 
957 	s = -1;
958 	if (setjmp(CtxAuthTimeout) != 0)
959 	{
960 		if (s >= 0)
961 			(void) close(s);
962 		goto noident;
963 	}
964 
965 	/* put a timeout around the whole thing */
966 	ev = setevent(TimeOuts.to_ident, authtimeout, 0);
967 
968 	/* connect to foreign IDENT server using same address as SMTP socket */
969 	s = socket(AF_INET, SOCK_STREAM, 0);
970 	if (s < 0)
971 	{
972 		clrevent(ev);
973 		goto noident;
974 	}
975 	if (bind(s, &la.sa, sizeof la.sin) < 0 ||
976 	    connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0)
977 	{
978 		goto closeident;
979 	}
980 
981 	if (tTd(9, 10))
982 		printf("getauthinfo: sent %s", hbuf);
983 
984 	/* send query */
985 	if (write(s, hbuf, strlen(hbuf)) < 0)
986 		goto closeident;
987 
988 	/* get result */
989 	i = read(s, hbuf, sizeof hbuf);
990 	(void) close(s);
991 	clrevent(ev);
992 	if (i <= 0)
993 		goto noident;
994 	if (hbuf[--i] == '\n' && hbuf[--i] == '\r')
995 		i--;
996 	hbuf[++i] = '\0';
997 
998 	if (tTd(9, 3))
999 		printf("getauthinfo:  got %s\n", hbuf);
1000 
1001 	/* parse result */
1002 	p = strchr(hbuf, ':');
1003 	if (p == NULL)
1004 	{
1005 		/* malformed response */
1006 		goto noident;
1007 	}
1008 	while (isascii(*++p) && isspace(*p))
1009 		continue;
1010 	if (strncasecmp(p, "userid", 6) != 0)
1011 	{
1012 		/* presumably an error string */
1013 		goto noident;
1014 	}
1015 	p += 6;
1016 	while (isascii(*p) && isspace(*p))
1017 		p++;
1018 	if (*p++ != ':')
1019 	{
1020 		/* either useridxx or malformed response */
1021 		goto noident;
1022 	}
1023 
1024 	/* p now points to the OSTYPE field */
1025 	p = strchr(p, ':');
1026 	if (p == NULL)
1027 	{
1028 		/* malformed response */
1029 		goto noident;
1030 	}
1031 
1032 	/* 1413 says don't do this -- but it's broken otherwise */
1033 	while (isascii(*++p) && isspace(*p))
1034 		continue;
1035 
1036 	/* p now points to the authenticated name */
1037 	(void) sprintf(hbuf, "%s@%s",
1038 		p, RealHostName == NULL ? "localhost" : RealHostName);
1039 	goto finish;
1040 
1041 closeident:
1042 	(void) close(s);
1043 	clrevent(ev);
1044 
1045 #endif /* IDENTPROTO */
1046 
1047 noident:
1048 	if (RealHostName == NULL)
1049 	{
1050 		if (tTd(9, 1))
1051 			printf("getauthinfo: NULL\n");
1052 		return NULL;
1053 	}
1054 	(void) strcpy(hbuf, RealHostName);
1055 
1056 finish:
1057 	if (RealHostName != NULL && RealHostName[0] != '[')
1058 	{
1059 		p = &hbuf[strlen(hbuf)];
1060 		(void) sprintf(p, " [%s]", anynet_ntoa(&RealHostAddr));
1061 	}
1062 	if (tTd(9, 1))
1063 		printf("getauthinfo: %s\n", hbuf);
1064 	return hbuf;
1065 }
1066 /*
1067 **  HOST_MAP_LOOKUP -- turn a hostname into canonical form
1068 **
1069 **	Parameters:
1070 **		map -- a pointer to this map (unused).
1071 **		name -- the (presumably unqualified) hostname.
1072 **		av -- unused -- for compatibility with other mapping
1073 **			functions.
1074 **		statp -- an exit status (out parameter) -- set to
1075 **			EX_TEMPFAIL if the name server is unavailable.
1076 **
1077 **	Returns:
1078 **		The mapping, if found.
1079 **		NULL if no mapping found.
1080 **
1081 **	Side Effects:
1082 **		Looks up the host specified in hbuf.  If it is not
1083 **		the canonical name for that host, return the canonical
1084 **		name.
1085 */
1086 
1087 char *
1088 host_map_lookup(map, name, av, statp)
1089 	MAP *map;
1090 	char *name;
1091 	char **av;
1092 	int *statp;
1093 {
1094 	register struct hostent *hp;
1095 	u_long in_addr;
1096 	char *cp;
1097 	int i;
1098 	register STAB *s;
1099 	char hbuf[MAXNAME];
1100 	extern struct hostent *gethostbyaddr();
1101 #if NAMED_BIND
1102 	extern int h_errno;
1103 #endif
1104 
1105 	/*
1106 	**  See if we have already looked up this name.  If so, just
1107 	**  return it.
1108 	*/
1109 
1110 	s = stab(name, ST_NAMECANON, ST_ENTER);
1111 	if (bitset(NCF_VALID, s->s_namecanon.nc_flags))
1112 	{
1113 		if (tTd(9, 1))
1114 			printf("host_map_lookup(%s) => CACHE %s\n",
1115 				name, s->s_namecanon.nc_cname);
1116 		errno = s->s_namecanon.nc_errno;
1117 #if NAMED_BIND
1118 		h_errno = s->s_namecanon.nc_herrno;
1119 #endif
1120 		*statp = s->s_namecanon.nc_stat;
1121 		if (CurEnv->e_message == NULL && *statp == EX_TEMPFAIL)
1122 		{
1123 			sprintf(hbuf, "%s: Name server timeout",
1124 				shortenstring(name, 33));
1125 			CurEnv->e_message = newstr(hbuf);
1126 		}
1127 		return s->s_namecanon.nc_cname;
1128 	}
1129 
1130 	/*
1131 	**  If first character is a bracket, then it is an address
1132 	**  lookup.  Address is copied into a temporary buffer to
1133 	**  strip the brackets and to preserve name if address is
1134 	**  unknown.
1135 	*/
1136 
1137 	if (*name != '[')
1138 	{
1139 		extern bool getcanonname();
1140 
1141 		if (tTd(9, 1))
1142 			printf("host_map_lookup(%s) => ", name);
1143 		s->s_namecanon.nc_flags |= NCF_VALID;		/* will be soon */
1144 		(void) strcpy(hbuf, name);
1145 		if (getcanonname(hbuf, sizeof hbuf - 1, TRUE))
1146 		{
1147 			if (tTd(9, 1))
1148 				printf("%s\n", hbuf);
1149 			cp = map_rewrite(map, hbuf, strlen(hbuf), av);
1150 			s->s_namecanon.nc_cname = newstr(cp);
1151 			return cp;
1152 		}
1153 		else
1154 		{
1155 			register struct hostent *hp;
1156 
1157 			s->s_namecanon.nc_errno = errno;
1158 #if NAMED_BIND
1159 			s->s_namecanon.nc_herrno = h_errno;
1160 			if (tTd(9, 1))
1161 				printf("FAIL (%d)\n", h_errno);
1162 			switch (h_errno)
1163 			{
1164 			  case TRY_AGAIN:
1165 				if (UseNameServer)
1166 				{
1167 					sprintf(hbuf, "%s: Name server timeout",
1168 						shortenstring(name, 33));
1169 					message("%s", hbuf);
1170 					if (CurEnv->e_message == NULL)
1171 						CurEnv->e_message = newstr(hbuf);
1172 				}
1173 				*statp = EX_TEMPFAIL;
1174 				break;
1175 
1176 			  case HOST_NOT_FOUND:
1177 				*statp = EX_NOHOST;
1178 				break;
1179 
1180 			  case NO_RECOVERY:
1181 				*statp = EX_SOFTWARE;
1182 				break;
1183 
1184 			  default:
1185 				*statp = EX_UNAVAILABLE;
1186 				break;
1187 			}
1188 #else
1189 			if (tTd(9, 1))
1190 				printf("FAIL\n");
1191 			*statp = EX_NOHOST;
1192 #endif
1193 			s->s_namecanon.nc_stat = *statp;
1194 			if (*statp != EX_TEMPFAIL || UseNameServer)
1195 				return NULL;
1196 
1197 			/*
1198 			**  Try to look it up in /etc/hosts
1199 			*/
1200 
1201 			hp = gethostbyname(name);
1202 			if (hp == NULL)
1203 			{
1204 				/* no dice there either */
1205 				s->s_namecanon.nc_stat = *statp = EX_NOHOST;
1206 				return NULL;
1207 			}
1208 
1209 			s->s_namecanon.nc_stat = *statp = EX_OK;
1210 			cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
1211 			s->s_namecanon.nc_cname = newstr(cp);
1212 			return cp;
1213 		}
1214 	}
1215 	if ((cp = strchr(name, ']')) == NULL)
1216 		return (NULL);
1217 	*cp = '\0';
1218 	in_addr = inet_addr(&name[1]);
1219 
1220 	/* nope -- ask the name server */
1221 	hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
1222 	s->s_namecanon.nc_errno = errno;
1223 #if NAMED_BIND
1224 	s->s_namecanon.nc_herrno = h_errno;
1225 #endif
1226 	s->s_namecanon.nc_flags |= NCF_VALID;		/* will be soon */
1227 	if (hp == NULL)
1228 	{
1229 		s->s_namecanon.nc_stat = *statp = EX_NOHOST;
1230 		return (NULL);
1231 	}
1232 
1233 	/* found a match -- copy out */
1234 	cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
1235 	s->s_namecanon.nc_stat = *statp = EX_OK;
1236 	s->s_namecanon.nc_cname = newstr(cp);
1237 	return cp;
1238 }
1239 /*
1240 **  ANYNET_NTOA -- convert a network address to printable form.
1241 **
1242 **	Parameters:
1243 **		sap -- a pointer to a sockaddr structure.
1244 **
1245 **	Returns:
1246 **		A printable version of that sockaddr.
1247 */
1248 
1249 char *
1250 anynet_ntoa(sap)
1251 	register SOCKADDR *sap;
1252 {
1253 	register char *bp;
1254 	register char *ap;
1255 	int l;
1256 	static char buf[100];
1257 
1258 	/* check for null/zero family */
1259 	if (sap == NULL)
1260 		return "NULLADDR";
1261 	if (sap->sa.sa_family == 0)
1262 		return "0";
1263 
1264 	switch (sap->sa.sa_family)
1265 	{
1266 #ifdef MAYBENEXTRELEASE		/*** UNTESTED *** UNTESTED *** UNTESTED ***/
1267 #ifdef NETUNIX
1268 	  case AF_UNIX:
1269 	  	if (sap->sunix.sun_path[0] != '\0')
1270 	  		sprintf(buf, "[UNIX: %.64s]", sap->sunix.sun_path);
1271 	  	else
1272 	  		sprintf(buf, "[UNIX: localhost]");
1273 		return buf;
1274 #endif
1275 #endif
1276 
1277 #ifdef NETINET
1278 	  case AF_INET:
1279 		return inet_ntoa(((struct sockaddr_in *) sap)->sin_addr);
1280 #endif
1281 
1282 	  default:
1283 	  	/* this case is only to ensure syntactic correctness */
1284 	  	break;
1285 	}
1286 
1287 	/* unknown family -- just dump bytes */
1288 	(void) sprintf(buf, "Family %d: ", sap->sa.sa_family);
1289 	bp = &buf[strlen(buf)];
1290 	ap = sap->sa.sa_data;
1291 	for (l = sizeof sap->sa.sa_data; --l >= 0; )
1292 	{
1293 		(void) sprintf(bp, "%02x:", *ap++ & 0377);
1294 		bp += 3;
1295 	}
1296 	*--bp = '\0';
1297 	return buf;
1298 }
1299 /*
1300 **  HOSTNAMEBYANYADDR -- return name of host based on address
1301 **
1302 **	Parameters:
1303 **		sap -- SOCKADDR pointer
1304 **
1305 **	Returns:
1306 **		text representation of host name.
1307 **
1308 **	Side Effects:
1309 **		none.
1310 */
1311 
1312 char *
1313 hostnamebyanyaddr(sap)
1314 	register SOCKADDR *sap;
1315 {
1316 	register struct hostent *hp;
1317 	int saveretry;
1318 
1319 #if NAMED_BIND
1320 	/* shorten name server timeout to avoid higher level timeouts */
1321 	saveretry = _res.retry;
1322 	_res.retry = 3;
1323 #endif /* NAMED_BIND */
1324 
1325 	switch (sap->sa.sa_family)
1326 	{
1327 #ifdef NETINET
1328 	  case AF_INET:
1329 		hp = gethostbyaddr((char *) &sap->sin.sin_addr,
1330 			sizeof sap->sin.sin_addr,
1331 			AF_INET);
1332 		break;
1333 #endif
1334 
1335 #ifdef NETISO
1336 	  case AF_ISO:
1337 		hp = gethostbyaddr((char *) &sap->siso.siso_addr,
1338 			sizeof sap->siso.siso_addr,
1339 			AF_ISO);
1340 		break;
1341 #endif
1342 
1343 #ifdef MAYBENEXTRELEASE		/*** UNTESTED *** UNTESTED *** UNTESTED ***/
1344 	  case AF_UNIX:
1345 		hp = NULL;
1346 		break;
1347 #endif
1348 
1349 	  default:
1350 		hp = gethostbyaddr(sap->sa.sa_data,
1351 			   sizeof sap->sa.sa_data,
1352 			   sap->sa.sa_family);
1353 		break;
1354 	}
1355 
1356 #if NAMED_BIND
1357 	_res.retry = saveretry;
1358 #endif /* NAMED_BIND */
1359 
1360 	if (hp != NULL)
1361 		return hp->h_name;
1362 	else
1363 	{
1364 		/* produce a dotted quad */
1365 		static char buf[512];
1366 
1367 		(void) sprintf(buf, "[%s]", anynet_ntoa(sap));
1368 		return buf;
1369 	}
1370 }
1371 
1372 # else /* DAEMON */
1373 /* code for systems without sophisticated networking */
1374 
1375 /*
1376 **  MYHOSTNAME -- stub version for case of no daemon code.
1377 **
1378 **	Can't convert to upper case here because might be a UUCP name.
1379 **
1380 **	Mark, you can change this to be anything you want......
1381 */
1382 
1383 char **
1384 myhostname(hostbuf, size)
1385 	char hostbuf[];
1386 	int size;
1387 {
1388 	register FILE *f;
1389 
1390 	hostbuf[0] = '\0';
1391 	f = fopen("/usr/include/whoami", "r");
1392 	if (f != NULL)
1393 	{
1394 		(void) fgets(hostbuf, size, f);
1395 		fixcrlf(hostbuf, TRUE);
1396 		(void) fclose(f);
1397 	}
1398 	return (NULL);
1399 }
1400 /*
1401 **  GETAUTHINFO -- get the real host name asociated with a file descriptor
1402 **
1403 **	Parameters:
1404 **		fd -- the descriptor
1405 **
1406 **	Returns:
1407 **		The host name associated with this descriptor, if it can
1408 **			be determined.
1409 **		NULL otherwise.
1410 **
1411 **	Side Effects:
1412 **		none
1413 */
1414 
1415 char *
1416 getauthinfo(fd)
1417 	int fd;
1418 {
1419 	return NULL;
1420 }
1421 /*
1422 **  MAPHOSTNAME -- turn a hostname into canonical form
1423 **
1424 **	Parameters:
1425 **		map -- a pointer to the database map.
1426 **		name -- a buffer containing a hostname.
1427 **		avp -- a pointer to a (cf file defined) argument vector.
1428 **		statp -- an exit status (out parameter).
1429 **
1430 **	Returns:
1431 **		mapped host name
1432 **		FALSE otherwise.
1433 **
1434 **	Side Effects:
1435 **		Looks up the host specified in name.  If it is not
1436 **		the canonical name for that host, replace it with
1437 **		the canonical name.  If the name is unknown, or it
1438 **		is already the canonical name, leave it unchanged.
1439 */
1440 
1441 /*ARGSUSED*/
1442 char *
1443 host_map_lookup(map, name, avp, statp)
1444 	MAP *map;
1445 	char *name;
1446 	char **avp;
1447 	char *statp;
1448 {
1449 	register struct hostent *hp;
1450 
1451 	hp = gethostbyname(name);
1452 	if (hp != NULL)
1453 		return hp->h_name;
1454 	*statp = EX_NOHOST;
1455 	return NULL;
1456 }
1457 
1458 #endif /* DAEMON */
1459