138032Speter/*
2261370Sgshapiro * Copyright (c) 1998-2007, 2009, 2010 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
15168515Sgshapiro#include "map.h"
1638032Speter
17266711SgshapiroSM_RCSID("@(#)$Id: daemon.c,v 8.698 2013-11-22 20:51:55 ca Exp $")
1864562Sgshapiro
1938032Speter#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
2038032Speter# define USE_SOCK_STREAM	1
2164562Sgshapiro#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
2238032Speter
2390792Sgshapiro#if defined(USE_SOCK_STREAM)
2464562Sgshapiro# if NETINET || NETINET6
2564562Sgshapiro#  include <arpa/inet.h>
2664562Sgshapiro# endif /* NETINET || NETINET6 */
2738032Speter# if NAMED_BIND
2838032Speter#  ifndef NO_DATA
2938032Speter#   define NO_DATA	NO_ADDRESS
3064562Sgshapiro#  endif /* ! NO_DATA */
3164562Sgshapiro# endif /* NAMED_BIND */
3290792Sgshapiro#endif /* defined(USE_SOCK_STREAM) */
3338032Speter
3490792Sgshapiro#if STARTTLS
3590792Sgshapiro#  include <openssl/rand.h>
3690792Sgshapiro#endif /* STARTTLS */
3738032Speter
38157001Sgshapiro#include <sm/time.h>
3966494Sgshapiro
4090792Sgshapiro#if IP_SRCROUTE && NETINET
4190792Sgshapiro# include <netinet/in_systm.h>
4290792Sgshapiro# include <netinet/ip.h>
4390792Sgshapiro# if HAS_IN_H
4490792Sgshapiro#  include <netinet/in.h>
4590792Sgshapiro#  ifndef IPOPTION
4690792Sgshapiro#   define IPOPTION	ip_opts
4790792Sgshapiro#   define IP_LIST	ip_opts
4890792Sgshapiro#   define IP_DST	ip_dst
4990792Sgshapiro#  endif /* ! IPOPTION */
5090792Sgshapiro# else /* HAS_IN_H */
5190792Sgshapiro#  include <netinet/ip_var.h>
5290792Sgshapiro#  ifndef IPOPTION
5390792Sgshapiro#   define IPOPTION	ipoption
5490792Sgshapiro#   define IP_LIST	ipopt_list
5590792Sgshapiro#   define IP_DST	ipopt_dst
5690792Sgshapiro#  endif /* ! IPOPTION */
5790792Sgshapiro# endif /* HAS_IN_H */
5890792Sgshapiro#endif /* IP_SRCROUTE && NETINET */
5938032Speter
6090792Sgshapiro#include <sm/fdset.h>
6138032Speter
62168515Sgshapiro#define DAEMON_C 1
63168515Sgshapiro#include <daemon.h>
6464562Sgshapiro
65141858Sgshapirostatic void		connecttimeout __P((int));
6690792Sgshapirostatic int		opendaemonsocket __P((DAEMON_T *, bool));
6790792Sgshapirostatic unsigned short	setupdaemon __P((SOCKADDR *));
6890792Sgshapirostatic void		getrequests_checkdiskspace __P((ENVELOPE *e));
69141858Sgshapirostatic void		setsockaddroptions __P((char *, DAEMON_T *));
70141858Sgshapirostatic void		printdaemonflags __P((DAEMON_T *));
71141858Sgshapirostatic int		addr_family __P((char *));
72141858Sgshapirostatic int		addrcmp __P((struct hostent *, char *, SOCKADDR *));
73141858Sgshapirostatic void		authtimeout __P((int));
7464562Sgshapiro
7538032Speter/*
7638032Speter**  DAEMON.C -- routines to use when running as a daemon.
7738032Speter**
7838032Speter**	This entire file is highly dependent on the 4.2 BSD
7938032Speter**	interprocess communication primitives.  No attempt has
8038032Speter**	been made to make this file portable to Version 7,
8138032Speter**	Version 6, MPX files, etc.  If you should try such a
8238032Speter**	thing yourself, I recommend chucking the entire file
8338032Speter**	and starting from scratch.  Basic semantics are:
8438032Speter**
8538032Speter**	getrequests(e)
8638032Speter**		Opens a port and initiates a connection.
8738032Speter**		Returns in a child.  Must set InChannel and
8838032Speter**		OutChannel appropriately.
8938032Speter**	clrdaemon()
9038032Speter**		Close any open files associated with getting
9138032Speter**		the connection; this is used when running the queue,
9238032Speter**		etc., to avoid having extra file descriptors during
9338032Speter**		the queue run and to avoid confusing the network
9438032Speter**		code (if it cares).
9590792Sgshapiro**	makeconnection(host, port, mci, e, enough)
9638032Speter**		Make a connection to the named host on the given
9790792Sgshapiro**		port. Returns zero on success, else an exit status
9890792Sgshapiro**		describing the error.
9938032Speter**	host_map_lookup(map, hbuf, avp, pstat)
10038032Speter**		Convert the entry in hbuf into a canonical form.
10138032Speter*/
10264562Sgshapiro
10390792Sgshapirostatic int	NDaemons = 0;			/* actual number of daemons */
10464562Sgshapiro
10590792Sgshapirostatic time_t	NextDiskSpaceCheck = 0;
10664562Sgshapiro
10790792Sgshapiro/*
10838032Speter**  GETREQUESTS -- open mail IPC port and get requests.
10938032Speter**
11038032Speter**	Parameters:
11138032Speter**		e -- the current envelope.
11238032Speter**
11338032Speter**	Returns:
11464562Sgshapiro**		pointer to flags.
11538032Speter**
11638032Speter**	Side Effects:
11738032Speter**		Waits until some interesting activity occurs.  When
11838032Speter**		it does, a child is created to process it, and the
11938032Speter**		parent waits for completion.  Return from this
12038032Speter**		routine is always in the child.  The file pointers
12138032Speter**		"InChannel" and "OutChannel" should be set to point
12238032Speter**		to the communication channel.
12390792Sgshapiro**		May restart persistent queue runners if they have ended
12490792Sgshapiro**		for some reason.
12538032Speter*/
12638032Speter
12764562SgshapiroBITMAP256 *
12838032Spetergetrequests(e)
12938032Speter	ENVELOPE *e;
13038032Speter{
13138032Speter	int t;
13264562Sgshapiro	int idx, curdaemon = -1;
13364562Sgshapiro	int i, olddaemon = 0;
13490792Sgshapiro#if XDEBUG
13538032Speter	bool j_has_dot;
13690792Sgshapiro#endif /* XDEBUG */
13742575Speter	char status[MAXLINE];
13864562Sgshapiro	SOCKADDR sa;
139168515Sgshapiro	SOCKADDR_LEN_T len = sizeof(sa);
14094334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
14194334Sgshapiro	time_t lastrun;
14294334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
14364562Sgshapiro# if NETUNIX
14442575Speter	extern int ControlSocket;
14564562Sgshapiro# endif /* NETUNIX */
14664562Sgshapiro	extern ENVELOPE BlankEnvelope;
14738032Speter
14838032Speter
149125820Sgshapiro	/* initialize data for function that generates queue ids */
150125820Sgshapiro	init_qid_alg();
15190792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
15238032Speter	{
15364562Sgshapiro		Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
15490792Sgshapiro		Daemons[idx].d_firsttime = true;
15564562Sgshapiro		Daemons[idx].d_refuse_connections_until = (time_t) 0;
15638032Speter	}
15771345Sgshapiro
15838032Speter	/*
15938032Speter	**  Try to actually open the connection.
16038032Speter	*/
16138032Speter
16238032Speter	if (tTd(15, 1))
16364562Sgshapiro	{
16490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
16571345Sgshapiro		{
16690792Sgshapiro			sm_dprintf("getrequests: daemon %s: port %d\n",
16790792Sgshapiro				   Daemons[idx].d_name,
16890792Sgshapiro				   ntohs(Daemons[idx].d_port));
16971345Sgshapiro		}
17064562Sgshapiro	}
17138032Speter
17238032Speter	/* get a socket for the SMTP connection */
17390792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
17490792Sgshapiro		Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
17538032Speter
17642575Speter	if (opencontrolsocket() < 0)
17742575Speter		sm_syslog(LOG_WARNING, NOQID,
17843730Speter			  "daemon could not open control socket %s: %s",
17990792Sgshapiro			  ControlSocketName, sm_errstring(errno));
18042575Speter
18190792Sgshapiro	/* If there are any queue runners released reapchild() co-ord's */
18290792Sgshapiro	(void) sm_signal(SIGCHLD, reapchild);
18338032Speter
18490792Sgshapiro	/* write the pid to file, command line args to syslog */
18564562Sgshapiro	log_sendmail_pid(e);
18638032Speter
18790792Sgshapiro#if XDEBUG
18838032Speter	{
18938032Speter		char jbuf[MAXHOSTNAMELEN];
19038032Speter
191168515Sgshapiro		expand("\201j", jbuf, sizeof(jbuf), e);
19238032Speter		j_has_dot = strchr(jbuf, '.') != NULL;
19338032Speter	}
19490792Sgshapiro#endif /* XDEBUG */
19538032Speter
19642575Speter	/* Add parent process as first item */
197132943Sgshapiro	proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1, NULL);
19842575Speter
19938032Speter	if (tTd(15, 1))
20064562Sgshapiro	{
20190792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
202203004Sgshapiro			sm_dprintf("getrequests: daemon %s: socket %d\n",
20364562Sgshapiro				Daemons[idx].d_name,
20464562Sgshapiro				Daemons[idx].d_socket);
20564562Sgshapiro	}
20638032Speter
20738032Speter	for (;;)
20838032Speter	{
20938032Speter		register pid_t pid;
21038032Speter		auto SOCKADDR_LEN_T lotherend;
21190792Sgshapiro		bool timedout = false;
21290792Sgshapiro		bool control = false;
21364562Sgshapiro		int save_errno;
21438032Speter		int pipefd[2];
21590792Sgshapiro		time_t now;
21690792Sgshapiro#if STARTTLS
21766494Sgshapiro		long seed;
21890792Sgshapiro#endif /* STARTTLS */
21938032Speter
22038032Speter		/* see if we are rejecting connections */
22190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
222120256Sgshapiro		CHECK_RESTART;
22364562Sgshapiro
22490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
22571345Sgshapiro		{
22690792Sgshapiro			/*
22790792Sgshapiro			**  XXX do this call outside the loop?
22890792Sgshapiro			**	no: refuse_connections may sleep().
22990792Sgshapiro			*/
23071345Sgshapiro
23190792Sgshapiro			now = curtime();
23290792Sgshapiro			if (now < Daemons[idx].d_refuse_connections_until)
23364562Sgshapiro				continue;
23490792Sgshapiro			if (bitnset(D_DISABLE, Daemons[idx].d_flags))
23590792Sgshapiro				continue;
236168515Sgshapiro			if (refuseconnections(e, idx, curdaemon == idx))
23738032Speter			{
23864562Sgshapiro				if (Daemons[idx].d_socket >= 0)
23942575Speter				{
24071345Sgshapiro					/* close socket so peer fails quickly */
24171345Sgshapiro					(void) close(Daemons[idx].d_socket);
24271345Sgshapiro					Daemons[idx].d_socket = -1;
24342575Speter				}
24442575Speter
24542575Speter				/* refuse connections for next 15 seconds */
24690792Sgshapiro				Daemons[idx].d_refuse_connections_until = now + 15;
24738032Speter			}
24864562Sgshapiro			else if (Daemons[idx].d_socket < 0 ||
24964562Sgshapiro				 Daemons[idx].d_firsttime)
25042575Speter			{
25190792Sgshapiro				if (!Daemons[idx].d_firsttime && LogLevel > 8)
25271345Sgshapiro					sm_syslog(LOG_INFO, NOQID,
25371345Sgshapiro						"accepting connections again for daemon %s",
25471345Sgshapiro						Daemons[idx].d_name);
25564562Sgshapiro
25671345Sgshapiro				/* arrange to (re)open the socket if needed */
25790792Sgshapiro				(void) opendaemonsocket(&Daemons[idx], false);
25890792Sgshapiro				Daemons[idx].d_firsttime = false;
25942575Speter			}
26038032Speter		}
26138032Speter
26277349Sgshapiro		/* May have been sleeping above, check again */
263120256Sgshapiro		CHECK_RESTART;
264132943Sgshapiro
26590792Sgshapiro		getrequests_checkdiskspace(e);
26671345Sgshapiro
26790792Sgshapiro#if XDEBUG
26838032Speter		/* check for disaster */
26938032Speter		{
27038032Speter			char jbuf[MAXHOSTNAMELEN];
27138032Speter
272168515Sgshapiro			expand("\201j", jbuf, sizeof(jbuf), e);
27338032Speter			if (!wordinclass(jbuf, 'w'))
27438032Speter			{
27538032Speter				dumpstate("daemon lost $j");
27638032Speter				sm_syslog(LOG_ALERT, NOQID,
27764562Sgshapiro					  "daemon process doesn't have $j in $=w; see syslog");
27838032Speter				abort();
27938032Speter			}
28038032Speter			else if (j_has_dot && strchr(jbuf, '.') == NULL)
28138032Speter			{
28238032Speter				dumpstate("daemon $j lost dot");
28338032Speter				sm_syslog(LOG_ALERT, NOQID,
28464562Sgshapiro					  "daemon process $j lost dot; see syslog");
28538032Speter				abort();
28638032Speter			}
28738032Speter		}
28890792Sgshapiro#endif /* XDEBUG */
28938032Speter
29090792Sgshapiro#if 0
29138032Speter		/*
29238032Speter		**  Andrew Sun <asun@ieps-sun.ml.com> claims that this will
29338032Speter		**  fix the SVr4 problem.  But it seems to have gone away,
29438032Speter		**  so is it worth doing this?
29538032Speter		*/
29638032Speter
29742575Speter		if (DaemonSocket >= 0 &&
29890792Sgshapiro		    SetNonBlocking(DaemonSocket, false) < 0)
29938032Speter			log an error here;
30090792Sgshapiro#endif /* 0 */
30190792Sgshapiro		(void) sm_releasesignal(SIGALRM);
30264562Sgshapiro
30338032Speter		for (;;)
30438032Speter		{
30590792Sgshapiro			bool setproc = false;
30642575Speter			int highest = -1;
30738032Speter			fd_set readfds;
30838032Speter			struct timeval timeout;
30938032Speter
310120256Sgshapiro			CHECK_RESTART;
31138032Speter			FD_ZERO(&readfds);
31290792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
31342575Speter			{
31464562Sgshapiro				/* wait for a connection */
31564562Sgshapiro				if (Daemons[idx].d_socket >= 0)
31664562Sgshapiro				{
31771345Sgshapiro					if (!setproc &&
31871345Sgshapiro					    !bitnset(D_ETRNONLY,
31971345Sgshapiro						     Daemons[idx].d_flags))
32064562Sgshapiro					{
32190792Sgshapiro						sm_setproctitle(true, e,
32264562Sgshapiro								"accepting connections");
32390792Sgshapiro						setproc = true;
32464562Sgshapiro					}
32564562Sgshapiro					if (Daemons[idx].d_socket > highest)
32664562Sgshapiro						highest = Daemons[idx].d_socket;
32790792Sgshapiro					SM_FD_SET(Daemons[idx].d_socket,
32890792Sgshapiro						  &readfds);
32964562Sgshapiro				}
33042575Speter			}
33164562Sgshapiro
33290792Sgshapiro#if NETUNIX
33342575Speter			if (ControlSocket >= 0)
33442575Speter			{
33542575Speter				if (ControlSocket > highest)
33642575Speter					highest = ControlSocket;
33790792Sgshapiro				SM_FD_SET(ControlSocket, &readfds);
33842575Speter			}
33990792Sgshapiro#endif /* NETUNIX */
34064562Sgshapiro
34177349Sgshapiro			timeout.tv_sec = 5;
34238032Speter			timeout.tv_usec = 0;
34338032Speter
34442575Speter			t = select(highest + 1, FDSET_CAST &readfds,
34564562Sgshapiro				   NULL, NULL, &timeout);
34642575Speter
34777349Sgshapiro			/* Did someone signal while waiting? */
348120256Sgshapiro			CHECK_RESTART;
34971345Sgshapiro
35090792Sgshapiro			curdaemon = -1;
35190792Sgshapiro			if (doqueuerun())
35294334Sgshapiro			{
35390792Sgshapiro				(void) runqueue(true, false, false, false);
35494334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
35594334Sgshapiro				lastrun = now;
35694334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
35794334Sgshapiro			}
35894334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
359157001Sgshapiro			else if (CheckQueueRunners > 0 && QueueIntvl > 0 &&
360157001Sgshapiro				 lastrun + QueueIntvl + CheckQueueRunners < now)
36194334Sgshapiro			{
36271345Sgshapiro
36394334Sgshapiro				/*
36494334Sgshapiro				**  set lastrun unconditionally to avoid
36594334Sgshapiro				**  calling checkqueuerunner() all the time.
36694334Sgshapiro				**  That's also why we currently ignore the
36794334Sgshapiro				**  result of the function call.
36894334Sgshapiro				*/
36994334Sgshapiro
37094334Sgshapiro				(void) checkqueuerunner();
37194334Sgshapiro				lastrun = now;
37294334Sgshapiro			}
37394334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
37494334Sgshapiro
37542575Speter			if (t <= 0)
37642575Speter			{
37790792Sgshapiro				timedout = true;
37842575Speter				break;
37942575Speter			}
38038032Speter
38190792Sgshapiro			control = false;
38238032Speter			errno = 0;
38364562Sgshapiro
38464562Sgshapiro			/* look "round-robin" for an active socket */
38590792Sgshapiro			if ((idx = olddaemon + 1) >= NDaemons)
38664562Sgshapiro				idx = 0;
38790792Sgshapiro			for (i = 0; i < NDaemons; i++)
38842575Speter			{
38964562Sgshapiro				if (Daemons[idx].d_socket >= 0 &&
39090792Sgshapiro				    SM_FD_ISSET(Daemons[idx].d_socket,
39190792Sgshapiro						&readfds))
39264562Sgshapiro				{
39364562Sgshapiro					lotherend = Daemons[idx].d_socksize;
39473188Sgshapiro					memset(&RealHostAddr, '\0',
395168515Sgshapiro					       sizeof(RealHostAddr));
39664562Sgshapiro					t = accept(Daemons[idx].d_socket,
39764562Sgshapiro						   (struct sockaddr *)&RealHostAddr,
39864562Sgshapiro						   &lotherend);
39973188Sgshapiro
40073188Sgshapiro					/*
40173188Sgshapiro					**  If remote side closes before
40273188Sgshapiro					**  accept() finishes, sockaddr
40373188Sgshapiro					**  might not be fully filled in.
40473188Sgshapiro					*/
40573188Sgshapiro
40673188Sgshapiro					if (t >= 0 &&
40773188Sgshapiro					    (lotherend == 0 ||
40873188Sgshapiro# ifdef BSD4_4_SOCKADDR
40973188Sgshapiro					     RealHostAddr.sa.sa_len == 0 ||
41073188Sgshapiro# endif /* BSD4_4_SOCKADDR */
41173188Sgshapiro					     RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
41273188Sgshapiro					{
41373188Sgshapiro						(void) close(t);
41473188Sgshapiro						t = -1;
41573188Sgshapiro						errno = EINVAL;
41673188Sgshapiro					}
41764562Sgshapiro					olddaemon = curdaemon = idx;
41864562Sgshapiro					break;
41964562Sgshapiro				}
42090792Sgshapiro				if (++idx >= NDaemons)
42164562Sgshapiro					idx = 0;
42242575Speter			}
42390792Sgshapiro#if NETUNIX
42464562Sgshapiro			if (curdaemon == -1 && ControlSocket >= 0 &&
42590792Sgshapiro			    SM_FD_ISSET(ControlSocket, &readfds))
42642575Speter			{
42742575Speter				struct sockaddr_un sa_un;
42842575Speter
429168515Sgshapiro				lotherend = sizeof(sa_un);
430168515Sgshapiro				memset(&sa_un, '\0', sizeof(sa_un));
43142575Speter				t = accept(ControlSocket,
43242575Speter					   (struct sockaddr *)&sa_un,
43342575Speter					   &lotherend);
43473188Sgshapiro
43573188Sgshapiro				/*
43673188Sgshapiro				**  If remote side closes before
43773188Sgshapiro				**  accept() finishes, sockaddr
43873188Sgshapiro				**  might not be fully filled in.
43973188Sgshapiro				*/
44073188Sgshapiro
44173188Sgshapiro				if (t >= 0 &&
44273188Sgshapiro				    (lotherend == 0 ||
44373188Sgshapiro# ifdef BSD4_4_SOCKADDR
44473188Sgshapiro				     sa_un.sun_len == 0 ||
44573188Sgshapiro# endif /* BSD4_4_SOCKADDR */
44673188Sgshapiro				     sa_un.sun_family != AF_UNIX))
44773188Sgshapiro				{
44873188Sgshapiro					(void) close(t);
44973188Sgshapiro					t = -1;
45073188Sgshapiro					errno = EINVAL;
45173188Sgshapiro				}
45273188Sgshapiro				if (t >= 0)
45390792Sgshapiro					control = true;
45442575Speter			}
45590792Sgshapiro#else /* NETUNIX */
45671345Sgshapiro			if (curdaemon == -1)
45771345Sgshapiro			{
45871345Sgshapiro				/* No daemon to service */
45971345Sgshapiro				continue;
46071345Sgshapiro			}
46190792Sgshapiro#endif /* NETUNIX */
46238032Speter			if (t >= 0 || errno != EINTR)
46338032Speter				break;
46438032Speter		}
46542575Speter		if (timedout)
46642575Speter		{
46790792Sgshapiro			timedout = false;
46842575Speter			continue;
46942575Speter		}
47064562Sgshapiro		save_errno = errno;
47190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
47238032Speter		if (t < 0)
47338032Speter		{
47464562Sgshapiro			errno = save_errno;
475132943Sgshapiro
476132943Sgshapiro			/* let's ignore these temporary errors */
477132943Sgshapiro			if (save_errno == EINTR
478132943Sgshapiro#ifdef EAGAIN
479132943Sgshapiro			    || save_errno == EAGAIN
480132943Sgshapiro#endif /* EAGAIN */
481132943Sgshapiro#ifdef ECONNABORTED
482132943Sgshapiro			    || save_errno == ECONNABORTED
483132943Sgshapiro#endif /* ECONNABORTED */
484132943Sgshapiro#ifdef EWOULDBLOCK
485132943Sgshapiro			    || save_errno == EWOULDBLOCK
486132943Sgshapiro#endif /* EWOULDBLOCK */
487132943Sgshapiro			   )
488132943Sgshapiro				continue;
489132943Sgshapiro
49038032Speter			syserr("getrequests: accept");
49138032Speter
492159609Sgshapiro			if (curdaemon >= 0)
493159609Sgshapiro			{
494159609Sgshapiro				/* arrange to re-open socket next time around */
495159609Sgshapiro				(void) close(Daemons[curdaemon].d_socket);
496159609Sgshapiro				Daemons[curdaemon].d_socket = -1;
49790792Sgshapiro#if SO_REUSEADDR_IS_BROKEN
498159609Sgshapiro				/*
499159609Sgshapiro				**  Give time for bound socket to be released.
500159609Sgshapiro				**  This creates a denial-of-service if you can
501159609Sgshapiro				**  force accept() to fail on affected systems.
502159609Sgshapiro				*/
50364562Sgshapiro
504159609Sgshapiro				Daemons[curdaemon].d_refuse_connections_until =
505159609Sgshapiro					curtime() + 15;
50690792Sgshapiro#endif /* SO_REUSEADDR_IS_BROKEN */
507159609Sgshapiro			}
50838032Speter			continue;
50938032Speter		}
51038032Speter
51164562Sgshapiro		if (!control)
51264562Sgshapiro		{
51364562Sgshapiro			/* set some daemon related macros */
51464562Sgshapiro			switch (Daemons[curdaemon].d_addr.sa.sa_family)
51564562Sgshapiro			{
51664562Sgshapiro			  case AF_UNSPEC:
51790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
51890792Sgshapiro					macid("{daemon_family}"), "unspec");
51964562Sgshapiro				break;
52090792Sgshapiro#if _FFR_DAEMON_NETUNIX
52190792Sgshapiro# if NETUNIX
52290792Sgshapiro			  case AF_UNIX:
52390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
52490792Sgshapiro					macid("{daemon_family}"), "local");
52590792Sgshapiro				break;
52690792Sgshapiro# endif /* NETUNIX */
52790792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
52890792Sgshapiro#if NETINET
52964562Sgshapiro			  case AF_INET:
53090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
53190792Sgshapiro					macid("{daemon_family}"), "inet");
53264562Sgshapiro				break;
53390792Sgshapiro#endif /* NETINET */
53490792Sgshapiro#if NETINET6
53564562Sgshapiro			  case AF_INET6:
53690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
53790792Sgshapiro					macid("{daemon_family}"), "inet6");
53864562Sgshapiro				break;
53990792Sgshapiro#endif /* NETINET6 */
54090792Sgshapiro#if NETISO
54164562Sgshapiro			  case AF_ISO:
54290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54390792Sgshapiro					macid("{daemon_family}"), "iso");
54464562Sgshapiro				break;
54590792Sgshapiro#endif /* NETISO */
54690792Sgshapiro#if NETNS
54764562Sgshapiro			  case AF_NS:
54890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54990792Sgshapiro					macid("{daemon_family}"), "ns");
55064562Sgshapiro				break;
55190792Sgshapiro#endif /* NETNS */
55290792Sgshapiro#if NETX25
55364562Sgshapiro			  case AF_CCITT:
55490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
55590792Sgshapiro					macid("{daemon_family}"), "x.25");
55664562Sgshapiro				break;
55790792Sgshapiro#endif /* NETX25 */
55864562Sgshapiro			}
55990792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
56090792Sgshapiro				macid("{daemon_name}"),
56190792Sgshapiro				Daemons[curdaemon].d_name);
56264562Sgshapiro			if (Daemons[curdaemon].d_mflags != NULL)
56390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56490792Sgshapiro					macid("{daemon_flags}"),
56590792Sgshapiro					Daemons[curdaemon].d_mflags);
56664562Sgshapiro			else
56790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56890792Sgshapiro					macid("{daemon_flags}"), "");
56964562Sgshapiro		}
57064562Sgshapiro
57138032Speter		/*
572132943Sgshapiro		**  If connection rate is exceeded here, connection shall be
573132943Sgshapiro		**  refused later by a new call after fork() by the
574132943Sgshapiro		**  validate_connection() function. Closing the connection
575132943Sgshapiro		**  at this point violates RFC 2821.
576132943Sgshapiro		**  Do NOT remove this call, its side effects are needed.
577132943Sgshapiro		*/
578132943Sgshapiro
579132943Sgshapiro		connection_rate_check(&RealHostAddr, NULL);
580132943Sgshapiro
581132943Sgshapiro		/*
58238032Speter		**  Create a subprocess to process the mail.
58338032Speter		*/
58438032Speter
58538032Speter		if (tTd(15, 2))
58690792Sgshapiro			sm_dprintf("getrequests: forking (fd = %d)\n", t);
58738032Speter
58838032Speter		/*
58990792Sgshapiro		**  Advance state of PRNG.
59090792Sgshapiro		**  This is necessary because otherwise all child processes
59164562Sgshapiro		**  will produce the same PRN sequence and hence the selection
59264562Sgshapiro		**  of a queue directory (and other things, e.g., MX selection)
59364562Sgshapiro		**  are not "really" random.
59464562Sgshapiro		*/
59590792Sgshapiro#if STARTTLS
59690792Sgshapiro		/* XXX get some better "random" data? */
59766494Sgshapiro		seed = get_random();
59890792Sgshapiro		RAND_seed((void *) &NextDiskSpaceCheck,
599168515Sgshapiro			  sizeof(NextDiskSpaceCheck));
600168515Sgshapiro		RAND_seed((void *) &now, sizeof(now));
601168515Sgshapiro		RAND_seed((void *) &seed, sizeof(seed));
60290792Sgshapiro#else /* STARTTLS */
60364562Sgshapiro		(void) get_random();
60490792Sgshapiro#endif /* STARTTLS */
60564562Sgshapiro
60690792Sgshapiro#if NAMED_BIND
60764562Sgshapiro		/*
608132943Sgshapiro		**  Update MX records for FallbackMX.
60990792Sgshapiro		**  Let's hope this is fast otherwise we screw up the
61090792Sgshapiro		**  response time.
61190792Sgshapiro		*/
61290792Sgshapiro
613132943Sgshapiro		if (FallbackMX != NULL)
614132943Sgshapiro			(void) getfallbackmxrr(FallbackMX);
61590792Sgshapiro#endif /* NAMED_BIND */
61690792Sgshapiro
617110560Sgshapiro		if (tTd(93, 100))
618110560Sgshapiro		{
619110560Sgshapiro			/* don't fork, handle connection in this process */
620110560Sgshapiro			pid = 0;
62138032Speter			pipefd[0] = pipefd[1] = -1;
622110560Sgshapiro		}
623110560Sgshapiro		else
624110560Sgshapiro		{
625110560Sgshapiro			/*
626110560Sgshapiro			**  Create a pipe to keep the child from writing to
627110560Sgshapiro			**  the socket until after the parent has closed
628110560Sgshapiro			**  it.  Otherwise the parent may hang if the child
629110560Sgshapiro			**  has closed it first.
630110560Sgshapiro			*/
63138032Speter
632110560Sgshapiro			if (pipe(pipefd) < 0)
633110560Sgshapiro				pipefd[0] = pipefd[1] = -1;
634110560Sgshapiro
635110560Sgshapiro			(void) sm_blocksignal(SIGCHLD);
636110560Sgshapiro			pid = fork();
637110560Sgshapiro			if (pid < 0)
63838032Speter			{
639110560Sgshapiro				syserr("daemon: cannot fork");
640110560Sgshapiro				if (pipefd[0] != -1)
641110560Sgshapiro				{
642110560Sgshapiro					(void) close(pipefd[0]);
643110560Sgshapiro					(void) close(pipefd[1]);
644110560Sgshapiro				}
645110560Sgshapiro				(void) sm_releasesignal(SIGCHLD);
646110560Sgshapiro				(void) sleep(10);
647110560Sgshapiro				(void) close(t);
648110560Sgshapiro				continue;
64938032Speter			}
65038032Speter		}
65190792Sgshapiro
65238032Speter		if (pid == 0)
65338032Speter		{
65438032Speter			char *p;
65590792Sgshapiro			SM_FILE_T *inchannel, *outchannel = NULL;
65638032Speter
65738032Speter			/*
65838032Speter			**  CHILD -- return to caller.
65938032Speter			**	Collect verified idea of sending host.
66038032Speter			**	Verify calling user id if possible here.
66138032Speter			*/
66238032Speter
66377349Sgshapiro			/* Reset global flags */
66477349Sgshapiro			RestartRequest = NULL;
66590792Sgshapiro			RestartWorkGroup = false;
66677349Sgshapiro			ShutdownRequest = NULL;
66777349Sgshapiro			PendingSignal = 0;
66890792Sgshapiro			CurrentPid = getpid();
669132943Sgshapiro			close_sendmail_pid();
67077349Sgshapiro
67190792Sgshapiro			(void) sm_releasesignal(SIGALRM);
67290792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
67390792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
67490792Sgshapiro			(void) sm_signal(SIGHUP, SIG_DFL);
67590792Sgshapiro			(void) sm_signal(SIGTERM, intsig);
67677349Sgshapiro
67790792Sgshapiro			/* turn on profiling */
67890792Sgshapiro			/* SM_PROF(0); */
67990792Sgshapiro
68090792Sgshapiro			/*
68190792Sgshapiro			**  Initialize exception stack and default exception
68290792Sgshapiro			**  handler for child process.
68390792Sgshapiro			*/
68490792Sgshapiro
68590792Sgshapiro			sm_exc_newthread(fatal_error);
68690792Sgshapiro
68764562Sgshapiro			if (!control)
68864562Sgshapiro			{
68990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
69090792Sgshapiro					macid("{daemon_addr}"),
69190792Sgshapiro					anynet_ntoa(&Daemons[curdaemon].d_addr));
692168515Sgshapiro				(void) sm_snprintf(status, sizeof(status), "%d",
69364562Sgshapiro						ntohs(Daemons[curdaemon].d_port));
69490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
69590792Sgshapiro					macid("{daemon_port}"), status);
69664562Sgshapiro			}
69764562Sgshapiro
69890792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
69964562Sgshapiro			{
70064562Sgshapiro				if (Daemons[idx].d_socket >= 0)
70164562Sgshapiro					(void) close(Daemons[idx].d_socket);
70280785Sgshapiro				Daemons[idx].d_socket = -1;
70364562Sgshapiro			}
70442575Speter			clrcontrol();
70538032Speter
70664562Sgshapiro			/* Avoid SMTP daemon actions if control command */
70764562Sgshapiro			if (control)
70864562Sgshapiro			{
70964562Sgshapiro				/* Add control socket process */
71090792Sgshapiro				proc_list_add(CurrentPid,
71190792Sgshapiro					      "console socket child",
712132943Sgshapiro					      PROC_CONTROL_CHILD, 0, -1, NULL);
71364562Sgshapiro			}
71464562Sgshapiro			else
71564562Sgshapiro			{
71664562Sgshapiro				proc_list_clear();
71742575Speter
71890792Sgshapiro				/* clean up background delivery children */
71990792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
72090792Sgshapiro
72164562Sgshapiro				/* Add parent process as first child item */
72290792Sgshapiro				proc_list_add(CurrentPid, "daemon child",
723132943Sgshapiro					      PROC_DAEMON_CHILD, 0, -1, NULL);
72464562Sgshapiro				/* don't schedule queue runs if ETRN */
72564562Sgshapiro				QueueIntvl = 0;
726168515Sgshapiro
727168515Sgshapiro				/*
728168515Sgshapiro				**  Hack: override global variables if
729168515Sgshapiro				**	the corresponding DaemonPortOption
730168515Sgshapiro				**	is set.
731168515Sgshapiro				*/
732147078Sgshapiro#if _FFR_SS_PER_DAEMON
733147078Sgshapiro				if (Daemons[curdaemon].d_supersafe !=
734168515Sgshapiro				    DPO_NOTSET)
735168515Sgshapiro					SuperSafe = Daemons[curdaemon].
736168515Sgshapiro								d_supersafe;
737147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
738147078Sgshapiro				if (Daemons[curdaemon].d_dm != DM_NOTSET)
739147078Sgshapiro					set_delivery_mode(
740147078Sgshapiro						Daemons[curdaemon].d_dm, e);
74138032Speter
742168515Sgshapiro				if (Daemons[curdaemon].d_refuseLA !=
743168515Sgshapiro				    DPO_NOTSET)
744168515Sgshapiro					RefuseLA = Daemons[curdaemon].
745168515Sgshapiro								d_refuseLA;
746168515Sgshapiro				if (Daemons[curdaemon].d_queueLA != DPO_NOTSET)
747168515Sgshapiro					QueueLA = Daemons[curdaemon].d_queueLA;
748168515Sgshapiro				if (Daemons[curdaemon].d_delayLA != DPO_NOTSET)
749168515Sgshapiro					DelayLA = Daemons[curdaemon].d_delayLA;
750168515Sgshapiro				if (Daemons[curdaemon].d_maxchildren !=
751168515Sgshapiro				    DPO_NOTSET)
752168515Sgshapiro					MaxChildren = Daemons[curdaemon].
753168515Sgshapiro								d_maxchildren;
754168515Sgshapiro
75590792Sgshapiro				sm_setproctitle(true, e, "startup with %s",
75664562Sgshapiro						anynet_ntoa(&RealHostAddr));
75764562Sgshapiro			}
75864562Sgshapiro
75938032Speter			if (pipefd[0] != -1)
76038032Speter			{
76138032Speter				auto char c;
76238032Speter
76338032Speter				/*
76438032Speter				**  Wait for the parent to close the write end
76538032Speter				**  of the pipe, which we will see as an EOF.
76638032Speter				**  This guarantees that we won't write to the
76738032Speter				**  socket until after the parent has closed
76838032Speter				**  the pipe.
76938032Speter				*/
77038032Speter
77138032Speter				/* close the write end of the pipe */
77238032Speter				(void) close(pipefd[1]);
77338032Speter
77438032Speter				/* we shouldn't be interrupted, but ... */
77538032Speter				while (read(pipefd[0], &c, 1) < 0 &&
77638032Speter				       errno == EINTR)
77738032Speter					continue;
77838032Speter				(void) close(pipefd[0]);
77938032Speter			}
78038032Speter
78164562Sgshapiro			/* control socket processing */
78264562Sgshapiro			if (control)
78364562Sgshapiro			{
78464562Sgshapiro				control_command(t, e);
78564562Sgshapiro				/* NOTREACHED */
78664562Sgshapiro				exit(EX_SOFTWARE);
78764562Sgshapiro			}
78864562Sgshapiro
78938032Speter			/* determine host name */
79038032Speter			p = hostnamebyanyaddr(&RealHostAddr);
79190792Sgshapiro			if (strlen(p) > MAXNAME) /* XXX  - 1 ? */
79238032Speter				p[MAXNAME] = '\0';
79338032Speter			RealHostName = newstr(p);
79464562Sgshapiro			if (RealHostName[0] == '[')
79564562Sgshapiro			{
79690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
79790792Sgshapiro					macid("{client_resolve}"),
79890792Sgshapiro					h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
79964562Sgshapiro			}
80064562Sgshapiro			else
801132943Sgshapiro			{
80290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
803132943Sgshapiro					  macid("{client_resolve}"), "OK");
804132943Sgshapiro			}
80590792Sgshapiro			sm_setproctitle(true, e, "startup with %s", p);
80694334Sgshapiro			markstats(e, NULL, STATS_CONNECT);
80738032Speter
80890792Sgshapiro			if ((inchannel = sm_io_open(SmFtStdiofd,
80990792Sgshapiro						    SM_TIME_DEFAULT,
81090792Sgshapiro						    (void *) &t,
811132943Sgshapiro						    SM_IO_RDONLY_B,
81290792Sgshapiro						    NULL)) == NULL ||
81338032Speter			    (t = dup(t)) < 0 ||
81490792Sgshapiro			    (outchannel = sm_io_open(SmFtStdiofd,
81590792Sgshapiro						     SM_TIME_DEFAULT,
81690792Sgshapiro						     (void *) &t,
817132943Sgshapiro						     SM_IO_WRONLY_B,
81890792Sgshapiro						     NULL)) == NULL)
81938032Speter			{
82090792Sgshapiro				syserr("cannot open SMTP server channel, fd=%d",
82190792Sgshapiro					t);
82290792Sgshapiro				finis(false, true, EX_OK);
82338032Speter			}
82490792Sgshapiro			sm_io_automode(inchannel, outchannel);
82538032Speter
82638032Speter			InChannel = inchannel;
82738032Speter			OutChannel = outchannel;
82890792Sgshapiro			DisConnected = false;
82938032Speter
830244928Sgshapiro
83190792Sgshapiro#if XLA
83238032Speter			if (!xla_host_ok(RealHostName))
83338032Speter			{
83464562Sgshapiro				message("421 4.4.5 Too many SMTP sessions for this host");
83590792Sgshapiro				finis(false, true, EX_OK);
83638032Speter			}
83790792Sgshapiro#endif /* XLA */
83864562Sgshapiro			/* find out name for interface of connection */
83990792Sgshapiro			if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
84090792Sgshapiro						      NULL), &sa.sa, &len) == 0)
84164562Sgshapiro			{
84264562Sgshapiro				p = hostnamebyanyaddr(&sa);
84364562Sgshapiro				if (tTd(15, 9))
84490792Sgshapiro					sm_dprintf("getreq: got name %s\n", p);
84590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
84690792Sgshapiro					macid("{if_name}"), p);
84764562Sgshapiro
84890792Sgshapiro				/*
84990792Sgshapiro				**  Do this only if it is not the loopback
85090792Sgshapiro				**  interface.
85190792Sgshapiro				*/
85290792Sgshapiro
85364562Sgshapiro				if (!isloopback(sa))
85464562Sgshapiro				{
85590792Sgshapiro					char *addr;
85690792Sgshapiro					char family[5];
85790792Sgshapiro
85890792Sgshapiro					addr = anynet_ntoa(&sa);
85990792Sgshapiro					(void) sm_snprintf(family,
86090792Sgshapiro						sizeof(family),
86190792Sgshapiro						"%d", sa.sa.sa_family);
86290792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
86390792Sgshapiro						A_TEMP,
86490792Sgshapiro						macid("{if_addr}"), addr);
86590792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
86690792Sgshapiro						A_TEMP,
86790792Sgshapiro						macid("{if_family}"), family);
86864562Sgshapiro					if (tTd(15, 7))
86990792Sgshapiro						sm_dprintf("getreq: got addr %s and family %s\n",
87090792Sgshapiro							addr, family);
87164562Sgshapiro				}
87264562Sgshapiro				else
87364562Sgshapiro				{
87490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
87590792Sgshapiro						A_PERM,
87690792Sgshapiro						macid("{if_addr}"), NULL);
87790792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
87890792Sgshapiro						A_PERM,
87990792Sgshapiro						macid("{if_family}"), NULL);
88064562Sgshapiro				}
88164562Sgshapiro			}
88264562Sgshapiro			else
88364562Sgshapiro			{
88464562Sgshapiro				if (tTd(15, 7))
88590792Sgshapiro					sm_dprintf("getreq: getsockname failed\n");
88690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
88790792Sgshapiro					macid("{if_name}"), NULL);
88890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
88990792Sgshapiro					macid("{if_addr}"), NULL);
89090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
89190792Sgshapiro					macid("{if_family}"), NULL);
89264562Sgshapiro			}
89338032Speter			break;
89438032Speter		}
89538032Speter
89638032Speter		/* parent -- keep track of children */
89764562Sgshapiro		if (control)
89864562Sgshapiro		{
899168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
90090792Sgshapiro					   "control socket server child");
901132943Sgshapiro			proc_list_add(pid, status, PROC_CONTROL, 0, -1, NULL);
90264562Sgshapiro		}
90364562Sgshapiro		else
90464562Sgshapiro		{
905168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
90690792Sgshapiro					   "SMTP server child for %s",
90790792Sgshapiro					   anynet_ntoa(&RealHostAddr));
908132943Sgshapiro			proc_list_add(pid, status, PROC_DAEMON, 0, -1,
909132943Sgshapiro					&RealHostAddr);
91064562Sgshapiro		}
91190792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
91238032Speter
91338032Speter		/* close the read end of the synchronization pipe */
91438032Speter		if (pipefd[0] != -1)
91564562Sgshapiro		{
91638032Speter			(void) close(pipefd[0]);
91764562Sgshapiro			pipefd[0] = -1;
91864562Sgshapiro		}
91938032Speter
92038032Speter		/* close the port so that others will hang (for a while) */
92138032Speter		(void) close(t);
92238032Speter
92338032Speter		/* release the child by closing the read end of the sync pipe */
92438032Speter		if (pipefd[1] != -1)
92564562Sgshapiro		{
92638032Speter			(void) close(pipefd[1]);
92764562Sgshapiro			pipefd[1] = -1;
92864562Sgshapiro		}
92938032Speter	}
93090792Sgshapiro	if (tTd(15, 2))
93190792Sgshapiro		sm_dprintf("getreq: returning\n");
93264562Sgshapiro
93390792Sgshapiro#if MILTER
93490792Sgshapiro	/* set the filters for this daemon */
93590792Sgshapiro	if (Daemons[curdaemon].d_inputfilterlist != NULL)
93690792Sgshapiro	{
93790792Sgshapiro		for (i = 0;
938110560Sgshapiro		     (i < MAXFILTERS &&
939110560Sgshapiro		      Daemons[curdaemon].d_inputfilters[i] != NULL);
94090792Sgshapiro		     i++)
94190792Sgshapiro		{
94290792Sgshapiro			InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
94390792Sgshapiro		}
94490792Sgshapiro		if (i < MAXFILTERS)
94590792Sgshapiro			InputFilters[i] = NULL;
94690792Sgshapiro	}
94790792Sgshapiro#endif /* MILTER */
94864562Sgshapiro	return &Daemons[curdaemon].d_flags;
94938032Speter}
95090792Sgshapiro
95190792Sgshapiro/*
95290792Sgshapiro**  GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
95390792Sgshapiro**
95490792Sgshapiro**	Parameters:
95590792Sgshapiro**		e -- envelope.
95690792Sgshapiro**
95790792Sgshapiro**	Returns:
95890792Sgshapiro**		none.
95990792Sgshapiro**
96090792Sgshapiro**	Side Effects:
96190792Sgshapiro**		Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
96290792Sgshapiro*/
96390792Sgshapiro
96490792Sgshapirostatic void
96590792Sgshapirogetrequests_checkdiskspace(e)
96690792Sgshapiro	ENVELOPE *e;
96790792Sgshapiro{
96890792Sgshapiro	bool logged = false;
96990792Sgshapiro	int idx;
97090792Sgshapiro	time_t now;
97190792Sgshapiro
97290792Sgshapiro	now = curtime();
97390792Sgshapiro	if (now < NextDiskSpaceCheck)
97490792Sgshapiro		return;
97590792Sgshapiro
97690792Sgshapiro	/* Check if there is available disk space in all queue groups. */
97790792Sgshapiro	if (!enoughdiskspace(0, NULL))
97890792Sgshapiro	{
97990792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
98090792Sgshapiro		{
98190792Sgshapiro			if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
98290792Sgshapiro				continue;
98390792Sgshapiro
98490792Sgshapiro			/* log only if not logged before */
98590792Sgshapiro			if (!logged)
98690792Sgshapiro			{
98790792Sgshapiro				if (LogLevel > 8)
98890792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
98990792Sgshapiro						  "rejecting new messages: min free: %ld",
99090792Sgshapiro						  MinBlocksFree);
99190792Sgshapiro				sm_setproctitle(true, e,
99290792Sgshapiro						"rejecting new messages: min free: %ld",
99390792Sgshapiro						MinBlocksFree);
99490792Sgshapiro				logged = true;
99590792Sgshapiro			}
99690792Sgshapiro			setbitn(D_ETRNONLY, Daemons[idx].d_flags);
99790792Sgshapiro		}
99890792Sgshapiro	}
99990792Sgshapiro	else
100090792Sgshapiro	{
100190792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
100290792Sgshapiro		{
100390792Sgshapiro			if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
100490792Sgshapiro				continue;
100590792Sgshapiro
100690792Sgshapiro			/* log only if not logged before */
100790792Sgshapiro			if (!logged)
100890792Sgshapiro			{
100990792Sgshapiro				if (LogLevel > 8)
101090792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
101190792Sgshapiro						  "accepting new messages (again)");
101290792Sgshapiro				logged = true;
101390792Sgshapiro			}
101490792Sgshapiro
101590792Sgshapiro			/* title will be set later */
101690792Sgshapiro			clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
101790792Sgshapiro		}
101890792Sgshapiro	}
101990792Sgshapiro
102090792Sgshapiro	/* only check disk space once a minute */
102190792Sgshapiro	NextDiskSpaceCheck = now + 60;
102290792Sgshapiro}
102390792Sgshapiro
102490792Sgshapiro/*
102564562Sgshapiro**  OPENDAEMONSOCKET -- open SMTP socket
102638032Speter**
102764562Sgshapiro**	Deals with setting all appropriate options.
102838032Speter**
102938032Speter**	Parameters:
103064562Sgshapiro**		d -- the structure for the daemon to open.
103138032Speter**		firsttime -- set if this is the initial open.
103238032Speter**
103338032Speter**	Returns:
103438032Speter**		Size in bytes of the daemon socket addr.
103538032Speter**
103638032Speter**	Side Effects:
103738032Speter**		Leaves DaemonSocket set to the open socket.
103838032Speter**		Exits if the socket cannot be created.
103938032Speter*/
104038032Speter
104190792Sgshapiro#define MAXOPENTRIES	10	/* maximum number of tries to open connection */
104238032Speter
104364562Sgshapirostatic int
104464562Sgshapiroopendaemonsocket(d, firsttime)
104590792Sgshapiro	DAEMON_T *d;
104638032Speter	bool firsttime;
104738032Speter{
104838032Speter	int on = 1;
104964562Sgshapiro	int fdflags;
105064562Sgshapiro	SOCKADDR_LEN_T socksize = 0;
105138032Speter	int ntries = 0;
105264562Sgshapiro	int save_errno;
105338032Speter
105438032Speter	if (tTd(15, 2))
105590792Sgshapiro		sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
105638032Speter
105738032Speter	do
105838032Speter	{
105938032Speter		if (ntries > 0)
106064562Sgshapiro			(void) sleep(5);
106164562Sgshapiro		if (firsttime || d->d_socket < 0)
106238032Speter		{
106390792Sgshapiro#if _FFR_DAEMON_NETUNIX
106490792Sgshapiro# if NETUNIX
106590792Sgshapiro			if (d->d_addr.sa.sa_family == AF_UNIX)
106690792Sgshapiro			{
106790792Sgshapiro				int rval;
106890792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
106990792Sgshapiro
107090792Sgshapiro				/* if not safe, don't use it */
107190792Sgshapiro				rval = safefile(d->d_addr.sunix.sun_path,
107290792Sgshapiro						RunAsUid, RunAsGid,
107390792Sgshapiro						RunAsUserName, sff,
107490792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
107590792Sgshapiro				if (rval != 0)
107690792Sgshapiro				{
107790792Sgshapiro					save_errno = errno;
107890792Sgshapiro					syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
107990792Sgshapiro					       d->d_name,
108090792Sgshapiro					       d->d_addr.sunix.sun_path);
108190792Sgshapiro					goto fail;
108290792Sgshapiro				}
108390792Sgshapiro
108490792Sgshapiro				/* Don't try to overtake an existing socket */
108590792Sgshapiro				(void) unlink(d->d_addr.sunix.sun_path);
108690792Sgshapiro			}
108790792Sgshapiro# endif /* NETUNIX */
108890792Sgshapiro#endif /* _FFR_DOMAIN_NETUNIX */
108964562Sgshapiro			d->d_socket = socket(d->d_addr.sa.sa_family,
109064562Sgshapiro					     SOCK_STREAM, 0);
109164562Sgshapiro			if (d->d_socket < 0)
109238032Speter			{
109364562Sgshapiro				save_errno = errno;
109490792Sgshapiro				syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
109590792Sgshapiro				       d->d_name);
109690792Sgshapiro			  fail:
109790792Sgshapiro				if (bitnset(D_OPTIONAL, d->d_flags) &&
109890792Sgshapiro				    (!transienterror(save_errno) ||
109990792Sgshapiro				     ntries >= MAXOPENTRIES - 1))
110090792Sgshapiro				{
110190792Sgshapiro					syserr("opendaemonsocket: daemon %s: optional socket disabled",
110290792Sgshapiro					       d->d_name);
110390792Sgshapiro					setbitn(D_DISABLE, d->d_flags);
110490792Sgshapiro					d->d_socket = -1;
110590792Sgshapiro					return -1;
110690792Sgshapiro				}
110738032Speter			  severe:
110838032Speter				if (LogLevel > 0)
110938032Speter					sm_syslog(LOG_ALERT, NOQID,
111090792Sgshapiro						  "daemon %s: problem creating SMTP socket",
111190792Sgshapiro						  d->d_name);
111264562Sgshapiro				d->d_socket = -1;
111338032Speter				continue;
111438032Speter			}
111538032Speter
1116110560Sgshapiro			if (SM_FD_SETSIZE > 0 && d->d_socket >= SM_FD_SETSIZE)
1117110560Sgshapiro			{
1118110560Sgshapiro				save_errno = EINVAL;
1119110560Sgshapiro				syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large",
1120110560Sgshapiro				       d->d_name, d->d_socket);
1121110560Sgshapiro				goto fail;
1122110560Sgshapiro			}
1123110560Sgshapiro
112438032Speter			/* turn on network debugging? */
112538032Speter			if (tTd(15, 101))
112664562Sgshapiro				(void) setsockopt(d->d_socket, SOL_SOCKET,
112738032Speter						  SO_DEBUG, (char *)&on,
1128168515Sgshapiro						  sizeof(on));
112938032Speter
113064562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1131168515Sgshapiro					  SO_REUSEADDR, (char *)&on, sizeof(on));
113264562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1133168515Sgshapiro					  SO_KEEPALIVE, (char *)&on, sizeof(on));
113438032Speter
113590792Sgshapiro#ifdef SO_RCVBUF
113664562Sgshapiro			if (d->d_tcprcvbufsize > 0)
113738032Speter			{
113864562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
113938032Speter					       SO_RCVBUF,
114064562Sgshapiro					       (char *) &d->d_tcprcvbufsize,
114164562Sgshapiro					       sizeof(d->d_tcprcvbufsize)) < 0)
114264562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
114338032Speter			}
114490792Sgshapiro#endif /* SO_RCVBUF */
114590792Sgshapiro#ifdef SO_SNDBUF
114664562Sgshapiro			if (d->d_tcpsndbufsize > 0)
114764562Sgshapiro			{
114864562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
114964562Sgshapiro					       SO_SNDBUF,
115064562Sgshapiro					       (char *) &d->d_tcpsndbufsize,
115164562Sgshapiro					       sizeof(d->d_tcpsndbufsize)) < 0)
115264562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
115364562Sgshapiro			}
115490792Sgshapiro#endif /* SO_SNDBUF */
115538032Speter
115664562Sgshapiro			if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
115764562Sgshapiro			    fcntl(d->d_socket, F_SETFD,
115864562Sgshapiro				  fdflags | FD_CLOEXEC) == -1)
115938032Speter			{
116064562Sgshapiro				save_errno = errno;
116164562Sgshapiro				syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
116264562Sgshapiro				       d->d_name,
116364562Sgshapiro				       fdflags == -1 ? "get" : "set",
116490792Sgshapiro				       sm_errstring(save_errno));
116564562Sgshapiro				(void) close(d->d_socket);
116664562Sgshapiro				goto severe;
116764562Sgshapiro			}
116864562Sgshapiro
116964562Sgshapiro			switch (d->d_addr.sa.sa_family)
117064562Sgshapiro			{
117190792Sgshapiro#if _FFR_DAEMON_NETUNIX
117290792Sgshapiro# ifdef NETUNIX
117390792Sgshapiro			  case AF_UNIX:
1174168515Sgshapiro				socksize = sizeof(d->d_addr.sunix);
117590792Sgshapiro				break;
117690792Sgshapiro# endif /* NETUNIX */
117790792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
117890792Sgshapiro#if NETINET
117938032Speter			  case AF_INET:
1180168515Sgshapiro				socksize = sizeof(d->d_addr.sin);
118138032Speter				break;
118290792Sgshapiro#endif /* NETINET */
118338032Speter
118490792Sgshapiro#if NETINET6
118564562Sgshapiro			  case AF_INET6:
1186168515Sgshapiro				socksize = sizeof(d->d_addr.sin6);
118764562Sgshapiro				break;
118890792Sgshapiro#endif /* NETINET6 */
118964562Sgshapiro
119090792Sgshapiro#if NETISO
119138032Speter			  case AF_ISO:
1192168515Sgshapiro				socksize = sizeof(d->d_addr.siso);
119338032Speter				break;
119490792Sgshapiro#endif /* NETISO */
119538032Speter
119638032Speter			  default:
1197168515Sgshapiro				socksize = sizeof(d->d_addr);
119838032Speter				break;
119938032Speter			}
120038032Speter
120164562Sgshapiro			if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
120238032Speter			{
120338032Speter				/* probably another daemon already */
120464562Sgshapiro				save_errno = errno;
120564562Sgshapiro				syserr("opendaemonsocket: daemon %s: cannot bind",
120664562Sgshapiro				       d->d_name);
120764562Sgshapiro				(void) close(d->d_socket);
120890792Sgshapiro				goto fail;
120938032Speter			}
121038032Speter		}
121164562Sgshapiro		if (!firsttime &&
121264562Sgshapiro		    listen(d->d_socket, d->d_listenqueue) < 0)
121338032Speter		{
121464562Sgshapiro			save_errno = errno;
121564562Sgshapiro			syserr("opendaemonsocket: daemon %s: cannot listen",
121664562Sgshapiro			       d->d_name);
121764562Sgshapiro			(void) close(d->d_socket);
121838032Speter			goto severe;
121938032Speter		}
122038032Speter		return socksize;
122164562Sgshapiro	} while (ntries++ < MAXOPENTRIES && transienterror(save_errno));
122264562Sgshapiro	syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting",
122364562Sgshapiro	       d->d_name);
122464562Sgshapiro	/* NOTREACHED */
122538032Speter	return -1;  /* avoid compiler warning on IRIX */
122638032Speter}
122790792Sgshapiro/*
122864562Sgshapiro**  SETUPDAEMON -- setup socket for daemon
122964562Sgshapiro**
123064562Sgshapiro**	Parameters:
123164562Sgshapiro**		daemonaddr -- socket for daemon
123264562Sgshapiro**
123364562Sgshapiro**	Returns:
123464562Sgshapiro**		port number on which daemon should run
123564562Sgshapiro**
123664562Sgshapiro*/
123790792Sgshapiro
123890792Sgshapirostatic unsigned short
123964562Sgshapirosetupdaemon(daemonaddr)
124064562Sgshapiro	SOCKADDR *daemonaddr;
124164562Sgshapiro{
124290792Sgshapiro	unsigned short port;
124364562Sgshapiro
124464562Sgshapiro	/*
124564562Sgshapiro	**  Set up the address for the mailer.
124664562Sgshapiro	*/
124764562Sgshapiro
124864562Sgshapiro	if (daemonaddr->sa.sa_family == AF_UNSPEC)
124964562Sgshapiro	{
1250168515Sgshapiro		memset(daemonaddr, '\0', sizeof(*daemonaddr));
125190792Sgshapiro#if NETINET
125264562Sgshapiro		daemonaddr->sa.sa_family = AF_INET;
125390792Sgshapiro#endif /* NETINET */
125464562Sgshapiro	}
125564562Sgshapiro
125664562Sgshapiro	switch (daemonaddr->sa.sa_family)
125764562Sgshapiro	{
125890792Sgshapiro#if NETINET
125964562Sgshapiro	  case AF_INET:
126064562Sgshapiro		if (daemonaddr->sin.sin_addr.s_addr == 0)
1261182352Sgshapiro			daemonaddr->sin.sin_addr.s_addr =
1262182352Sgshapiro			    LocalDaemon ? htonl(INADDR_LOOPBACK) : INADDR_ANY;
126364562Sgshapiro		port = daemonaddr->sin.sin_port;
126464562Sgshapiro		break;
126590792Sgshapiro#endif /* NETINET */
126664562Sgshapiro
126790792Sgshapiro#if NETINET6
126864562Sgshapiro	  case AF_INET6:
126964562Sgshapiro		if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr))
1270182352Sgshapiro			daemonaddr->sin6.sin6_addr =
1271223067Sgshapiro			    (LocalDaemon && V6LoopbackAddrFound) ?
1272223067Sgshapiro			    in6addr_loopback : in6addr_any;
127364562Sgshapiro		port = daemonaddr->sin6.sin6_port;
127464562Sgshapiro		break;
127590792Sgshapiro#endif /* NETINET6 */
127664562Sgshapiro
127764562Sgshapiro	  default:
127864562Sgshapiro		/* unknown protocol */
127964562Sgshapiro		port = 0;
128064562Sgshapiro		break;
128164562Sgshapiro	}
128264562Sgshapiro	if (port == 0)
128364562Sgshapiro	{
128490792Sgshapiro#ifdef NO_GETSERVBYNAME
128564562Sgshapiro		port = htons(25);
128690792Sgshapiro#else /* NO_GETSERVBYNAME */
128764562Sgshapiro		{
128864562Sgshapiro			register struct servent *sp;
128964562Sgshapiro
129064562Sgshapiro			sp = getservbyname("smtp", "tcp");
129164562Sgshapiro			if (sp == NULL)
129264562Sgshapiro			{
129364562Sgshapiro				syserr("554 5.3.5 service \"smtp\" unknown");
129464562Sgshapiro				port = htons(25);
129564562Sgshapiro			}
129664562Sgshapiro			else
129764562Sgshapiro				port = sp->s_port;
129864562Sgshapiro		}
129990792Sgshapiro#endif /* NO_GETSERVBYNAME */
130064562Sgshapiro	}
130164562Sgshapiro
130264562Sgshapiro	switch (daemonaddr->sa.sa_family)
130364562Sgshapiro	{
130490792Sgshapiro#if NETINET
130564562Sgshapiro	  case AF_INET:
130664562Sgshapiro		daemonaddr->sin.sin_port = port;
130764562Sgshapiro		break;
130890792Sgshapiro#endif /* NETINET */
130964562Sgshapiro
131090792Sgshapiro#if NETINET6
131164562Sgshapiro	  case AF_INET6:
131264562Sgshapiro		daemonaddr->sin6.sin6_port = port;
131364562Sgshapiro		break;
131490792Sgshapiro#endif /* NETINET6 */
131564562Sgshapiro
131664562Sgshapiro	  default:
131764562Sgshapiro		/* unknown protocol */
131864562Sgshapiro		break;
131964562Sgshapiro	}
132090792Sgshapiro	return port;
132164562Sgshapiro}
132290792Sgshapiro/*
132338032Speter**  CLRDAEMON -- reset the daemon connection
132438032Speter**
132538032Speter**	Parameters:
132638032Speter**		none.
132738032Speter**
132838032Speter**	Returns:
132938032Speter**		none.
133038032Speter**
133138032Speter**	Side Effects:
133238032Speter**		releases any resources used by the passive daemon.
133338032Speter*/
133438032Speter
133538032Spetervoid
133638032Speterclrdaemon()
133738032Speter{
133864562Sgshapiro	int i;
133964562Sgshapiro
134090792Sgshapiro	for (i = 0; i < NDaemons; i++)
134164562Sgshapiro	{
134264562Sgshapiro		if (Daemons[i].d_socket >= 0)
134364562Sgshapiro			(void) close(Daemons[i].d_socket);
134464562Sgshapiro		Daemons[i].d_socket = -1;
134564562Sgshapiro	}
134638032Speter}
134790792Sgshapiro
134890792Sgshapiro/*
134990792Sgshapiro**  GETMODIFIERS -- get modifier flags
135090792Sgshapiro**
135190792Sgshapiro**	Parameters:
135290792Sgshapiro**		v -- the modifiers (input text line).
135390792Sgshapiro**		modifiers -- pointer to flag field to represent modifiers.
135490792Sgshapiro**
135590792Sgshapiro**	Returns:
135690792Sgshapiro**		(xallocat()ed) string representation of modifiers.
135790792Sgshapiro**
135890792Sgshapiro**	Side Effects:
135990792Sgshapiro**		fills in modifiers.
136090792Sgshapiro*/
136190792Sgshapiro
136290792Sgshapirochar *
136390792Sgshapirogetmodifiers(v, modifiers)
136490792Sgshapiro	char *v;
136590792Sgshapiro	BITMAP256 modifiers;
136690792Sgshapiro{
136790792Sgshapiro	int l;
136890792Sgshapiro	char *h, *f, *flags;
136990792Sgshapiro
137090792Sgshapiro	/* maximum length of flags: upper case Option -> "OO " */
137190792Sgshapiro	l = 3 * strlen(v) + 3;
137290792Sgshapiro
137390792Sgshapiro	/* is someone joking? */
137490792Sgshapiro	if (l < 0 || l > 256)
137590792Sgshapiro	{
137690792Sgshapiro		if (LogLevel > 2)
137790792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
137890792Sgshapiro				  "getmodifiers too long, ignored");
137990792Sgshapiro		return NULL;
138090792Sgshapiro	}
138190792Sgshapiro	flags = xalloc(l);
138290792Sgshapiro	f = flags;
138390792Sgshapiro	clrbitmap(modifiers);
138490792Sgshapiro	for (h = v; *h != '\0'; h++)
138590792Sgshapiro	{
138690792Sgshapiro		if (isascii(*h) && !isspace(*h) && isprint(*h))
138790792Sgshapiro		{
138890792Sgshapiro			setbitn(*h, modifiers);
138990792Sgshapiro			if (flags != f)
139090792Sgshapiro				*flags++ = ' ';
139190792Sgshapiro			*flags++ = *h;
139290792Sgshapiro			if (isupper(*h))
139390792Sgshapiro				*flags++ = *h;
139490792Sgshapiro		}
139590792Sgshapiro	}
139690792Sgshapiro	*flags++ = '\0';
139790792Sgshapiro	return f;
139890792Sgshapiro}
139990792Sgshapiro
140090792Sgshapiro/*
140190792Sgshapiro**  CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
140290792Sgshapiro**
140390792Sgshapiro**	Parameters:
140490792Sgshapiro**		flag -- the flag to test.
140590792Sgshapiro**
140690792Sgshapiro**	Returns:
140790792Sgshapiro**		true iff all daemons have set flag.
140890792Sgshapiro*/
140990792Sgshapiro
141090792Sgshapirobool
141190792Sgshapirochkdaemonmodifiers(flag)
141290792Sgshapiro	int flag;
141390792Sgshapiro{
141490792Sgshapiro	int i;
141590792Sgshapiro
141690792Sgshapiro	for (i = 0; i < NDaemons; i++)
141790792Sgshapiro		if (!bitnset((char) flag, Daemons[i].d_flags))
141890792Sgshapiro			return false;
141990792Sgshapiro	return true;
142090792Sgshapiro}
142190792Sgshapiro
142290792Sgshapiro/*
142364562Sgshapiro**  SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
142438032Speter**
142538032Speter**	Parameters:
142638032Speter**		p -- the options line.
142764562Sgshapiro**		d -- the daemon structure to fill in.
142838032Speter**
142938032Speter**	Returns:
143038032Speter**		none.
143138032Speter*/
143238032Speter
143364562Sgshapirostatic void
143464562Sgshapirosetsockaddroptions(p, d)
1435141858Sgshapiro	char *p;
143690792Sgshapiro	DAEMON_T *d;
143738032Speter{
143890792Sgshapiro#if NETISO
143971345Sgshapiro	short portno;
144090792Sgshapiro#endif /* NETISO */
144171345Sgshapiro	char *port = NULL;
144271345Sgshapiro	char *addr = NULL;
144338032Speter
144490792Sgshapiro#if NETINET
144564562Sgshapiro	if (d->d_addr.sa.sa_family == AF_UNSPEC)
144664562Sgshapiro		d->d_addr.sa.sa_family = AF_INET;
144790792Sgshapiro#endif /* NETINET */
1448157001Sgshapiro#if _FFR_SS_PER_DAEMON
1449168515Sgshapiro	d->d_supersafe = DPO_NOTSET;
1450157001Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
1451157001Sgshapiro	d->d_dm = DM_NOTSET;
1452168515Sgshapiro	d->d_refuseLA = DPO_NOTSET;
1453168515Sgshapiro	d->d_queueLA = DPO_NOTSET;
1454168515Sgshapiro	d->d_delayLA = DPO_NOTSET;
1455168515Sgshapiro	d->d_maxchildren = DPO_NOTSET;
145664562Sgshapiro
145738032Speter	while (p != NULL)
145838032Speter	{
145938032Speter		register char *f;
146038032Speter		register char *v;
146138032Speter
146238032Speter		while (isascii(*p) && isspace(*p))
146338032Speter			p++;
146438032Speter		if (*p == '\0')
146538032Speter			break;
146638032Speter		f = p;
146738032Speter		p = strchr(p, ',');
146838032Speter		if (p != NULL)
146938032Speter			*p++ = '\0';
147038032Speter		v = strchr(f, '=');
147138032Speter		if (v == NULL)
147238032Speter			continue;
147338032Speter		while (isascii(*++v) && isspace(*v))
147438032Speter			continue;
147538032Speter
147638032Speter		switch (*f)
147738032Speter		{
1478147078Sgshapiro		  case 'A':		/* address */
1479168515Sgshapiro#if !_FFR_DPO_CS
1480168515Sgshapiro		  case 'a':
1481168515Sgshapiro#endif /* !_FFR_DPO_CS */
1482147078Sgshapiro			addr = v;
1483147078Sgshapiro			break;
1484147078Sgshapiro
1485168515Sgshapiro		  case 'c':
1486168515Sgshapiro			d->d_maxchildren = atoi(v);
1487168515Sgshapiro			break;
1488168515Sgshapiro
1489147078Sgshapiro		  case 'D':		/* DeliveryMode */
1490147078Sgshapiro			switch (*v)
1491147078Sgshapiro			{
1492147078Sgshapiro			  case SM_QUEUE:
1493147078Sgshapiro			  case SM_DEFER:
1494147078Sgshapiro			  case SM_DELIVER:
1495157001Sgshapiro			  case SM_FORK:
1496147078Sgshapiro				d->d_dm = *v;
1497147078Sgshapiro				break;
1498147078Sgshapiro			  default:
1499147078Sgshapiro				syserr("554 5.3.5 Unknown delivery mode %c",
1500147078Sgshapiro					*v);
1501147078Sgshapiro				break;
1502147078Sgshapiro			}
1503147078Sgshapiro			break;
1504147078Sgshapiro
1505168515Sgshapiro		  case 'd':		/* delayLA */
1506168515Sgshapiro			d->d_delayLA = atoi(v);
1507168515Sgshapiro			break;
1508168515Sgshapiro
150938032Speter		  case 'F':		/* address family */
1510168515Sgshapiro#if !_FFR_DPO_CS
1511168515Sgshapiro		  case 'f':
1512168515Sgshapiro#endif /* !_FFR_DPO_CS */
151338032Speter			if (isascii(*v) && isdigit(*v))
151464562Sgshapiro				d->d_addr.sa.sa_family = atoi(v);
151590792Sgshapiro#if _FFR_DAEMON_NETUNIX
151690792Sgshapiro# ifdef NETUNIX
151790792Sgshapiro			else if (sm_strcasecmp(v, "unix") == 0 ||
151890792Sgshapiro				 sm_strcasecmp(v, "local") == 0)
151990792Sgshapiro				d->d_addr.sa.sa_family = AF_UNIX;
152090792Sgshapiro# endif /* NETUNIX */
152190792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
152290792Sgshapiro#if NETINET
152390792Sgshapiro			else if (sm_strcasecmp(v, "inet") == 0)
152464562Sgshapiro				d->d_addr.sa.sa_family = AF_INET;
152590792Sgshapiro#endif /* NETINET */
152690792Sgshapiro#if NETINET6
152790792Sgshapiro			else if (sm_strcasecmp(v, "inet6") == 0)
152864562Sgshapiro				d->d_addr.sa.sa_family = AF_INET6;
152990792Sgshapiro#endif /* NETINET6 */
153090792Sgshapiro#if NETISO
153190792Sgshapiro			else if (sm_strcasecmp(v, "iso") == 0)
153264562Sgshapiro				d->d_addr.sa.sa_family = AF_ISO;
153390792Sgshapiro#endif /* NETISO */
153490792Sgshapiro#if NETNS
153590792Sgshapiro			else if (sm_strcasecmp(v, "ns") == 0)
153664562Sgshapiro				d->d_addr.sa.sa_family = AF_NS;
153790792Sgshapiro#endif /* NETNS */
153890792Sgshapiro#if NETX25
153990792Sgshapiro			else if (sm_strcasecmp(v, "x.25") == 0)
154064562Sgshapiro				d->d_addr.sa.sa_family = AF_CCITT;
154190792Sgshapiro#endif /* NETX25 */
154238032Speter			else
154364562Sgshapiro				syserr("554 5.3.5 Unknown address family %s in Family=option",
154464562Sgshapiro				       v);
154538032Speter			break;
154638032Speter
154790792Sgshapiro#if MILTER
154890792Sgshapiro		  case 'I':
1549168515Sgshapiro# if !_FFR_DPO_CS
1550168515Sgshapiro		  case 'i':
1551168515Sgshapiro# endif /* !_FFR_DPO_CS */
155290792Sgshapiro			d->d_inputfilterlist = v;
155390792Sgshapiro			break;
155490792Sgshapiro#endif /* MILTER */
155590792Sgshapiro
155638032Speter		  case 'L':		/* listen queue size */
1557168515Sgshapiro#if !_FFR_DPO_CS
1558168515Sgshapiro		  case 'l':
1559168515Sgshapiro#endif /* !_FFR_DPO_CS */
156064562Sgshapiro			d->d_listenqueue = atoi(v);
156138032Speter			break;
156238032Speter
156364562Sgshapiro		  case 'M':		/* modifiers (flags) */
1564168515Sgshapiro#if !_FFR_DPO_CS
1565168515Sgshapiro		  case 'm':
1566168515Sgshapiro#endif /* !_FFR_DPO_CS */
156790792Sgshapiro			d->d_mflags = getmodifiers(v, d->d_flags);
156864562Sgshapiro			break;
156964562Sgshapiro
1570147078Sgshapiro		  case 'N':		/* name */
1571168515Sgshapiro#if !_FFR_DPO_CS
1572168515Sgshapiro		  case 'n':
1573168515Sgshapiro#endif /* !_FFR_DPO_CS */
1574147078Sgshapiro			d->d_name = v;
157538032Speter			break;
157638032Speter
1577147078Sgshapiro		  case 'P':		/* port */
1578168515Sgshapiro#if !_FFR_DPO_CS
1579168515Sgshapiro		  case 'p':
1580168515Sgshapiro#endif /* !_FFR_DPO_CS */
1581147078Sgshapiro			port = v;
1582147078Sgshapiro			break;
1583147078Sgshapiro
1584168515Sgshapiro		  case 'q':
1585168515Sgshapiro			d->d_queueLA = atoi(v);
1586168515Sgshapiro			break;
1587168515Sgshapiro
158838032Speter		  case 'R':		/* receive buffer size */
158964562Sgshapiro			d->d_tcprcvbufsize = atoi(v);
159038032Speter			break;
159138032Speter
1592168515Sgshapiro		  case 'r':
1593168515Sgshapiro			d->d_refuseLA = atoi(v);
1594168515Sgshapiro			break;
1595168515Sgshapiro
1596147078Sgshapiro		  case 'S':		/* send buffer size */
1597168515Sgshapiro#if !_FFR_DPO_CS
1598168515Sgshapiro		  case 's':
1599168515Sgshapiro#endif /* !_FFR_DPO_CS */
1600147078Sgshapiro			d->d_tcpsndbufsize = atoi(v);
160164562Sgshapiro			break;
160264562Sgshapiro
1603147078Sgshapiro#if _FFR_SS_PER_DAEMON
1604147078Sgshapiro		  case 'T':		/* SuperSafe */
1605147078Sgshapiro			if (tolower(*v) == 'i')
1606147078Sgshapiro				d->d_supersafe = SAFE_INTERACTIVE;
1607147078Sgshapiro			else if (tolower(*v) == 'p')
1608147078Sgshapiro# if MILTER
1609147078Sgshapiro				d->d_supersafe = SAFE_REALLY_POSTMILTER;
1610147078Sgshapiro# else /* MILTER */
1611147078Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1612147078Sgshapiro					"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
1613147078Sgshapiro# endif /* MILTER */
1614147078Sgshapiro			else
1615147078Sgshapiro				d->d_supersafe = atobool(v) ? SAFE_REALLY
1616147078Sgshapiro							: SAFE_NO;
1617147078Sgshapiro			break;
1618147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
1619147078Sgshapiro
162038032Speter		  default:
162164562Sgshapiro			syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
162264562Sgshapiro			       f);
162338032Speter		}
162438032Speter	}
162571345Sgshapiro
162671345Sgshapiro	/* Check addr and port after finding family */
162771345Sgshapiro	if (addr != NULL)
162871345Sgshapiro	{
162971345Sgshapiro		switch (d->d_addr.sa.sa_family)
163071345Sgshapiro		{
163190792Sgshapiro#if _FFR_DAEMON_NETUNIX
163290792Sgshapiro# if NETUNIX
163390792Sgshapiro		  case AF_UNIX:
163490792Sgshapiro			if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
163590792Sgshapiro			{
163690792Sgshapiro				errno = ENAMETOOLONG;
163790792Sgshapiro				syserr("setsockaddroptions: domain socket name too long: %s > %d",
163890792Sgshapiro				       addr, sizeof(d->d_addr.sunix.sun_path));
163990792Sgshapiro				break;
164090792Sgshapiro			}
164190792Sgshapiro
164290792Sgshapiro			/* file safety check done in opendaemonsocket() */
164390792Sgshapiro			(void) memset(&d->d_addr.sunix.sun_path, '\0',
164490792Sgshapiro				      sizeof(d->d_addr.sunix.sun_path));
164590792Sgshapiro			(void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
164690792Sgshapiro					  addr,
164790792Sgshapiro					  sizeof(d->d_addr.sunix.sun_path));
164890792Sgshapiro			break;
164990792Sgshapiro# endif /* NETUNIX */
165090792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
165190792Sgshapiro#if NETINET
165271345Sgshapiro		  case AF_INET:
165371345Sgshapiro			if (!isascii(*addr) || !isdigit(*addr) ||
165490792Sgshapiro			    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
165590792Sgshapiro			     == INADDR_NONE))
165671345Sgshapiro			{
165771345Sgshapiro				register struct hostent *hp;
165871345Sgshapiro
165971345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET);
166071345Sgshapiro				if (hp == NULL)
166171345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
166271345Sgshapiro					       addr);
166371345Sgshapiro				else
166471345Sgshapiro				{
166571345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
166671345Sgshapiro					       hp->h_addrtype != AF_INET)
166771345Sgshapiro						hp->h_addr_list++;
166871345Sgshapiro					if (*(hp->h_addr_list) == NULL)
166971345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
167071345Sgshapiro						       addr);
167171345Sgshapiro					else
167271345Sgshapiro						memmove(&d->d_addr.sin.sin_addr,
167371345Sgshapiro							*(hp->h_addr_list),
167471345Sgshapiro							INADDRSZ);
167590792Sgshapiro# if NETINET6
167671345Sgshapiro					freehostent(hp);
167771345Sgshapiro					hp = NULL;
167890792Sgshapiro# endif /* NETINET6 */
167971345Sgshapiro				}
168071345Sgshapiro			}
168171345Sgshapiro			break;
168290792Sgshapiro#endif /* NETINET */
168371345Sgshapiro
168490792Sgshapiro#if NETINET6
168571345Sgshapiro		  case AF_INET6:
168690792Sgshapiro			if (anynet_pton(AF_INET6, addr,
168790792Sgshapiro					&d->d_addr.sin6.sin6_addr) != 1)
168871345Sgshapiro			{
168971345Sgshapiro				register struct hostent *hp;
169071345Sgshapiro
169171345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET6);
169271345Sgshapiro				if (hp == NULL)
169371345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
169471345Sgshapiro					       addr);
169571345Sgshapiro				else
169671345Sgshapiro				{
169771345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
169871345Sgshapiro					       hp->h_addrtype != AF_INET6)
169971345Sgshapiro						hp->h_addr_list++;
170071345Sgshapiro					if (*(hp->h_addr_list) == NULL)
170171345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
170271345Sgshapiro						       addr);
170371345Sgshapiro					else
170471345Sgshapiro						memmove(&d->d_addr.sin6.sin6_addr,
170571345Sgshapiro							*(hp->h_addr_list),
170671345Sgshapiro							IN6ADDRSZ);
170771345Sgshapiro					freehostent(hp);
170871345Sgshapiro					hp = NULL;
170971345Sgshapiro				}
171071345Sgshapiro			}
171171345Sgshapiro			break;
171290792Sgshapiro#endif /* NETINET6 */
171371345Sgshapiro
171471345Sgshapiro		  default:
171571345Sgshapiro			syserr("554 5.3.5 address= option unsupported for family %d",
171671345Sgshapiro			       d->d_addr.sa.sa_family);
171771345Sgshapiro			break;
171871345Sgshapiro		}
171971345Sgshapiro	}
172071345Sgshapiro
172171345Sgshapiro	if (port != NULL)
172271345Sgshapiro	{
172371345Sgshapiro		switch (d->d_addr.sa.sa_family)
172471345Sgshapiro		{
172590792Sgshapiro#if NETINET
172671345Sgshapiro		  case AF_INET:
172771345Sgshapiro			if (isascii(*port) && isdigit(*port))
172890792Sgshapiro				d->d_addr.sin.sin_port = htons((unsigned short)
172990792Sgshapiro						     atoi((const char *) port));
173071345Sgshapiro			else
173171345Sgshapiro			{
173290792Sgshapiro# ifdef NO_GETSERVBYNAME
173371345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
173471345Sgshapiro				       port);
173590792Sgshapiro# else /* NO_GETSERVBYNAME */
173671345Sgshapiro				register struct servent *sp;
173771345Sgshapiro
173871345Sgshapiro				sp = getservbyname(port, "tcp");
173971345Sgshapiro				if (sp == NULL)
174071345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
174171345Sgshapiro					       port);
174271345Sgshapiro				else
174371345Sgshapiro					d->d_addr.sin.sin_port = sp->s_port;
174490792Sgshapiro# endif /* NO_GETSERVBYNAME */
174571345Sgshapiro			}
174671345Sgshapiro			break;
174790792Sgshapiro#endif /* NETINET */
174871345Sgshapiro
174990792Sgshapiro#if NETINET6
175071345Sgshapiro		  case AF_INET6:
175171345Sgshapiro			if (isascii(*port) && isdigit(*port))
175290792Sgshapiro				d->d_addr.sin6.sin6_port = htons((unsigned short)
175390792Sgshapiro								  atoi(port));
175471345Sgshapiro			else
175571345Sgshapiro			{
175690792Sgshapiro# ifdef NO_GETSERVBYNAME
175771345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
175871345Sgshapiro				       port);
175990792Sgshapiro# else /* NO_GETSERVBYNAME */
176071345Sgshapiro				register struct servent *sp;
176171345Sgshapiro
176271345Sgshapiro				sp = getservbyname(port, "tcp");
176371345Sgshapiro				if (sp == NULL)
176471345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
176571345Sgshapiro					       port);
176671345Sgshapiro				else
176771345Sgshapiro					d->d_addr.sin6.sin6_port = sp->s_port;
176890792Sgshapiro# endif /* NO_GETSERVBYNAME */
176971345Sgshapiro			}
177071345Sgshapiro			break;
177190792Sgshapiro#endif /* NETINET6 */
177271345Sgshapiro
177390792Sgshapiro#if NETISO
177471345Sgshapiro		  case AF_ISO:
177571345Sgshapiro			/* assume two byte transport selector */
177671345Sgshapiro			if (isascii(*port) && isdigit(*port))
177790792Sgshapiro				portno = htons((unsigned short) atoi(port));
177871345Sgshapiro			else
177971345Sgshapiro			{
178090792Sgshapiro# ifdef NO_GETSERVBYNAME
178171345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
178271345Sgshapiro				       port);
178390792Sgshapiro# else /* NO_GETSERVBYNAME */
178471345Sgshapiro				register struct servent *sp;
178571345Sgshapiro
178671345Sgshapiro				sp = getservbyname(port, "tcp");
178771345Sgshapiro				if (sp == NULL)
178871345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
178971345Sgshapiro					       port);
179071345Sgshapiro				else
179171345Sgshapiro					portno = sp->s_port;
179290792Sgshapiro# endif /* NO_GETSERVBYNAME */
179371345Sgshapiro			}
179471345Sgshapiro			memmove(TSEL(&d->d_addr.siso),
179571345Sgshapiro				(char *) &portno, 2);
179671345Sgshapiro			break;
179790792Sgshapiro#endif /* NETISO */
179871345Sgshapiro
179971345Sgshapiro		  default:
180071345Sgshapiro			syserr("554 5.3.5 Port= option unsupported for family %d",
180171345Sgshapiro			       d->d_addr.sa.sa_family);
180271345Sgshapiro			break;
180371345Sgshapiro		}
180471345Sgshapiro	}
180538032Speter}
180690792Sgshapiro/*
180764562Sgshapiro**  SETDAEMONOPTIONS -- set options for running the MTA daemon
180838032Speter**
180938032Speter**	Parameters:
181064562Sgshapiro**		p -- the options line.
181164562Sgshapiro**
181264562Sgshapiro**	Returns:
181390792Sgshapiro**		true if successful, false otherwise.
181490792Sgshapiro**
181590792Sgshapiro**	Side Effects:
181690792Sgshapiro**		increments number of daemons.
181764562Sgshapiro*/
181864562Sgshapiro
181990792Sgshapiro#define DEF_LISTENQUEUE	10
182090792Sgshapiro
182198841Sgshapirostruct dflags
182298841Sgshapiro{
182398841Sgshapiro	char	*d_name;
182498841Sgshapiro	int	d_flag;
182598841Sgshapiro};
182698841Sgshapiro
182798841Sgshapirostatic struct dflags	DaemonFlags[] =
182898841Sgshapiro{
182998841Sgshapiro	{ "AUTHREQ",		D_AUTHREQ	},
183098841Sgshapiro	{ "BINDIF",		D_BINDIF	},
183198841Sgshapiro	{ "CANONREQ",		D_CANONREQ	},
183298841Sgshapiro	{ "IFNHELO",		D_IFNHELO	},
183398841Sgshapiro	{ "FQMAIL",		D_FQMAIL	},
183498841Sgshapiro	{ "FQRCPT",		D_FQRCPT	},
183598841Sgshapiro	{ "SMTPS",		D_SMTPS		},
183698841Sgshapiro	{ "UNQUALOK",		D_UNQUALOK	},
183798841Sgshapiro	{ "NOAUTH",		D_NOAUTH	},
183898841Sgshapiro	{ "NOCANON",		D_NOCANON	},
183998841Sgshapiro	{ "NOETRN",		D_NOETRN	},
184098841Sgshapiro	{ "NOTLS",		D_NOTLS		},
184198841Sgshapiro	{ "ETRNONLY",		D_ETRNONLY	},
184298841Sgshapiro	{ "OPTIONAL",		D_OPTIONAL	},
184398841Sgshapiro	{ "DISABLE",		D_DISABLE	},
184498841Sgshapiro	{ "ISSET",		D_ISSET		},
184598841Sgshapiro	{ NULL,			0		}
184698841Sgshapiro};
184798841Sgshapiro
184898841Sgshapirostatic void
184998841Sgshapiroprintdaemonflags(d)
185098841Sgshapiro	DAEMON_T *d;
185198841Sgshapiro{
185298841Sgshapiro	register struct dflags *df;
185398841Sgshapiro	bool first = true;
185498841Sgshapiro
185598841Sgshapiro	for (df = DaemonFlags; df->d_name != NULL; df++)
185698841Sgshapiro	{
185798841Sgshapiro		if (!bitnset(df->d_flag, d->d_flags))
185898841Sgshapiro			continue;
185998841Sgshapiro		if (first)
1860132943Sgshapiro			sm_dprintf("<%s", df->d_name);
186198841Sgshapiro		else
1862132943Sgshapiro			sm_dprintf(",%s", df->d_name);
186398841Sgshapiro		first = false;
186498841Sgshapiro	}
186598841Sgshapiro	if (!first)
1866132943Sgshapiro		sm_dprintf(">");
186798841Sgshapiro}
186898841Sgshapiro
186964562Sgshapirobool
187064562Sgshapirosetdaemonoptions(p)
187164562Sgshapiro	register char *p;
187264562Sgshapiro{
187390792Sgshapiro	if (NDaemons >= MAXDAEMONS)
187490792Sgshapiro		return false;
187590792Sgshapiro	Daemons[NDaemons].d_socket = -1;
187690792Sgshapiro	Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
187790792Sgshapiro	clrbitmap(Daemons[NDaemons].d_flags);
187890792Sgshapiro	setsockaddroptions(p, &Daemons[NDaemons]);
187964562Sgshapiro
188090792Sgshapiro#if MILTER
188190792Sgshapiro	if (Daemons[NDaemons].d_inputfilterlist != NULL)
188290792Sgshapiro		Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
188390792Sgshapiro#endif /* MILTER */
188490792Sgshapiro
188590792Sgshapiro	if (Daemons[NDaemons].d_name != NULL)
188690792Sgshapiro		Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
188764562Sgshapiro	else
188864562Sgshapiro	{
188964562Sgshapiro		char num[30];
189064562Sgshapiro
1891168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Daemon%d", NDaemons);
189290792Sgshapiro		Daemons[NDaemons].d_name = newstr(num);
189364562Sgshapiro	}
189464562Sgshapiro
189564562Sgshapiro	if (tTd(37, 1))
189664562Sgshapiro	{
189790792Sgshapiro		sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
189898841Sgshapiro		printdaemonflags(&Daemons[NDaemons]);
189990792Sgshapiro		sm_dprintf("\n");
190064562Sgshapiro	}
190190792Sgshapiro	++NDaemons;
190290792Sgshapiro	return true;
190364562Sgshapiro}
190490792Sgshapiro/*
190564562Sgshapiro**  INITDAEMON -- initialize daemon if not yet done.
190664562Sgshapiro**
190764562Sgshapiro**	Parameters:
190864562Sgshapiro**		none
190964562Sgshapiro**
191064562Sgshapiro**	Returns:
191164562Sgshapiro**		none
191264562Sgshapiro**
191364562Sgshapiro**	Side Effects:
191464562Sgshapiro**		initializes structure for one daemon.
191564562Sgshapiro*/
191690792Sgshapiro
191764562Sgshapirovoid
191864562Sgshapiroinitdaemon()
191964562Sgshapiro{
192090792Sgshapiro	if (NDaemons == 0)
192164562Sgshapiro	{
192290792Sgshapiro		Daemons[NDaemons].d_socket = -1;
192390792Sgshapiro		Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
192490792Sgshapiro		Daemons[NDaemons].d_name = "Daemon0";
192590792Sgshapiro		NDaemons = 1;
192664562Sgshapiro	}
192764562Sgshapiro}
192890792Sgshapiro/*
192964562Sgshapiro**  SETCLIENTOPTIONS -- set options for running the client
193064562Sgshapiro**
193164562Sgshapiro**	Parameters:
193264562Sgshapiro**		p -- the options line.
193364562Sgshapiro**
193464562Sgshapiro**	Returns:
193564562Sgshapiro**		none.
193664562Sgshapiro*/
193764562Sgshapiro
193890792Sgshapirostatic DAEMON_T	ClientSettings[AF_MAX + 1];
193964562Sgshapiro
194064562Sgshapirovoid
194164562Sgshapirosetclientoptions(p)
194264562Sgshapiro	register char *p;
194364562Sgshapiro{
194490792Sgshapiro	int family;
194590792Sgshapiro	DAEMON_T d;
194664562Sgshapiro
1947168515Sgshapiro	memset(&d, '\0', sizeof(d));
194864562Sgshapiro	setsockaddroptions(p, &d);
194964562Sgshapiro
195064562Sgshapiro	/* grab what we need */
195190792Sgshapiro	family = d.d_addr.sa.sa_family;
195290792Sgshapiro	STRUCTCOPY(d, ClientSettings[family]);
195390792Sgshapiro	setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
195490792Sgshapiro	if (d.d_name != NULL)
195590792Sgshapiro		ClientSettings[family].d_name = newstr(d.d_name);
195664562Sgshapiro	else
195790792Sgshapiro	{
195890792Sgshapiro		char num[30];
195990792Sgshapiro
1960168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Client%d", family);
196190792Sgshapiro		ClientSettings[family].d_name = newstr(num);
196290792Sgshapiro	}
196364562Sgshapiro}
196490792Sgshapiro/*
196564562Sgshapiro**  ADDR_FAMILY -- determine address family from address
196664562Sgshapiro**
196764562Sgshapiro**	Parameters:
196864562Sgshapiro**		addr -- the string representation of the address
196964562Sgshapiro**
197064562Sgshapiro**	Returns:
197164562Sgshapiro**		AF_INET, AF_INET6 or AF_UNSPEC
197264562Sgshapiro**
197364562Sgshapiro**	Side Effects:
197464562Sgshapiro**		none.
197564562Sgshapiro*/
197664562Sgshapiro
197764562Sgshapirostatic int
197864562Sgshapiroaddr_family(addr)
197964562Sgshapiro	char *addr;
198064562Sgshapiro{
198190792Sgshapiro#if NETINET6
198264562Sgshapiro	SOCKADDR clt_addr;
198390792Sgshapiro#endif /* NETINET6 */
198464562Sgshapiro
198590792Sgshapiro#if NETINET
198664562Sgshapiro	if (inet_addr(addr) != INADDR_NONE)
198764562Sgshapiro	{
198864562Sgshapiro		if (tTd(16, 9))
198990792Sgshapiro			sm_dprintf("addr_family(%s): INET\n", addr);
199064562Sgshapiro		return AF_INET;
199164562Sgshapiro	}
199290792Sgshapiro#endif /* NETINET */
199390792Sgshapiro#if NETINET6
199490792Sgshapiro	if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
199564562Sgshapiro	{
199664562Sgshapiro		if (tTd(16, 9))
199790792Sgshapiro			sm_dprintf("addr_family(%s): INET6\n", addr);
199864562Sgshapiro		return AF_INET6;
199964562Sgshapiro	}
200090792Sgshapiro#endif /* NETINET6 */
200190792Sgshapiro#if _FFR_DAEMON_NETUNIX
200290792Sgshapiro# if NETUNIX
200390792Sgshapiro	if (*addr == '/')
200490792Sgshapiro	{
200590792Sgshapiro		if (tTd(16, 9))
200690792Sgshapiro			sm_dprintf("addr_family(%s): LOCAL\n", addr);
200790792Sgshapiro		return AF_UNIX;
200890792Sgshapiro	}
200990792Sgshapiro# endif /* NETUNIX */
201090792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
201164562Sgshapiro	if (tTd(16, 9))
201290792Sgshapiro		sm_dprintf("addr_family(%s): UNSPEC\n", addr);
201364562Sgshapiro	return AF_UNSPEC;
201464562Sgshapiro}
201590792Sgshapiro
201690792Sgshapiro/*
201790792Sgshapiro**  CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
201890792Sgshapiro**
201990792Sgshapiro**	Parameters:
202090792Sgshapiro**		flag -- the flag to test.
202190792Sgshapiro**
202290792Sgshapiro**	Returns:
202390792Sgshapiro**		true iff all configured clients have set the flag.
202490792Sgshapiro*/
202590792Sgshapiro
202690792Sgshapirobool
202790792Sgshapirochkclientmodifiers(flag)
202890792Sgshapiro	int flag;
202990792Sgshapiro{
203090792Sgshapiro	int i;
203190792Sgshapiro	bool flagisset;
203290792Sgshapiro
203390792Sgshapiro	flagisset = false;
203490792Sgshapiro	for (i = 0; i < AF_MAX; i++)
203590792Sgshapiro	{
203690792Sgshapiro		if (bitnset(D_ISSET, ClientSettings[i].d_flags))
203790792Sgshapiro		{
203890792Sgshapiro			if (!bitnset((char) flag, ClientSettings[i].d_flags))
203990792Sgshapiro				return false;
204090792Sgshapiro			flagisset = true;
204190792Sgshapiro		}
204290792Sgshapiro	}
204390792Sgshapiro	return flagisset;
204490792Sgshapiro}
204590792Sgshapiro
204690792Sgshapiro#if MILTER
204790792Sgshapiro/*
204890792Sgshapiro**  SETUP_DAEMON_FILTERS -- Parse per-socket filters
204990792Sgshapiro**
205090792Sgshapiro**	Parameters:
205190792Sgshapiro**		none
205290792Sgshapiro**
205390792Sgshapiro**	Returns:
205490792Sgshapiro**		none
205590792Sgshapiro*/
205690792Sgshapiro
205790792Sgshapirovoid
205890792Sgshapirosetup_daemon_milters()
205990792Sgshapiro{
206090792Sgshapiro	int idx;
206190792Sgshapiro
206290792Sgshapiro	if (OpMode == MD_SMTP)
206390792Sgshapiro	{
206490792Sgshapiro		/* no need to configure the daemons */
206590792Sgshapiro		return;
206690792Sgshapiro	}
206790792Sgshapiro
206890792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
206990792Sgshapiro	{
207090792Sgshapiro		if (Daemons[idx].d_inputfilterlist != NULL)
207190792Sgshapiro		{
207290792Sgshapiro			milter_config(Daemons[idx].d_inputfilterlist,
207390792Sgshapiro				      Daemons[idx].d_inputfilters,
207490792Sgshapiro				      MAXFILTERS);
207590792Sgshapiro		}
207690792Sgshapiro	}
207790792Sgshapiro}
207890792Sgshapiro#endif /* MILTER */
207990792Sgshapiro/*
208064562Sgshapiro**  MAKECONNECTION -- make a connection to an SMTP socket on a machine.
208164562Sgshapiro**
208264562Sgshapiro**	Parameters:
208338032Speter**		host -- the name of the host.
208438032Speter**		port -- the port number to connect to.
208538032Speter**		mci -- a pointer to the mail connection information
208638032Speter**			structure to be filled in.
208738032Speter**		e -- the current envelope.
208890792Sgshapiro**		enough -- time at which to stop further connection attempts.
208990792Sgshapiro**			(0 means no limit)
209038032Speter**
209138032Speter**	Returns:
209238032Speter**		An exit code telling whether the connection could be
209338032Speter**			made and if not why not.
209438032Speter**
209538032Speter**	Side Effects:
209638032Speter**		none.
209738032Speter*/
209838032Speter
209938032Speterstatic jmp_buf	CtxConnectTimeout;
210038032Speter
210138032SpeterSOCKADDR	CurHostAddr;		/* address of current host */
210238032Speter
210338032Speterint
210490792Sgshapiromakeconnection(host, port, mci, e, enough)
210538032Speter	char *host;
210690792Sgshapiro	volatile unsigned int port;
210738032Speter	register MCI *mci;
210838032Speter	ENVELOPE *e;
210990792Sgshapiro	time_t enough;
211038032Speter{
211138032Speter	register volatile int addrno = 0;
211290792Sgshapiro	volatile int s;
211390792Sgshapiro	register struct hostent *volatile hp = (struct hostent *) NULL;
211438032Speter	SOCKADDR addr;
211564562Sgshapiro	SOCKADDR clt_addr;
211664562Sgshapiro	int save_errno = 0;
211764562Sgshapiro	volatile SOCKADDR_LEN_T addrlen;
2118159609Sgshapiro	volatile bool firstconnect = true;
211990792Sgshapiro	SM_EVENT *volatile ev = NULL;
212090792Sgshapiro#if NETINET6
212190792Sgshapiro	volatile bool v6found = false;
212290792Sgshapiro#endif /* NETINET6 */
212364562Sgshapiro	volatile int family = InetMode;
212464562Sgshapiro	SOCKADDR_LEN_T len;
212564562Sgshapiro	volatile SOCKADDR_LEN_T socksize = 0;
212664562Sgshapiro	volatile bool clt_bind;
212764562Sgshapiro	BITMAP256 d_flags;
212864562Sgshapiro	char *p;
212964562Sgshapiro	extern ENVELOPE BlankEnvelope;
213038032Speter
213190792Sgshapiro	/* retranslate {daemon_flags} into bitmap */
213264562Sgshapiro	clrbitmap(d_flags);
213390792Sgshapiro	if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL)
213464562Sgshapiro	{
213564562Sgshapiro		for (; *p != '\0'; p++)
213664562Sgshapiro		{
213764562Sgshapiro			if (!(isascii(*p) && isspace(*p)))
213871345Sgshapiro				setbitn(bitidx(*p), d_flags);
213964562Sgshapiro		}
214064562Sgshapiro	}
214164562Sgshapiro
214290792Sgshapiro#if NETINET6
214364562Sgshapiro v4retry:
214490792Sgshapiro#endif /* NETINET6 */
214590792Sgshapiro	clt_bind = false;
214664562Sgshapiro
214764562Sgshapiro	/* Set up the address for outgoing connection. */
214864562Sgshapiro	if (bitnset(D_BINDIF, d_flags) &&
214990792Sgshapiro	    (p = macvalue(macid("{if_addr}"), e)) != NULL &&
215073188Sgshapiro	    *p != '\0')
215164562Sgshapiro	{
215290792Sgshapiro#if NETINET6
215364562Sgshapiro		char p6[INET6_ADDRSTRLEN];
215490792Sgshapiro#endif /* NETINET6 */
215564562Sgshapiro
2156168515Sgshapiro		memset(&clt_addr, '\0', sizeof(clt_addr));
215764562Sgshapiro
215864562Sgshapiro		/* infer the address family from the address itself */
215964562Sgshapiro		clt_addr.sa.sa_family = addr_family(p);
216064562Sgshapiro		switch (clt_addr.sa.sa_family)
216164562Sgshapiro		{
216290792Sgshapiro#if NETINET
216364562Sgshapiro		  case AF_INET:
216473188Sgshapiro			clt_addr.sin.sin_addr.s_addr = inet_addr(p);
216573188Sgshapiro			if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
2166203004Sgshapiro			    clt_addr.sin.sin_addr.s_addr !=
2167203004Sgshapiro				htonl(INADDR_LOOPBACK))
216864562Sgshapiro			{
216990792Sgshapiro				clt_bind = true;
2170168515Sgshapiro				socksize = sizeof(struct sockaddr_in);
217164562Sgshapiro			}
217264562Sgshapiro			break;
217390792Sgshapiro#endif /* NETINET */
217464562Sgshapiro
217590792Sgshapiro#if NETINET6
217664562Sgshapiro		  case AF_INET6:
217764562Sgshapiro			if (inet_addr(p) != INADDR_NONE)
2178168515Sgshapiro				(void) sm_snprintf(p6, sizeof(p6),
217990792Sgshapiro						   "IPv6:::ffff:%s", p);
218064562Sgshapiro			else
2181168515Sgshapiro				(void) sm_strlcpy(p6, p, sizeof(p6));
218290792Sgshapiro			if (anynet_pton(AF_INET6, p6,
218390792Sgshapiro					&clt_addr.sin6.sin6_addr) == 1 &&
218473188Sgshapiro			    !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
218564562Sgshapiro			{
218690792Sgshapiro				clt_bind = true;
2187168515Sgshapiro				socksize = sizeof(struct sockaddr_in6);
218864562Sgshapiro			}
218964562Sgshapiro			break;
219090792Sgshapiro#endif /* NETINET6 */
219164562Sgshapiro
219290792Sgshapiro#if 0
219364562Sgshapiro		  default:
219464562Sgshapiro			syserr("554 5.3.5 Address= option unsupported for family %d",
219564562Sgshapiro			       clt_addr.sa.sa_family);
219664562Sgshapiro			break;
219790792Sgshapiro#endif /* 0 */
219864562Sgshapiro		}
219964562Sgshapiro		if (clt_bind)
220064562Sgshapiro			family = clt_addr.sa.sa_family;
220164562Sgshapiro	}
220290792Sgshapiro
220390792Sgshapiro	/* D_BINDIF not set or not available, fallback to ClientPortOptions */
220490792Sgshapiro	if (!clt_bind)
220564562Sgshapiro	{
220690792Sgshapiro		STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
220764562Sgshapiro		switch (clt_addr.sa.sa_family)
220864562Sgshapiro		{
220990792Sgshapiro#if NETINET
221064562Sgshapiro		  case AF_INET:
221164562Sgshapiro			if (clt_addr.sin.sin_addr.s_addr == 0)
2212182352Sgshapiro				clt_addr.sin.sin_addr.s_addr = LocalDaemon ?
2213182352Sgshapiro					htonl(INADDR_LOOPBACK) : INADDR_ANY;
221464562Sgshapiro			else
221590792Sgshapiro				clt_bind = true;
221664562Sgshapiro			if (clt_addr.sin.sin_port != 0)
221790792Sgshapiro				clt_bind = true;
2218168515Sgshapiro			socksize = sizeof(struct sockaddr_in);
221964562Sgshapiro			break;
222090792Sgshapiro#endif /* NETINET */
222190792Sgshapiro#if NETINET6
222264562Sgshapiro		  case AF_INET6:
222364562Sgshapiro			if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
2224223067Sgshapiro				clt_addr.sin6.sin6_addr =
2225223067Sgshapiro					(LocalDaemon && V6LoopbackAddrFound) ?
2226182352Sgshapiro					in6addr_loopback : in6addr_any;
222764562Sgshapiro			else
222890792Sgshapiro				clt_bind = true;
2229168515Sgshapiro			socksize = sizeof(struct sockaddr_in6);
223064562Sgshapiro			if (clt_addr.sin6.sin6_port != 0)
223190792Sgshapiro				clt_bind = true;
223264562Sgshapiro			break;
223390792Sgshapiro#endif /* NETINET6 */
223490792Sgshapiro#if NETISO
223564562Sgshapiro		  case AF_ISO:
2236168515Sgshapiro			socksize = sizeof(clt_addr.siso);
223790792Sgshapiro			clt_bind = true;
223864562Sgshapiro			break;
223990792Sgshapiro#endif /* NETISO */
224064562Sgshapiro		  default:
224164562Sgshapiro			break;
224264562Sgshapiro		}
224364562Sgshapiro	}
224464562Sgshapiro
224538032Speter	/*
224638032Speter	**  Set up the address for the mailer.
224738032Speter	**	Accept "[a.b.c.d]" syntax for host name.
224838032Speter	*/
224938032Speter
225073188Sgshapiro	SM_SET_H_ERRNO(0);
225138032Speter	errno = 0;
2252168515Sgshapiro	memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
2253168515Sgshapiro	memset(&addr, '\0', sizeof(addr));
225438032Speter	SmtpPhase = mci->mci_phase = "initial connection";
225538032Speter	CurHostName = host;
225638032Speter
225738032Speter	if (host[0] == '[')
225838032Speter	{
225964562Sgshapiro		p = strchr(host, ']');
226038032Speter		if (p != NULL)
226138032Speter		{
226290792Sgshapiro#if NETINET
226364562Sgshapiro			unsigned long hid = INADDR_NONE;
226490792Sgshapiro#endif /* NETINET */
226590792Sgshapiro#if NETINET6
226664562Sgshapiro			struct sockaddr_in6 hid6;
226790792Sgshapiro#endif /* NETINET6 */
226864562Sgshapiro
226938032Speter			*p = '\0';
227090792Sgshapiro#if NETINET6
2271168515Sgshapiro			memset(&hid6, '\0', sizeof(hid6));
227290792Sgshapiro#endif /* NETINET6 */
227390792Sgshapiro#if NETINET
227464562Sgshapiro			if (family == AF_INET &&
227564562Sgshapiro			    (hid = inet_addr(&host[1])) != INADDR_NONE)
227638032Speter			{
227764562Sgshapiro				addr.sin.sin_family = AF_INET;
227864562Sgshapiro				addr.sin.sin_addr.s_addr = hid;
227964562Sgshapiro			}
228064562Sgshapiro			else
228190792Sgshapiro#endif /* NETINET */
228290792Sgshapiro#if NETINET6
228364562Sgshapiro			if (family == AF_INET6 &&
228490792Sgshapiro			    anynet_pton(AF_INET6, &host[1],
228590792Sgshapiro					&hid6.sin6_addr) == 1)
228664562Sgshapiro			{
228764562Sgshapiro				addr.sin6.sin6_family = AF_INET6;
228864562Sgshapiro				addr.sin6.sin6_addr = hid6.sin6_addr;
228964562Sgshapiro			}
229064562Sgshapiro			else
229190792Sgshapiro#endif /* NETINET6 */
229264562Sgshapiro			{
229338032Speter				/* try it as a host name (avoid MX lookup) */
229464562Sgshapiro				hp = sm_gethostbyname(&host[1], family);
229538032Speter				if (hp == NULL && p[-1] == '.')
229638032Speter				{
229790792Sgshapiro#if NAMED_BIND
229838032Speter					int oldopts = _res.options;
229938032Speter
230038032Speter					_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
230190792Sgshapiro#endif /* NAMED_BIND */
230238032Speter					p[-1] = '\0';
230364562Sgshapiro					hp = sm_gethostbyname(&host[1],
230464562Sgshapiro							      family);
230538032Speter					p[-1] = '.';
230690792Sgshapiro#if NAMED_BIND
230738032Speter					_res.options = oldopts;
230890792Sgshapiro#endif /* NAMED_BIND */
230938032Speter				}
231038032Speter				*p = ']';
231138032Speter				goto gothostent;
231238032Speter			}
231338032Speter			*p = ']';
231438032Speter		}
231538032Speter		if (p == NULL)
231638032Speter		{
231738032Speter			extern char MsgBuf[];
231838032Speter
231964562Sgshapiro			usrerrenh("5.1.2",
232064562Sgshapiro				  "553 Invalid numeric domain spec \"%s\"",
232164562Sgshapiro				  host);
232238032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
232364562Sgshapiro			errno = EINVAL;
232438032Speter			return EX_NOHOST;
232538032Speter		}
232638032Speter	}
232738032Speter	else
232838032Speter	{
232938032Speter		/* contortion to get around SGI cc complaints */
233038032Speter		{
233164562Sgshapiro			p = &host[strlen(host) - 1];
233264562Sgshapiro			hp = sm_gethostbyname(host, family);
233338032Speter			if (hp == NULL && *p == '.')
233438032Speter			{
233590792Sgshapiro#if NAMED_BIND
233638032Speter				int oldopts = _res.options;
233738032Speter
233838032Speter				_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
233990792Sgshapiro#endif /* NAMED_BIND */
234038032Speter				*p = '\0';
234164562Sgshapiro				hp = sm_gethostbyname(host, family);
234238032Speter				*p = '.';
234390792Sgshapiro#if NAMED_BIND
234438032Speter				_res.options = oldopts;
234590792Sgshapiro#endif /* NAMED_BIND */
234638032Speter			}
234738032Speter		}
234838032Spetergothostent:
2349203004Sgshapiro		if (hp == NULL || hp->h_addr == NULL)
235038032Speter		{
235190792Sgshapiro#if NAMED_BIND
235238032Speter			/* check for name server timeouts */
235390792Sgshapiro# if NETINET6
235490792Sgshapiro			if (WorkAroundBrokenAAAA && family == AF_INET6 &&
2355261370Sgshapiro			    (h_errno == TRY_AGAIN || errno == ETIMEDOUT))
235638032Speter			{
235790792Sgshapiro				/*
235890792Sgshapiro				**  An attempt with family AF_INET may
2359261370Sgshapiro				**  succeed. By skipping the next section
236090792Sgshapiro				**  of code, we will try AF_INET before
236190792Sgshapiro				**  failing.
236290792Sgshapiro				*/
236390792Sgshapiro
236490792Sgshapiro				if (tTd(16, 10))
236590792Sgshapiro					sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
236638032Speter			}
236790792Sgshapiro			else
236890792Sgshapiro# endif /* NETINET6 */
236990792Sgshapiro			{
237090792Sgshapiro				if (errno == ETIMEDOUT ||
2371168515Sgshapiro# if _FFR_GETHBN_ExFILE
2372168515Sgshapiro#  ifdef EMFILE
2373168515Sgshapiro				   errno == EMFILE ||
2374168515Sgshapiro#  endif /* EMFILE */
2375168515Sgshapiro#  ifdef ENFILE
2376168515Sgshapiro				   errno == ENFILE ||
2377168515Sgshapiro#  endif /* ENFILE */
2378168515Sgshapiro# endif /* _FFR_GETHBN_ExFILE */
237990792Sgshapiro				    h_errno == TRY_AGAIN ||
238090792Sgshapiro				    (errno == ECONNREFUSED && UseNameServer))
238190792Sgshapiro				{
238290792Sgshapiro					save_errno = errno;
238390792Sgshapiro					mci_setstat(mci, EX_TEMPFAIL,
238490792Sgshapiro						    "4.4.3", NULL);
238590792Sgshapiro					errno = save_errno;
238690792Sgshapiro					return EX_TEMPFAIL;
238790792Sgshapiro				}
238890792Sgshapiro			}
238990792Sgshapiro#endif /* NAMED_BIND */
239090792Sgshapiro#if NETINET6
239164562Sgshapiro			/*
239264562Sgshapiro			**  Try v6 first, then fall back to v4.
239364562Sgshapiro			**  If we found a v6 address, but no v4
239464562Sgshapiro			**  addresses, then TEMPFAIL.
239564562Sgshapiro			*/
239664562Sgshapiro
239764562Sgshapiro			if (family == AF_INET6)
239864562Sgshapiro			{
239964562Sgshapiro				family = AF_INET;
240064562Sgshapiro				goto v4retry;
240164562Sgshapiro			}
240264562Sgshapiro			if (v6found)
240364562Sgshapiro				goto v6tempfail;
240490792Sgshapiro#endif /* NETINET6 */
240564562Sgshapiro			save_errno = errno;
240638032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
240764562Sgshapiro			errno = save_errno;
240864562Sgshapiro			return EX_NOHOST;
240938032Speter		}
241038032Speter		addr.sa.sa_family = hp->h_addrtype;
241138032Speter		switch (hp->h_addrtype)
241238032Speter		{
241390792Sgshapiro#if NETINET
241438032Speter		  case AF_INET:
241564562Sgshapiro			memmove(&addr.sin.sin_addr,
241664562Sgshapiro				hp->h_addr,
241738032Speter				INADDRSZ);
241838032Speter			break;
241990792Sgshapiro#endif /* NETINET */
242038032Speter
242190792Sgshapiro#if NETINET6
242264562Sgshapiro		  case AF_INET6:
242364562Sgshapiro			memmove(&addr.sin6.sin6_addr,
242464562Sgshapiro				hp->h_addr,
242564562Sgshapiro				IN6ADDRSZ);
242664562Sgshapiro			break;
242790792Sgshapiro#endif /* NETINET6 */
242864562Sgshapiro
242938032Speter		  default:
2430168515Sgshapiro			if (hp->h_length > sizeof(addr.sa.sa_data))
243138032Speter			{
243238032Speter				syserr("makeconnection: long sa_data: family %d len %d",
243338032Speter					hp->h_addrtype, hp->h_length);
243438032Speter				mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
243564562Sgshapiro				errno = EINVAL;
243638032Speter				return EX_NOHOST;
243738032Speter			}
243890792Sgshapiro			memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
243938032Speter			break;
244038032Speter		}
244138032Speter		addrno = 1;
244238032Speter	}
244338032Speter
244438032Speter	/*
244538032Speter	**  Determine the port number.
244638032Speter	*/
244738032Speter
244838032Speter	if (port == 0)
244938032Speter	{
245090792Sgshapiro#ifdef NO_GETSERVBYNAME
245164562Sgshapiro		port = htons(25);
245290792Sgshapiro#else /* NO_GETSERVBYNAME */
245338032Speter		register struct servent *sp = getservbyname("smtp", "tcp");
245438032Speter
245538032Speter		if (sp == NULL)
245638032Speter		{
245738032Speter			if (LogLevel > 2)
245838032Speter				sm_syslog(LOG_ERR, NOQID,
245964562Sgshapiro					  "makeconnection: service \"smtp\" unknown");
246038032Speter			port = htons(25);
246138032Speter		}
246238032Speter		else
246338032Speter			port = sp->s_port;
246490792Sgshapiro#endif /* NO_GETSERVBYNAME */
246538032Speter	}
246638032Speter
246790792Sgshapiro#if NETINET6
246890792Sgshapiro	if (addr.sa.sa_family == AF_INET6 &&
246990792Sgshapiro	    IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
247090792Sgshapiro	    ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
247190792Sgshapiro	{
247290792Sgshapiro		/*
247390792Sgshapiro		**  Ignore mapped IPv4 address since
247490792Sgshapiro		**  there is a ClientPortOptions setting
247590792Sgshapiro		**  for IPv4.
247690792Sgshapiro		*/
247790792Sgshapiro
247890792Sgshapiro		goto nextaddr;
247990792Sgshapiro	}
248090792Sgshapiro#endif /* NETINET6 */
248190792Sgshapiro
248238032Speter	switch (addr.sa.sa_family)
248338032Speter	{
248490792Sgshapiro#if NETINET
248538032Speter	  case AF_INET:
248638032Speter		addr.sin.sin_port = port;
2487168515Sgshapiro		addrlen = sizeof(struct sockaddr_in);
248838032Speter		break;
248990792Sgshapiro#endif /* NETINET */
249038032Speter
249190792Sgshapiro#if NETINET6
249264562Sgshapiro	  case AF_INET6:
249364562Sgshapiro		addr.sin6.sin6_port = port;
2494168515Sgshapiro		addrlen = sizeof(struct sockaddr_in6);
249564562Sgshapiro		break;
249690792Sgshapiro#endif /* NETINET6 */
249764562Sgshapiro
249890792Sgshapiro#if NETISO
249938032Speter	  case AF_ISO:
250038032Speter		/* assume two byte transport selector */
250164562Sgshapiro		memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
2502168515Sgshapiro		addrlen = sizeof(struct sockaddr_iso);
250338032Speter		break;
250490792Sgshapiro#endif /* NETISO */
250538032Speter
250638032Speter	  default:
250738032Speter		syserr("Can't connect to address family %d", addr.sa.sa_family);
250838032Speter		mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
250964562Sgshapiro		errno = EINVAL;
251090792Sgshapiro#if NETINET6
251171345Sgshapiro		if (hp != NULL)
251271345Sgshapiro			freehostent(hp);
251390792Sgshapiro#endif /* NETINET6 */
251464562Sgshapiro		return EX_NOHOST;
251538032Speter	}
251638032Speter
251738032Speter	/*
251838032Speter	**  Try to actually open the connection.
251938032Speter	*/
252038032Speter
252190792Sgshapiro#if XLA
252238032Speter	/* if too many connections, don't bother trying */
252338032Speter	if (!xla_noqueue_ok(host))
252471345Sgshapiro	{
252590792Sgshapiro# if NETINET6
252671345Sgshapiro		if (hp != NULL)
252771345Sgshapiro			freehostent(hp);
252890792Sgshapiro# endif /* NETINET6 */
252938032Speter		return EX_TEMPFAIL;
253071345Sgshapiro	}
253190792Sgshapiro#endif /* XLA */
253238032Speter
253338032Speter	for (;;)
253438032Speter	{
253538032Speter		if (tTd(16, 1))
253690792Sgshapiro			sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
253790792Sgshapiro				   host, anynet_ntoa(&addr), ntohs(port),
253890792Sgshapiro				   (int) addr.sa.sa_family);
253938032Speter
254038032Speter		/* save for logging */
254138032Speter		CurHostAddr = addr;
254238032Speter
254390792Sgshapiro#if HASRRESVPORT
254438032Speter		if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
254538032Speter		{
254638032Speter			int rport = IPPORT_RESERVED - 1;
254738032Speter
254838032Speter			s = rresvport(&rport);
254938032Speter		}
255038032Speter		else
255190792Sgshapiro#endif /* HASRRESVPORT */
255238032Speter		{
255390792Sgshapiro			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
255438032Speter		}
255538032Speter		if (s < 0)
255638032Speter		{
255764562Sgshapiro			save_errno = errno;
255838032Speter			syserr("makeconnection: cannot create socket");
255990792Sgshapiro#if XLA
256038032Speter			xla_host_end(host);
256190792Sgshapiro#endif /* XLA */
256238032Speter			mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
256390792Sgshapiro#if NETINET6
256471345Sgshapiro			if (hp != NULL)
256571345Sgshapiro				freehostent(hp);
256690792Sgshapiro#endif /* NETINET6 */
256764562Sgshapiro			errno = save_errno;
256838032Speter			return EX_TEMPFAIL;
256938032Speter		}
257038032Speter
257190792Sgshapiro#ifdef SO_SNDBUF
257290792Sgshapiro		if (ClientSettings[family].d_tcpsndbufsize > 0)
257338032Speter		{
257438032Speter			if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
257590792Sgshapiro				       (char *) &ClientSettings[family].d_tcpsndbufsize,
257690792Sgshapiro				       sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
257738032Speter				syserr("makeconnection: setsockopt(SO_SNDBUF)");
257838032Speter		}
257990792Sgshapiro#endif /* SO_SNDBUF */
258090792Sgshapiro#ifdef SO_RCVBUF
258190792Sgshapiro		if (ClientSettings[family].d_tcprcvbufsize > 0)
258264562Sgshapiro		{
258364562Sgshapiro			if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
258490792Sgshapiro				       (char *) &ClientSettings[family].d_tcprcvbufsize,
258590792Sgshapiro				       sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
258664562Sgshapiro				syserr("makeconnection: setsockopt(SO_RCVBUF)");
258764562Sgshapiro		}
258890792Sgshapiro#endif /* SO_RCVBUF */
258938032Speter
259038032Speter		if (tTd(16, 1))
259190792Sgshapiro			sm_dprintf("makeconnection: fd=%d\n", s);
259238032Speter
259338032Speter		/* turn on network debugging? */
259438032Speter		if (tTd(16, 101))
259538032Speter		{
259638032Speter			int on = 1;
259764562Sgshapiro
259838032Speter			(void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
2599168515Sgshapiro					  (char *)&on, sizeof(on));
260038032Speter		}
260190792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
260290792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
260390792Sgshapiro		errno = 0;		/* for debugging */
260438032Speter
260564562Sgshapiro		if (clt_bind)
260664562Sgshapiro		{
260764562Sgshapiro			int on = 1;
260864562Sgshapiro
260964562Sgshapiro			switch (clt_addr.sa.sa_family)
261064562Sgshapiro			{
261190792Sgshapiro#if NETINET
261264562Sgshapiro			  case AF_INET:
261364562Sgshapiro				if (clt_addr.sin.sin_port != 0)
261464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
261564562Sgshapiro							  SO_REUSEADDR,
261664562Sgshapiro							  (char *) &on,
2617168515Sgshapiro							  sizeof(on));
261864562Sgshapiro				break;
261990792Sgshapiro#endif /* NETINET */
262064562Sgshapiro
262190792Sgshapiro#if NETINET6
262264562Sgshapiro			  case AF_INET6:
262364562Sgshapiro				if (clt_addr.sin6.sin6_port != 0)
262464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
262564562Sgshapiro							  SO_REUSEADDR,
262664562Sgshapiro							  (char *) &on,
2627168515Sgshapiro							  sizeof(on));
262864562Sgshapiro				break;
262990792Sgshapiro#endif /* NETINET6 */
263064562Sgshapiro			}
263164562Sgshapiro
263264562Sgshapiro			if (bind(s, &clt_addr.sa, socksize) < 0)
263364562Sgshapiro			{
263464562Sgshapiro				save_errno = errno;
263564562Sgshapiro				(void) close(s);
263664562Sgshapiro				errno = save_errno;
263764562Sgshapiro				syserr("makeconnection: cannot bind socket [%s]",
263864562Sgshapiro				       anynet_ntoa(&clt_addr));
263990792Sgshapiro#if NETINET6
264071345Sgshapiro				if (hp != NULL)
264171345Sgshapiro					freehostent(hp);
264290792Sgshapiro#endif /* NETINET6 */
264364562Sgshapiro				errno = save_errno;
264464562Sgshapiro				return EX_TEMPFAIL;
264564562Sgshapiro			}
264664562Sgshapiro		}
264764562Sgshapiro
264838032Speter		/*
264938032Speter		**  Linux seems to hang in connect for 90 minutes (!!!).
265038032Speter		**  Time out the connect to avoid this problem.
265138032Speter		*/
265238032Speter
265338032Speter		if (setjmp(CtxConnectTimeout) == 0)
265438032Speter		{
265538032Speter			int i;
265638032Speter
265738032Speter			if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
265890792Sgshapiro				ev = sm_setevent(TimeOuts.to_iconnect,
265990792Sgshapiro						 connecttimeout, 0);
266038032Speter			else if (TimeOuts.to_connect != 0)
266190792Sgshapiro				ev = sm_setevent(TimeOuts.to_connect,
266290792Sgshapiro						 connecttimeout, 0);
266338032Speter			else
266438032Speter				ev = NULL;
266538032Speter
266664562Sgshapiro			switch (ConnectOnlyTo.sa.sa_family)
266764562Sgshapiro			{
266890792Sgshapiro#if NETINET
266964562Sgshapiro			  case AF_INET:
267064562Sgshapiro				addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
2671223067Sgshapiro				addr.sa.sa_family = ConnectOnlyTo.sa.sa_family;
267264562Sgshapiro				break;
267390792Sgshapiro#endif /* NETINET */
267464562Sgshapiro
267590792Sgshapiro#if NETINET6
267664562Sgshapiro			  case AF_INET6:
267764562Sgshapiro				memmove(&addr.sin6.sin6_addr,
267864562Sgshapiro					&ConnectOnlyTo.sin6.sin6_addr,
267964562Sgshapiro					IN6ADDRSZ);
268064562Sgshapiro				break;
268190792Sgshapiro#endif /* NETINET6 */
268264562Sgshapiro			}
2683141858Sgshapiro			if (tTd(16, 1))
2684141858Sgshapiro				sm_dprintf("Connecting to [%s]...\n", anynet_ntoa(&addr));
268538032Speter			i = connect(s, (struct sockaddr *) &addr, addrlen);
268664562Sgshapiro			save_errno = errno;
268738032Speter			if (ev != NULL)
268890792Sgshapiro				sm_clrevent(ev);
268938032Speter			if (i >= 0)
269038032Speter				break;
269138032Speter		}
269238032Speter		else
269364562Sgshapiro			save_errno = errno;
269438032Speter
269594334Sgshapiro		/* couldn't connect.... figure out why */
269694334Sgshapiro		(void) close(s);
269794334Sgshapiro
269838032Speter		/* if running demand-dialed connection, try again */
269990792Sgshapiro		if (DialDelay > 0 && firstconnect &&
270090792Sgshapiro		    bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
270138032Speter		{
270238032Speter			if (tTd(16, 1))
270390792Sgshapiro				sm_dprintf("Connect failed (%s); trying again...\n",
270490792Sgshapiro					   sm_errstring(save_errno));
270590792Sgshapiro			firstconnect = false;
270664562Sgshapiro			(void) sleep(DialDelay);
270738032Speter			continue;
270838032Speter		}
270938032Speter
271090792Sgshapiro		if (LogLevel > 13)
271138032Speter			sm_syslog(LOG_INFO, e->e_id,
271238032Speter				  "makeconnection (%s [%s]) failed: %s",
271338032Speter				  host, anynet_ntoa(&addr),
271490792Sgshapiro				  sm_errstring(save_errno));
271538032Speter
271690792Sgshapiro#if NETINET6
271790792Sgshapironextaddr:
271890792Sgshapiro#endif /* NETINET6 */
271990792Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
272090792Sgshapiro		    (enough == 0 || curtime() < enough))
272138032Speter		{
272238032Speter			if (tTd(16, 1))
272390792Sgshapiro				sm_dprintf("Connect failed (%s); trying new address....\n",
272490792Sgshapiro					   sm_errstring(save_errno));
272538032Speter			switch (addr.sa.sa_family)
272638032Speter			{
272790792Sgshapiro#if NETINET
272838032Speter			  case AF_INET:
272964562Sgshapiro				memmove(&addr.sin.sin_addr,
273064562Sgshapiro					hp->h_addr_list[addrno++],
273164562Sgshapiro					INADDRSZ);
273238032Speter				break;
273390792Sgshapiro#endif /* NETINET */
273438032Speter
273590792Sgshapiro#if NETINET6
273664562Sgshapiro			  case AF_INET6:
273764562Sgshapiro				memmove(&addr.sin6.sin6_addr,
273864562Sgshapiro					hp->h_addr_list[addrno++],
273964562Sgshapiro					IN6ADDRSZ);
274064562Sgshapiro				break;
274190792Sgshapiro#endif /* NETINET6 */
274264562Sgshapiro
274338032Speter			  default:
274464562Sgshapiro				memmove(addr.sa.sa_data,
274564562Sgshapiro					hp->h_addr_list[addrno++],
274638032Speter					hp->h_length);
274738032Speter				break;
274838032Speter			}
274938032Speter			continue;
275038032Speter		}
275164562Sgshapiro		errno = save_errno;
275238032Speter
275390792Sgshapiro#if NETINET6
275464562Sgshapiro		if (family == AF_INET6)
275564562Sgshapiro		{
275664562Sgshapiro			if (tTd(16, 1))
275790792Sgshapiro				sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
275890792Sgshapiro					   sm_errstring(save_errno));
275990792Sgshapiro			v6found = true;
276064562Sgshapiro			family = AF_INET;
276171345Sgshapiro			if (hp != NULL)
276271345Sgshapiro			{
276371345Sgshapiro				freehostent(hp);
276471345Sgshapiro				hp = NULL;
276571345Sgshapiro			}
276664562Sgshapiro			goto v4retry;
276764562Sgshapiro		}
276864562Sgshapiro	v6tempfail:
276990792Sgshapiro#endif /* NETINET6 */
277038032Speter		/* couldn't open connection */
277190792Sgshapiro#if NETINET6
277264562Sgshapiro		/* Don't clobber an already saved errno from v4retry */
277364562Sgshapiro		if (errno > 0)
277490792Sgshapiro#endif /* NETINET6 */
277564562Sgshapiro			save_errno = errno;
277664562Sgshapiro		if (tTd(16, 1))
277790792Sgshapiro			sm_dprintf("Connect failed (%s)\n",
277890792Sgshapiro				   sm_errstring(save_errno));
277990792Sgshapiro#if XLA
278038032Speter		xla_host_end(host);
278190792Sgshapiro#endif /* XLA */
278238032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
278390792Sgshapiro#if NETINET6
278471345Sgshapiro		if (hp != NULL)
278571345Sgshapiro			freehostent(hp);
278690792Sgshapiro#endif /* NETINET6 */
278764562Sgshapiro		errno = save_errno;
278838032Speter		return EX_TEMPFAIL;
278938032Speter	}
279038032Speter
279190792Sgshapiro#if NETINET6
279271345Sgshapiro	if (hp != NULL)
279371345Sgshapiro	{
279471345Sgshapiro		freehostent(hp);
279571345Sgshapiro		hp = NULL;
279671345Sgshapiro	}
279790792Sgshapiro#endif /* NETINET6 */
279871345Sgshapiro
279938032Speter	/* connection ok, put it into canonical form */
280064562Sgshapiro	mci->mci_out = NULL;
280190792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
280290792Sgshapiro				       (void *) &s,
2803132943Sgshapiro				       SM_IO_WRONLY_B, NULL)) == NULL ||
280438032Speter	    (s = dup(s)) < 0 ||
280590792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
280690792Sgshapiro				      (void *) &s,
2807132943Sgshapiro				      SM_IO_RDONLY_B, NULL)) == NULL)
280838032Speter	{
280964562Sgshapiro		save_errno = errno;
281038032Speter		syserr("cannot open SMTP client channel, fd=%d", s);
281138032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
281264562Sgshapiro		if (mci->mci_out != NULL)
281390792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
281464562Sgshapiro		(void) close(s);
281564562Sgshapiro		errno = save_errno;
281638032Speter		return EX_TEMPFAIL;
281738032Speter	}
281890792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
281938032Speter
282090792Sgshapiro	/* set {client_flags} */
282190792Sgshapiro	if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
282290792Sgshapiro	{
282390792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
282490792Sgshapiro			  macid("{client_flags}"),
282590792Sgshapiro			  ClientSettings[addr.sa.sa_family].d_mflags);
282690792Sgshapiro	}
282790792Sgshapiro	else
282890792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
282990792Sgshapiro			  macid("{client_flags}"), "");
283090792Sgshapiro
283190792Sgshapiro	/* "add" {client_flags} to bitmap */
283290792Sgshapiro	if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
283390792Sgshapiro	{
283490792Sgshapiro		/* look for just this one flag */
283590792Sgshapiro		setbitn(D_IFNHELO, d_flags);
283690792Sgshapiro	}
283790792Sgshapiro
283864562Sgshapiro	/* find out name for Interface through which we connect */
2839168515Sgshapiro	len = sizeof(addr);
284064562Sgshapiro	if (getsockname(s, &addr.sa, &len) == 0)
284164562Sgshapiro	{
284264562Sgshapiro		char *name;
284390792Sgshapiro		char family[5];
284464562Sgshapiro
284590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
284690792Sgshapiro			macid("{if_addr_out}"), anynet_ntoa(&addr));
284790792Sgshapiro		(void) sm_snprintf(family, sizeof(family), "%d",
284890792Sgshapiro			addr.sa.sa_family);
284990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
285090792Sgshapiro			macid("{if_family_out}"), family);
285164562Sgshapiro
285264562Sgshapiro		name = hostnamebyanyaddr(&addr);
285390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
285490792Sgshapiro			macid("{if_name_out}"), name);
285564562Sgshapiro		if (LogLevel > 11)
285664562Sgshapiro		{
285764562Sgshapiro			/* log connection information */
285864562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
285964562Sgshapiro				  "SMTP outgoing connect on %.40s", name);
286064562Sgshapiro		}
286164562Sgshapiro		if (bitnset(D_IFNHELO, d_flags))
286264562Sgshapiro		{
286364562Sgshapiro			if (name[0] != '[' && strchr(name, '.') != NULL)
286464562Sgshapiro				mci->mci_heloname = newstr(name);
286564562Sgshapiro		}
286664562Sgshapiro	}
286764562Sgshapiro	else
286864562Sgshapiro	{
286990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
287090792Sgshapiro			macid("{if_name_out}"), NULL);
287190792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
287290792Sgshapiro			macid("{if_addr_out}"), NULL);
287390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
287490792Sgshapiro			macid("{if_family_out}"), NULL);
287564562Sgshapiro	}
2876132943Sgshapiro
2877132943Sgshapiro	/* Use the configured HeloName as appropriate */
2878132943Sgshapiro	if (HeloName != NULL && HeloName[0] != '\0')
2879223067Sgshapiro	{
2880223067Sgshapiro		SM_FREE_CLR(mci->mci_heloname);
2881132943Sgshapiro		mci->mci_heloname = newstr(HeloName);
2882223067Sgshapiro	}
2883132943Sgshapiro
288438032Speter	mci_setstat(mci, EX_OK, NULL, NULL);
288564562Sgshapiro	return EX_OK;
288638032Speter}
288764562Sgshapiro
288864562Sgshapirostatic void
2889141858Sgshapiroconnecttimeout(ignore)
2890141858Sgshapiro	int ignore;
289164562Sgshapiro{
289277349Sgshapiro	/*
289377349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
289477349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
289577349Sgshapiro	**	DOING.
289677349Sgshapiro	*/
289777349Sgshapiro
289864562Sgshapiro	errno = ETIMEDOUT;
289964562Sgshapiro	longjmp(CtxConnectTimeout, 1);
290064562Sgshapiro}
290190792Sgshapiro/*
290264562Sgshapiro**  MAKECONNECTION_DS -- make a connection to a domain socket.
290364562Sgshapiro**
290464562Sgshapiro**	Parameters:
290564562Sgshapiro**		mux_path -- the path of the socket to connect to.
290664562Sgshapiro**		mci -- a pointer to the mail connection information
290764562Sgshapiro**			structure to be filled in.
290864562Sgshapiro**
290964562Sgshapiro**	Returns:
291064562Sgshapiro**		An exit code telling whether the connection could be
291164562Sgshapiro**			made and if not why not.
291264562Sgshapiro**
291364562Sgshapiro**	Side Effects:
291464562Sgshapiro**		none.
291564562Sgshapiro*/
291664562Sgshapiro
291790792Sgshapiro#if NETUNIX
291890792Sgshapiroint
291990792Sgshapiromakeconnection_ds(mux_path, mci)
292064562Sgshapiro	char *mux_path;
292164562Sgshapiro	register MCI *mci;
292264562Sgshapiro{
292364562Sgshapiro	int sock;
292464562Sgshapiro	int rval, save_errno;
292564562Sgshapiro	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK;
292664562Sgshapiro	struct sockaddr_un unix_addr;
292764562Sgshapiro
292864562Sgshapiro	/* if not safe, don't connect */
292964562Sgshapiro	rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName,
293064562Sgshapiro			sff, S_IRUSR|S_IWUSR, NULL);
293164562Sgshapiro
293264562Sgshapiro	if (rval != 0)
293364562Sgshapiro	{
2934132943Sgshapiro		syserr("makeconnection_ds: unsafe domain socket %s",
2935132943Sgshapiro			mux_path);
293664562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL);
293764562Sgshapiro		errno = rval;
293864562Sgshapiro		return EX_TEMPFAIL;
293964562Sgshapiro	}
294064562Sgshapiro
294164562Sgshapiro	/* prepare address structure */
2942168515Sgshapiro	memset(&unix_addr, '\0', sizeof(unix_addr));
294364562Sgshapiro	unix_addr.sun_family = AF_UNIX;
294464562Sgshapiro
2945168515Sgshapiro	if (strlen(mux_path) >= sizeof(unix_addr.sun_path))
294664562Sgshapiro	{
2947132943Sgshapiro		syserr("makeconnection_ds: domain socket name %s too long",
2948132943Sgshapiro			mux_path);
294990792Sgshapiro
295090792Sgshapiro		/* XXX why TEMPFAIL but 5.x.y ? */
295164562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
295264562Sgshapiro		errno = ENAMETOOLONG;
295364562Sgshapiro		return EX_UNAVAILABLE;
295464562Sgshapiro	}
295590792Sgshapiro	(void) sm_strlcpy(unix_addr.sun_path, mux_path,
2956168515Sgshapiro			  sizeof(unix_addr.sun_path));
295764562Sgshapiro
295864562Sgshapiro	/* initialize domain socket */
295964562Sgshapiro	sock = socket(AF_UNIX, SOCK_STREAM, 0);
296064562Sgshapiro	if (sock == -1)
296164562Sgshapiro	{
296264562Sgshapiro		save_errno = errno;
2963132943Sgshapiro		syserr("makeconnection_ds: could not create domain socket %s",
2964132943Sgshapiro			mux_path);
296564562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
296664562Sgshapiro		errno = save_errno;
296764562Sgshapiro		return EX_TEMPFAIL;
296864562Sgshapiro	}
296964562Sgshapiro
297064562Sgshapiro	/* connect to server */
297164562Sgshapiro	if (connect(sock, (struct sockaddr *) &unix_addr,
297264562Sgshapiro		    sizeof(unix_addr)) == -1)
297364562Sgshapiro	{
297464562Sgshapiro		save_errno = errno;
297564562Sgshapiro		syserr("Could not connect to socket %s", mux_path);
297664562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
297764562Sgshapiro		(void) close(sock);
297864562Sgshapiro		errno = save_errno;
297964562Sgshapiro		return EX_TEMPFAIL;
298064562Sgshapiro	}
298164562Sgshapiro
298264562Sgshapiro	/* connection ok, put it into canonical form */
298364562Sgshapiro	mci->mci_out = NULL;
298490792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2985132943Sgshapiro				       (void *) &sock, SM_IO_WRONLY_B, NULL))
298690792Sgshapiro					== NULL
298790792Sgshapiro	    || (sock = dup(sock)) < 0 ||
298890792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2989132943Sgshapiro				      (void *) &sock, SM_IO_RDONLY_B, NULL))
299090792Sgshapiro					== NULL)
299164562Sgshapiro	{
299264562Sgshapiro		save_errno = errno;
299364562Sgshapiro		syserr("cannot open SMTP client channel, fd=%d", sock);
299464562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
299564562Sgshapiro		if (mci->mci_out != NULL)
299690792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
299764562Sgshapiro		(void) close(sock);
299864562Sgshapiro		errno = save_errno;
299964562Sgshapiro		return EX_TEMPFAIL;
300064562Sgshapiro	}
300190792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
300264562Sgshapiro
300364562Sgshapiro	mci_setstat(mci, EX_OK, NULL, NULL);
300464562Sgshapiro	errno = 0;
300564562Sgshapiro	return EX_OK;
300664562Sgshapiro}
300790792Sgshapiro#endif /* NETUNIX */
300890792Sgshapiro/*
300990792Sgshapiro**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
301077349Sgshapiro**
301177349Sgshapiro**	Parameters:
301290792Sgshapiro**		none.
301377349Sgshapiro**
301477349Sgshapiro**	Returns:
301577349Sgshapiro**		none.
301677349Sgshapiro**
301777349Sgshapiro**	Side Effects:
301890792Sgshapiro**		closes control socket, exits.
301977349Sgshapiro*/
302077349Sgshapiro
302190792Sgshapirovoid
302290792Sgshapiroshutdown_daemon()
302377349Sgshapiro{
302490792Sgshapiro	int i;
302590792Sgshapiro	char *reason;
302677349Sgshapiro
302790792Sgshapiro	sm_allsignals(true);
302890792Sgshapiro
302990792Sgshapiro	reason = ShutdownRequest;
303090792Sgshapiro	ShutdownRequest = NULL;
303190792Sgshapiro	PendingSignal = 0;
303290792Sgshapiro
3033132943Sgshapiro	if (LogLevel > 9)
3034132943Sgshapiro		sm_syslog(LOG_INFO, CurEnv->e_id, "stopping daemon, reason=%s",
303590792Sgshapiro			  reason == NULL ? "implicit call" : reason);
303690792Sgshapiro
303790792Sgshapiro	FileName = NULL;
303890792Sgshapiro	closecontrolsocket(true);
303990792Sgshapiro#if XLA
304090792Sgshapiro	xla_all_end();
304190792Sgshapiro#endif /* XLA */
304290792Sgshapiro
304390792Sgshapiro	for (i = 0; i < NDaemons; i++)
304490792Sgshapiro	{
304590792Sgshapiro		if (Daemons[i].d_socket >= 0)
304690792Sgshapiro		{
304790792Sgshapiro			(void) close(Daemons[i].d_socket);
304890792Sgshapiro			Daemons[i].d_socket = -1;
304990792Sgshapiro
305090792Sgshapiro#if _FFR_DAEMON_NETUNIX
305190792Sgshapiro# if NETUNIX
305290792Sgshapiro			/* Remove named sockets */
305390792Sgshapiro			if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
305490792Sgshapiro			{
305590792Sgshapiro				int rval;
305690792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
305790792Sgshapiro
305890792Sgshapiro				/* if not safe, don't use it */
305990792Sgshapiro				rval = safefile(Daemons[i].d_addr.sunix.sun_path,
306090792Sgshapiro						RunAsUid, RunAsGid,
306190792Sgshapiro						RunAsUserName, sff,
306290792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
306390792Sgshapiro				if (rval == 0 &&
306490792Sgshapiro				    unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
306590792Sgshapiro				{
306690792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
306790792Sgshapiro						  "Could not remove daemon %s socket: %s: %s",
306890792Sgshapiro						  Daemons[i].d_name,
306990792Sgshapiro						  Daemons[i].d_addr.sunix.sun_path,
307090792Sgshapiro						  sm_errstring(errno));
307190792Sgshapiro				}
307290792Sgshapiro			}
307390792Sgshapiro# endif /* NETUNIX */
307490792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
307590792Sgshapiro		}
307690792Sgshapiro	}
307790792Sgshapiro
307890792Sgshapiro	finis(false, true, EX_OK);
307977349Sgshapiro}
308090792Sgshapiro/*
308177349Sgshapiro**  RESTART_DAEMON -- Performs a clean restart of the daemon
308277349Sgshapiro**
308377349Sgshapiro**	Parameters:
308477349Sgshapiro**		none.
308577349Sgshapiro**
308677349Sgshapiro**	Returns:
308777349Sgshapiro**		none.
308877349Sgshapiro**
308977349Sgshapiro**	Side Effects:
309077349Sgshapiro**		restarts the daemon or exits if restart fails.
309177349Sgshapiro*/
309277349Sgshapiro
309380785Sgshapiro/* Make a non-DFL/IGN signal a noop */
309480785Sgshapiro#define SM_NOOP_SIGNAL(sig, old)				\
309580785Sgshapirodo								\
309680785Sgshapiro{								\
309790792Sgshapiro	(old) = sm_signal((sig), sm_signal_noop);		\
309880785Sgshapiro	if ((old) == SIG_IGN || (old) == SIG_DFL)		\
309990792Sgshapiro		(void) sm_signal((sig), (old));			\
310080785Sgshapiro} while (0)
310180785Sgshapiro
310290792Sgshapirovoid
310377349Sgshapirorestart_daemon()
310477349Sgshapiro{
310590792Sgshapiro	bool drop;
310677349Sgshapiro	int save_errno;
310777349Sgshapiro	char *reason;
310880785Sgshapiro	sigfunc_t ignore, oalrm, ousr1;
310977349Sgshapiro	extern int DtableSize;
311077349Sgshapiro
311180785Sgshapiro	/* clear the events to turn off SIGALRMs */
311290792Sgshapiro	sm_clear_events();
311390792Sgshapiro	sm_allsignals(true);
311477349Sgshapiro
311577349Sgshapiro	reason = RestartRequest;
311677349Sgshapiro	RestartRequest = NULL;
311777349Sgshapiro	PendingSignal = 0;
311877349Sgshapiro
311977349Sgshapiro	if (SaveArgv[0][0] != '/')
312077349Sgshapiro	{
312177349Sgshapiro		if (LogLevel > 3)
312277349Sgshapiro			sm_syslog(LOG_INFO, NOQID,
312377349Sgshapiro				  "could not restart: need full path");
312490792Sgshapiro		finis(false, true, EX_OSFILE);
312590792Sgshapiro		/* NOTREACHED */
312677349Sgshapiro	}
312777349Sgshapiro	if (LogLevel > 3)
312877349Sgshapiro		sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
312977349Sgshapiro			  SaveArgv[0],
313077349Sgshapiro			  reason == NULL ? "implicit call" : reason);
313177349Sgshapiro
313290792Sgshapiro	closecontrolsocket(true);
313398121Sgshapiro#if SM_CONF_SHM
313498121Sgshapiro	cleanup_shm(DaemonPid == getpid());
313598121Sgshapiro#endif /* SM_CONF_SHM */
313690792Sgshapiro
3137132943Sgshapiro	/* close locked pid file */
3138132943Sgshapiro	close_sendmail_pid();
3139132943Sgshapiro
314090792Sgshapiro	/*
314190792Sgshapiro	**  Want to drop to the user who started the process in all cases
314290792Sgshapiro	**  *but* when running as "smmsp" for the clientmqueue queue run
314390792Sgshapiro	**  daemon.  In that case, UseMSP will be true, RunAsUid should not
314490792Sgshapiro	**  be root, and RealUid should be either 0 or RunAsUid.
314590792Sgshapiro	*/
314690792Sgshapiro
314790792Sgshapiro	drop = !(UseMSP && RunAsUid != 0 &&
314890792Sgshapiro		 (RealUid == 0 || RealUid == RunAsUid));
314990792Sgshapiro
315090792Sgshapiro	if (drop_privileges(drop) != EX_OK)
315177349Sgshapiro	{
315277349Sgshapiro		if (LogLevel > 0)
315377349Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
315490792Sgshapiro				  "could not drop privileges: %s",
315590792Sgshapiro				  sm_errstring(errno));
315690792Sgshapiro		finis(false, true, EX_OSERR);
315790792Sgshapiro		/* NOTREACHED */
315877349Sgshapiro	}
315977349Sgshapiro
3160132943Sgshapiro	sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
316177349Sgshapiro
316280785Sgshapiro	/*
316380785Sgshapiro	**  Need to allow signals before execve() to make them "harmless".
316480785Sgshapiro	**  However, the default action can be "terminate", so it isn't
316580785Sgshapiro	**  really harmless.  Setting signals to IGN will cause them to be
316680785Sgshapiro	**  ignored in the new process to, so that isn't a good alternative.
316780785Sgshapiro	*/
316880785Sgshapiro
316980785Sgshapiro	SM_NOOP_SIGNAL(SIGALRM, oalrm);
317080785Sgshapiro	SM_NOOP_SIGNAL(SIGCHLD, ignore);
317180785Sgshapiro	SM_NOOP_SIGNAL(SIGHUP, ignore);
317280785Sgshapiro	SM_NOOP_SIGNAL(SIGINT, ignore);
317380785Sgshapiro	SM_NOOP_SIGNAL(SIGPIPE, ignore);
317480785Sgshapiro	SM_NOOP_SIGNAL(SIGTERM, ignore);
317580785Sgshapiro#ifdef SIGUSR1
317680785Sgshapiro	SM_NOOP_SIGNAL(SIGUSR1, ousr1);
317780785Sgshapiro#endif /* SIGUSR1 */
317894334Sgshapiro
317994334Sgshapiro	/* Turn back on signals */
318090792Sgshapiro	sm_allsignals(false);
318177349Sgshapiro
318277349Sgshapiro	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
318377349Sgshapiro	save_errno = errno;
318477349Sgshapiro
318580785Sgshapiro	/* block signals again and restore needed signals */
318690792Sgshapiro	sm_allsignals(true);
318780785Sgshapiro
318880785Sgshapiro	/* For finis() events */
318990792Sgshapiro	(void) sm_signal(SIGALRM, oalrm);
319080785Sgshapiro
319180785Sgshapiro#ifdef SIGUSR1
319280785Sgshapiro	/* For debugging finis() */
319390792Sgshapiro	(void) sm_signal(SIGUSR1, ousr1);
319480785Sgshapiro#endif /* SIGUSR1 */
319577349Sgshapiro
319677349Sgshapiro	errno = save_errno;
319777349Sgshapiro	if (LogLevel > 0)
319890792Sgshapiro		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
319990792Sgshapiro			  SaveArgv[0], sm_errstring(errno));
320090792Sgshapiro	finis(false, true, EX_OSFILE);
320190792Sgshapiro	/* NOTREACHED */
320277349Sgshapiro}
320390792Sgshapiro/*
320438032Speter**  MYHOSTNAME -- return the name of this host.
320538032Speter**
320638032Speter**	Parameters:
320738032Speter**		hostbuf -- a place to return the name of this host.
320838032Speter**		size -- the size of hostbuf.
320938032Speter**
321038032Speter**	Returns:
321138032Speter**		A list of aliases for this host.
321238032Speter**
321338032Speter**	Side Effects:
321438032Speter**		Adds numeric codes to $=w.
321538032Speter*/
321638032Speter
321738032Speterstruct hostent *
321838032Spetermyhostname(hostbuf, size)
321938032Speter	char hostbuf[];
322038032Speter	int size;
322138032Speter{
322238032Speter	register struct hostent *hp;
322338032Speter
322473188Sgshapiro	if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
322590792Sgshapiro		(void) sm_strlcpy(hostbuf, "localhost", size);
322664562Sgshapiro	hp = sm_gethostbyname(hostbuf, InetMode);
322790792Sgshapiro#if NETINET && NETINET6
322880785Sgshapiro	if (hp == NULL && InetMode == AF_INET6)
322980785Sgshapiro	{
323080785Sgshapiro		/*
323180785Sgshapiro		**  It's possible that this IPv6 enabled machine doesn't
323280785Sgshapiro		**  actually have any IPv6 interfaces and, therefore, no
323380785Sgshapiro		**  IPv6 addresses.  Fall back to AF_INET.
323480785Sgshapiro		*/
323580785Sgshapiro
323680785Sgshapiro		hp = sm_gethostbyname(hostbuf, AF_INET);
323780785Sgshapiro	}
323890792Sgshapiro#endif /* NETINET && NETINET6 */
323938032Speter	if (hp == NULL)
324038032Speter		return NULL;
324138032Speter	if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
324264562Sgshapiro		(void) cleanstrcpy(hostbuf, hp->h_name, size);
324364562Sgshapiro
324490792Sgshapiro#if NETINFO
324564562Sgshapiro	if (strchr(hostbuf, '.') == NULL)
324638032Speter	{
324764562Sgshapiro		char *domainname;
324864562Sgshapiro
324964562Sgshapiro		domainname = ni_propval("/locations", NULL, "resolver",
325064562Sgshapiro					"domain", '\0');
325164562Sgshapiro		if (domainname != NULL &&
325264562Sgshapiro		    strlen(domainname) + strlen(hostbuf) + 1 < size)
325390792Sgshapiro			(void) sm_strlcat2(hostbuf, ".", domainname, size);
325438032Speter	}
325590792Sgshapiro#endif /* NETINFO */
325638032Speter
325738032Speter	/*
325838032Speter	**  If there is still no dot in the name, try looking for a
325938032Speter	**  dotted alias.
326038032Speter	*/
326138032Speter
326238032Speter	if (strchr(hostbuf, '.') == NULL)
326338032Speter	{
326438032Speter		char **ha;
326538032Speter
326664562Sgshapiro		for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
326738032Speter		{
326838032Speter			if (strchr(*ha, '.') != NULL)
326938032Speter			{
327064562Sgshapiro				(void) cleanstrcpy(hostbuf, *ha, size - 1);
327138032Speter				hostbuf[size - 1] = '\0';
327238032Speter				break;
327338032Speter			}
327438032Speter		}
327538032Speter	}
327638032Speter
327738032Speter	/*
327838032Speter	**  If _still_ no dot, wait for a while and try again -- it is
327938032Speter	**  possible that some service is starting up.  This can result
328038032Speter	**  in excessive delays if the system is badly configured, but
328138032Speter	**  there really isn't a way around that, particularly given that
328238032Speter	**  the config file hasn't been read at this point.
328338032Speter	**  All in all, a bit of a mess.
328438032Speter	*/
328538032Speter
328638032Speter	if (strchr(hostbuf, '.') == NULL &&
328790792Sgshapiro	    !getcanonname(hostbuf, size, true, NULL))
328838032Speter	{
3289182352Sgshapiro		sm_syslog(LocalDaemon ? LOG_WARNING : LOG_CRIT, NOQID,
329064562Sgshapiro			  "My unqualified host name (%s) unknown; sleeping for retry",
329164562Sgshapiro			  hostbuf);
329238032Speter		message("My unqualified host name (%s) unknown; sleeping for retry",
329338032Speter			hostbuf);
329464562Sgshapiro		(void) sleep(60);
329590792Sgshapiro		if (!getcanonname(hostbuf, size, true, NULL))
329638032Speter		{
3297182352Sgshapiro			sm_syslog(LocalDaemon ? LOG_WARNING : LOG_ALERT, NOQID,
329864562Sgshapiro				  "unable to qualify my own domain name (%s) -- using short name",
329964562Sgshapiro				  hostbuf);
330038032Speter			message("WARNING: unable to qualify my own domain name (%s) -- using short name",
330138032Speter				hostbuf);
330238032Speter		}
330338032Speter	}
330464562Sgshapiro	return hp;
330538032Speter}
330690792Sgshapiro/*
330738032Speter**  ADDRCMP -- compare two host addresses
330838032Speter**
330938032Speter**	Parameters:
331038032Speter**		hp -- hostent structure for the first address
331138032Speter**		ha -- actual first address
331238032Speter**		sa -- second address
331338032Speter**
331438032Speter**	Returns:
331538032Speter**		0 -- if ha and sa match
331638032Speter**		else -- they don't match
331738032Speter*/
331838032Speter
331964562Sgshapirostatic int
332038032Speteraddrcmp(hp, ha, sa)
332138032Speter	struct hostent *hp;
332238032Speter	char *ha;
332338032Speter	SOCKADDR *sa;
332438032Speter{
332590792Sgshapiro#if NETINET6
332690792Sgshapiro	unsigned char *a;
332790792Sgshapiro#endif /* NETINET6 */
332864562Sgshapiro
332938032Speter	switch (sa->sa.sa_family)
333038032Speter	{
333190792Sgshapiro#if NETINET
333238032Speter	  case AF_INET:
333338032Speter		if (hp->h_addrtype == AF_INET)
333464562Sgshapiro			return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
333538032Speter		break;
333690792Sgshapiro#endif /* NETINET */
333738032Speter
333890792Sgshapiro#if NETINET6
333964562Sgshapiro	  case AF_INET6:
334090792Sgshapiro		a = (unsigned char *) &sa->sin6.sin6_addr;
334164562Sgshapiro
334264562Sgshapiro		/* Straight binary comparison */
334364562Sgshapiro		if (hp->h_addrtype == AF_INET6)
334464562Sgshapiro			return memcmp(ha, a, IN6ADDRSZ);
334564562Sgshapiro
334664562Sgshapiro		/* If IPv4-mapped IPv6 address, compare the IPv4 section */
334764562Sgshapiro		if (hp->h_addrtype == AF_INET &&
334864562Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
334964562Sgshapiro			return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
335064562Sgshapiro		break;
335190792Sgshapiro#endif /* NETINET6 */
335238032Speter	}
335338032Speter	return -1;
335438032Speter}
335590792Sgshapiro/*
335664562Sgshapiro**  GETAUTHINFO -- get the real host name associated with a file descriptor
335738032Speter**
335838032Speter**	Uses RFC1413 protocol to try to get info from the other end.
335938032Speter**
336038032Speter**	Parameters:
336138032Speter**		fd -- the descriptor
336290792Sgshapiro**		may_be_forged -- an outage that is set to true if the
336338032Speter**			forward lookup of RealHostName does not match
336490792Sgshapiro**			RealHostAddr; set to false if they do match.
336538032Speter**
336638032Speter**	Returns:
336738032Speter**		The user@host information associated with this descriptor.
336838032Speter*/
336938032Speter
337038032Speterstatic jmp_buf	CtxAuthTimeout;
337138032Speter
337238032Speterstatic void
3373141858Sgshapiroauthtimeout(ignore)
3374141858Sgshapiro	int ignore;
337538032Speter{
337677349Sgshapiro	/*
337777349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
337877349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
337977349Sgshapiro	**	DOING.
338077349Sgshapiro	*/
338177349Sgshapiro
338277349Sgshapiro	errno = ETIMEDOUT;
338338032Speter	longjmp(CtxAuthTimeout, 1);
338438032Speter}
338538032Speter
338638032Speterchar *
338738032Spetergetauthinfo(fd, may_be_forged)
338838032Speter	int fd;
338938032Speter	bool *may_be_forged;
339038032Speter{
339190792Sgshapiro	unsigned short SM_NONVOLATILE port = 0;
339238032Speter	SOCKADDR_LEN_T falen;
339338032Speter	register char *volatile p = NULL;
339438032Speter	SOCKADDR la;
339538032Speter	SOCKADDR_LEN_T lalen;
339690792Sgshapiro#ifndef NO_GETSERVBYNAME
339738032Speter	register struct servent *sp;
339890792Sgshapiro# if NETINET
339990792Sgshapiro	static unsigned short port4 = 0;
340090792Sgshapiro# endif /* NETINET */
340190792Sgshapiro# if NETINET6
340290792Sgshapiro	static unsigned short port6 = 0;
340390792Sgshapiro# endif /* NETINET6 */
340490792Sgshapiro#endif /* ! NO_GETSERVBYNAME */
340538032Speter	volatile int s;
340638032Speter	int i = 0;
340790792Sgshapiro	size_t len;
340890792Sgshapiro	SM_EVENT *ev;
340938032Speter	int nleft;
341038032Speter	struct hostent *hp;
341138032Speter	char *ostype = NULL;
341238032Speter	char **ha;
341338032Speter	char ibuf[MAXNAME + 1];
3414110560Sgshapiro	static char hbuf[MAXNAME + MAXAUTHINFO + 11];
341538032Speter
341690792Sgshapiro	*may_be_forged = false;
3417168515Sgshapiro	falen = sizeof(RealHostAddr);
341838032Speter	if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
341938032Speter	    falen <= 0 || RealHostAddr.sa.sa_family == 0)
342038032Speter	{
342164562Sgshapiro		if (i < 0)
342264562Sgshapiro		{
342364562Sgshapiro			/*
342464562Sgshapiro			**  ENOTSOCK is OK: bail on anything else, but reset
342564562Sgshapiro			**  errno in this case, so a mis-report doesn't
342664562Sgshapiro			**  happen later.
342764562Sgshapiro			*/
342890792Sgshapiro
342964562Sgshapiro			if (errno != ENOTSOCK)
343064562Sgshapiro				return NULL;
343164562Sgshapiro			errno = 0;
343264562Sgshapiro		}
3433168515Sgshapiro		(void) sm_strlcpyn(hbuf, sizeof(hbuf), 2, RealUserName,
343490792Sgshapiro				   "@localhost");
343538032Speter		if (tTd(9, 1))
343690792Sgshapiro			sm_dprintf("getauthinfo: %s\n", hbuf);
343738032Speter		return hbuf;
343838032Speter	}
343938032Speter
344038032Speter	if (RealHostName == NULL)
344138032Speter	{
344238032Speter		/* translate that to a host name */
344338032Speter		RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
344438032Speter		if (strlen(RealHostName) > MAXNAME)
344590792Sgshapiro			RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
344638032Speter	}
344738032Speter
344838032Speter	/* cross check RealHostName with forward DNS lookup */
344990792Sgshapiro	if (anynet_ntoa(&RealHostAddr)[0] != '[' &&
345090792Sgshapiro	    RealHostName[0] != '[')
345138032Speter	{
345280785Sgshapiro		int family;
345380785Sgshapiro
345480785Sgshapiro		family = RealHostAddr.sa.sa_family;
345590792Sgshapiro#if NETINET6 && NEEDSGETIPNODE
345680785Sgshapiro		/*
345780785Sgshapiro		**  If RealHostAddr is an IPv6 connection with an
345880785Sgshapiro		**  IPv4-mapped address, we need RealHostName's IPv4
345980785Sgshapiro		**  address(es) for addrcmp() to compare against
346080785Sgshapiro		**  RealHostAddr.
346180785Sgshapiro		**
346280785Sgshapiro		**  Actually, we only need to do this for systems
346380785Sgshapiro		**  which NEEDSGETIPNODE since the real getipnodebyname()
346480785Sgshapiro		**  already does V4MAPPED address via the AI_V4MAPPEDCFG
346580785Sgshapiro		**  flag.  A better fix to this problem is to add this
346680785Sgshapiro		**  functionality to our stub getipnodebyname().
346780785Sgshapiro		*/
346880785Sgshapiro
346980785Sgshapiro		if (family == AF_INET6 &&
347080785Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
347180785Sgshapiro			family = AF_INET;
347290792Sgshapiro#endif /* NETINET6 && NEEDSGETIPNODE */
347380785Sgshapiro
347438032Speter		/* try to match the reverse against the forward lookup */
347580785Sgshapiro		hp = sm_gethostbyname(RealHostName, family);
347638032Speter		if (hp == NULL)
3477120256Sgshapiro		{
3478132943Sgshapiro			/* XXX: Could be a temporary error on forward lookup */
347990792Sgshapiro			*may_be_forged = true;
3480120256Sgshapiro		}
348138032Speter		else
348238032Speter		{
348338032Speter			for (ha = hp->h_addr_list; *ha != NULL; ha++)
348490792Sgshapiro			{
348538032Speter				if (addrcmp(hp, *ha, &RealHostAddr) == 0)
348638032Speter					break;
348790792Sgshapiro			}
348838032Speter			*may_be_forged = *ha == NULL;
348990792Sgshapiro#if NETINET6
349071345Sgshapiro			freehostent(hp);
349171345Sgshapiro			hp = NULL;
349290792Sgshapiro#endif /* NETINET6 */
349338032Speter		}
349438032Speter	}
349538032Speter
349638032Speter	if (TimeOuts.to_ident == 0)
349738032Speter		goto noident;
349838032Speter
3499168515Sgshapiro	lalen = sizeof(la);
350064562Sgshapiro	switch (RealHostAddr.sa.sa_family)
350138032Speter	{
350290792Sgshapiro#if NETINET
350364562Sgshapiro	  case AF_INET:
350464562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
350564562Sgshapiro		    lalen <= 0 ||
350664562Sgshapiro		    la.sa.sa_family != AF_INET)
350764562Sgshapiro		{
350864562Sgshapiro			/* no ident info */
350964562Sgshapiro			goto noident;
351064562Sgshapiro		}
351164562Sgshapiro		port = RealHostAddr.sin.sin_port;
351238032Speter
351364562Sgshapiro		/* create ident query */
3514168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
351564562Sgshapiro				ntohs(RealHostAddr.sin.sin_port),
351664562Sgshapiro				ntohs(la.sin.sin_port));
351738032Speter
351864562Sgshapiro		/* create local address */
351964562Sgshapiro		la.sin.sin_port = 0;
352038032Speter
352164562Sgshapiro		/* create foreign address */
352290792Sgshapiro# ifdef NO_GETSERVBYNAME
352338032Speter		RealHostAddr.sin.sin_port = htons(113);
352490792Sgshapiro# else /* NO_GETSERVBYNAME */
352590792Sgshapiro
352690792Sgshapiro		/*
352790792Sgshapiro		**  getservbyname() consumes about 5% of the time
352890792Sgshapiro		**  when receiving a small message (almost all of the time
352990792Sgshapiro		**  spent in this routine).
353090792Sgshapiro		**  Hence we store the port in a static variable
353190792Sgshapiro		**  to save this time.
353290792Sgshapiro		**  The portnumber shouldn't change very often...
353390792Sgshapiro		**  This code makes the assumption that the port number
353490792Sgshapiro		**  is not 0.
353590792Sgshapiro		*/
353690792Sgshapiro
353790792Sgshapiro		if (port4 == 0)
353890792Sgshapiro		{
353990792Sgshapiro			sp = getservbyname("auth", "tcp");
354090792Sgshapiro			if (sp != NULL)
354190792Sgshapiro				port4 = sp->s_port;
354290792Sgshapiro			else
354390792Sgshapiro				port4 = htons(113);
354490792Sgshapiro		}
354590792Sgshapiro		RealHostAddr.sin.sin_port = port4;
354664562Sgshapiro		break;
354790792Sgshapiro# endif /* NO_GETSERVBYNAME */
354890792Sgshapiro#endif /* NETINET */
354938032Speter
355090792Sgshapiro#if NETINET6
355164562Sgshapiro	  case AF_INET6:
355264562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
355364562Sgshapiro		    lalen <= 0 ||
355464562Sgshapiro		    la.sa.sa_family != AF_INET6)
355564562Sgshapiro		{
355664562Sgshapiro			/* no ident info */
355764562Sgshapiro			goto noident;
355864562Sgshapiro		}
355964562Sgshapiro		port = RealHostAddr.sin6.sin6_port;
356064562Sgshapiro
356164562Sgshapiro		/* create ident query */
3562168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
356364562Sgshapiro				ntohs(RealHostAddr.sin6.sin6_port),
356464562Sgshapiro				ntohs(la.sin6.sin6_port));
356564562Sgshapiro
356664562Sgshapiro		/* create local address */
356764562Sgshapiro		la.sin6.sin6_port = 0;
356864562Sgshapiro
356964562Sgshapiro		/* create foreign address */
357090792Sgshapiro# ifdef NO_GETSERVBYNAME
357164562Sgshapiro		RealHostAddr.sin6.sin6_port = htons(113);
357290792Sgshapiro# else /* NO_GETSERVBYNAME */
357390792Sgshapiro		if (port6 == 0)
357490792Sgshapiro		{
357590792Sgshapiro			sp = getservbyname("auth", "tcp");
357690792Sgshapiro			if (sp != NULL)
357790792Sgshapiro				port6 = sp->s_port;
357890792Sgshapiro			else
357990792Sgshapiro				port6 = htons(113);
358090792Sgshapiro		}
358190792Sgshapiro		RealHostAddr.sin6.sin6_port = port6;
358264562Sgshapiro		break;
358390792Sgshapiro# endif /* NO_GETSERVBYNAME */
358490792Sgshapiro#endif /* NETINET6 */
358564562Sgshapiro	  default:
358664562Sgshapiro		/* no ident info */
358764562Sgshapiro		goto noident;
358864562Sgshapiro	}
358964562Sgshapiro
359038032Speter	s = -1;
359138032Speter	if (setjmp(CtxAuthTimeout) != 0)
359238032Speter	{
359338032Speter		if (s >= 0)
359438032Speter			(void) close(s);
359538032Speter		goto noident;
359638032Speter	}
359738032Speter
359838032Speter	/* put a timeout around the whole thing */
359990792Sgshapiro	ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
360038032Speter
360138032Speter	/* connect to foreign IDENT server using same address as SMTP socket */
360264562Sgshapiro	s = socket(la.sa.sa_family, SOCK_STREAM, 0);
360338032Speter	if (s < 0)
360438032Speter	{
360590792Sgshapiro		sm_clrevent(ev);
360638032Speter		goto noident;
360738032Speter	}
360864562Sgshapiro	if (bind(s, &la.sa, lalen) < 0 ||
360964562Sgshapiro	    connect(s, &RealHostAddr.sa, lalen) < 0)
361038032Speter		goto closeident;
361138032Speter
361238032Speter	if (tTd(9, 10))
361390792Sgshapiro		sm_dprintf("getauthinfo: sent %s", ibuf);
361438032Speter
361538032Speter	/* send query */
361638032Speter	if (write(s, ibuf, strlen(ibuf)) < 0)
361738032Speter		goto closeident;
361838032Speter
361938032Speter	/* get result */
362038032Speter	p = &ibuf[0];
3621168515Sgshapiro	nleft = sizeof(ibuf) - 1;
362238032Speter	while ((i = read(s, p, nleft)) > 0)
362338032Speter	{
3624125820Sgshapiro		char *s;
3625125820Sgshapiro
362638032Speter		p += i;
362738032Speter		nleft -= i;
362838032Speter		*p = '\0';
3629125820Sgshapiro		if ((s = strchr(ibuf, '\n')) != NULL)
3630125820Sgshapiro		{
3631125820Sgshapiro			if (p > s + 1)
3632125820Sgshapiro			{
3633125820Sgshapiro				p = s + 1;
3634125820Sgshapiro				*p = '\0';
3635125820Sgshapiro			}
363638032Speter			break;
3637125820Sgshapiro		}
3638125820Sgshapiro		if (nleft <= 0)
3639125820Sgshapiro			break;
364038032Speter	}
364138032Speter	(void) close(s);
364290792Sgshapiro	sm_clrevent(ev);
364338032Speter	if (i < 0 || p == &ibuf[0])
364438032Speter		goto noident;
364538032Speter
3646111823Sgshapiro	if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r')
364738032Speter		p--;
364838032Speter	*++p = '\0';
364938032Speter
365038032Speter	if (tTd(9, 3))
365190792Sgshapiro		sm_dprintf("getauthinfo:  got %s\n", ibuf);
365238032Speter
365338032Speter	/* parse result */
365438032Speter	p = strchr(ibuf, ':');
365538032Speter	if (p == NULL)
365638032Speter	{
365738032Speter		/* malformed response */
365838032Speter		goto noident;
365938032Speter	}
366038032Speter	while (isascii(*++p) && isspace(*p))
366138032Speter		continue;
366290792Sgshapiro	if (sm_strncasecmp(p, "userid", 6) != 0)
366338032Speter	{
366438032Speter		/* presumably an error string */
366538032Speter		goto noident;
366638032Speter	}
366738032Speter	p += 6;
366838032Speter	while (isascii(*p) && isspace(*p))
366938032Speter		p++;
367038032Speter	if (*p++ != ':')
367138032Speter	{
367238032Speter		/* either useridxx or malformed response */
367338032Speter		goto noident;
367438032Speter	}
367538032Speter
367638032Speter	/* p now points to the OSTYPE field */
367738032Speter	while (isascii(*p) && isspace(*p))
367838032Speter		p++;
367938032Speter	ostype = p;
368038032Speter	p = strchr(p, ':');
368138032Speter	if (p == NULL)
368238032Speter	{
368338032Speter		/* malformed response */
368438032Speter		goto noident;
368538032Speter	}
368638032Speter	else
368738032Speter	{
368838032Speter		char *charset;
368938032Speter
369038032Speter		*p = '\0';
369138032Speter		charset = strchr(ostype, ',');
369238032Speter		if (charset != NULL)
369338032Speter			*charset = '\0';
369438032Speter	}
369538032Speter
369638032Speter	/* 1413 says don't do this -- but it's broken otherwise */
369738032Speter	while (isascii(*++p) && isspace(*p))
369838032Speter		continue;
369938032Speter
370038032Speter	/* p now points to the authenticated name -- copy carefully */
370190792Sgshapiro	if (sm_strncasecmp(ostype, "other", 5) == 0 &&
370238032Speter	    (ostype[5] == ' ' || ostype[5] == '\0'))
370338032Speter	{
3704168515Sgshapiro		(void) sm_strlcpy(hbuf, "IDENT:", sizeof(hbuf));
3705110560Sgshapiro		cleanstrcpy(&hbuf[6], p, MAXAUTHINFO);
370638032Speter	}
370738032Speter	else
3708110560Sgshapiro		cleanstrcpy(hbuf, p, MAXAUTHINFO);
370990792Sgshapiro	len = strlen(hbuf);
3710168515Sgshapiro	(void) sm_strlcpyn(&hbuf[len], sizeof(hbuf) - len, 2, "@",
371190792Sgshapiro			   RealHostName == NULL ? "localhost" : RealHostName);
371238032Speter	goto postident;
371338032Speter
371438032Spetercloseident:
371538032Speter	(void) close(s);
371690792Sgshapiro	sm_clrevent(ev);
371738032Speter
371838032Speternoident:
371964562Sgshapiro	/* put back the original incoming port */
372064562Sgshapiro	switch (RealHostAddr.sa.sa_family)
372164562Sgshapiro	{
372290792Sgshapiro#if NETINET
372364562Sgshapiro	  case AF_INET:
372464562Sgshapiro		if (port > 0)
372564562Sgshapiro			RealHostAddr.sin.sin_port = port;
372664562Sgshapiro		break;
372790792Sgshapiro#endif /* NETINET */
372864562Sgshapiro
372990792Sgshapiro#if NETINET6
373064562Sgshapiro	  case AF_INET6:
373164562Sgshapiro		if (port > 0)
373264562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
373364562Sgshapiro		break;
373490792Sgshapiro#endif /* NETINET6 */
373564562Sgshapiro	}
373664562Sgshapiro
373738032Speter	if (RealHostName == NULL)
373838032Speter	{
373938032Speter		if (tTd(9, 1))
374090792Sgshapiro			sm_dprintf("getauthinfo: NULL\n");
374138032Speter		return NULL;
374238032Speter	}
3743168515Sgshapiro	(void) sm_strlcpy(hbuf, RealHostName, sizeof(hbuf));
374438032Speter
374538032Speterpostident:
374690792Sgshapiro#if IP_SRCROUTE
374790792Sgshapiro# ifndef GET_IPOPT_DST
374890792Sgshapiro#  define GET_IPOPT_DST(dst)	(dst)
374990792Sgshapiro# endif /* ! GET_IPOPT_DST */
375038032Speter	/*
375138032Speter	**  Extract IP source routing information.
375238032Speter	**
375338032Speter	**	Format of output for a connection from site a through b
375438032Speter	**	through c to d:
375538032Speter	**		loose:      @site-c@site-b:site-a
375638032Speter	**		strict:	   !@site-c@site-b:site-a
375738032Speter	**
375838032Speter	**	o - pointer within ipopt_list structure.
375938032Speter	**	q - pointer within ls/ss rr route data
376038032Speter	**	p - pointer to hbuf
376138032Speter	*/
376238032Speter
376338032Speter	if (RealHostAddr.sa.sa_family == AF_INET)
376438032Speter	{
376538032Speter		SOCKOPT_LEN_T ipoptlen;
376638032Speter		int j;
376790792Sgshapiro		unsigned char *q;
376890792Sgshapiro		unsigned char *o;
376938032Speter		int l;
377064562Sgshapiro		struct IPOPTION ipopt;
377138032Speter
3772168515Sgshapiro		ipoptlen = sizeof(ipopt);
377338032Speter		if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
377438032Speter			       (char *) &ipopt, &ipoptlen) < 0)
377538032Speter			goto noipsr;
377638032Speter		if (ipoptlen == 0)
377738032Speter			goto noipsr;
377890792Sgshapiro		o = (unsigned char *) ipopt.IP_LIST;
377990792Sgshapiro		while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
378038032Speter		{
378138032Speter			switch (*o)
378238032Speter			{
378364562Sgshapiro			  case IPOPT_EOL:
378438032Speter				o = NULL;
378538032Speter				break;
378638032Speter
378738032Speter			  case IPOPT_NOP:
378838032Speter				o++;
378938032Speter				break;
379038032Speter
379138032Speter			  case IPOPT_SSRR:
379238032Speter			  case IPOPT_LSRR:
379338032Speter				/*
379438032Speter				**  Source routing.
379538032Speter				**	o[0] is the option type (loose/strict).
379638032Speter				**	o[1] is the length of this option,
379738032Speter				**		including option type and
379838032Speter				**		length.
379938032Speter				**	o[2] is the pointer into the route
380038032Speter				**		data.
380138032Speter				**	o[3] begins the route data.
380238032Speter				*/
380338032Speter
380438032Speter				p = &hbuf[strlen(hbuf)];
3805168515Sgshapiro				l = sizeof(hbuf) - (hbuf - p) - 6;
380690792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(hbuf, p),
380790792Sgshapiro					" [%s@%.*s",
380890792Sgshapiro					*o == IPOPT_SSRR ? "!" : "",
380990792Sgshapiro					l > 240 ? 120 : l / 2,
381090792Sgshapiro					inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
381138032Speter				i = strlen(p);
381238032Speter				p += i;
381338032Speter				l -= strlen(p);
381438032Speter
381538032Speter				j = o[1] / sizeof(struct in_addr) - 1;
381638032Speter
381738032Speter				/* q skips length and router pointer to data */
381838032Speter				q = &o[3];
381938032Speter				for ( ; j >= 0; j--)
382038032Speter				{
382164562Sgshapiro					struct in_addr addr;
382264562Sgshapiro
382338032Speter					memcpy(&addr, q, sizeof(addr));
382490792Sgshapiro					(void) sm_snprintf(p,
382590792Sgshapiro						SPACELEFT(hbuf, p),
382690792Sgshapiro						"%c%.*s",
382790792Sgshapiro						j != 0 ? '@' : ':',
382890792Sgshapiro						l > 240 ? 120 :
382990792Sgshapiro							j == 0 ? l : l / 2,
383090792Sgshapiro						inet_ntoa(addr));
383138032Speter					i = strlen(p);
383238032Speter					p += i;
383338032Speter					l -= i + 1;
383464562Sgshapiro					q += sizeof(struct in_addr);
383538032Speter				}
383638032Speter				o += o[1];
383738032Speter				break;
383838032Speter
383938032Speter			  default:
384038032Speter				/* Skip over option */
384138032Speter				o += o[1];
384238032Speter				break;
384338032Speter			}
384438032Speter		}
384590792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
384638032Speter		goto postipsr;
384738032Speter	}
384838032Speter
384938032Speternoipsr:
385090792Sgshapiro#endif /* IP_SRCROUTE */
385138032Speter	if (RealHostName != NULL && RealHostName[0] != '[')
385238032Speter	{
385338032Speter		p = &hbuf[strlen(hbuf)];
385490792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
385590792Sgshapiro				   anynet_ntoa(&RealHostAddr));
385638032Speter	}
385738032Speter	if (*may_be_forged)
385838032Speter	{
385938032Speter		p = &hbuf[strlen(hbuf)];
386090792Sgshapiro		(void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
386190792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
386290792Sgshapiro			  macid("{client_resolve}"), "FORGED");
386338032Speter	}
386438032Speter
386590792Sgshapiro#if IP_SRCROUTE
386638032Speterpostipsr:
386790792Sgshapiro#endif /* IP_SRCROUTE */
386864562Sgshapiro
386964562Sgshapiro	/* put back the original incoming port */
387064562Sgshapiro	switch (RealHostAddr.sa.sa_family)
387164562Sgshapiro	{
387290792Sgshapiro#if NETINET
387364562Sgshapiro	  case AF_INET:
387464562Sgshapiro		if (port > 0)
387564562Sgshapiro			RealHostAddr.sin.sin_port = port;
387664562Sgshapiro		break;
387790792Sgshapiro#endif /* NETINET */
387864562Sgshapiro
387990792Sgshapiro#if NETINET6
388064562Sgshapiro	  case AF_INET6:
388164562Sgshapiro		if (port > 0)
388264562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
388364562Sgshapiro		break;
388490792Sgshapiro#endif /* NETINET6 */
388564562Sgshapiro	}
388664562Sgshapiro
388790792Sgshapiro	if (tTd(9, 1))
388890792Sgshapiro		sm_dprintf("getauthinfo: %s\n", hbuf);
388938032Speter	return hbuf;
389038032Speter}
389190792Sgshapiro/*
389238032Speter**  HOST_MAP_LOOKUP -- turn a hostname into canonical form
389338032Speter**
389438032Speter**	Parameters:
389538032Speter**		map -- a pointer to this map.
389638032Speter**		name -- the (presumably unqualified) hostname.
389738032Speter**		av -- unused -- for compatibility with other mapping
389838032Speter**			functions.
389938032Speter**		statp -- an exit status (out parameter) -- set to
390038032Speter**			EX_TEMPFAIL if the name server is unavailable.
390138032Speter**
390238032Speter**	Returns:
390338032Speter**		The mapping, if found.
390438032Speter**		NULL if no mapping found.
390538032Speter**
390638032Speter**	Side Effects:
390738032Speter**		Looks up the host specified in hbuf.  If it is not
390838032Speter**		the canonical name for that host, return the canonical
390938032Speter**		name (unless MF_MATCHONLY is set, which will cause the
391038032Speter**		status only to be returned).
391138032Speter*/
391238032Speter
391338032Speterchar *
391438032Speterhost_map_lookup(map, name, av, statp)
391538032Speter	MAP *map;
391638032Speter	char *name;
391738032Speter	char **av;
391838032Speter	int *statp;
391938032Speter{
392038032Speter	register struct hostent *hp;
392190792Sgshapiro#if NETINET
392238032Speter	struct in_addr in_addr;
392390792Sgshapiro#endif /* NETINET */
392490792Sgshapiro#if NETINET6
392564562Sgshapiro	struct in6_addr in6_addr;
392690792Sgshapiro#endif /* NETINET6 */
392764562Sgshapiro	char *cp, *ans = NULL;
392838032Speter	register STAB *s;
392990792Sgshapiro	time_t now;
393090792Sgshapiro#if NAMED_BIND
393190792Sgshapiro	time_t SM_NONVOLATILE retrans = 0;
393290792Sgshapiro	int SM_NONVOLATILE retry = 0;
393390792Sgshapiro#endif /* NAMED_BIND */
393438032Speter	char hbuf[MAXNAME + 1];
393538032Speter
393638032Speter	/*
393738032Speter	**  See if we have already looked up this name.  If so, just
393890792Sgshapiro	**  return it (unless expired).
393938032Speter	*/
394038032Speter
394190792Sgshapiro	now = curtime();
394238032Speter	s = stab(name, ST_NAMECANON, ST_ENTER);
394390792Sgshapiro	if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
394490792Sgshapiro	    s->s_namecanon.nc_exp >= now)
394538032Speter	{
394638032Speter		if (tTd(9, 1))
394790792Sgshapiro			sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
394890792Sgshapiro				    name,
394990792Sgshapiro				    s->s_namecanon.nc_cname == NULL
395038032Speter					? "NULL"
395138032Speter					: s->s_namecanon.nc_cname);
395238032Speter		errno = s->s_namecanon.nc_errno;
395373188Sgshapiro		SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
395438032Speter		*statp = s->s_namecanon.nc_stat;
395538032Speter		if (*statp == EX_TEMPFAIL)
395638032Speter		{
395738032Speter			CurEnv->e_status = "4.4.3";
395838032Speter			message("851 %s: Name server timeout",
395938032Speter				shortenstring(name, 33));
396038032Speter		}
396138032Speter		if (*statp != EX_OK)
396238032Speter			return NULL;
396338032Speter		if (s->s_namecanon.nc_cname == NULL)
396438032Speter		{
3965132943Sgshapiro			syserr("host_map_lookup(%s): bogus NULL cache entry, errno=%d, h_errno=%d",
396664562Sgshapiro			       name,
396764562Sgshapiro			       s->s_namecanon.nc_errno,
396864562Sgshapiro			       s->s_namecanon.nc_herrno);
396938032Speter			return NULL;
397038032Speter		}
397138032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
397238032Speter			cp = map_rewrite(map, name, strlen(name), NULL);
397338032Speter		else
397438032Speter			cp = map_rewrite(map,
397538032Speter					 s->s_namecanon.nc_cname,
397638032Speter					 strlen(s->s_namecanon.nc_cname),
397738032Speter					 av);
397838032Speter		return cp;
397938032Speter	}
398038032Speter
398138032Speter	/*
398238032Speter	**  If we are running without a regular network connection (usually
398338032Speter	**  dial-on-demand) and we are just queueing, we want to avoid DNS
398438032Speter	**  lookups because those could try to connect to a server.
398538032Speter	*/
398638032Speter
398764562Sgshapiro	if (CurEnv->e_sendmode == SM_DEFER &&
398864562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
398938032Speter	{
399038032Speter		if (tTd(9, 1))
399190792Sgshapiro			sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
399238032Speter		*statp = EX_TEMPFAIL;
399338032Speter		return NULL;
399438032Speter	}
399538032Speter
399638032Speter	/*
399738032Speter	**  If first character is a bracket, then it is an address
399838032Speter	**  lookup.  Address is copied into a temporary buffer to
399938032Speter	**  strip the brackets and to preserve name if address is
400038032Speter	**  unknown.
400138032Speter	*/
400238032Speter
400364562Sgshapiro	if (tTd(9, 1))
400490792Sgshapiro		sm_dprintf("host_map_lookup(%s) => ", name);
400590792Sgshapiro#if NAMED_BIND
400690792Sgshapiro	if (map->map_timeout > 0)
400790792Sgshapiro	{
400890792Sgshapiro		retrans = _res.retrans;
400990792Sgshapiro		_res.retrans = map->map_timeout;
401090792Sgshapiro	}
401190792Sgshapiro	if (map->map_retry > 0)
401290792Sgshapiro	{
401390792Sgshapiro		retry = _res.retry;
401490792Sgshapiro		_res.retry = map->map_retry;
401590792Sgshapiro	}
401690792Sgshapiro#endif /* NAMED_BIND */
401790792Sgshapiro
401890792Sgshapiro	/* set default TTL */
401990792Sgshapiro	s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
402038032Speter	if (*name != '[')
402138032Speter	{
402290792Sgshapiro		int ttl;
402390792Sgshapiro
4024168515Sgshapiro		(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
4025168515Sgshapiro		if (getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl))
402690792Sgshapiro		{
402764562Sgshapiro			ans = hbuf;
402890792Sgshapiro			if (ttl > 0)
402990792Sgshapiro				s->s_namecanon.nc_exp = now + SM_MIN(ttl,
403090792Sgshapiro								SM_DEFAULT_TTL);
403190792Sgshapiro		}
403264562Sgshapiro	}
403364562Sgshapiro	else
403464562Sgshapiro	{
403564562Sgshapiro		if ((cp = strchr(name, ']')) == NULL)
403671345Sgshapiro		{
403771345Sgshapiro			if (tTd(9, 1))
403890792Sgshapiro				sm_dprintf("FAILED\n");
403964562Sgshapiro			return NULL;
404071345Sgshapiro		}
404164562Sgshapiro		*cp = '\0';
404264562Sgshapiro
404364562Sgshapiro		hp = NULL;
404490792Sgshapiro#if NETINET
404564562Sgshapiro		if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE)
404664562Sgshapiro			hp = sm_gethostbyaddr((char *)&in_addr,
404764562Sgshapiro					      INADDRSZ, AF_INET);
404890792Sgshapiro#endif /* NETINET */
404990792Sgshapiro#if NETINET6
405064562Sgshapiro		if (hp == NULL &&
405190792Sgshapiro		    anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
405264562Sgshapiro			hp = sm_gethostbyaddr((char *)&in6_addr,
405364562Sgshapiro					      IN6ADDRSZ, AF_INET6);
405490792Sgshapiro#endif /* NETINET6 */
405564562Sgshapiro		*cp = ']';
405664562Sgshapiro
405764562Sgshapiro		if (hp != NULL)
405838032Speter		{
405964562Sgshapiro			/* found a match -- copy out */
406090792Sgshapiro			ans = denlstring((char *) hp->h_name, true, true);
406190792Sgshapiro#if NETINET6
406290792Sgshapiro			if (ans == hp->h_name)
406390792Sgshapiro			{
406490792Sgshapiro				static char n[MAXNAME + 1];
406590792Sgshapiro
406690792Sgshapiro				/* hp->h_name is about to disappear */
4067168515Sgshapiro				(void) sm_strlcpy(n, ans, sizeof(n));
406890792Sgshapiro				ans = n;
406990792Sgshapiro			}
407071345Sgshapiro			freehostent(hp);
407171345Sgshapiro			hp = NULL;
407290792Sgshapiro#endif /* NETINET6 */
407338032Speter		}
407464562Sgshapiro	}
407590792Sgshapiro#if NAMED_BIND
407690792Sgshapiro	if (map->map_timeout > 0)
407790792Sgshapiro		_res.retrans = retrans;
407890792Sgshapiro	if (map->map_retry > 0)
407990792Sgshapiro		_res.retry = retry;
408090792Sgshapiro#endif /* NAMED_BIND */
408138032Speter
408264562Sgshapiro	s->s_namecanon.nc_flags |= NCF_VALID;	/* will be soon */
408338032Speter
408464562Sgshapiro	/* Found an answer */
408564562Sgshapiro	if (ans != NULL)
408664562Sgshapiro	{
408764562Sgshapiro		s->s_namecanon.nc_stat = *statp = EX_OK;
408890792Sgshapiro		if (s->s_namecanon.nc_cname != NULL)
408990792Sgshapiro			sm_free(s->s_namecanon.nc_cname);
409090792Sgshapiro		s->s_namecanon.nc_cname = sm_strdup_x(ans);
409164562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
409264562Sgshapiro			cp = map_rewrite(map, name, strlen(name), NULL);
409364562Sgshapiro		else
409464562Sgshapiro			cp = map_rewrite(map, ans, strlen(ans), av);
409571345Sgshapiro		if (tTd(9, 1))
409690792Sgshapiro			sm_dprintf("FOUND %s\n", ans);
409764562Sgshapiro		return cp;
409838032Speter	}
409938032Speter
410064562Sgshapiro
410164562Sgshapiro	/* No match found */
410238032Speter	s->s_namecanon.nc_errno = errno;
410390792Sgshapiro#if NAMED_BIND
410438032Speter	s->s_namecanon.nc_herrno = h_errno;
410564562Sgshapiro	if (tTd(9, 1))
410690792Sgshapiro		sm_dprintf("FAIL (%d)\n", h_errno);
410764562Sgshapiro	switch (h_errno)
410838032Speter	{
410964562Sgshapiro	  case TRY_AGAIN:
411064562Sgshapiro		if (UseNameServer)
411164562Sgshapiro		{
411264562Sgshapiro			CurEnv->e_status = "4.4.3";
411364562Sgshapiro			message("851 %s: Name server timeout",
411464562Sgshapiro				shortenstring(name, 33));
411564562Sgshapiro		}
411664562Sgshapiro		*statp = EX_TEMPFAIL;
411764562Sgshapiro		break;
411864562Sgshapiro
411964562Sgshapiro	  case HOST_NOT_FOUND:
412064562Sgshapiro	  case NO_DATA:
412164562Sgshapiro		*statp = EX_NOHOST;
412264562Sgshapiro		break;
412364562Sgshapiro
412464562Sgshapiro	  case NO_RECOVERY:
412564562Sgshapiro		*statp = EX_SOFTWARE;
412664562Sgshapiro		break;
412764562Sgshapiro
412864562Sgshapiro	  default:
412964562Sgshapiro		*statp = EX_UNAVAILABLE;
413064562Sgshapiro		break;
413138032Speter	}
413290792Sgshapiro#else /* NAMED_BIND */
413364562Sgshapiro	if (tTd(9, 1))
413490792Sgshapiro		sm_dprintf("FAIL\n");
413564562Sgshapiro	*statp = EX_NOHOST;
413690792Sgshapiro#endif /* NAMED_BIND */
413764562Sgshapiro	s->s_namecanon.nc_stat = *statp;
413864562Sgshapiro	return NULL;
413938032Speter}
414038032Speter/*
414190792Sgshapiro**  HOST_MAP_INIT -- initialize host class structures
414238032Speter**
414338032Speter**	Parameters:
414490792Sgshapiro**		map -- a pointer to this map.
414590792Sgshapiro**		args -- argument string.
414638032Speter**
414738032Speter**	Returns:
414890792Sgshapiro**		true.
414938032Speter*/
415038032Speter
415138032Speterbool
415238032Speterhost_map_init(map, args)
415338032Speter	MAP *map;
415438032Speter	char *args;
415538032Speter{
415638032Speter	register char *p = args;
415738032Speter
415838032Speter	for (;;)
415938032Speter	{
416038032Speter		while (isascii(*p) && isspace(*p))
416138032Speter			p++;
416238032Speter		if (*p != '-')
416338032Speter			break;
416438032Speter		switch (*++p)
416538032Speter		{
416638032Speter		  case 'a':
416738032Speter			map->map_app = ++p;
416838032Speter			break;
416938032Speter
417038032Speter		  case 'T':
417138032Speter			map->map_tapp = ++p;
417238032Speter			break;
417338032Speter
417438032Speter		  case 'm':
417538032Speter			map->map_mflags |= MF_MATCHONLY;
417638032Speter			break;
417738032Speter
417838032Speter		  case 't':
417938032Speter			map->map_mflags |= MF_NODEFER;
418038032Speter			break;
418164562Sgshapiro
418264562Sgshapiro		  case 'S':	/* only for consistency */
418364562Sgshapiro			map->map_spacesub = *++p;
418464562Sgshapiro			break;
418564562Sgshapiro
418664562Sgshapiro		  case 'D':
418764562Sgshapiro			map->map_mflags |= MF_DEFER;
418864562Sgshapiro			break;
418990792Sgshapiro
419090792Sgshapiro		  case 'd':
419190792Sgshapiro			{
419290792Sgshapiro				char *h;
419390792Sgshapiro
419490792Sgshapiro				while (isascii(*++p) && isspace(*p))
419590792Sgshapiro					continue;
419690792Sgshapiro				h = strchr(p, ' ');
419790792Sgshapiro				if (h != NULL)
419890792Sgshapiro					*h = '\0';
419990792Sgshapiro				map->map_timeout = convtime(p, 's');
420090792Sgshapiro				if (h != NULL)
420190792Sgshapiro					*h = ' ';
420290792Sgshapiro			}
420390792Sgshapiro			break;
420490792Sgshapiro
420590792Sgshapiro		  case 'r':
420690792Sgshapiro			while (isascii(*++p) && isspace(*p))
420790792Sgshapiro				continue;
420890792Sgshapiro			map->map_retry = atoi(p);
420990792Sgshapiro			break;
421038032Speter		}
421138032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
421238032Speter			p++;
421338032Speter		if (*p != '\0')
421438032Speter			*p++ = '\0';
421538032Speter	}
421638032Speter	if (map->map_app != NULL)
421738032Speter		map->map_app = newstr(map->map_app);
421838032Speter	if (map->map_tapp != NULL)
421938032Speter		map->map_tapp = newstr(map->map_tapp);
422090792Sgshapiro	return true;
422138032Speter}
422290792Sgshapiro
422364562Sgshapiro#if NETINET6
422464562Sgshapiro/*
422564562Sgshapiro**  ANYNET_NTOP -- convert an IPv6 network address to printable form.
422664562Sgshapiro**
422764562Sgshapiro**	Parameters:
422864562Sgshapiro**		s6a -- a pointer to an in6_addr structure.
422964562Sgshapiro**		dst -- buffer to store result in
423064562Sgshapiro**		dst_len -- size of dst buffer
423164562Sgshapiro**
423264562Sgshapiro**	Returns:
423364562Sgshapiro**		A printable version of that structure.
423464562Sgshapiro*/
423590792Sgshapiro
423664562Sgshapirochar *
423764562Sgshapiroanynet_ntop(s6a, dst, dst_len)
423864562Sgshapiro	struct in6_addr *s6a;
423964562Sgshapiro	char *dst;
424064562Sgshapiro	size_t dst_len;
424164562Sgshapiro{
424264562Sgshapiro	register char *ap;
424364562Sgshapiro
424464562Sgshapiro	if (IN6_IS_ADDR_V4MAPPED(s6a))
424564562Sgshapiro		ap = (char *) inet_ntop(AF_INET,
424664562Sgshapiro					&s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
424764562Sgshapiro					dst, dst_len);
424864562Sgshapiro	else
424990792Sgshapiro	{
425090792Sgshapiro		char *d;
425190792Sgshapiro		size_t sz;
425290792Sgshapiro
425390792Sgshapiro		/* Save pointer to beginning of string */
425490792Sgshapiro		d = dst;
425590792Sgshapiro
425690792Sgshapiro		/* Add IPv6: protocol tag */
425790792Sgshapiro		sz = sm_strlcpy(dst, "IPv6:", dst_len);
425890792Sgshapiro		if (sz >= dst_len)
425990792Sgshapiro			return NULL;
426090792Sgshapiro		dst += sz;
426190792Sgshapiro		dst_len -= sz;
4262261370Sgshapiro# if _FFR_IPV6_FULL
4263261370Sgshapiro		ap = sm_inet6_ntop(s6a, dst, dst_len);
4264261370Sgshapiro# else /* _FFR_IPV6_FULL */
426564562Sgshapiro		ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
4266261370Sgshapiro# endif /* _FFR_IPV6_FULL */
426790792Sgshapiro
426890792Sgshapiro		/* Restore pointer to beginning of string */
426990792Sgshapiro		if (ap != NULL)
427090792Sgshapiro			ap = d;
427190792Sgshapiro	}
427264562Sgshapiro	return ap;
427364562Sgshapiro}
427490792Sgshapiro
427590792Sgshapiro/*
427690792Sgshapiro**  ANYNET_PTON -- convert printed form to network address.
427790792Sgshapiro**
427890792Sgshapiro**	Wrapper for inet_pton() which handles IPv6: labels.
427990792Sgshapiro**
428090792Sgshapiro**	Parameters:
428190792Sgshapiro**		family -- address family
428290792Sgshapiro**		src -- string
428390792Sgshapiro**		dst -- destination address structure
428490792Sgshapiro**
428590792Sgshapiro**	Returns:
428690792Sgshapiro**		1 if the address was valid
428790792Sgshapiro**		0 if the address wasn't parseable
428890792Sgshapiro**		-1 if error
428990792Sgshapiro*/
429090792Sgshapiro
429190792Sgshapiroint
429290792Sgshapiroanynet_pton(family, src, dst)
429390792Sgshapiro	int family;
429490792Sgshapiro	const char *src;
429590792Sgshapiro	void *dst;
429690792Sgshapiro{
429790792Sgshapiro	if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
429890792Sgshapiro		src += 5;
429990792Sgshapiro	return inet_pton(family, src, dst);
430090792Sgshapiro}
430164562Sgshapiro#endif /* NETINET6 */
430290792Sgshapiro/*
430338032Speter**  ANYNET_NTOA -- convert a network address to printable form.
430438032Speter**
430538032Speter**	Parameters:
430638032Speter**		sap -- a pointer to a sockaddr structure.
430738032Speter**
430838032Speter**	Returns:
430938032Speter**		A printable version of that sockaddr.
431038032Speter*/
431138032Speter
431238032Speter#ifdef USE_SOCK_STREAM
431338032Speter
431464562Sgshapiro# if NETLINK
431564562Sgshapiro#  include <net/if_dl.h>
431664562Sgshapiro# endif /* NETLINK */
431738032Speter
431838032Speterchar *
431938032Speteranynet_ntoa(sap)
432038032Speter	register SOCKADDR *sap;
432138032Speter{
432238032Speter	register char *bp;
432338032Speter	register char *ap;
432438032Speter	int l;
432538032Speter	static char buf[100];
432638032Speter
432738032Speter	/* check for null/zero family */
432838032Speter	if (sap == NULL)
432938032Speter		return "NULLADDR";
433038032Speter	if (sap->sa.sa_family == 0)
433138032Speter		return "0";
433238032Speter
433338032Speter	switch (sap->sa.sa_family)
433438032Speter	{
433564562Sgshapiro# if NETUNIX
433638032Speter	  case AF_UNIX:
433764562Sgshapiro		if (sap->sunix.sun_path[0] != '\0')
4338168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "[UNIX: %.64s]",
433990792Sgshapiro					   sap->sunix.sun_path);
434064562Sgshapiro		else
4341168515Sgshapiro			(void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof(buf));
434238032Speter		return buf;
434364562Sgshapiro# endif /* NETUNIX */
434438032Speter
434564562Sgshapiro# if NETINET
434638032Speter	  case AF_INET:
434764562Sgshapiro		return (char *) inet_ntoa(sap->sin.sin_addr);
434864562Sgshapiro# endif /* NETINET */
434938032Speter
435064562Sgshapiro# if NETINET6
435164562Sgshapiro	  case AF_INET6:
4352168515Sgshapiro		ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof(buf));
435364562Sgshapiro		if (ap != NULL)
435464562Sgshapiro			return ap;
435564562Sgshapiro		break;
435664562Sgshapiro# endif /* NETINET6 */
435764562Sgshapiro
435864562Sgshapiro# if NETLINK
435938032Speter	  case AF_LINK:
4360168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[LINK: %s]",
436190792Sgshapiro				   link_ntoa((struct sockaddr_dl *) &sap->sa));
436238032Speter		return buf;
436364562Sgshapiro# endif /* NETLINK */
436438032Speter	  default:
436538032Speter		/* this case is needed when nothing is #defined */
436638032Speter		/* in order to keep the switch syntactically correct */
436738032Speter		break;
436838032Speter	}
436938032Speter
437038032Speter	/* unknown family -- just dump bytes */
4371168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family);
437238032Speter	bp = &buf[strlen(buf)];
437338032Speter	ap = sap->sa.sa_data;
4374168515Sgshapiro	for (l = sizeof(sap->sa.sa_data); --l >= 0; )
437538032Speter	{
437690792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
437790792Sgshapiro				   *ap++ & 0377);
437838032Speter		bp += 3;
437938032Speter	}
438038032Speter	*--bp = '\0';
438138032Speter	return buf;
438238032Speter}
438390792Sgshapiro/*
438438032Speter**  HOSTNAMEBYANYADDR -- return name of host based on address
438538032Speter**
438638032Speter**	Parameters:
438738032Speter**		sap -- SOCKADDR pointer
438838032Speter**
438938032Speter**	Returns:
439038032Speter**		text representation of host name.
439138032Speter**
439238032Speter**	Side Effects:
439338032Speter**		none.
439438032Speter*/
439538032Speter
439638032Speterchar *
439738032Speterhostnamebyanyaddr(sap)
439838032Speter	register SOCKADDR *sap;
439938032Speter{
440038032Speter	register struct hostent *hp;
440164562Sgshapiro# if NAMED_BIND
440238032Speter	int saveretry;
440364562Sgshapiro# endif /* NAMED_BIND */
440464562Sgshapiro# if NETINET6
440564562Sgshapiro	struct in6_addr in6_addr;
440664562Sgshapiro# endif /* NETINET6 */
440738032Speter
440864562Sgshapiro# if NAMED_BIND
440938032Speter	/* shorten name server timeout to avoid higher level timeouts */
441038032Speter	saveretry = _res.retry;
441164562Sgshapiro	if (_res.retry * _res.retrans > 20)
441264562Sgshapiro		_res.retry = 20 / _res.retrans;
4413244928Sgshapiro	if (_res.retry == 0)
4414244928Sgshapiro		_res.retry = 1;
441564562Sgshapiro# endif /* NAMED_BIND */
441638032Speter
441738032Speter	switch (sap->sa.sa_family)
441838032Speter	{
441964562Sgshapiro# if NETINET
442038032Speter	  case AF_INET:
442138032Speter		hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
442290792Sgshapiro				      INADDRSZ, AF_INET);
442338032Speter		break;
442464562Sgshapiro# endif /* NETINET */
442538032Speter
442664562Sgshapiro# if NETINET6
442764562Sgshapiro	  case AF_INET6:
442864562Sgshapiro		hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
442990792Sgshapiro				      IN6ADDRSZ, AF_INET6);
443064562Sgshapiro		break;
443164562Sgshapiro# endif /* NETINET6 */
443264562Sgshapiro
443364562Sgshapiro# if NETISO
443438032Speter	  case AF_ISO:
443538032Speter		hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
4436168515Sgshapiro				      sizeof(sap->siso.siso_addr), AF_ISO);
443738032Speter		break;
443864562Sgshapiro# endif /* NETISO */
443938032Speter
444064562Sgshapiro# if NETUNIX
444138032Speter	  case AF_UNIX:
444238032Speter		hp = NULL;
444338032Speter		break;
444464562Sgshapiro# endif /* NETUNIX */
444538032Speter
444638032Speter	  default:
4447168515Sgshapiro		hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof(sap->sa.sa_data),
444890792Sgshapiro				      sap->sa.sa_family);
444938032Speter		break;
445038032Speter	}
445138032Speter
445264562Sgshapiro# if NAMED_BIND
445338032Speter	_res.retry = saveretry;
445464562Sgshapiro# endif /* NAMED_BIND */
445538032Speter
445664562Sgshapiro# if NETINET || NETINET6
445764562Sgshapiro	if (hp != NULL && hp->h_name[0] != '['
445864562Sgshapiro#  if NETINET6
445964562Sgshapiro	    && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1
446064562Sgshapiro#  endif /* NETINET6 */
446164562Sgshapiro#  if NETINET
446264562Sgshapiro	    && inet_addr(hp->h_name) == INADDR_NONE
446364562Sgshapiro#  endif /* NETINET */
446464562Sgshapiro	    )
446571345Sgshapiro	{
446671345Sgshapiro		char *name;
446771345Sgshapiro
446890792Sgshapiro		name = denlstring((char *) hp->h_name, true, true);
446990792Sgshapiro#  if NETINET6
447071345Sgshapiro		if (name == hp->h_name)
447171345Sgshapiro		{
447271345Sgshapiro			static char n[MAXNAME + 1];
447371345Sgshapiro
447471345Sgshapiro			/* Copy the string, hp->h_name is about to disappear */
4475168515Sgshapiro			(void) sm_strlcpy(n, name, sizeof(n));
447671345Sgshapiro			name = n;
447771345Sgshapiro		}
447871345Sgshapiro		freehostent(hp);
447990792Sgshapiro#  endif /* NETINET6 */
448071345Sgshapiro		return name;
448171345Sgshapiro	}
448264562Sgshapiro# endif /* NETINET || NETINET6 */
448371345Sgshapiro
448490792Sgshapiro# if NETINET6
448571345Sgshapiro	if (hp != NULL)
448671345Sgshapiro	{
448771345Sgshapiro		freehostent(hp);
448871345Sgshapiro		hp = NULL;
448971345Sgshapiro	}
449090792Sgshapiro# endif /* NETINET6 */
449171345Sgshapiro
449264562Sgshapiro# if NETUNIX
449364562Sgshapiro	if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
449438032Speter		return "localhost";
449564562Sgshapiro# endif /* NETUNIX */
449638032Speter	{
449738032Speter		static char buf[203];
449838032Speter
4499168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[%.200s]",
450090792Sgshapiro				   anynet_ntoa(sap));
450138032Speter		return buf;
450238032Speter	}
450338032Speter}
450464562Sgshapiro#endif /* USE_SOCK_STREAM */
4505