138032Speter/*
2261194Sgshapiro * 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
17266527SgshapiroSM_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
21363466Sgshapiro#endif
2238032Speter
2390792Sgshapiro#if defined(USE_SOCK_STREAM)
2464562Sgshapiro# if NETINET || NETINET6
2564562Sgshapiro#  include <arpa/inet.h>
26363466Sgshapiro# endif
2738032Speter# if NAMED_BIND
2838032Speter#  ifndef NO_DATA
2938032Speter#   define NO_DATA	NO_ADDRESS
30363466Sgshapiro#  endif
3164562Sgshapiro# endif /* NAMED_BIND */
3290792Sgshapiro#endif /* defined(USE_SOCK_STREAM) */
3338032Speter
3490792Sgshapiro#if STARTTLS
35285229Sgshapiro# include <openssl/rand.h>
36363466Sgshapiro# if DANE
37363466Sgshapiro#  include "tls.h"
38363466Sgshapiro#  include "sm_resolve.h"
39363466Sgshapiro# endif
40363466Sgshapiro#endif
4138032Speter
42363466Sgshapiro#if NETINET6
43363466Sgshapiro# define FREEHOSTENT(h, s)			\
44363466Sgshapiro	do					\
45363466Sgshapiro	{					\
46363466Sgshapiro		if ((h) != (s) && (h) != NULL)	\
47363466Sgshapiro		{				\
48363466Sgshapiro			freehostent((h));	\
49363466Sgshapiro			(h) = NULL;		\
50363466Sgshapiro		}				\
51363466Sgshapiro	} while (0)
52363466Sgshapiro#else
53363466Sgshapiro#  define FREEHOSTENT(h, s)
54363466Sgshapiro#endif
55363466Sgshapiro
56157001Sgshapiro#include <sm/time.h>
5766494Sgshapiro
5890792Sgshapiro#if IP_SRCROUTE && NETINET
5990792Sgshapiro# include <netinet/in_systm.h>
6090792Sgshapiro# include <netinet/ip.h>
6190792Sgshapiro# if HAS_IN_H
6290792Sgshapiro#  include <netinet/in.h>
6390792Sgshapiro#  ifndef IPOPTION
6490792Sgshapiro#   define IPOPTION	ip_opts
6590792Sgshapiro#   define IP_LIST	ip_opts
6690792Sgshapiro#   define IP_DST	ip_dst
6790792Sgshapiro#  endif /* ! IPOPTION */
6890792Sgshapiro# else /* HAS_IN_H */
6990792Sgshapiro#  include <netinet/ip_var.h>
7090792Sgshapiro#  ifndef IPOPTION
7190792Sgshapiro#   define IPOPTION	ipoption
7290792Sgshapiro#   define IP_LIST	ipopt_list
7390792Sgshapiro#   define IP_DST	ipopt_dst
7490792Sgshapiro#  endif /* ! IPOPTION */
7590792Sgshapiro# endif /* HAS_IN_H */
7690792Sgshapiro#endif /* IP_SRCROUTE && NETINET */
7738032Speter
7890792Sgshapiro#include <sm/fdset.h>
7938032Speter
80363466Sgshapiro#include <ratectrl.h>
81363466Sgshapiro
82168515Sgshapiro#define DAEMON_C 1
83168515Sgshapiro#include <daemon.h>
8464562Sgshapiro
85141858Sgshapirostatic void		connecttimeout __P((int));
8690792Sgshapirostatic int		opendaemonsocket __P((DAEMON_T *, bool));
8790792Sgshapirostatic unsigned short	setupdaemon __P((SOCKADDR *));
8890792Sgshapirostatic void		getrequests_checkdiskspace __P((ENVELOPE *e));
89141858Sgshapirostatic void		setsockaddroptions __P((char *, DAEMON_T *));
90141858Sgshapirostatic void		printdaemonflags __P((DAEMON_T *));
91141858Sgshapirostatic int		addr_family __P((char *));
92141858Sgshapirostatic int		addrcmp __P((struct hostent *, char *, SOCKADDR *));
93141858Sgshapirostatic void		authtimeout __P((int));
9464562Sgshapiro
9538032Speter/*
9638032Speter**  DAEMON.C -- routines to use when running as a daemon.
9738032Speter**
9838032Speter**	This entire file is highly dependent on the 4.2 BSD
9938032Speter**	interprocess communication primitives.  No attempt has
10038032Speter**	been made to make this file portable to Version 7,
10138032Speter**	Version 6, MPX files, etc.  If you should try such a
10238032Speter**	thing yourself, I recommend chucking the entire file
10338032Speter**	and starting from scratch.  Basic semantics are:
10438032Speter**
10538032Speter**	getrequests(e)
10638032Speter**		Opens a port and initiates a connection.
10738032Speter**		Returns in a child.  Must set InChannel and
10838032Speter**		OutChannel appropriately.
10938032Speter**	clrdaemon()
11038032Speter**		Close any open files associated with getting
11138032Speter**		the connection; this is used when running the queue,
11238032Speter**		etc., to avoid having extra file descriptors during
11338032Speter**		the queue run and to avoid confusing the network
11438032Speter**		code (if it cares).
11590792Sgshapiro**	makeconnection(host, port, mci, e, enough)
11638032Speter**		Make a connection to the named host on the given
11790792Sgshapiro**		port. Returns zero on success, else an exit status
11890792Sgshapiro**		describing the error.
11938032Speter**	host_map_lookup(map, hbuf, avp, pstat)
12038032Speter**		Convert the entry in hbuf into a canonical form.
12138032Speter*/
12264562Sgshapiro
12390792Sgshapirostatic int	NDaemons = 0;			/* actual number of daemons */
12464562Sgshapiro
12590792Sgshapirostatic time_t	NextDiskSpaceCheck = 0;
12664562Sgshapiro
12790792Sgshapiro/*
12838032Speter**  GETREQUESTS -- open mail IPC port and get requests.
12938032Speter**
13038032Speter**	Parameters:
13138032Speter**		e -- the current envelope.
13238032Speter**
13338032Speter**	Returns:
13464562Sgshapiro**		pointer to flags.
13538032Speter**
13638032Speter**	Side Effects:
13738032Speter**		Waits until some interesting activity occurs.  When
13838032Speter**		it does, a child is created to process it, and the
13938032Speter**		parent waits for completion.  Return from this
14038032Speter**		routine is always in the child.  The file pointers
14138032Speter**		"InChannel" and "OutChannel" should be set to point
14238032Speter**		to the communication channel.
14390792Sgshapiro**		May restart persistent queue runners if they have ended
14490792Sgshapiro**		for some reason.
14538032Speter*/
14638032Speter
14764562SgshapiroBITMAP256 *
14838032Spetergetrequests(e)
14938032Speter	ENVELOPE *e;
15038032Speter{
15138032Speter	int t;
15264562Sgshapiro	int idx, curdaemon = -1;
15364562Sgshapiro	int i, olddaemon = 0;
15490792Sgshapiro#if XDEBUG
15538032Speter	bool j_has_dot;
156363466Sgshapiro#endif
15742575Speter	char status[MAXLINE];
15864562Sgshapiro	SOCKADDR sa;
159168515Sgshapiro	SOCKADDR_LEN_T len = sizeof(sa);
16094334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
16194334Sgshapiro	time_t lastrun;
162363466Sgshapiro#endif
163363466Sgshapiro#if NETUNIX
16442575Speter	extern int ControlSocket;
165363466Sgshapiro#endif
16664562Sgshapiro	extern ENVELOPE BlankEnvelope;
16738032Speter
16838032Speter
169125820Sgshapiro	/* initialize data for function that generates queue ids */
170125820Sgshapiro	init_qid_alg();
17190792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
17238032Speter	{
17364562Sgshapiro		Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
17490792Sgshapiro		Daemons[idx].d_firsttime = true;
17564562Sgshapiro		Daemons[idx].d_refuse_connections_until = (time_t) 0;
17638032Speter	}
17771345Sgshapiro
17838032Speter	/*
17938032Speter	**  Try to actually open the connection.
18038032Speter	*/
18138032Speter
18238032Speter	if (tTd(15, 1))
18364562Sgshapiro	{
18490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
18571345Sgshapiro		{
18690792Sgshapiro			sm_dprintf("getrequests: daemon %s: port %d\n",
18790792Sgshapiro				   Daemons[idx].d_name,
18890792Sgshapiro				   ntohs(Daemons[idx].d_port));
18971345Sgshapiro		}
19064562Sgshapiro	}
19138032Speter
19238032Speter	/* get a socket for the SMTP connection */
19390792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
19490792Sgshapiro		Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
19538032Speter
19642575Speter	if (opencontrolsocket() < 0)
19742575Speter		sm_syslog(LOG_WARNING, NOQID,
19843730Speter			  "daemon could not open control socket %s: %s",
19990792Sgshapiro			  ControlSocketName, sm_errstring(errno));
20042575Speter
20190792Sgshapiro	/* If there are any queue runners released reapchild() co-ord's */
20290792Sgshapiro	(void) sm_signal(SIGCHLD, reapchild);
20338032Speter
20490792Sgshapiro	/* write the pid to file, command line args to syslog */
20564562Sgshapiro	log_sendmail_pid(e);
20638032Speter
20790792Sgshapiro#if XDEBUG
20838032Speter	{
20938032Speter		char jbuf[MAXHOSTNAMELEN];
21038032Speter
211168515Sgshapiro		expand("\201j", jbuf, sizeof(jbuf), e);
21238032Speter		j_has_dot = strchr(jbuf, '.') != NULL;
21338032Speter	}
21490792Sgshapiro#endif /* XDEBUG */
21538032Speter
21642575Speter	/* Add parent process as first item */
217132943Sgshapiro	proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1, NULL);
21842575Speter
21938032Speter	if (tTd(15, 1))
22064562Sgshapiro	{
22190792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
222203004Sgshapiro			sm_dprintf("getrequests: daemon %s: socket %d\n",
22364562Sgshapiro				Daemons[idx].d_name,
22464562Sgshapiro				Daemons[idx].d_socket);
22564562Sgshapiro	}
22638032Speter
22738032Speter	for (;;)
22838032Speter	{
22938032Speter		register pid_t pid;
23038032Speter		auto SOCKADDR_LEN_T lotherend;
23190792Sgshapiro		bool timedout = false;
23290792Sgshapiro		bool control = false;
23364562Sgshapiro		int save_errno;
23438032Speter		int pipefd[2];
23590792Sgshapiro		time_t now;
23690792Sgshapiro#if STARTTLS
23766494Sgshapiro		long seed;
238363466Sgshapiro#endif
23938032Speter
24038032Speter		/* see if we are rejecting connections */
24190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
242120256Sgshapiro		CHECK_RESTART;
24364562Sgshapiro
24490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
24571345Sgshapiro		{
24690792Sgshapiro			/*
24790792Sgshapiro			**  XXX do this call outside the loop?
24890792Sgshapiro			**	no: refuse_connections may sleep().
24990792Sgshapiro			*/
25071345Sgshapiro
25190792Sgshapiro			now = curtime();
25290792Sgshapiro			if (now < Daemons[idx].d_refuse_connections_until)
25364562Sgshapiro				continue;
25490792Sgshapiro			if (bitnset(D_DISABLE, Daemons[idx].d_flags))
25590792Sgshapiro				continue;
256168515Sgshapiro			if (refuseconnections(e, idx, curdaemon == idx))
25738032Speter			{
25864562Sgshapiro				if (Daemons[idx].d_socket >= 0)
25942575Speter				{
26071345Sgshapiro					/* close socket so peer fails quickly */
26171345Sgshapiro					(void) close(Daemons[idx].d_socket);
26271345Sgshapiro					Daemons[idx].d_socket = -1;
26342575Speter				}
26442575Speter
26542575Speter				/* refuse connections for next 15 seconds */
26690792Sgshapiro				Daemons[idx].d_refuse_connections_until = now + 15;
26738032Speter			}
26864562Sgshapiro			else if (Daemons[idx].d_socket < 0 ||
26964562Sgshapiro				 Daemons[idx].d_firsttime)
27042575Speter			{
27190792Sgshapiro				if (!Daemons[idx].d_firsttime && LogLevel > 8)
27271345Sgshapiro					sm_syslog(LOG_INFO, NOQID,
27371345Sgshapiro						"accepting connections again for daemon %s",
27471345Sgshapiro						Daemons[idx].d_name);
27564562Sgshapiro
27671345Sgshapiro				/* arrange to (re)open the socket if needed */
27790792Sgshapiro				(void) opendaemonsocket(&Daemons[idx], false);
27890792Sgshapiro				Daemons[idx].d_firsttime = false;
27942575Speter			}
28038032Speter		}
28138032Speter
28277349Sgshapiro		/* May have been sleeping above, check again */
283120256Sgshapiro		CHECK_RESTART;
284132943Sgshapiro
28590792Sgshapiro		getrequests_checkdiskspace(e);
28671345Sgshapiro
28790792Sgshapiro#if XDEBUG
28838032Speter		/* check for disaster */
28938032Speter		{
29038032Speter			char jbuf[MAXHOSTNAMELEN];
29138032Speter
292168515Sgshapiro			expand("\201j", jbuf, sizeof(jbuf), e);
29338032Speter			if (!wordinclass(jbuf, 'w'))
29438032Speter			{
29538032Speter				dumpstate("daemon lost $j");
29638032Speter				sm_syslog(LOG_ALERT, NOQID,
29764562Sgshapiro					  "daemon process doesn't have $j in $=w; see syslog");
29838032Speter				abort();
29938032Speter			}
30038032Speter			else if (j_has_dot && strchr(jbuf, '.') == NULL)
30138032Speter			{
30238032Speter				dumpstate("daemon $j lost dot");
30338032Speter				sm_syslog(LOG_ALERT, NOQID,
30464562Sgshapiro					  "daemon process $j lost dot; see syslog");
30538032Speter				abort();
30638032Speter			}
30738032Speter		}
30890792Sgshapiro#endif /* XDEBUG */
30938032Speter
31090792Sgshapiro#if 0
31138032Speter		/*
31238032Speter		**  Andrew Sun <asun@ieps-sun.ml.com> claims that this will
31338032Speter		**  fix the SVr4 problem.  But it seems to have gone away,
31438032Speter		**  so is it worth doing this?
31538032Speter		*/
31638032Speter
31742575Speter		if (DaemonSocket >= 0 &&
31890792Sgshapiro		    SetNonBlocking(DaemonSocket, false) < 0)
31938032Speter			log an error here;
32090792Sgshapiro#endif /* 0 */
32190792Sgshapiro		(void) sm_releasesignal(SIGALRM);
32264562Sgshapiro
32338032Speter		for (;;)
32438032Speter		{
32590792Sgshapiro			bool setproc = false;
32642575Speter			int highest = -1;
32738032Speter			fd_set readfds;
32838032Speter			struct timeval timeout;
32938032Speter
330120256Sgshapiro			CHECK_RESTART;
33138032Speter			FD_ZERO(&readfds);
33290792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
33342575Speter			{
33464562Sgshapiro				/* wait for a connection */
33564562Sgshapiro				if (Daemons[idx].d_socket >= 0)
33664562Sgshapiro				{
33771345Sgshapiro					if (!setproc &&
33871345Sgshapiro					    !bitnset(D_ETRNONLY,
33971345Sgshapiro						     Daemons[idx].d_flags))
34064562Sgshapiro					{
34190792Sgshapiro						sm_setproctitle(true, e,
34264562Sgshapiro								"accepting connections");
34390792Sgshapiro						setproc = true;
34464562Sgshapiro					}
34564562Sgshapiro					if (Daemons[idx].d_socket > highest)
34664562Sgshapiro						highest = Daemons[idx].d_socket;
34790792Sgshapiro					SM_FD_SET(Daemons[idx].d_socket,
34890792Sgshapiro						  &readfds);
34964562Sgshapiro				}
35042575Speter			}
35164562Sgshapiro
35290792Sgshapiro#if NETUNIX
35342575Speter			if (ControlSocket >= 0)
35442575Speter			{
35542575Speter				if (ControlSocket > highest)
35642575Speter					highest = ControlSocket;
35790792Sgshapiro				SM_FD_SET(ControlSocket, &readfds);
35842575Speter			}
35990792Sgshapiro#endif /* NETUNIX */
36064562Sgshapiro
36177349Sgshapiro			timeout.tv_sec = 5;
36238032Speter			timeout.tv_usec = 0;
36338032Speter
36442575Speter			t = select(highest + 1, FDSET_CAST &readfds,
36564562Sgshapiro				   NULL, NULL, &timeout);
36642575Speter
36777349Sgshapiro			/* Did someone signal while waiting? */
368120256Sgshapiro			CHECK_RESTART;
36971345Sgshapiro
37090792Sgshapiro			curdaemon = -1;
37190792Sgshapiro			if (doqueuerun())
37294334Sgshapiro			{
37390792Sgshapiro				(void) runqueue(true, false, false, false);
37494334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
37594334Sgshapiro				lastrun = now;
376363466Sgshapiro#endif
37794334Sgshapiro			}
37894334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
379157001Sgshapiro			else if (CheckQueueRunners > 0 && QueueIntvl > 0 &&
380157001Sgshapiro				 lastrun + QueueIntvl + CheckQueueRunners < now)
38194334Sgshapiro			{
38271345Sgshapiro
38394334Sgshapiro				/*
38494334Sgshapiro				**  set lastrun unconditionally to avoid
38594334Sgshapiro				**  calling checkqueuerunner() all the time.
38694334Sgshapiro				**  That's also why we currently ignore the
38794334Sgshapiro				**  result of the function call.
38894334Sgshapiro				*/
38994334Sgshapiro
39094334Sgshapiro				(void) checkqueuerunner();
39194334Sgshapiro				lastrun = now;
39294334Sgshapiro			}
39394334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
39494334Sgshapiro
39542575Speter			if (t <= 0)
39642575Speter			{
39790792Sgshapiro				timedout = true;
39842575Speter				break;
39942575Speter			}
40038032Speter
40190792Sgshapiro			control = false;
40238032Speter			errno = 0;
40364562Sgshapiro
40464562Sgshapiro			/* look "round-robin" for an active socket */
40590792Sgshapiro			if ((idx = olddaemon + 1) >= NDaemons)
40664562Sgshapiro				idx = 0;
40790792Sgshapiro			for (i = 0; i < NDaemons; i++)
40842575Speter			{
40964562Sgshapiro				if (Daemons[idx].d_socket >= 0 &&
41090792Sgshapiro				    SM_FD_ISSET(Daemons[idx].d_socket,
41190792Sgshapiro						&readfds))
41264562Sgshapiro				{
41364562Sgshapiro					lotherend = Daemons[idx].d_socksize;
41473188Sgshapiro					memset(&RealHostAddr, '\0',
415168515Sgshapiro					       sizeof(RealHostAddr));
41664562Sgshapiro					t = accept(Daemons[idx].d_socket,
41764562Sgshapiro						   (struct sockaddr *)&RealHostAddr,
41864562Sgshapiro						   &lotherend);
41973188Sgshapiro
42073188Sgshapiro					/*
42173188Sgshapiro					**  If remote side closes before
42273188Sgshapiro					**  accept() finishes, sockaddr
42373188Sgshapiro					**  might not be fully filled in.
42473188Sgshapiro					*/
42573188Sgshapiro
42673188Sgshapiro					if (t >= 0 &&
42773188Sgshapiro					    (lotherend == 0 ||
428363466Sgshapiro#ifdef BSD4_4_SOCKADDR
42973188Sgshapiro					     RealHostAddr.sa.sa_len == 0 ||
430363466Sgshapiro#endif
43173188Sgshapiro					     RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
43273188Sgshapiro					{
43373188Sgshapiro						(void) close(t);
43473188Sgshapiro						t = -1;
43573188Sgshapiro						errno = EINVAL;
43673188Sgshapiro					}
43764562Sgshapiro					olddaemon = curdaemon = idx;
43864562Sgshapiro					break;
43964562Sgshapiro				}
44090792Sgshapiro				if (++idx >= NDaemons)
44164562Sgshapiro					idx = 0;
44242575Speter			}
44390792Sgshapiro#if NETUNIX
44464562Sgshapiro			if (curdaemon == -1 && ControlSocket >= 0 &&
44590792Sgshapiro			    SM_FD_ISSET(ControlSocket, &readfds))
44642575Speter			{
44742575Speter				struct sockaddr_un sa_un;
44842575Speter
449168515Sgshapiro				lotherend = sizeof(sa_un);
450168515Sgshapiro				memset(&sa_un, '\0', sizeof(sa_un));
45142575Speter				t = accept(ControlSocket,
45242575Speter					   (struct sockaddr *)&sa_un,
45342575Speter					   &lotherend);
45473188Sgshapiro
45573188Sgshapiro				/*
45673188Sgshapiro				**  If remote side closes before
45773188Sgshapiro				**  accept() finishes, sockaddr
45873188Sgshapiro				**  might not be fully filled in.
45973188Sgshapiro				*/
46073188Sgshapiro
46173188Sgshapiro				if (t >= 0 &&
46273188Sgshapiro				    (lotherend == 0 ||
46373188Sgshapiro# ifdef BSD4_4_SOCKADDR
46473188Sgshapiro				     sa_un.sun_len == 0 ||
465363466Sgshapiro# endif
46673188Sgshapiro				     sa_un.sun_family != AF_UNIX))
46773188Sgshapiro				{
46873188Sgshapiro					(void) close(t);
46973188Sgshapiro					t = -1;
47073188Sgshapiro					errno = EINVAL;
47173188Sgshapiro				}
47273188Sgshapiro				if (t >= 0)
47390792Sgshapiro					control = true;
47442575Speter			}
47590792Sgshapiro#else /* NETUNIX */
47671345Sgshapiro			if (curdaemon == -1)
47771345Sgshapiro			{
47871345Sgshapiro				/* No daemon to service */
47971345Sgshapiro				continue;
48071345Sgshapiro			}
48190792Sgshapiro#endif /* NETUNIX */
48238032Speter			if (t >= 0 || errno != EINTR)
48338032Speter				break;
48438032Speter		}
48542575Speter		if (timedout)
48642575Speter		{
48790792Sgshapiro			timedout = false;
48842575Speter			continue;
48942575Speter		}
49064562Sgshapiro		save_errno = errno;
49190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
49238032Speter		if (t < 0)
49338032Speter		{
49464562Sgshapiro			errno = save_errno;
495132943Sgshapiro
496132943Sgshapiro			/* let's ignore these temporary errors */
497132943Sgshapiro			if (save_errno == EINTR
498132943Sgshapiro#ifdef EAGAIN
499132943Sgshapiro			    || save_errno == EAGAIN
500363466Sgshapiro#endif
501132943Sgshapiro#ifdef ECONNABORTED
502132943Sgshapiro			    || save_errno == ECONNABORTED
503363466Sgshapiro#endif
504132943Sgshapiro#ifdef EWOULDBLOCK
505132943Sgshapiro			    || save_errno == EWOULDBLOCK
506363466Sgshapiro#endif
507132943Sgshapiro			   )
508132943Sgshapiro				continue;
509132943Sgshapiro
51038032Speter			syserr("getrequests: accept");
51138032Speter
512159609Sgshapiro			if (curdaemon >= 0)
513159609Sgshapiro			{
514159609Sgshapiro				/* arrange to re-open socket next time around */
515159609Sgshapiro				(void) close(Daemons[curdaemon].d_socket);
516159609Sgshapiro				Daemons[curdaemon].d_socket = -1;
51790792Sgshapiro#if SO_REUSEADDR_IS_BROKEN
518159609Sgshapiro				/*
519159609Sgshapiro				**  Give time for bound socket to be released.
520159609Sgshapiro				**  This creates a denial-of-service if you can
521159609Sgshapiro				**  force accept() to fail on affected systems.
522159609Sgshapiro				*/
52364562Sgshapiro
524159609Sgshapiro				Daemons[curdaemon].d_refuse_connections_until =
525159609Sgshapiro					curtime() + 15;
52690792Sgshapiro#endif /* SO_REUSEADDR_IS_BROKEN */
527159609Sgshapiro			}
52838032Speter			continue;
52938032Speter		}
53038032Speter
53164562Sgshapiro		if (!control)
53264562Sgshapiro		{
53364562Sgshapiro			/* set some daemon related macros */
53464562Sgshapiro			switch (Daemons[curdaemon].d_addr.sa.sa_family)
53564562Sgshapiro			{
53664562Sgshapiro			  case AF_UNSPEC:
53790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
53890792Sgshapiro					macid("{daemon_family}"), "unspec");
53964562Sgshapiro				break;
540285229Sgshapiro#if NETUNIX
54190792Sgshapiro			  case AF_UNIX:
54290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54390792Sgshapiro					macid("{daemon_family}"), "local");
54490792Sgshapiro				break;
545363466Sgshapiro#endif
54690792Sgshapiro#if NETINET
54764562Sgshapiro			  case AF_INET:
54890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54990792Sgshapiro					macid("{daemon_family}"), "inet");
55064562Sgshapiro				break;
551363466Sgshapiro#endif
55290792Sgshapiro#if NETINET6
55364562Sgshapiro			  case AF_INET6:
55490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
55590792Sgshapiro					macid("{daemon_family}"), "inet6");
55664562Sgshapiro				break;
557363466Sgshapiro#endif
55890792Sgshapiro#if NETISO
55964562Sgshapiro			  case AF_ISO:
56090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56190792Sgshapiro					macid("{daemon_family}"), "iso");
56264562Sgshapiro				break;
563363466Sgshapiro#endif
56490792Sgshapiro#if NETNS
56564562Sgshapiro			  case AF_NS:
56690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56790792Sgshapiro					macid("{daemon_family}"), "ns");
56864562Sgshapiro				break;
569363466Sgshapiro#endif
57090792Sgshapiro#if NETX25
57164562Sgshapiro			  case AF_CCITT:
57290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
57390792Sgshapiro					macid("{daemon_family}"), "x.25");
57464562Sgshapiro				break;
575363466Sgshapiro#endif
57664562Sgshapiro			}
57790792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
57890792Sgshapiro				macid("{daemon_name}"),
57990792Sgshapiro				Daemons[curdaemon].d_name);
58064562Sgshapiro			if (Daemons[curdaemon].d_mflags != NULL)
58190792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
58290792Sgshapiro					macid("{daemon_flags}"),
58390792Sgshapiro					Daemons[curdaemon].d_mflags);
58464562Sgshapiro			else
58590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
58690792Sgshapiro					macid("{daemon_flags}"), "");
58764562Sgshapiro		}
58864562Sgshapiro
58938032Speter		/*
590132943Sgshapiro		**  If connection rate is exceeded here, connection shall be
591132943Sgshapiro		**  refused later by a new call after fork() by the
592132943Sgshapiro		**  validate_connection() function. Closing the connection
593132943Sgshapiro		**  at this point violates RFC 2821.
594132943Sgshapiro		**  Do NOT remove this call, its side effects are needed.
595132943Sgshapiro		*/
596132943Sgshapiro
597132943Sgshapiro		connection_rate_check(&RealHostAddr, NULL);
598132943Sgshapiro
599132943Sgshapiro		/*
60038032Speter		**  Create a subprocess to process the mail.
60138032Speter		*/
60238032Speter
60338032Speter		if (tTd(15, 2))
60490792Sgshapiro			sm_dprintf("getrequests: forking (fd = %d)\n", t);
60538032Speter
60638032Speter		/*
60790792Sgshapiro		**  Advance state of PRNG.
60890792Sgshapiro		**  This is necessary because otherwise all child processes
60964562Sgshapiro		**  will produce the same PRN sequence and hence the selection
61064562Sgshapiro		**  of a queue directory (and other things, e.g., MX selection)
61164562Sgshapiro		**  are not "really" random.
61264562Sgshapiro		*/
61390792Sgshapiro#if STARTTLS
61490792Sgshapiro		/* XXX get some better "random" data? */
61566494Sgshapiro		seed = get_random();
61690792Sgshapiro		RAND_seed((void *) &NextDiskSpaceCheck,
617168515Sgshapiro			  sizeof(NextDiskSpaceCheck));
618168515Sgshapiro		RAND_seed((void *) &now, sizeof(now));
619168515Sgshapiro		RAND_seed((void *) &seed, sizeof(seed));
62090792Sgshapiro#else /* STARTTLS */
62164562Sgshapiro		(void) get_random();
62290792Sgshapiro#endif /* STARTTLS */
62364562Sgshapiro
62490792Sgshapiro#if NAMED_BIND
62564562Sgshapiro		/*
626132943Sgshapiro		**  Update MX records for FallbackMX.
62790792Sgshapiro		**  Let's hope this is fast otherwise we screw up the
62890792Sgshapiro		**  response time.
62990792Sgshapiro		*/
63090792Sgshapiro
631132943Sgshapiro		if (FallbackMX != NULL)
632132943Sgshapiro			(void) getfallbackmxrr(FallbackMX);
63390792Sgshapiro#endif /* NAMED_BIND */
63490792Sgshapiro
635110560Sgshapiro		if (tTd(93, 100))
636110560Sgshapiro		{
637110560Sgshapiro			/* don't fork, handle connection in this process */
638110560Sgshapiro			pid = 0;
63938032Speter			pipefd[0] = pipefd[1] = -1;
640110560Sgshapiro		}
641110560Sgshapiro		else
642110560Sgshapiro		{
643110560Sgshapiro			/*
644110560Sgshapiro			**  Create a pipe to keep the child from writing to
645110560Sgshapiro			**  the socket until after the parent has closed
646110560Sgshapiro			**  it.  Otherwise the parent may hang if the child
647110560Sgshapiro			**  has closed it first.
648110560Sgshapiro			*/
64938032Speter
650110560Sgshapiro			if (pipe(pipefd) < 0)
651110560Sgshapiro				pipefd[0] = pipefd[1] = -1;
652110560Sgshapiro
653110560Sgshapiro			(void) sm_blocksignal(SIGCHLD);
654110560Sgshapiro			pid = fork();
655110560Sgshapiro			if (pid < 0)
65638032Speter			{
657110560Sgshapiro				syserr("daemon: cannot fork");
658110560Sgshapiro				if (pipefd[0] != -1)
659110560Sgshapiro				{
660110560Sgshapiro					(void) close(pipefd[0]);
661110560Sgshapiro					(void) close(pipefd[1]);
662110560Sgshapiro				}
663110560Sgshapiro				(void) sm_releasesignal(SIGCHLD);
664110560Sgshapiro				(void) sleep(10);
665110560Sgshapiro				(void) close(t);
666110560Sgshapiro				continue;
66738032Speter			}
66838032Speter		}
66990792Sgshapiro
67038032Speter		if (pid == 0)
67138032Speter		{
67238032Speter			char *p;
67390792Sgshapiro			SM_FILE_T *inchannel, *outchannel = NULL;
67438032Speter
67538032Speter			/*
67638032Speter			**  CHILD -- return to caller.
67738032Speter			**	Collect verified idea of sending host.
67838032Speter			**	Verify calling user id if possible here.
67938032Speter			*/
68038032Speter
68177349Sgshapiro			/* Reset global flags */
68277349Sgshapiro			RestartRequest = NULL;
68390792Sgshapiro			RestartWorkGroup = false;
68477349Sgshapiro			ShutdownRequest = NULL;
68577349Sgshapiro			PendingSignal = 0;
68690792Sgshapiro			CurrentPid = getpid();
687132943Sgshapiro			close_sendmail_pid();
68877349Sgshapiro
68990792Sgshapiro			(void) sm_releasesignal(SIGALRM);
69090792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
69190792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
69290792Sgshapiro			(void) sm_signal(SIGHUP, SIG_DFL);
69390792Sgshapiro			(void) sm_signal(SIGTERM, intsig);
69477349Sgshapiro
69590792Sgshapiro			/* turn on profiling */
69690792Sgshapiro			/* SM_PROF(0); */
69790792Sgshapiro
69890792Sgshapiro			/*
69990792Sgshapiro			**  Initialize exception stack and default exception
70090792Sgshapiro			**  handler for child process.
70190792Sgshapiro			*/
70290792Sgshapiro
70390792Sgshapiro			sm_exc_newthread(fatal_error);
70490792Sgshapiro
70564562Sgshapiro			if (!control)
70664562Sgshapiro			{
70790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
70890792Sgshapiro					macid("{daemon_addr}"),
70990792Sgshapiro					anynet_ntoa(&Daemons[curdaemon].d_addr));
710168515Sgshapiro				(void) sm_snprintf(status, sizeof(status), "%d",
71164562Sgshapiro						ntohs(Daemons[curdaemon].d_port));
71290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
71390792Sgshapiro					macid("{daemon_port}"), status);
71464562Sgshapiro			}
71564562Sgshapiro
71690792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
71764562Sgshapiro			{
71864562Sgshapiro				if (Daemons[idx].d_socket >= 0)
71964562Sgshapiro					(void) close(Daemons[idx].d_socket);
72080785Sgshapiro				Daemons[idx].d_socket = -1;
72164562Sgshapiro			}
72242575Speter			clrcontrol();
72338032Speter
72464562Sgshapiro			/* Avoid SMTP daemon actions if control command */
72564562Sgshapiro			if (control)
72664562Sgshapiro			{
72764562Sgshapiro				/* Add control socket process */
72890792Sgshapiro				proc_list_add(CurrentPid,
72990792Sgshapiro					      "console socket child",
730132943Sgshapiro					      PROC_CONTROL_CHILD, 0, -1, NULL);
73164562Sgshapiro			}
73264562Sgshapiro			else
73364562Sgshapiro			{
73464562Sgshapiro				proc_list_clear();
73542575Speter
73690792Sgshapiro				/* clean up background delivery children */
73790792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
73890792Sgshapiro
73964562Sgshapiro				/* Add parent process as first child item */
74090792Sgshapiro				proc_list_add(CurrentPid, "daemon child",
741132943Sgshapiro					      PROC_DAEMON_CHILD, 0, -1, NULL);
74264562Sgshapiro				/* don't schedule queue runs if ETRN */
74364562Sgshapiro				QueueIntvl = 0;
744168515Sgshapiro
745168515Sgshapiro				/*
746168515Sgshapiro				**  Hack: override global variables if
747168515Sgshapiro				**	the corresponding DaemonPortOption
748168515Sgshapiro				**	is set.
749168515Sgshapiro				*/
750147078Sgshapiro#if _FFR_SS_PER_DAEMON
751147078Sgshapiro				if (Daemons[curdaemon].d_supersafe !=
752168515Sgshapiro				    DPO_NOTSET)
753168515Sgshapiro					SuperSafe = Daemons[curdaemon].
754168515Sgshapiro								d_supersafe;
755147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
756147078Sgshapiro				if (Daemons[curdaemon].d_dm != DM_NOTSET)
757147078Sgshapiro					set_delivery_mode(
758147078Sgshapiro						Daemons[curdaemon].d_dm, e);
75938032Speter
760168515Sgshapiro				if (Daemons[curdaemon].d_refuseLA !=
761168515Sgshapiro				    DPO_NOTSET)
762168515Sgshapiro					RefuseLA = Daemons[curdaemon].
763168515Sgshapiro								d_refuseLA;
764168515Sgshapiro				if (Daemons[curdaemon].d_queueLA != DPO_NOTSET)
765168515Sgshapiro					QueueLA = Daemons[curdaemon].d_queueLA;
766168515Sgshapiro				if (Daemons[curdaemon].d_delayLA != DPO_NOTSET)
767168515Sgshapiro					DelayLA = Daemons[curdaemon].d_delayLA;
768168515Sgshapiro				if (Daemons[curdaemon].d_maxchildren !=
769168515Sgshapiro				    DPO_NOTSET)
770168515Sgshapiro					MaxChildren = Daemons[curdaemon].
771168515Sgshapiro								d_maxchildren;
772168515Sgshapiro
77390792Sgshapiro				sm_setproctitle(true, e, "startup with %s",
77464562Sgshapiro						anynet_ntoa(&RealHostAddr));
77564562Sgshapiro			}
77664562Sgshapiro
77738032Speter			if (pipefd[0] != -1)
77838032Speter			{
77938032Speter				auto char c;
78038032Speter
78138032Speter				/*
78238032Speter				**  Wait for the parent to close the write end
78338032Speter				**  of the pipe, which we will see as an EOF.
78438032Speter				**  This guarantees that we won't write to the
78538032Speter				**  socket until after the parent has closed
78638032Speter				**  the pipe.
78738032Speter				*/
78838032Speter
78938032Speter				/* close the write end of the pipe */
79038032Speter				(void) close(pipefd[1]);
79138032Speter
79238032Speter				/* we shouldn't be interrupted, but ... */
79338032Speter				while (read(pipefd[0], &c, 1) < 0 &&
79438032Speter				       errno == EINTR)
79538032Speter					continue;
79638032Speter				(void) close(pipefd[0]);
79738032Speter			}
79838032Speter
79964562Sgshapiro			/* control socket processing */
80064562Sgshapiro			if (control)
80164562Sgshapiro			{
80264562Sgshapiro				control_command(t, e);
80364562Sgshapiro				/* NOTREACHED */
80464562Sgshapiro				exit(EX_SOFTWARE);
80564562Sgshapiro			}
80664562Sgshapiro
80738032Speter			/* determine host name */
80838032Speter			p = hostnamebyanyaddr(&RealHostAddr);
80990792Sgshapiro			if (strlen(p) > MAXNAME) /* XXX  - 1 ? */
81038032Speter				p[MAXNAME] = '\0';
81138032Speter			RealHostName = newstr(p);
81264562Sgshapiro			if (RealHostName[0] == '[')
81364562Sgshapiro			{
81490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
81590792Sgshapiro					macid("{client_resolve}"),
81690792Sgshapiro					h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
81764562Sgshapiro			}
81864562Sgshapiro			else
819132943Sgshapiro			{
82090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
821132943Sgshapiro					  macid("{client_resolve}"), "OK");
822132943Sgshapiro			}
82390792Sgshapiro			sm_setproctitle(true, e, "startup with %s", p);
82494334Sgshapiro			markstats(e, NULL, STATS_CONNECT);
82538032Speter
82690792Sgshapiro			if ((inchannel = sm_io_open(SmFtStdiofd,
82790792Sgshapiro						    SM_TIME_DEFAULT,
82890792Sgshapiro						    (void *) &t,
829132943Sgshapiro						    SM_IO_RDONLY_B,
83090792Sgshapiro						    NULL)) == NULL ||
83138032Speter			    (t = dup(t)) < 0 ||
83290792Sgshapiro			    (outchannel = sm_io_open(SmFtStdiofd,
83390792Sgshapiro						     SM_TIME_DEFAULT,
83490792Sgshapiro						     (void *) &t,
835132943Sgshapiro						     SM_IO_WRONLY_B,
83690792Sgshapiro						     NULL)) == NULL)
83738032Speter			{
83890792Sgshapiro				syserr("cannot open SMTP server channel, fd=%d",
83990792Sgshapiro					t);
84090792Sgshapiro				finis(false, true, EX_OK);
84138032Speter			}
84290792Sgshapiro			sm_io_automode(inchannel, outchannel);
84338032Speter
84438032Speter			InChannel = inchannel;
84538032Speter			OutChannel = outchannel;
84690792Sgshapiro			DisConnected = false;
84738032Speter
848285229Sgshapiro#if _FFR_XCNCT
849285229Sgshapiro			t = xconnect(inchannel);
850285229Sgshapiro			if (t <= 0)
851285229Sgshapiro			{
852285229Sgshapiro				clrbitn(D_XCNCT, Daemons[curdaemon].d_flags);
853285229Sgshapiro				clrbitn(D_XCNCT_M, Daemons[curdaemon].d_flags);
854285229Sgshapiro			}
855285229Sgshapiro			else
856285229Sgshapiro				setbitn(t, Daemons[curdaemon].d_flags);
857244833Sgshapiro
858285229Sgshapiro#endif /* _FFR_XCNCT */
859285229Sgshapiro
86090792Sgshapiro#if XLA
86138032Speter			if (!xla_host_ok(RealHostName))
86238032Speter			{
86364562Sgshapiro				message("421 4.4.5 Too many SMTP sessions for this host");
86490792Sgshapiro				finis(false, true, EX_OK);
86538032Speter			}
86690792Sgshapiro#endif /* XLA */
86764562Sgshapiro			/* find out name for interface of connection */
86890792Sgshapiro			if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
86990792Sgshapiro						      NULL), &sa.sa, &len) == 0)
87064562Sgshapiro			{
87164562Sgshapiro				p = hostnamebyanyaddr(&sa);
87264562Sgshapiro				if (tTd(15, 9))
87390792Sgshapiro					sm_dprintf("getreq: got name %s\n", p);
87490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
87590792Sgshapiro					macid("{if_name}"), p);
87664562Sgshapiro
87790792Sgshapiro				/*
87890792Sgshapiro				**  Do this only if it is not the loopback
87990792Sgshapiro				**  interface.
88090792Sgshapiro				*/
88190792Sgshapiro
88264562Sgshapiro				if (!isloopback(sa))
88364562Sgshapiro				{
88490792Sgshapiro					char *addr;
88590792Sgshapiro					char family[5];
88690792Sgshapiro
88790792Sgshapiro					addr = anynet_ntoa(&sa);
88890792Sgshapiro					(void) sm_snprintf(family,
88990792Sgshapiro						sizeof(family),
89090792Sgshapiro						"%d", sa.sa.sa_family);
89190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
89290792Sgshapiro						A_TEMP,
89390792Sgshapiro						macid("{if_addr}"), addr);
89490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
89590792Sgshapiro						A_TEMP,
89690792Sgshapiro						macid("{if_family}"), family);
89764562Sgshapiro					if (tTd(15, 7))
89890792Sgshapiro						sm_dprintf("getreq: got addr %s and family %s\n",
89990792Sgshapiro							addr, family);
90064562Sgshapiro				}
90164562Sgshapiro				else
90264562Sgshapiro				{
90390792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
90490792Sgshapiro						A_PERM,
90590792Sgshapiro						macid("{if_addr}"), NULL);
90690792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
90790792Sgshapiro						A_PERM,
90890792Sgshapiro						macid("{if_family}"), NULL);
90964562Sgshapiro				}
91064562Sgshapiro			}
91164562Sgshapiro			else
91264562Sgshapiro			{
91364562Sgshapiro				if (tTd(15, 7))
91490792Sgshapiro					sm_dprintf("getreq: getsockname failed\n");
91590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
91690792Sgshapiro					macid("{if_name}"), NULL);
91790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
91890792Sgshapiro					macid("{if_addr}"), NULL);
91990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
92090792Sgshapiro					macid("{if_family}"), NULL);
92164562Sgshapiro			}
92238032Speter			break;
92338032Speter		}
92438032Speter
92538032Speter		/* parent -- keep track of children */
92664562Sgshapiro		if (control)
92764562Sgshapiro		{
928168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
92990792Sgshapiro					   "control socket server child");
930132943Sgshapiro			proc_list_add(pid, status, PROC_CONTROL, 0, -1, NULL);
93164562Sgshapiro		}
93264562Sgshapiro		else
93364562Sgshapiro		{
934168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
93590792Sgshapiro					   "SMTP server child for %s",
93690792Sgshapiro					   anynet_ntoa(&RealHostAddr));
937132943Sgshapiro			proc_list_add(pid, status, PROC_DAEMON, 0, -1,
938132943Sgshapiro					&RealHostAddr);
93964562Sgshapiro		}
94090792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
94138032Speter
94238032Speter		/* close the read end of the synchronization pipe */
94338032Speter		if (pipefd[0] != -1)
94464562Sgshapiro		{
94538032Speter			(void) close(pipefd[0]);
94664562Sgshapiro			pipefd[0] = -1;
94764562Sgshapiro		}
94838032Speter
94938032Speter		/* close the port so that others will hang (for a while) */
95038032Speter		(void) close(t);
95138032Speter
95238032Speter		/* release the child by closing the read end of the sync pipe */
95338032Speter		if (pipefd[1] != -1)
95464562Sgshapiro		{
95538032Speter			(void) close(pipefd[1]);
95664562Sgshapiro			pipefd[1] = -1;
95764562Sgshapiro		}
95838032Speter	}
95990792Sgshapiro	if (tTd(15, 2))
96090792Sgshapiro		sm_dprintf("getreq: returning\n");
96164562Sgshapiro
96290792Sgshapiro#if MILTER
96390792Sgshapiro	/* set the filters for this daemon */
96490792Sgshapiro	if (Daemons[curdaemon].d_inputfilterlist != NULL)
96590792Sgshapiro	{
96690792Sgshapiro		for (i = 0;
967110560Sgshapiro		     (i < MAXFILTERS &&
968110560Sgshapiro		      Daemons[curdaemon].d_inputfilters[i] != NULL);
96990792Sgshapiro		     i++)
97090792Sgshapiro		{
97190792Sgshapiro			InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
97290792Sgshapiro		}
97390792Sgshapiro		if (i < MAXFILTERS)
97490792Sgshapiro			InputFilters[i] = NULL;
97590792Sgshapiro	}
97690792Sgshapiro#endif /* MILTER */
97764562Sgshapiro	return &Daemons[curdaemon].d_flags;
97838032Speter}
97990792Sgshapiro
98090792Sgshapiro/*
98190792Sgshapiro**  GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
98290792Sgshapiro**
98390792Sgshapiro**	Parameters:
98490792Sgshapiro**		e -- envelope.
98590792Sgshapiro**
98690792Sgshapiro**	Returns:
98790792Sgshapiro**		none.
98890792Sgshapiro**
98990792Sgshapiro**	Side Effects:
99090792Sgshapiro**		Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
99190792Sgshapiro*/
99290792Sgshapiro
99390792Sgshapirostatic void
99490792Sgshapirogetrequests_checkdiskspace(e)
99590792Sgshapiro	ENVELOPE *e;
99690792Sgshapiro{
99790792Sgshapiro	bool logged = false;
99890792Sgshapiro	int idx;
99990792Sgshapiro	time_t now;
100090792Sgshapiro
100190792Sgshapiro	now = curtime();
100290792Sgshapiro	if (now < NextDiskSpaceCheck)
100390792Sgshapiro		return;
100490792Sgshapiro
100590792Sgshapiro	/* Check if there is available disk space in all queue groups. */
100690792Sgshapiro	if (!enoughdiskspace(0, NULL))
100790792Sgshapiro	{
100890792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
100990792Sgshapiro		{
101090792Sgshapiro			if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
101190792Sgshapiro				continue;
101290792Sgshapiro
101390792Sgshapiro			/* log only if not logged before */
101490792Sgshapiro			if (!logged)
101590792Sgshapiro			{
101690792Sgshapiro				if (LogLevel > 8)
101790792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
101890792Sgshapiro						  "rejecting new messages: min free: %ld",
101990792Sgshapiro						  MinBlocksFree);
102090792Sgshapiro				sm_setproctitle(true, e,
102190792Sgshapiro						"rejecting new messages: min free: %ld",
102290792Sgshapiro						MinBlocksFree);
102390792Sgshapiro				logged = true;
102490792Sgshapiro			}
102590792Sgshapiro			setbitn(D_ETRNONLY, Daemons[idx].d_flags);
102690792Sgshapiro		}
102790792Sgshapiro	}
102890792Sgshapiro	else
102990792Sgshapiro	{
103090792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
103190792Sgshapiro		{
103290792Sgshapiro			if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
103390792Sgshapiro				continue;
103490792Sgshapiro
103590792Sgshapiro			/* log only if not logged before */
103690792Sgshapiro			if (!logged)
103790792Sgshapiro			{
103890792Sgshapiro				if (LogLevel > 8)
103990792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
104090792Sgshapiro						  "accepting new messages (again)");
104190792Sgshapiro				logged = true;
104290792Sgshapiro			}
104390792Sgshapiro
104490792Sgshapiro			/* title will be set later */
104590792Sgshapiro			clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
104690792Sgshapiro		}
104790792Sgshapiro	}
104890792Sgshapiro
104990792Sgshapiro	/* only check disk space once a minute */
105090792Sgshapiro	NextDiskSpaceCheck = now + 60;
105190792Sgshapiro}
105290792Sgshapiro
105390792Sgshapiro/*
105464562Sgshapiro**  OPENDAEMONSOCKET -- open SMTP socket
105538032Speter**
105664562Sgshapiro**	Deals with setting all appropriate options.
105738032Speter**
105838032Speter**	Parameters:
105964562Sgshapiro**		d -- the structure for the daemon to open.
106038032Speter**		firsttime -- set if this is the initial open.
106138032Speter**
106238032Speter**	Returns:
106338032Speter**		Size in bytes of the daemon socket addr.
106438032Speter**
106538032Speter**	Side Effects:
106638032Speter**		Leaves DaemonSocket set to the open socket.
106738032Speter**		Exits if the socket cannot be created.
106838032Speter*/
106938032Speter
107090792Sgshapiro#define MAXOPENTRIES	10	/* maximum number of tries to open connection */
107138032Speter
107264562Sgshapirostatic int
107364562Sgshapiroopendaemonsocket(d, firsttime)
107490792Sgshapiro	DAEMON_T *d;
107538032Speter	bool firsttime;
107638032Speter{
107738032Speter	int on = 1;
107864562Sgshapiro	int fdflags;
107964562Sgshapiro	SOCKADDR_LEN_T socksize = 0;
108038032Speter	int ntries = 0;
108164562Sgshapiro	int save_errno;
108238032Speter
108338032Speter	if (tTd(15, 2))
108490792Sgshapiro		sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
108538032Speter
108638032Speter	do
108738032Speter	{
108838032Speter		if (ntries > 0)
108964562Sgshapiro			(void) sleep(5);
109064562Sgshapiro		if (firsttime || d->d_socket < 0)
109138032Speter		{
1092285229Sgshapiro#if NETUNIX
109390792Sgshapiro			if (d->d_addr.sa.sa_family == AF_UNIX)
109490792Sgshapiro			{
109590792Sgshapiro				int rval;
109690792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
109790792Sgshapiro
109890792Sgshapiro				/* if not safe, don't use it */
109990792Sgshapiro				rval = safefile(d->d_addr.sunix.sun_path,
110090792Sgshapiro						RunAsUid, RunAsGid,
110190792Sgshapiro						RunAsUserName, sff,
110290792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
110390792Sgshapiro				if (rval != 0)
110490792Sgshapiro				{
110590792Sgshapiro					save_errno = errno;
110690792Sgshapiro					syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
110790792Sgshapiro					       d->d_name,
110890792Sgshapiro					       d->d_addr.sunix.sun_path);
110990792Sgshapiro					goto fail;
111090792Sgshapiro				}
111190792Sgshapiro
111290792Sgshapiro				/* Don't try to overtake an existing socket */
111390792Sgshapiro				(void) unlink(d->d_addr.sunix.sun_path);
111490792Sgshapiro			}
1115285229Sgshapiro#endif /* NETUNIX */
111664562Sgshapiro			d->d_socket = socket(d->d_addr.sa.sa_family,
111764562Sgshapiro					     SOCK_STREAM, 0);
111864562Sgshapiro			if (d->d_socket < 0)
111938032Speter			{
112064562Sgshapiro				save_errno = errno;
112190792Sgshapiro				syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
112290792Sgshapiro				       d->d_name);
112390792Sgshapiro			  fail:
112490792Sgshapiro				if (bitnset(D_OPTIONAL, d->d_flags) &&
112590792Sgshapiro				    (!transienterror(save_errno) ||
112690792Sgshapiro				     ntries >= MAXOPENTRIES - 1))
112790792Sgshapiro				{
112890792Sgshapiro					syserr("opendaemonsocket: daemon %s: optional socket disabled",
112990792Sgshapiro					       d->d_name);
113090792Sgshapiro					setbitn(D_DISABLE, d->d_flags);
113190792Sgshapiro					d->d_socket = -1;
113290792Sgshapiro					return -1;
113390792Sgshapiro				}
113438032Speter			  severe:
113538032Speter				if (LogLevel > 0)
113638032Speter					sm_syslog(LOG_ALERT, NOQID,
113790792Sgshapiro						  "daemon %s: problem creating SMTP socket",
113890792Sgshapiro						  d->d_name);
113964562Sgshapiro				d->d_socket = -1;
114038032Speter				continue;
114138032Speter			}
114238032Speter
1143285229Sgshapiro			if (!SM_FD_OK_SELECT(d->d_socket))
1144110560Sgshapiro			{
1145110560Sgshapiro				save_errno = EINVAL;
1146110560Sgshapiro				syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large",
1147110560Sgshapiro				       d->d_name, d->d_socket);
1148110560Sgshapiro				goto fail;
1149110560Sgshapiro			}
1150110560Sgshapiro
115138032Speter			/* turn on network debugging? */
115238032Speter			if (tTd(15, 101))
115364562Sgshapiro				(void) setsockopt(d->d_socket, SOL_SOCKET,
115438032Speter						  SO_DEBUG, (char *)&on,
1155168515Sgshapiro						  sizeof(on));
115638032Speter
115764562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1158168515Sgshapiro					  SO_REUSEADDR, (char *)&on, sizeof(on));
115964562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1160168515Sgshapiro					  SO_KEEPALIVE, (char *)&on, sizeof(on));
116138032Speter
116290792Sgshapiro#ifdef SO_RCVBUF
116364562Sgshapiro			if (d->d_tcprcvbufsize > 0)
116438032Speter			{
116564562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
116638032Speter					       SO_RCVBUF,
116764562Sgshapiro					       (char *) &d->d_tcprcvbufsize,
116864562Sgshapiro					       sizeof(d->d_tcprcvbufsize)) < 0)
116964562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
117038032Speter			}
117190792Sgshapiro#endif /* SO_RCVBUF */
117290792Sgshapiro#ifdef SO_SNDBUF
117364562Sgshapiro			if (d->d_tcpsndbufsize > 0)
117464562Sgshapiro			{
117564562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
117664562Sgshapiro					       SO_SNDBUF,
117764562Sgshapiro					       (char *) &d->d_tcpsndbufsize,
117864562Sgshapiro					       sizeof(d->d_tcpsndbufsize)) < 0)
117964562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
118064562Sgshapiro			}
118190792Sgshapiro#endif /* SO_SNDBUF */
118238032Speter
118364562Sgshapiro			if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
118464562Sgshapiro			    fcntl(d->d_socket, F_SETFD,
118564562Sgshapiro				  fdflags | FD_CLOEXEC) == -1)
118638032Speter			{
118764562Sgshapiro				save_errno = errno;
118864562Sgshapiro				syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
118964562Sgshapiro				       d->d_name,
119064562Sgshapiro				       fdflags == -1 ? "get" : "set",
119190792Sgshapiro				       sm_errstring(save_errno));
119264562Sgshapiro				(void) close(d->d_socket);
119364562Sgshapiro				goto severe;
119464562Sgshapiro			}
119564562Sgshapiro
119664562Sgshapiro			switch (d->d_addr.sa.sa_family)
119764562Sgshapiro			{
1198285229Sgshapiro#ifdef NETUNIX
119990792Sgshapiro			  case AF_UNIX:
1200168515Sgshapiro				socksize = sizeof(d->d_addr.sunix);
120190792Sgshapiro				break;
1202363466Sgshapiro#endif
120390792Sgshapiro#if NETINET
120438032Speter			  case AF_INET:
1205168515Sgshapiro				socksize = sizeof(d->d_addr.sin);
120638032Speter				break;
1207363466Sgshapiro#endif
120838032Speter
120990792Sgshapiro#if NETINET6
121064562Sgshapiro			  case AF_INET6:
1211168515Sgshapiro				socksize = sizeof(d->d_addr.sin6);
121264562Sgshapiro				break;
1213363466Sgshapiro#endif
121464562Sgshapiro
121590792Sgshapiro#if NETISO
121638032Speter			  case AF_ISO:
1217168515Sgshapiro				socksize = sizeof(d->d_addr.siso);
121838032Speter				break;
1219363466Sgshapiro#endif
122038032Speter
122138032Speter			  default:
1222168515Sgshapiro				socksize = sizeof(d->d_addr);
122338032Speter				break;
122438032Speter			}
122538032Speter
122664562Sgshapiro			if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
122738032Speter			{
122838032Speter				/* probably another daemon already */
122964562Sgshapiro				save_errno = errno;
123064562Sgshapiro				syserr("opendaemonsocket: daemon %s: cannot bind",
123164562Sgshapiro				       d->d_name);
123264562Sgshapiro				(void) close(d->d_socket);
123390792Sgshapiro				goto fail;
123438032Speter			}
123538032Speter		}
123664562Sgshapiro		if (!firsttime &&
123764562Sgshapiro		    listen(d->d_socket, d->d_listenqueue) < 0)
123838032Speter		{
123964562Sgshapiro			save_errno = errno;
124064562Sgshapiro			syserr("opendaemonsocket: daemon %s: cannot listen",
124164562Sgshapiro			       d->d_name);
124264562Sgshapiro			(void) close(d->d_socket);
124338032Speter			goto severe;
124438032Speter		}
124538032Speter		return socksize;
124664562Sgshapiro	} while (ntries++ < MAXOPENTRIES && transienterror(save_errno));
124764562Sgshapiro	syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting",
124864562Sgshapiro	       d->d_name);
124964562Sgshapiro	/* NOTREACHED */
125038032Speter	return -1;  /* avoid compiler warning on IRIX */
125138032Speter}
125290792Sgshapiro/*
125364562Sgshapiro**  SETUPDAEMON -- setup socket for daemon
125464562Sgshapiro**
125564562Sgshapiro**	Parameters:
125664562Sgshapiro**		daemonaddr -- socket for daemon
125764562Sgshapiro**
125864562Sgshapiro**	Returns:
125964562Sgshapiro**		port number on which daemon should run
126064562Sgshapiro**
126164562Sgshapiro*/
126290792Sgshapiro
126390792Sgshapirostatic unsigned short
126464562Sgshapirosetupdaemon(daemonaddr)
126564562Sgshapiro	SOCKADDR *daemonaddr;
126664562Sgshapiro{
126790792Sgshapiro	unsigned short port;
126864562Sgshapiro
126964562Sgshapiro	/*
127064562Sgshapiro	**  Set up the address for the mailer.
127164562Sgshapiro	*/
127264562Sgshapiro
127364562Sgshapiro	if (daemonaddr->sa.sa_family == AF_UNSPEC)
127464562Sgshapiro	{
1275168515Sgshapiro		memset(daemonaddr, '\0', sizeof(*daemonaddr));
127690792Sgshapiro#if NETINET
127764562Sgshapiro		daemonaddr->sa.sa_family = AF_INET;
1278363466Sgshapiro#endif
127964562Sgshapiro	}
128064562Sgshapiro
128164562Sgshapiro	switch (daemonaddr->sa.sa_family)
128264562Sgshapiro	{
128390792Sgshapiro#if NETINET
128464562Sgshapiro	  case AF_INET:
128564562Sgshapiro		if (daemonaddr->sin.sin_addr.s_addr == 0)
1286182352Sgshapiro			daemonaddr->sin.sin_addr.s_addr =
1287182352Sgshapiro			    LocalDaemon ? htonl(INADDR_LOOPBACK) : INADDR_ANY;
128864562Sgshapiro		port = daemonaddr->sin.sin_port;
128964562Sgshapiro		break;
129090792Sgshapiro#endif /* NETINET */
129164562Sgshapiro
129290792Sgshapiro#if NETINET6
129364562Sgshapiro	  case AF_INET6:
129464562Sgshapiro		if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr))
1295182352Sgshapiro			daemonaddr->sin6.sin6_addr =
1296223067Sgshapiro			    (LocalDaemon && V6LoopbackAddrFound) ?
1297223067Sgshapiro			    in6addr_loopback : in6addr_any;
129864562Sgshapiro		port = daemonaddr->sin6.sin6_port;
129964562Sgshapiro		break;
130090792Sgshapiro#endif /* NETINET6 */
130164562Sgshapiro
130264562Sgshapiro	  default:
130364562Sgshapiro		/* unknown protocol */
130464562Sgshapiro		port = 0;
130564562Sgshapiro		break;
130664562Sgshapiro	}
130764562Sgshapiro	if (port == 0)
130864562Sgshapiro	{
130990792Sgshapiro#ifdef NO_GETSERVBYNAME
131064562Sgshapiro		port = htons(25);
131190792Sgshapiro#else /* NO_GETSERVBYNAME */
131264562Sgshapiro		{
131364562Sgshapiro			register struct servent *sp;
131464562Sgshapiro
131564562Sgshapiro			sp = getservbyname("smtp", "tcp");
131664562Sgshapiro			if (sp == NULL)
131764562Sgshapiro			{
131864562Sgshapiro				syserr("554 5.3.5 service \"smtp\" unknown");
131964562Sgshapiro				port = htons(25);
132064562Sgshapiro			}
132164562Sgshapiro			else
132264562Sgshapiro				port = sp->s_port;
132364562Sgshapiro		}
132490792Sgshapiro#endif /* NO_GETSERVBYNAME */
132564562Sgshapiro	}
132664562Sgshapiro
132764562Sgshapiro	switch (daemonaddr->sa.sa_family)
132864562Sgshapiro	{
132990792Sgshapiro#if NETINET
133064562Sgshapiro	  case AF_INET:
133164562Sgshapiro		daemonaddr->sin.sin_port = port;
133264562Sgshapiro		break;
1333363466Sgshapiro#endif
133464562Sgshapiro
133590792Sgshapiro#if NETINET6
133664562Sgshapiro	  case AF_INET6:
133764562Sgshapiro		daemonaddr->sin6.sin6_port = port;
133864562Sgshapiro		break;
1339363466Sgshapiro#endif
134064562Sgshapiro
134164562Sgshapiro	  default:
134264562Sgshapiro		/* unknown protocol */
134364562Sgshapiro		break;
134464562Sgshapiro	}
134590792Sgshapiro	return port;
134664562Sgshapiro}
134790792Sgshapiro/*
134838032Speter**  CLRDAEMON -- reset the daemon connection
134938032Speter**
135038032Speter**	Parameters:
135138032Speter**		none.
135238032Speter**
135338032Speter**	Returns:
135438032Speter**		none.
135538032Speter**
135638032Speter**	Side Effects:
135738032Speter**		releases any resources used by the passive daemon.
135838032Speter*/
135938032Speter
136038032Spetervoid
136138032Speterclrdaemon()
136238032Speter{
136364562Sgshapiro	int i;
136464562Sgshapiro
136590792Sgshapiro	for (i = 0; i < NDaemons; i++)
136664562Sgshapiro	{
136764562Sgshapiro		if (Daemons[i].d_socket >= 0)
136864562Sgshapiro			(void) close(Daemons[i].d_socket);
136964562Sgshapiro		Daemons[i].d_socket = -1;
137064562Sgshapiro	}
137138032Speter}
137290792Sgshapiro
137390792Sgshapiro/*
137490792Sgshapiro**  GETMODIFIERS -- get modifier flags
137590792Sgshapiro**
137690792Sgshapiro**	Parameters:
137790792Sgshapiro**		v -- the modifiers (input text line).
137890792Sgshapiro**		modifiers -- pointer to flag field to represent modifiers.
137990792Sgshapiro**
138090792Sgshapiro**	Returns:
138190792Sgshapiro**		(xallocat()ed) string representation of modifiers.
138290792Sgshapiro**
138390792Sgshapiro**	Side Effects:
138490792Sgshapiro**		fills in modifiers.
138590792Sgshapiro*/
138690792Sgshapiro
138790792Sgshapirochar *
138890792Sgshapirogetmodifiers(v, modifiers)
138990792Sgshapiro	char *v;
139090792Sgshapiro	BITMAP256 modifiers;
139190792Sgshapiro{
139290792Sgshapiro	int l;
139390792Sgshapiro	char *h, *f, *flags;
139490792Sgshapiro
139590792Sgshapiro	/* maximum length of flags: upper case Option -> "OO " */
139690792Sgshapiro	l = 3 * strlen(v) + 3;
139790792Sgshapiro
139890792Sgshapiro	/* is someone joking? */
139990792Sgshapiro	if (l < 0 || l > 256)
140090792Sgshapiro	{
140190792Sgshapiro		if (LogLevel > 2)
140290792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
140390792Sgshapiro				  "getmodifiers too long, ignored");
140490792Sgshapiro		return NULL;
140590792Sgshapiro	}
140690792Sgshapiro	flags = xalloc(l);
140790792Sgshapiro	f = flags;
140890792Sgshapiro	clrbitmap(modifiers);
140990792Sgshapiro	for (h = v; *h != '\0'; h++)
141090792Sgshapiro	{
141190792Sgshapiro		if (isascii(*h) && !isspace(*h) && isprint(*h))
141290792Sgshapiro		{
141390792Sgshapiro			setbitn(*h, modifiers);
141490792Sgshapiro			if (flags != f)
141590792Sgshapiro				*flags++ = ' ';
141690792Sgshapiro			*flags++ = *h;
141790792Sgshapiro			if (isupper(*h))
141890792Sgshapiro				*flags++ = *h;
141990792Sgshapiro		}
142090792Sgshapiro	}
142190792Sgshapiro	*flags++ = '\0';
142290792Sgshapiro	return f;
142390792Sgshapiro}
142490792Sgshapiro
142590792Sgshapiro/*
142690792Sgshapiro**  CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
142790792Sgshapiro**
142890792Sgshapiro**	Parameters:
142990792Sgshapiro**		flag -- the flag to test.
143090792Sgshapiro**
143190792Sgshapiro**	Returns:
143290792Sgshapiro**		true iff all daemons have set flag.
143390792Sgshapiro*/
143490792Sgshapiro
143590792Sgshapirobool
143690792Sgshapirochkdaemonmodifiers(flag)
143790792Sgshapiro	int flag;
143890792Sgshapiro{
143990792Sgshapiro	int i;
144090792Sgshapiro
144190792Sgshapiro	for (i = 0; i < NDaemons; i++)
144290792Sgshapiro		if (!bitnset((char) flag, Daemons[i].d_flags))
144390792Sgshapiro			return false;
144490792Sgshapiro	return true;
144590792Sgshapiro}
144690792Sgshapiro
144790792Sgshapiro/*
144864562Sgshapiro**  SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
144938032Speter**
145038032Speter**	Parameters:
145138032Speter**		p -- the options line.
145264562Sgshapiro**		d -- the daemon structure to fill in.
145338032Speter**
145438032Speter**	Returns:
145538032Speter**		none.
145638032Speter*/
145738032Speter
145864562Sgshapirostatic void
145964562Sgshapirosetsockaddroptions(p, d)
1460141858Sgshapiro	char *p;
146190792Sgshapiro	DAEMON_T *d;
146238032Speter{
146390792Sgshapiro#if NETISO
146471345Sgshapiro	short portno;
1465363466Sgshapiro#endif
146671345Sgshapiro	char *port = NULL;
146771345Sgshapiro	char *addr = NULL;
146838032Speter
146990792Sgshapiro#if NETINET
147064562Sgshapiro	if (d->d_addr.sa.sa_family == AF_UNSPEC)
147164562Sgshapiro		d->d_addr.sa.sa_family = AF_INET;
1472363466Sgshapiro#endif
1473157001Sgshapiro#if _FFR_SS_PER_DAEMON
1474168515Sgshapiro	d->d_supersafe = DPO_NOTSET;
1475363466Sgshapiro#endif
1476157001Sgshapiro	d->d_dm = DM_NOTSET;
1477168515Sgshapiro	d->d_refuseLA = DPO_NOTSET;
1478168515Sgshapiro	d->d_queueLA = DPO_NOTSET;
1479168515Sgshapiro	d->d_delayLA = DPO_NOTSET;
1480168515Sgshapiro	d->d_maxchildren = DPO_NOTSET;
148164562Sgshapiro
148238032Speter	while (p != NULL)
148338032Speter	{
148438032Speter		register char *f;
148538032Speter		register char *v;
148638032Speter
1487363466Sgshapiro		while (SM_ISSPACE(*p))
148838032Speter			p++;
148938032Speter		if (*p == '\0')
149038032Speter			break;
149138032Speter		f = p;
149238032Speter		p = strchr(p, ',');
149338032Speter		if (p != NULL)
149438032Speter			*p++ = '\0';
149538032Speter		v = strchr(f, '=');
149638032Speter		if (v == NULL)
149738032Speter			continue;
149838032Speter		while (isascii(*++v) && isspace(*v))
149938032Speter			continue;
150038032Speter
150138032Speter		switch (*f)
150238032Speter		{
1503147078Sgshapiro		  case 'A':		/* address */
1504168515Sgshapiro#if !_FFR_DPO_CS
1505168515Sgshapiro		  case 'a':
1506363466Sgshapiro#endif
1507147078Sgshapiro			addr = v;
1508147078Sgshapiro			break;
1509147078Sgshapiro
1510168515Sgshapiro		  case 'c':
1511168515Sgshapiro			d->d_maxchildren = atoi(v);
1512168515Sgshapiro			break;
1513168515Sgshapiro
1514147078Sgshapiro		  case 'D':		/* DeliveryMode */
1515147078Sgshapiro			switch (*v)
1516147078Sgshapiro			{
1517147078Sgshapiro			  case SM_QUEUE:
1518147078Sgshapiro			  case SM_DEFER:
1519147078Sgshapiro			  case SM_DELIVER:
1520157001Sgshapiro			  case SM_FORK:
1521285229Sgshapiro#if _FFR_PROXY
1522285229Sgshapiro			  case SM_PROXY_REQ:
1523363466Sgshapiro#endif
1524147078Sgshapiro				d->d_dm = *v;
1525147078Sgshapiro				break;
1526147078Sgshapiro			  default:
1527147078Sgshapiro				syserr("554 5.3.5 Unknown delivery mode %c",
1528147078Sgshapiro					*v);
1529147078Sgshapiro				break;
1530147078Sgshapiro			}
1531147078Sgshapiro			break;
1532147078Sgshapiro
1533168515Sgshapiro		  case 'd':		/* delayLA */
1534168515Sgshapiro			d->d_delayLA = atoi(v);
1535168515Sgshapiro			break;
1536168515Sgshapiro
153738032Speter		  case 'F':		/* address family */
1538168515Sgshapiro#if !_FFR_DPO_CS
1539168515Sgshapiro		  case 'f':
1540363466Sgshapiro#endif
154138032Speter			if (isascii(*v) && isdigit(*v))
154264562Sgshapiro				d->d_addr.sa.sa_family = atoi(v);
1543285229Sgshapiro#ifdef NETUNIX
154490792Sgshapiro			else if (sm_strcasecmp(v, "unix") == 0 ||
154590792Sgshapiro				 sm_strcasecmp(v, "local") == 0)
154690792Sgshapiro				d->d_addr.sa.sa_family = AF_UNIX;
1547363466Sgshapiro#endif
154890792Sgshapiro#if NETINET
154990792Sgshapiro			else if (sm_strcasecmp(v, "inet") == 0)
155064562Sgshapiro				d->d_addr.sa.sa_family = AF_INET;
1551363466Sgshapiro#endif
155290792Sgshapiro#if NETINET6
155390792Sgshapiro			else if (sm_strcasecmp(v, "inet6") == 0)
155464562Sgshapiro				d->d_addr.sa.sa_family = AF_INET6;
1555363466Sgshapiro#endif
155690792Sgshapiro#if NETISO
155790792Sgshapiro			else if (sm_strcasecmp(v, "iso") == 0)
155864562Sgshapiro				d->d_addr.sa.sa_family = AF_ISO;
1559363466Sgshapiro#endif
156090792Sgshapiro#if NETNS
156190792Sgshapiro			else if (sm_strcasecmp(v, "ns") == 0)
156264562Sgshapiro				d->d_addr.sa.sa_family = AF_NS;
1563363466Sgshapiro#endif
156490792Sgshapiro#if NETX25
156590792Sgshapiro			else if (sm_strcasecmp(v, "x.25") == 0)
156664562Sgshapiro				d->d_addr.sa.sa_family = AF_CCITT;
1567363466Sgshapiro#endif
156838032Speter			else
156964562Sgshapiro				syserr("554 5.3.5 Unknown address family %s in Family=option",
157064562Sgshapiro				       v);
157138032Speter			break;
157238032Speter
157390792Sgshapiro#if MILTER
157490792Sgshapiro		  case 'I':
1575168515Sgshapiro# if !_FFR_DPO_CS
1576168515Sgshapiro		  case 'i':
1577363466Sgshapiro# endif
157890792Sgshapiro			d->d_inputfilterlist = v;
157990792Sgshapiro			break;
158090792Sgshapiro#endif /* MILTER */
158190792Sgshapiro
158238032Speter		  case 'L':		/* listen queue size */
1583168515Sgshapiro#if !_FFR_DPO_CS
1584168515Sgshapiro		  case 'l':
1585363466Sgshapiro#endif
158664562Sgshapiro			d->d_listenqueue = atoi(v);
158738032Speter			break;
158838032Speter
158964562Sgshapiro		  case 'M':		/* modifiers (flags) */
1590168515Sgshapiro#if !_FFR_DPO_CS
1591168515Sgshapiro		  case 'm':
1592363466Sgshapiro#endif
159390792Sgshapiro			d->d_mflags = getmodifiers(v, d->d_flags);
159464562Sgshapiro			break;
159564562Sgshapiro
1596147078Sgshapiro		  case 'N':		/* name */
1597168515Sgshapiro#if !_FFR_DPO_CS
1598168515Sgshapiro		  case 'n':
1599363466Sgshapiro#endif
1600147078Sgshapiro			d->d_name = v;
160138032Speter			break;
160238032Speter
1603147078Sgshapiro		  case 'P':		/* port */
1604168515Sgshapiro#if !_FFR_DPO_CS
1605168515Sgshapiro		  case 'p':
1606363466Sgshapiro#endif
1607147078Sgshapiro			port = v;
1608147078Sgshapiro			break;
1609147078Sgshapiro
1610168515Sgshapiro		  case 'q':
1611168515Sgshapiro			d->d_queueLA = atoi(v);
1612168515Sgshapiro			break;
1613168515Sgshapiro
161438032Speter		  case 'R':		/* receive buffer size */
161564562Sgshapiro			d->d_tcprcvbufsize = atoi(v);
161638032Speter			break;
161738032Speter
1618168515Sgshapiro		  case 'r':
1619168515Sgshapiro			d->d_refuseLA = atoi(v);
1620168515Sgshapiro			break;
1621168515Sgshapiro
1622147078Sgshapiro		  case 'S':		/* send buffer size */
1623168515Sgshapiro#if !_FFR_DPO_CS
1624168515Sgshapiro		  case 's':
1625363466Sgshapiro#endif
1626147078Sgshapiro			d->d_tcpsndbufsize = atoi(v);
162764562Sgshapiro			break;
162864562Sgshapiro
1629147078Sgshapiro#if _FFR_SS_PER_DAEMON
1630147078Sgshapiro		  case 'T':		/* SuperSafe */
1631147078Sgshapiro			if (tolower(*v) == 'i')
1632147078Sgshapiro				d->d_supersafe = SAFE_INTERACTIVE;
1633147078Sgshapiro			else if (tolower(*v) == 'p')
1634147078Sgshapiro# if MILTER
1635147078Sgshapiro				d->d_supersafe = SAFE_REALLY_POSTMILTER;
1636147078Sgshapiro# else /* MILTER */
1637147078Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1638147078Sgshapiro					"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
1639147078Sgshapiro# endif /* MILTER */
1640147078Sgshapiro			else
1641147078Sgshapiro				d->d_supersafe = atobool(v) ? SAFE_REALLY
1642147078Sgshapiro							: SAFE_NO;
1643147078Sgshapiro			break;
1644147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
1645147078Sgshapiro
164638032Speter		  default:
164764562Sgshapiro			syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
164864562Sgshapiro			       f);
164938032Speter		}
165038032Speter	}
165171345Sgshapiro
165271345Sgshapiro	/* Check addr and port after finding family */
165371345Sgshapiro	if (addr != NULL)
165471345Sgshapiro	{
165571345Sgshapiro		switch (d->d_addr.sa.sa_family)
165671345Sgshapiro		{
1657285229Sgshapiro#if NETUNIX
165890792Sgshapiro		  case AF_UNIX:
165990792Sgshapiro			if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
166090792Sgshapiro			{
166190792Sgshapiro				errno = ENAMETOOLONG;
1662285229Sgshapiro				syserr("setsockaddroptions: domain socket name too long: %s > %ld",
1663285229Sgshapiro				       addr,
1664285229Sgshapiro				       (long) sizeof(d->d_addr.sunix.sun_path));
166590792Sgshapiro				break;
166690792Sgshapiro			}
166790792Sgshapiro
166890792Sgshapiro			/* file safety check done in opendaemonsocket() */
166990792Sgshapiro			(void) memset(&d->d_addr.sunix.sun_path, '\0',
167090792Sgshapiro				      sizeof(d->d_addr.sunix.sun_path));
167190792Sgshapiro			(void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
167290792Sgshapiro					  addr,
167390792Sgshapiro					  sizeof(d->d_addr.sunix.sun_path));
167490792Sgshapiro			break;
1675285229Sgshapiro#endif /* NETUNIX */
167690792Sgshapiro#if NETINET
167771345Sgshapiro		  case AF_INET:
167871345Sgshapiro			if (!isascii(*addr) || !isdigit(*addr) ||
167990792Sgshapiro			    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
168090792Sgshapiro			     == INADDR_NONE))
168171345Sgshapiro			{
168271345Sgshapiro				register struct hostent *hp;
168371345Sgshapiro
168471345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET);
168571345Sgshapiro				if (hp == NULL)
168671345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
168771345Sgshapiro					       addr);
168871345Sgshapiro				else
168971345Sgshapiro				{
169071345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
169171345Sgshapiro					       hp->h_addrtype != AF_INET)
169271345Sgshapiro						hp->h_addr_list++;
169371345Sgshapiro					if (*(hp->h_addr_list) == NULL)
169471345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
169571345Sgshapiro						       addr);
169671345Sgshapiro					else
169771345Sgshapiro						memmove(&d->d_addr.sin.sin_addr,
169871345Sgshapiro							*(hp->h_addr_list),
169971345Sgshapiro							INADDRSZ);
1700363466Sgshapiro					FREEHOSTENT(hp, NULL);
170171345Sgshapiro				}
170271345Sgshapiro			}
170371345Sgshapiro			break;
170490792Sgshapiro#endif /* NETINET */
170571345Sgshapiro
170690792Sgshapiro#if NETINET6
170771345Sgshapiro		  case AF_INET6:
170890792Sgshapiro			if (anynet_pton(AF_INET6, addr,
170990792Sgshapiro					&d->d_addr.sin6.sin6_addr) != 1)
171071345Sgshapiro			{
171171345Sgshapiro				register struct hostent *hp;
171271345Sgshapiro
171371345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET6);
171471345Sgshapiro				if (hp == NULL)
171571345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
171671345Sgshapiro					       addr);
171771345Sgshapiro				else
171871345Sgshapiro				{
171971345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
172071345Sgshapiro					       hp->h_addrtype != AF_INET6)
172171345Sgshapiro						hp->h_addr_list++;
172271345Sgshapiro					if (*(hp->h_addr_list) == NULL)
172371345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
172471345Sgshapiro						       addr);
172571345Sgshapiro					else
172671345Sgshapiro						memmove(&d->d_addr.sin6.sin6_addr,
172771345Sgshapiro							*(hp->h_addr_list),
172871345Sgshapiro							IN6ADDRSZ);
1729363466Sgshapiro					FREEHOSTENT(hp, NULL);
173071345Sgshapiro				}
173171345Sgshapiro			}
173271345Sgshapiro			break;
173390792Sgshapiro#endif /* NETINET6 */
173471345Sgshapiro
173571345Sgshapiro		  default:
173671345Sgshapiro			syserr("554 5.3.5 address= option unsupported for family %d",
173771345Sgshapiro			       d->d_addr.sa.sa_family);
173871345Sgshapiro			break;
173971345Sgshapiro		}
174071345Sgshapiro	}
174171345Sgshapiro
174271345Sgshapiro	if (port != NULL)
174371345Sgshapiro	{
174471345Sgshapiro		switch (d->d_addr.sa.sa_family)
174571345Sgshapiro		{
174690792Sgshapiro#if NETINET
174771345Sgshapiro		  case AF_INET:
174871345Sgshapiro			if (isascii(*port) && isdigit(*port))
174990792Sgshapiro				d->d_addr.sin.sin_port = htons((unsigned short)
175090792Sgshapiro						     atoi((const char *) port));
175171345Sgshapiro			else
175271345Sgshapiro			{
175390792Sgshapiro# ifdef NO_GETSERVBYNAME
175471345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
175571345Sgshapiro				       port);
175690792Sgshapiro# else /* NO_GETSERVBYNAME */
175771345Sgshapiro				register struct servent *sp;
175871345Sgshapiro
175971345Sgshapiro				sp = getservbyname(port, "tcp");
176071345Sgshapiro				if (sp == NULL)
176171345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
176271345Sgshapiro					       port);
176371345Sgshapiro				else
176471345Sgshapiro					d->d_addr.sin.sin_port = sp->s_port;
176590792Sgshapiro# endif /* NO_GETSERVBYNAME */
176671345Sgshapiro			}
176771345Sgshapiro			break;
176890792Sgshapiro#endif /* NETINET */
176971345Sgshapiro
177090792Sgshapiro#if NETINET6
177171345Sgshapiro		  case AF_INET6:
177271345Sgshapiro			if (isascii(*port) && isdigit(*port))
177390792Sgshapiro				d->d_addr.sin6.sin6_port = htons((unsigned short)
177490792Sgshapiro								  atoi(port));
177571345Sgshapiro			else
177671345Sgshapiro			{
177790792Sgshapiro# ifdef NO_GETSERVBYNAME
177871345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
177971345Sgshapiro				       port);
178090792Sgshapiro# else /* NO_GETSERVBYNAME */
178171345Sgshapiro				register struct servent *sp;
178271345Sgshapiro
178371345Sgshapiro				sp = getservbyname(port, "tcp");
178471345Sgshapiro				if (sp == NULL)
178571345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
178671345Sgshapiro					       port);
178771345Sgshapiro				else
178871345Sgshapiro					d->d_addr.sin6.sin6_port = sp->s_port;
178990792Sgshapiro# endif /* NO_GETSERVBYNAME */
179071345Sgshapiro			}
179171345Sgshapiro			break;
179290792Sgshapiro#endif /* NETINET6 */
179371345Sgshapiro
179490792Sgshapiro#if NETISO
179571345Sgshapiro		  case AF_ISO:
179671345Sgshapiro			/* assume two byte transport selector */
179771345Sgshapiro			if (isascii(*port) && isdigit(*port))
179890792Sgshapiro				portno = htons((unsigned short) atoi(port));
179971345Sgshapiro			else
180071345Sgshapiro			{
180190792Sgshapiro# ifdef NO_GETSERVBYNAME
180271345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
180371345Sgshapiro				       port);
180490792Sgshapiro# else /* NO_GETSERVBYNAME */
180571345Sgshapiro				register struct servent *sp;
180671345Sgshapiro
180771345Sgshapiro				sp = getservbyname(port, "tcp");
180871345Sgshapiro				if (sp == NULL)
180971345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
181071345Sgshapiro					       port);
181171345Sgshapiro				else
181271345Sgshapiro					portno = sp->s_port;
181390792Sgshapiro# endif /* NO_GETSERVBYNAME */
181471345Sgshapiro			}
181571345Sgshapiro			memmove(TSEL(&d->d_addr.siso),
181671345Sgshapiro				(char *) &portno, 2);
181771345Sgshapiro			break;
181890792Sgshapiro#endif /* NETISO */
181971345Sgshapiro
182071345Sgshapiro		  default:
182171345Sgshapiro			syserr("554 5.3.5 Port= option unsupported for family %d",
182271345Sgshapiro			       d->d_addr.sa.sa_family);
182371345Sgshapiro			break;
182471345Sgshapiro		}
182571345Sgshapiro	}
182638032Speter}
182790792Sgshapiro/*
182864562Sgshapiro**  SETDAEMONOPTIONS -- set options for running the MTA daemon
182938032Speter**
183038032Speter**	Parameters:
183164562Sgshapiro**		p -- the options line.
183264562Sgshapiro**
183364562Sgshapiro**	Returns:
183490792Sgshapiro**		true if successful, false otherwise.
183590792Sgshapiro**
183690792Sgshapiro**	Side Effects:
183790792Sgshapiro**		increments number of daemons.
183864562Sgshapiro*/
183964562Sgshapiro
184090792Sgshapiro#define DEF_LISTENQUEUE	10
184190792Sgshapiro
184298841Sgshapirostruct dflags
184398841Sgshapiro{
184498841Sgshapiro	char	*d_name;
184598841Sgshapiro	int	d_flag;
184698841Sgshapiro};
184798841Sgshapiro
184898841Sgshapirostatic struct dflags	DaemonFlags[] =
184998841Sgshapiro{
185098841Sgshapiro	{ "AUTHREQ",		D_AUTHREQ	},
185198841Sgshapiro	{ "BINDIF",		D_BINDIF	},
185298841Sgshapiro	{ "CANONREQ",		D_CANONREQ	},
185398841Sgshapiro	{ "IFNHELO",		D_IFNHELO	},
185498841Sgshapiro	{ "FQMAIL",		D_FQMAIL	},
185598841Sgshapiro	{ "FQRCPT",		D_FQRCPT	},
185698841Sgshapiro	{ "SMTPS",		D_SMTPS		},
185798841Sgshapiro	{ "UNQUALOK",		D_UNQUALOK	},
185898841Sgshapiro	{ "NOAUTH",		D_NOAUTH	},
185998841Sgshapiro	{ "NOCANON",		D_NOCANON	},
186098841Sgshapiro	{ "NOETRN",		D_NOETRN	},
186198841Sgshapiro	{ "NOTLS",		D_NOTLS		},
186298841Sgshapiro	{ "ETRNONLY",		D_ETRNONLY	},
186398841Sgshapiro	{ "OPTIONAL",		D_OPTIONAL	},
186498841Sgshapiro	{ "DISABLE",		D_DISABLE	},
186598841Sgshapiro	{ "ISSET",		D_ISSET		},
186698841Sgshapiro	{ NULL,			0		}
186798841Sgshapiro};
186898841Sgshapiro
186998841Sgshapirostatic void
187098841Sgshapiroprintdaemonflags(d)
187198841Sgshapiro	DAEMON_T *d;
187298841Sgshapiro{
187398841Sgshapiro	register struct dflags *df;
187498841Sgshapiro	bool first = true;
187598841Sgshapiro
187698841Sgshapiro	for (df = DaemonFlags; df->d_name != NULL; df++)
187798841Sgshapiro	{
187898841Sgshapiro		if (!bitnset(df->d_flag, d->d_flags))
187998841Sgshapiro			continue;
188098841Sgshapiro		if (first)
1881132943Sgshapiro			sm_dprintf("<%s", df->d_name);
188298841Sgshapiro		else
1883132943Sgshapiro			sm_dprintf(",%s", df->d_name);
188498841Sgshapiro		first = false;
188598841Sgshapiro	}
188698841Sgshapiro	if (!first)
1887132943Sgshapiro		sm_dprintf(">");
188898841Sgshapiro}
188998841Sgshapiro
189064562Sgshapirobool
189164562Sgshapirosetdaemonoptions(p)
189264562Sgshapiro	register char *p;
189364562Sgshapiro{
189490792Sgshapiro	if (NDaemons >= MAXDAEMONS)
189590792Sgshapiro		return false;
189690792Sgshapiro	Daemons[NDaemons].d_socket = -1;
189790792Sgshapiro	Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
189890792Sgshapiro	clrbitmap(Daemons[NDaemons].d_flags);
189990792Sgshapiro	setsockaddroptions(p, &Daemons[NDaemons]);
190064562Sgshapiro
190190792Sgshapiro#if MILTER
190290792Sgshapiro	if (Daemons[NDaemons].d_inputfilterlist != NULL)
190390792Sgshapiro		Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
1904363466Sgshapiro#endif
190590792Sgshapiro
190690792Sgshapiro	if (Daemons[NDaemons].d_name != NULL)
190790792Sgshapiro		Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
190864562Sgshapiro	else
190964562Sgshapiro	{
191064562Sgshapiro		char num[30];
191164562Sgshapiro
1912168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Daemon%d", NDaemons);
191390792Sgshapiro		Daemons[NDaemons].d_name = newstr(num);
191464562Sgshapiro	}
191564562Sgshapiro
191664562Sgshapiro	if (tTd(37, 1))
191764562Sgshapiro	{
191890792Sgshapiro		sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
191998841Sgshapiro		printdaemonflags(&Daemons[NDaemons]);
192090792Sgshapiro		sm_dprintf("\n");
192164562Sgshapiro	}
192290792Sgshapiro	++NDaemons;
192390792Sgshapiro	return true;
192464562Sgshapiro}
192590792Sgshapiro/*
192664562Sgshapiro**  INITDAEMON -- initialize daemon if not yet done.
192764562Sgshapiro**
192864562Sgshapiro**	Parameters:
192964562Sgshapiro**		none
193064562Sgshapiro**
193164562Sgshapiro**	Returns:
193264562Sgshapiro**		none
193364562Sgshapiro**
193464562Sgshapiro**	Side Effects:
193564562Sgshapiro**		initializes structure for one daemon.
193664562Sgshapiro*/
193790792Sgshapiro
193864562Sgshapirovoid
193964562Sgshapiroinitdaemon()
194064562Sgshapiro{
194190792Sgshapiro	if (NDaemons == 0)
194264562Sgshapiro	{
194390792Sgshapiro		Daemons[NDaemons].d_socket = -1;
194490792Sgshapiro		Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
194590792Sgshapiro		Daemons[NDaemons].d_name = "Daemon0";
194690792Sgshapiro		NDaemons = 1;
194764562Sgshapiro	}
194864562Sgshapiro}
194990792Sgshapiro/*
195064562Sgshapiro**  SETCLIENTOPTIONS -- set options for running the client
195164562Sgshapiro**
195264562Sgshapiro**	Parameters:
195364562Sgshapiro**		p -- the options line.
195464562Sgshapiro**
195564562Sgshapiro**	Returns:
195664562Sgshapiro**		none.
195764562Sgshapiro*/
195864562Sgshapiro
195990792Sgshapirostatic DAEMON_T	ClientSettings[AF_MAX + 1];
196064562Sgshapiro
196164562Sgshapirovoid
196264562Sgshapirosetclientoptions(p)
196364562Sgshapiro	register char *p;
196464562Sgshapiro{
196590792Sgshapiro	int family;
196690792Sgshapiro	DAEMON_T d;
196764562Sgshapiro
1968168515Sgshapiro	memset(&d, '\0', sizeof(d));
196964562Sgshapiro	setsockaddroptions(p, &d);
197064562Sgshapiro
197164562Sgshapiro	/* grab what we need */
197290792Sgshapiro	family = d.d_addr.sa.sa_family;
197390792Sgshapiro	STRUCTCOPY(d, ClientSettings[family]);
197490792Sgshapiro	setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
197590792Sgshapiro	if (d.d_name != NULL)
197690792Sgshapiro		ClientSettings[family].d_name = newstr(d.d_name);
197764562Sgshapiro	else
197890792Sgshapiro	{
197990792Sgshapiro		char num[30];
198090792Sgshapiro
1981168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Client%d", family);
198290792Sgshapiro		ClientSettings[family].d_name = newstr(num);
198390792Sgshapiro	}
198464562Sgshapiro}
198590792Sgshapiro/*
198664562Sgshapiro**  ADDR_FAMILY -- determine address family from address
198764562Sgshapiro**
198864562Sgshapiro**	Parameters:
198964562Sgshapiro**		addr -- the string representation of the address
199064562Sgshapiro**
199164562Sgshapiro**	Returns:
199264562Sgshapiro**		AF_INET, AF_INET6 or AF_UNSPEC
199364562Sgshapiro**
199464562Sgshapiro**	Side Effects:
199564562Sgshapiro**		none.
199664562Sgshapiro*/
199764562Sgshapiro
199864562Sgshapirostatic int
199964562Sgshapiroaddr_family(addr)
200064562Sgshapiro	char *addr;
200164562Sgshapiro{
200290792Sgshapiro#if NETINET6
200364562Sgshapiro	SOCKADDR clt_addr;
2004363466Sgshapiro#endif
200564562Sgshapiro
200690792Sgshapiro#if NETINET
200764562Sgshapiro	if (inet_addr(addr) != INADDR_NONE)
200864562Sgshapiro	{
200964562Sgshapiro		if (tTd(16, 9))
201090792Sgshapiro			sm_dprintf("addr_family(%s): INET\n", addr);
201164562Sgshapiro		return AF_INET;
201264562Sgshapiro	}
201390792Sgshapiro#endif /* NETINET */
201490792Sgshapiro#if NETINET6
201590792Sgshapiro	if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
201664562Sgshapiro	{
201764562Sgshapiro		if (tTd(16, 9))
201890792Sgshapiro			sm_dprintf("addr_family(%s): INET6\n", addr);
201964562Sgshapiro		return AF_INET6;
202064562Sgshapiro	}
202190792Sgshapiro#endif /* NETINET6 */
2022285229Sgshapiro#if NETUNIX
202390792Sgshapiro	if (*addr == '/')
202490792Sgshapiro	{
202590792Sgshapiro		if (tTd(16, 9))
202690792Sgshapiro			sm_dprintf("addr_family(%s): LOCAL\n", addr);
202790792Sgshapiro		return AF_UNIX;
202890792Sgshapiro	}
2029285229Sgshapiro#endif /* NETUNIX */
203064562Sgshapiro	if (tTd(16, 9))
203190792Sgshapiro		sm_dprintf("addr_family(%s): UNSPEC\n", addr);
203264562Sgshapiro	return AF_UNSPEC;
203364562Sgshapiro}
203490792Sgshapiro
203590792Sgshapiro/*
203690792Sgshapiro**  CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
203790792Sgshapiro**
203890792Sgshapiro**	Parameters:
203990792Sgshapiro**		flag -- the flag to test.
204090792Sgshapiro**
204190792Sgshapiro**	Returns:
204290792Sgshapiro**		true iff all configured clients have set the flag.
204390792Sgshapiro*/
204490792Sgshapiro
204590792Sgshapirobool
204690792Sgshapirochkclientmodifiers(flag)
204790792Sgshapiro	int flag;
204890792Sgshapiro{
204990792Sgshapiro	int i;
205090792Sgshapiro	bool flagisset;
205190792Sgshapiro
205290792Sgshapiro	flagisset = false;
205390792Sgshapiro	for (i = 0; i < AF_MAX; i++)
205490792Sgshapiro	{
205590792Sgshapiro		if (bitnset(D_ISSET, ClientSettings[i].d_flags))
205690792Sgshapiro		{
205790792Sgshapiro			if (!bitnset((char) flag, ClientSettings[i].d_flags))
205890792Sgshapiro				return false;
205990792Sgshapiro			flagisset = true;
206090792Sgshapiro		}
206190792Sgshapiro	}
206290792Sgshapiro	return flagisset;
206390792Sgshapiro}
206490792Sgshapiro
206590792Sgshapiro#if MILTER
206690792Sgshapiro/*
2067285229Sgshapiro**  SETUP_DAEMON_MILTERS -- Parse per-socket filters
206890792Sgshapiro**
206990792Sgshapiro**	Parameters:
207090792Sgshapiro**		none
207190792Sgshapiro**
207290792Sgshapiro**	Returns:
207390792Sgshapiro**		none
207490792Sgshapiro*/
207590792Sgshapiro
207690792Sgshapirovoid
207790792Sgshapirosetup_daemon_milters()
207890792Sgshapiro{
207990792Sgshapiro	int idx;
208090792Sgshapiro
208190792Sgshapiro	if (OpMode == MD_SMTP)
208290792Sgshapiro	{
208390792Sgshapiro		/* no need to configure the daemons */
208490792Sgshapiro		return;
208590792Sgshapiro	}
208690792Sgshapiro
208790792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
208890792Sgshapiro	{
208990792Sgshapiro		if (Daemons[idx].d_inputfilterlist != NULL)
209090792Sgshapiro		{
209190792Sgshapiro			milter_config(Daemons[idx].d_inputfilterlist,
209290792Sgshapiro				      Daemons[idx].d_inputfilters,
209390792Sgshapiro				      MAXFILTERS);
209490792Sgshapiro		}
209590792Sgshapiro	}
209690792Sgshapiro}
209790792Sgshapiro#endif /* MILTER */
209890792Sgshapiro/*
209964562Sgshapiro**  MAKECONNECTION -- make a connection to an SMTP socket on a machine.
210064562Sgshapiro**
210164562Sgshapiro**	Parameters:
210238032Speter**		host -- the name of the host.
210338032Speter**		port -- the port number to connect to.
210438032Speter**		mci -- a pointer to the mail connection information
210538032Speter**			structure to be filled in.
210638032Speter**		e -- the current envelope.
210790792Sgshapiro**		enough -- time at which to stop further connection attempts.
210890792Sgshapiro**			(0 means no limit)
210938032Speter**
211038032Speter**	Returns:
211138032Speter**		An exit code telling whether the connection could be
211238032Speter**			made and if not why not.
211338032Speter**
211438032Speter**	Side Effects:
211538032Speter**		none.
211638032Speter*/
211738032Speter
211838032Speterstatic jmp_buf	CtxConnectTimeout;
211938032Speter
212038032SpeterSOCKADDR	CurHostAddr;		/* address of current host */
212138032Speter
212238032Speterint
2123363466Sgshapiromakeconnection(host, port, mci, e, enough
2124363466Sgshapiro#if DANE
2125363466Sgshapiro	, ptlsa_flags
2126363466Sgshapiro#endif
2127363466Sgshapiro	)
212838032Speter	char *host;
212990792Sgshapiro	volatile unsigned int port;
213038032Speter	register MCI *mci;
213138032Speter	ENVELOPE *e;
213290792Sgshapiro	time_t enough;
2133363466Sgshapiro#if DANE
2134363466Sgshapiro	unsigned long *ptlsa_flags;
2135363466Sgshapiro#endif
213638032Speter{
213738032Speter	register volatile int addrno = 0;
213890792Sgshapiro	volatile int s;
213990792Sgshapiro	register struct hostent *volatile hp = (struct hostent *) NULL;
214038032Speter	SOCKADDR addr;
214164562Sgshapiro	SOCKADDR clt_addr;
214264562Sgshapiro	int save_errno = 0;
214364562Sgshapiro	volatile SOCKADDR_LEN_T addrlen;
2144159609Sgshapiro	volatile bool firstconnect = true;
214590792Sgshapiro	SM_EVENT *volatile ev = NULL;
214690792Sgshapiro#if NETINET6
214790792Sgshapiro	volatile bool v6found = false;
2148363466Sgshapiro#endif
214964562Sgshapiro	volatile int family = InetMode;
215064562Sgshapiro	SOCKADDR_LEN_T len;
215164562Sgshapiro	volatile SOCKADDR_LEN_T socksize = 0;
215264562Sgshapiro	volatile bool clt_bind;
215364562Sgshapiro	BITMAP256 d_flags;
215464562Sgshapiro	char *p;
215564562Sgshapiro	extern ENVELOPE BlankEnvelope;
2156363466Sgshapiro#if DANE
2157363466Sgshapiro	unsigned long tlsa_flags;
2158363466Sgshapiro#endif
2159363466Sgshapiro#if DANE && NETINET6
2160363466Sgshapiro	struct hostent *volatile hs = (struct hostent *) NULL;
2161363466Sgshapiro#else
2162363466Sgshapiro# define hs ((struct hostent *) NULL)
2163363466Sgshapiro#endif
216438032Speter
2165363466Sgshapiro#if DANE
2166363466Sgshapiro	SM_REQUIRE(ptlsa_flags != NULL);
2167363466Sgshapiro	tlsa_flags = *ptlsa_flags;
2168363466Sgshapiro	*ptlsa_flags &= ~(TLSAFLALWAYS|TLSAFLSECURE);
2169363466Sgshapiro#endif
2170363466Sgshapiro
217190792Sgshapiro	/* retranslate {daemon_flags} into bitmap */
217264562Sgshapiro	clrbitmap(d_flags);
217390792Sgshapiro	if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL)
217464562Sgshapiro	{
217564562Sgshapiro		for (; *p != '\0'; p++)
217664562Sgshapiro		{
2177363466Sgshapiro			if (!(SM_ISSPACE(*p)))
217871345Sgshapiro				setbitn(bitidx(*p), d_flags);
217964562Sgshapiro		}
218064562Sgshapiro	}
218164562Sgshapiro
218290792Sgshapiro#if NETINET6
218364562Sgshapiro v4retry:
2184363466Sgshapiro#endif
218590792Sgshapiro	clt_bind = false;
218664562Sgshapiro
218764562Sgshapiro	/* Set up the address for outgoing connection. */
218864562Sgshapiro	if (bitnset(D_BINDIF, d_flags) &&
218990792Sgshapiro	    (p = macvalue(macid("{if_addr}"), e)) != NULL &&
219073188Sgshapiro	    *p != '\0')
219164562Sgshapiro	{
219290792Sgshapiro#if NETINET6
219364562Sgshapiro		char p6[INET6_ADDRSTRLEN];
2194363466Sgshapiro#endif
219564562Sgshapiro
2196168515Sgshapiro		memset(&clt_addr, '\0', sizeof(clt_addr));
219764562Sgshapiro
219864562Sgshapiro		/* infer the address family from the address itself */
219964562Sgshapiro		clt_addr.sa.sa_family = addr_family(p);
220064562Sgshapiro		switch (clt_addr.sa.sa_family)
220164562Sgshapiro		{
220290792Sgshapiro#if NETINET
220364562Sgshapiro		  case AF_INET:
220473188Sgshapiro			clt_addr.sin.sin_addr.s_addr = inet_addr(p);
220573188Sgshapiro			if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
2206203004Sgshapiro			    clt_addr.sin.sin_addr.s_addr !=
2207203004Sgshapiro				htonl(INADDR_LOOPBACK))
220864562Sgshapiro			{
220990792Sgshapiro				clt_bind = true;
2210168515Sgshapiro				socksize = sizeof(struct sockaddr_in);
221164562Sgshapiro			}
221264562Sgshapiro			break;
221390792Sgshapiro#endif /* NETINET */
221464562Sgshapiro
221590792Sgshapiro#if NETINET6
221664562Sgshapiro		  case AF_INET6:
221764562Sgshapiro			if (inet_addr(p) != INADDR_NONE)
2218168515Sgshapiro				(void) sm_snprintf(p6, sizeof(p6),
221990792Sgshapiro						   "IPv6:::ffff:%s", p);
222064562Sgshapiro			else
2221168515Sgshapiro				(void) sm_strlcpy(p6, p, sizeof(p6));
222290792Sgshapiro			if (anynet_pton(AF_INET6, p6,
222390792Sgshapiro					&clt_addr.sin6.sin6_addr) == 1 &&
222473188Sgshapiro			    !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
222564562Sgshapiro			{
222690792Sgshapiro				clt_bind = true;
2227168515Sgshapiro				socksize = sizeof(struct sockaddr_in6);
222864562Sgshapiro			}
222964562Sgshapiro			break;
223090792Sgshapiro#endif /* NETINET6 */
223164562Sgshapiro
223290792Sgshapiro#if 0
223364562Sgshapiro		  default:
223464562Sgshapiro			syserr("554 5.3.5 Address= option unsupported for family %d",
223564562Sgshapiro			       clt_addr.sa.sa_family);
223664562Sgshapiro			break;
223790792Sgshapiro#endif /* 0 */
223864562Sgshapiro		}
223964562Sgshapiro		if (clt_bind)
224064562Sgshapiro			family = clt_addr.sa.sa_family;
224164562Sgshapiro	}
224290792Sgshapiro
224390792Sgshapiro	/* D_BINDIF not set or not available, fallback to ClientPortOptions */
224490792Sgshapiro	if (!clt_bind)
224564562Sgshapiro	{
224690792Sgshapiro		STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
224764562Sgshapiro		switch (clt_addr.sa.sa_family)
224864562Sgshapiro		{
224990792Sgshapiro#if NETINET
225064562Sgshapiro		  case AF_INET:
225164562Sgshapiro			if (clt_addr.sin.sin_addr.s_addr == 0)
2252182352Sgshapiro				clt_addr.sin.sin_addr.s_addr = LocalDaemon ?
2253182352Sgshapiro					htonl(INADDR_LOOPBACK) : INADDR_ANY;
225464562Sgshapiro			else
225590792Sgshapiro				clt_bind = true;
225664562Sgshapiro			if (clt_addr.sin.sin_port != 0)
225790792Sgshapiro				clt_bind = true;
2258168515Sgshapiro			socksize = sizeof(struct sockaddr_in);
225964562Sgshapiro			break;
226090792Sgshapiro#endif /* NETINET */
226190792Sgshapiro#if NETINET6
226264562Sgshapiro		  case AF_INET6:
226364562Sgshapiro			if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
2264223067Sgshapiro				clt_addr.sin6.sin6_addr =
2265223067Sgshapiro					(LocalDaemon && V6LoopbackAddrFound) ?
2266182352Sgshapiro					in6addr_loopback : in6addr_any;
226764562Sgshapiro			else
226890792Sgshapiro				clt_bind = true;
2269168515Sgshapiro			socksize = sizeof(struct sockaddr_in6);
227064562Sgshapiro			if (clt_addr.sin6.sin6_port != 0)
227190792Sgshapiro				clt_bind = true;
227264562Sgshapiro			break;
227390792Sgshapiro#endif /* NETINET6 */
227490792Sgshapiro#if NETISO
227564562Sgshapiro		  case AF_ISO:
2276168515Sgshapiro			socksize = sizeof(clt_addr.siso);
227790792Sgshapiro			clt_bind = true;
227864562Sgshapiro			break;
227990792Sgshapiro#endif /* NETISO */
228064562Sgshapiro		  default:
228164562Sgshapiro			break;
228264562Sgshapiro		}
228364562Sgshapiro	}
228464562Sgshapiro
228538032Speter	/*
228638032Speter	**  Set up the address for the mailer.
228738032Speter	**	Accept "[a.b.c.d]" syntax for host name.
228838032Speter	*/
228938032Speter
229073188Sgshapiro	SM_SET_H_ERRNO(0);
229138032Speter	errno = 0;
2292168515Sgshapiro	memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
2293168515Sgshapiro	memset(&addr, '\0', sizeof(addr));
229438032Speter	SmtpPhase = mci->mci_phase = "initial connection";
229538032Speter	CurHostName = host;
229638032Speter
229738032Speter	if (host[0] == '[')
229838032Speter	{
229964562Sgshapiro		p = strchr(host, ']');
230038032Speter		if (p != NULL)
230138032Speter		{
230290792Sgshapiro#if NETINET
230364562Sgshapiro			unsigned long hid = INADDR_NONE;
2304363466Sgshapiro#endif
230590792Sgshapiro#if NETINET6
230664562Sgshapiro			struct sockaddr_in6 hid6;
2307363466Sgshapiro#endif
230864562Sgshapiro
230938032Speter			*p = '\0';
231090792Sgshapiro#if NETINET6
2311168515Sgshapiro			memset(&hid6, '\0', sizeof(hid6));
2312363466Sgshapiro#endif
231390792Sgshapiro#if NETINET
231464562Sgshapiro			if (family == AF_INET &&
231564562Sgshapiro			    (hid = inet_addr(&host[1])) != INADDR_NONE)
231638032Speter			{
231764562Sgshapiro				addr.sin.sin_family = AF_INET;
231864562Sgshapiro				addr.sin.sin_addr.s_addr = hid;
231964562Sgshapiro			}
232064562Sgshapiro			else
232190792Sgshapiro#endif /* NETINET */
232290792Sgshapiro#if NETINET6
232364562Sgshapiro			if (family == AF_INET6 &&
232490792Sgshapiro			    anynet_pton(AF_INET6, &host[1],
232590792Sgshapiro					&hid6.sin6_addr) == 1)
232664562Sgshapiro			{
232764562Sgshapiro				addr.sin6.sin6_family = AF_INET6;
232864562Sgshapiro				addr.sin6.sin6_addr = hid6.sin6_addr;
232964562Sgshapiro			}
233064562Sgshapiro			else
233190792Sgshapiro#endif /* NETINET6 */
233264562Sgshapiro			{
233338032Speter				/* try it as a host name (avoid MX lookup) */
233464562Sgshapiro				hp = sm_gethostbyname(&host[1], family);
233538032Speter				if (hp == NULL && p[-1] == '.')
233638032Speter				{
233790792Sgshapiro#if NAMED_BIND
233838032Speter					int oldopts = _res.options;
233938032Speter
234038032Speter					_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
234190792Sgshapiro#endif /* NAMED_BIND */
234238032Speter					p[-1] = '\0';
234364562Sgshapiro					hp = sm_gethostbyname(&host[1],
234464562Sgshapiro							      family);
234538032Speter					p[-1] = '.';
234690792Sgshapiro#if NAMED_BIND
234738032Speter					_res.options = oldopts;
2348363466Sgshapiro#endif
234938032Speter				}
235038032Speter				*p = ']';
235138032Speter				goto gothostent;
235238032Speter			}
235338032Speter			*p = ']';
235438032Speter		}
235538032Speter		if (p == NULL)
235638032Speter		{
235738032Speter			extern char MsgBuf[];
235838032Speter
235964562Sgshapiro			usrerrenh("5.1.2",
236064562Sgshapiro				  "553 Invalid numeric domain spec \"%s\"",
236164562Sgshapiro				  host);
236238032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
236364562Sgshapiro			errno = EINVAL;
236438032Speter			return EX_NOHOST;
236538032Speter		}
236638032Speter	}
236738032Speter	else
236838032Speter	{
236938032Speter		/* contortion to get around SGI cc complaints */
237038032Speter		{
237164562Sgshapiro			p = &host[strlen(host) - 1];
2372363466Sgshapiro#if DANE
2373363466Sgshapiro			if (tTd(16, 40))
2374363466Sgshapiro				sm_dprintf("makeconnection: tlsa_flags=%lX, host=%s\n",
2375363466Sgshapiro					tlsa_flags, host);
2376363466Sgshapiro			if (DANEMODE(tlsa_flags) == DANE_SECURE
2377363466Sgshapiro# if DNSSEC_TEST
2378363466Sgshapiro			    || tTd(8, 120)
2379363466Sgshapiro# endif
2380363466Sgshapiro			    )
2381363466Sgshapiro			{
2382363466Sgshapiro				DNS_REPLY_T *rr;
2383363466Sgshapiro				int err, herr;
2384363466Sgshapiro
2385363466Sgshapiro				rr = dns_lookup_int(host, C_IN, FAM2T_(family),
2386363466Sgshapiro					0, 0, SM_RES_DNSSEC, 0, &err, &herr);
2387363466Sgshapiro
2388363466Sgshapiro				/*
2389363466Sgshapiro				**  Check for errors!
2390363466Sgshapiro				**  If no ad: turn off TLSA.
2391363466Sgshapiro				**  permail: use "normal" method?
2392363466Sgshapiro				**  tempfail: delay or use "normal" method?
2393363466Sgshapiro				*/
2394363466Sgshapiro
2395363466Sgshapiro				if (rr != NULL && rr->dns_r_h.ad == 1)
2396363466Sgshapiro				{
2397363466Sgshapiro					*ptlsa_flags |= DANE_SECURE;
2398363466Sgshapiro					if ((TLSAFLTEMP & *ptlsa_flags) != 0)
2399363466Sgshapiro					{
2400363466Sgshapiro						dns_free_data(rr);
2401363466Sgshapiro						rr = NULL;
2402363466Sgshapiro						return EX_TEMPFAIL;
2403363466Sgshapiro					}
2404363466Sgshapiro					hp = dns2he(rr, family);
2405363466Sgshapiro#if NETINET6
2406363466Sgshapiro					hs = hp;
2407363466Sgshapiro#endif
2408363466Sgshapiro				}
2409363466Sgshapiro
2410363466Sgshapiro				/* other possible "tempfails"? */
2411363466Sgshapiro				if (rr == NULL && h_errno == TRY_AGAIN)
2412363466Sgshapiro					goto gothostent;
2413363466Sgshapiro
2414363466Sgshapiro				dns_free_data(rr);
2415363466Sgshapiro				rr = NULL;
2416363466Sgshapiro			}
2417363466Sgshapiro#endif
2418363466Sgshapiro			if (hp == NULL)
2419363466Sgshapiro				hp = sm_gethostbyname(host, family);
242038032Speter			if (hp == NULL && *p == '.')
242138032Speter			{
242290792Sgshapiro#if NAMED_BIND
242338032Speter				int oldopts = _res.options;
242438032Speter
242538032Speter				_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
2426363466Sgshapiro#endif
242738032Speter				*p = '\0';
242864562Sgshapiro				hp = sm_gethostbyname(host, family);
242938032Speter				*p = '.';
243090792Sgshapiro#if NAMED_BIND
243138032Speter				_res.options = oldopts;
2432363466Sgshapiro#endif
243338032Speter			}
243438032Speter		}
243538032Spetergothostent:
2436203004Sgshapiro		if (hp == NULL || hp->h_addr == NULL)
243738032Speter		{
243890792Sgshapiro#if NAMED_BIND
243938032Speter			/* check for name server timeouts */
244090792Sgshapiro# if NETINET6
244190792Sgshapiro			if (WorkAroundBrokenAAAA && family == AF_INET6 &&
2442261194Sgshapiro			    (h_errno == TRY_AGAIN || errno == ETIMEDOUT))
244338032Speter			{
244490792Sgshapiro				/*
244590792Sgshapiro				**  An attempt with family AF_INET may
2446261194Sgshapiro				**  succeed. By skipping the next section
244790792Sgshapiro				**  of code, we will try AF_INET before
244890792Sgshapiro				**  failing.
244990792Sgshapiro				*/
245090792Sgshapiro
245190792Sgshapiro				if (tTd(16, 10))
245290792Sgshapiro					sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
245338032Speter			}
245490792Sgshapiro			else
245590792Sgshapiro# endif /* NETINET6 */
245690792Sgshapiro			{
245790792Sgshapiro				if (errno == ETIMEDOUT ||
2458168515Sgshapiro# if _FFR_GETHBN_ExFILE
2459168515Sgshapiro#  ifdef EMFILE
2460168515Sgshapiro				   errno == EMFILE ||
2461363466Sgshapiro#  endif
2462168515Sgshapiro#  ifdef ENFILE
2463168515Sgshapiro				   errno == ENFILE ||
2464363466Sgshapiro#  endif
2465168515Sgshapiro# endif /* _FFR_GETHBN_ExFILE */
246690792Sgshapiro				    h_errno == TRY_AGAIN ||
246790792Sgshapiro				    (errno == ECONNREFUSED && UseNameServer))
246890792Sgshapiro				{
246990792Sgshapiro					save_errno = errno;
247090792Sgshapiro					mci_setstat(mci, EX_TEMPFAIL,
247190792Sgshapiro						    "4.4.3", NULL);
247290792Sgshapiro					errno = save_errno;
247390792Sgshapiro					return EX_TEMPFAIL;
247490792Sgshapiro				}
247590792Sgshapiro			}
247690792Sgshapiro#endif /* NAMED_BIND */
247790792Sgshapiro#if NETINET6
247864562Sgshapiro			/*
247964562Sgshapiro			**  Try v6 first, then fall back to v4.
248064562Sgshapiro			**  If we found a v6 address, but no v4
248164562Sgshapiro			**  addresses, then TEMPFAIL.
248264562Sgshapiro			*/
248364562Sgshapiro
248464562Sgshapiro			if (family == AF_INET6)
248564562Sgshapiro			{
248664562Sgshapiro				family = AF_INET;
248764562Sgshapiro				goto v4retry;
248864562Sgshapiro			}
248964562Sgshapiro			if (v6found)
249064562Sgshapiro				goto v6tempfail;
249190792Sgshapiro#endif /* NETINET6 */
249264562Sgshapiro			save_errno = errno;
249338032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
249464562Sgshapiro			errno = save_errno;
249564562Sgshapiro			return EX_NOHOST;
249638032Speter		}
249738032Speter		addr.sa.sa_family = hp->h_addrtype;
249838032Speter		switch (hp->h_addrtype)
249938032Speter		{
250090792Sgshapiro#if NETINET
250138032Speter		  case AF_INET:
250264562Sgshapiro			memmove(&addr.sin.sin_addr,
250364562Sgshapiro				hp->h_addr,
250438032Speter				INADDRSZ);
250538032Speter			break;
250690792Sgshapiro#endif /* NETINET */
250738032Speter
250890792Sgshapiro#if NETINET6
250964562Sgshapiro		  case AF_INET6:
251064562Sgshapiro			memmove(&addr.sin6.sin6_addr,
251164562Sgshapiro				hp->h_addr,
251264562Sgshapiro				IN6ADDRSZ);
251364562Sgshapiro			break;
251490792Sgshapiro#endif /* NETINET6 */
251564562Sgshapiro
251638032Speter		  default:
2517168515Sgshapiro			if (hp->h_length > sizeof(addr.sa.sa_data))
251838032Speter			{
251938032Speter				syserr("makeconnection: long sa_data: family %d len %d",
252038032Speter					hp->h_addrtype, hp->h_length);
252138032Speter				mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
252264562Sgshapiro				errno = EINVAL;
252338032Speter				return EX_NOHOST;
252438032Speter			}
252590792Sgshapiro			memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
252638032Speter			break;
252738032Speter		}
252838032Speter		addrno = 1;
252938032Speter	}
253038032Speter
2531363466Sgshapiro#if _FFR_TESTS
2532363466Sgshapiro		/*
2533363466Sgshapiro		**  Hack for testing.
2534363466Sgshapiro		**  Hardcoded:
2535363466Sgshapiro		**  10.1.1.12: see meta1.tns XREF IP address
2536363466Sgshapiro		**  8754: see common.sh XREF SNKPORT2
2537363466Sgshapiro		*/
2538363466Sgshapiro
2539363466Sgshapiro		if (tTd(77, 101) && hp->h_addrtype == AF_INET &&
2540363466Sgshapiro		    addr.sin.sin_addr.s_addr == inet_addr("10.1.1.12"))
2541363466Sgshapiro		{
2542363466Sgshapiro			addr.sin.sin_addr.s_addr = inet_addr("127.0.0.1");
2543363466Sgshapiro			port = htons(8754);
2544363466Sgshapiro			sm_dprintf("hack host=%s addr=[%s].%d\n", host,
2545363466Sgshapiro				anynet_ntoa(&addr), ntohs(port));
2546363466Sgshapiro		}
2547363466Sgshapiro#endif
2548363466Sgshapiro
254938032Speter	/*
255038032Speter	**  Determine the port number.
255138032Speter	*/
255238032Speter
255338032Speter	if (port == 0)
255438032Speter	{
255590792Sgshapiro#ifdef NO_GETSERVBYNAME
255664562Sgshapiro		port = htons(25);
255790792Sgshapiro#else /* NO_GETSERVBYNAME */
255838032Speter		register struct servent *sp = getservbyname("smtp", "tcp");
255938032Speter
256038032Speter		if (sp == NULL)
256138032Speter		{
256238032Speter			if (LogLevel > 2)
256338032Speter				sm_syslog(LOG_ERR, NOQID,
256464562Sgshapiro					  "makeconnection: service \"smtp\" unknown");
256538032Speter			port = htons(25);
256638032Speter		}
256738032Speter		else
256838032Speter			port = sp->s_port;
256990792Sgshapiro#endif /* NO_GETSERVBYNAME */
257038032Speter	}
257138032Speter
257290792Sgshapiro#if NETINET6
257390792Sgshapiro	if (addr.sa.sa_family == AF_INET6 &&
257490792Sgshapiro	    IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
257590792Sgshapiro	    ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
257690792Sgshapiro	{
257790792Sgshapiro		/*
257890792Sgshapiro		**  Ignore mapped IPv4 address since
257990792Sgshapiro		**  there is a ClientPortOptions setting
258090792Sgshapiro		**  for IPv4.
258190792Sgshapiro		*/
258290792Sgshapiro
258390792Sgshapiro		goto nextaddr;
258490792Sgshapiro	}
258590792Sgshapiro#endif /* NETINET6 */
258690792Sgshapiro
258738032Speter	switch (addr.sa.sa_family)
258838032Speter	{
258990792Sgshapiro#if NETINET
259038032Speter	  case AF_INET:
259138032Speter		addr.sin.sin_port = port;
2592168515Sgshapiro		addrlen = sizeof(struct sockaddr_in);
259338032Speter		break;
259490792Sgshapiro#endif /* NETINET */
259538032Speter
259690792Sgshapiro#if NETINET6
259764562Sgshapiro	  case AF_INET6:
259864562Sgshapiro		addr.sin6.sin6_port = port;
2599168515Sgshapiro		addrlen = sizeof(struct sockaddr_in6);
260064562Sgshapiro		break;
260190792Sgshapiro#endif /* NETINET6 */
260264562Sgshapiro
260390792Sgshapiro#if NETISO
260438032Speter	  case AF_ISO:
260538032Speter		/* assume two byte transport selector */
260664562Sgshapiro		memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
2607168515Sgshapiro		addrlen = sizeof(struct sockaddr_iso);
260838032Speter		break;
260990792Sgshapiro#endif /* NETISO */
261038032Speter
261138032Speter	  default:
261238032Speter		syserr("Can't connect to address family %d", addr.sa.sa_family);
261338032Speter		mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
261464562Sgshapiro		errno = EINVAL;
2615363466Sgshapiro		FREEHOSTENT(hp, hs);
261664562Sgshapiro		return EX_NOHOST;
261738032Speter	}
261838032Speter
261938032Speter	/*
262038032Speter	**  Try to actually open the connection.
262138032Speter	*/
262238032Speter
262390792Sgshapiro#if XLA
262438032Speter	/* if too many connections, don't bother trying */
262538032Speter	if (!xla_noqueue_ok(host))
262671345Sgshapiro	{
2627363466Sgshapiro		FREEHOSTENT(hp, hs);
262838032Speter		return EX_TEMPFAIL;
262971345Sgshapiro	}
263090792Sgshapiro#endif /* XLA */
263138032Speter
2632363466Sgshapiro#if _FFR_OCC
2633363466Sgshapiro# define OCC_CLOSE occ_close(e, mci, host, &addr)
2634363466Sgshapiro	/* HACK!!!! just to see if this can work at all... */
2635363466Sgshapiro	if (occ_exceeded(e, mci, host, &addr))
2636363466Sgshapiro	{
2637363466Sgshapiro		FREEHOSTENT(hp, hs);
2638363466Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id,
2639363466Sgshapiro			"stat=occ_exceeded, host=%s, addr=%s",
2640363466Sgshapiro			host, anynet_ntoa(&addr));
2641363466Sgshapiro
2642363466Sgshapiro		/*
2643363466Sgshapiro		**  to get a more specific stat= message set errno
2644363466Sgshapiro		**  or make up one in sm, see sm_errstring()
2645363466Sgshapiro		*/
2646363466Sgshapiro
2647363466Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", "450 occ_exceeded"); /* check D.S.N */
2648363466Sgshapiro		errno = EAGAIN;
2649363466Sgshapiro		return EX_TEMPFAIL;
2650363466Sgshapiro	}
2651363466Sgshapiro#else /* _FFR_OCC */
2652363466Sgshapiro# define OCC_CLOSE
2653363466Sgshapiro#endif /* _FFR_OCC */
2654363466Sgshapiro
265538032Speter	for (;;)
265638032Speter	{
265738032Speter		if (tTd(16, 1))
265890792Sgshapiro			sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
265990792Sgshapiro				   host, anynet_ntoa(&addr), ntohs(port),
266090792Sgshapiro				   (int) addr.sa.sa_family);
266138032Speter
266238032Speter		/* save for logging */
266338032Speter		CurHostAddr = addr;
266438032Speter
266590792Sgshapiro#if HASRRESVPORT
266638032Speter		if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
266738032Speter		{
266838032Speter			int rport = IPPORT_RESERVED - 1;
266938032Speter
267038032Speter			s = rresvport(&rport);
267138032Speter		}
267238032Speter		else
267390792Sgshapiro#endif /* HASRRESVPORT */
267438032Speter		{
267590792Sgshapiro			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
267638032Speter		}
267738032Speter		if (s < 0)
267838032Speter		{
267964562Sgshapiro			save_errno = errno;
268038032Speter			syserr("makeconnection: cannot create socket");
268190792Sgshapiro#if XLA
268238032Speter			xla_host_end(host);
2683363466Sgshapiro#endif
268438032Speter			mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
2685363466Sgshapiro			FREEHOSTENT(hp, hs);
268664562Sgshapiro			errno = save_errno;
2687363466Sgshapiro			OCC_CLOSE;
268838032Speter			return EX_TEMPFAIL;
268938032Speter		}
269038032Speter
269190792Sgshapiro#ifdef SO_SNDBUF
269290792Sgshapiro		if (ClientSettings[family].d_tcpsndbufsize > 0)
269338032Speter		{
269438032Speter			if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
269590792Sgshapiro				       (char *) &ClientSettings[family].d_tcpsndbufsize,
269690792Sgshapiro				       sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
269738032Speter				syserr("makeconnection: setsockopt(SO_SNDBUF)");
269838032Speter		}
269990792Sgshapiro#endif /* SO_SNDBUF */
270090792Sgshapiro#ifdef SO_RCVBUF
270190792Sgshapiro		if (ClientSettings[family].d_tcprcvbufsize > 0)
270264562Sgshapiro		{
270364562Sgshapiro			if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
270490792Sgshapiro				       (char *) &ClientSettings[family].d_tcprcvbufsize,
270590792Sgshapiro				       sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
270664562Sgshapiro				syserr("makeconnection: setsockopt(SO_RCVBUF)");
270764562Sgshapiro		}
270890792Sgshapiro#endif /* SO_RCVBUF */
270938032Speter
271038032Speter		if (tTd(16, 1))
271190792Sgshapiro			sm_dprintf("makeconnection: fd=%d\n", s);
271238032Speter
271338032Speter		/* turn on network debugging? */
271438032Speter		if (tTd(16, 101))
271538032Speter		{
271638032Speter			int on = 1;
271764562Sgshapiro
271838032Speter			(void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
2719168515Sgshapiro					  (char *)&on, sizeof(on));
272038032Speter		}
272190792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
272290792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
272390792Sgshapiro		errno = 0;		/* for debugging */
272438032Speter
272564562Sgshapiro		if (clt_bind)
272664562Sgshapiro		{
272764562Sgshapiro			int on = 1;
272864562Sgshapiro
272964562Sgshapiro			switch (clt_addr.sa.sa_family)
273064562Sgshapiro			{
273190792Sgshapiro#if NETINET
273264562Sgshapiro			  case AF_INET:
273364562Sgshapiro				if (clt_addr.sin.sin_port != 0)
273464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
273564562Sgshapiro							  SO_REUSEADDR,
273664562Sgshapiro							  (char *) &on,
2737168515Sgshapiro							  sizeof(on));
273864562Sgshapiro				break;
273990792Sgshapiro#endif /* NETINET */
274064562Sgshapiro
274190792Sgshapiro#if NETINET6
274264562Sgshapiro			  case AF_INET6:
274364562Sgshapiro				if (clt_addr.sin6.sin6_port != 0)
274464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
274564562Sgshapiro							  SO_REUSEADDR,
274664562Sgshapiro							  (char *) &on,
2747168515Sgshapiro							  sizeof(on));
274864562Sgshapiro				break;
274990792Sgshapiro#endif /* NETINET6 */
275064562Sgshapiro			}
275164562Sgshapiro
275264562Sgshapiro			if (bind(s, &clt_addr.sa, socksize) < 0)
275364562Sgshapiro			{
275464562Sgshapiro				save_errno = errno;
275564562Sgshapiro				(void) close(s);
275664562Sgshapiro				errno = save_errno;
275764562Sgshapiro				syserr("makeconnection: cannot bind socket [%s]",
275864562Sgshapiro				       anynet_ntoa(&clt_addr));
2759363466Sgshapiro				FREEHOSTENT(hp, hs);
276064562Sgshapiro				errno = save_errno;
2761363466Sgshapiro				OCC_CLOSE;
276264562Sgshapiro				return EX_TEMPFAIL;
276364562Sgshapiro			}
276464562Sgshapiro		}
276564562Sgshapiro
276638032Speter		/*
276738032Speter		**  Linux seems to hang in connect for 90 minutes (!!!).
276838032Speter		**  Time out the connect to avoid this problem.
276938032Speter		*/
277038032Speter
277138032Speter		if (setjmp(CtxConnectTimeout) == 0)
277238032Speter		{
277338032Speter			int i;
277438032Speter
277538032Speter			if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
277690792Sgshapiro				ev = sm_setevent(TimeOuts.to_iconnect,
277790792Sgshapiro						 connecttimeout, 0);
277838032Speter			else if (TimeOuts.to_connect != 0)
277990792Sgshapiro				ev = sm_setevent(TimeOuts.to_connect,
278090792Sgshapiro						 connecttimeout, 0);
278138032Speter			else
278238032Speter				ev = NULL;
278338032Speter
278464562Sgshapiro			switch (ConnectOnlyTo.sa.sa_family)
278564562Sgshapiro			{
278690792Sgshapiro#if NETINET
278764562Sgshapiro			  case AF_INET:
278864562Sgshapiro				addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
2789223067Sgshapiro				addr.sa.sa_family = ConnectOnlyTo.sa.sa_family;
2790363466Sgshapiro				if (ConnectOnlyTo.sin.sin_port != 0)
2791363466Sgshapiro				{
2792363466Sgshapiro					port = ConnectOnlyTo.sin.sin_port;
2793363466Sgshapiro					addr.sin.sin_port = port;
2794363466Sgshapiro				}
279564562Sgshapiro				break;
279690792Sgshapiro#endif /* NETINET */
279764562Sgshapiro
279890792Sgshapiro#if NETINET6
279964562Sgshapiro			  case AF_INET6:
280064562Sgshapiro				memmove(&addr.sin6.sin6_addr,
280164562Sgshapiro					&ConnectOnlyTo.sin6.sin6_addr,
280264562Sgshapiro					IN6ADDRSZ);
2803363466Sgshapiro				if (ConnectOnlyTo.sin6.sin6_port != 0)
2804363466Sgshapiro				{
2805363466Sgshapiro					port = ConnectOnlyTo.sin6.sin6_port;
2806363466Sgshapiro					addr.sin6.sin6_port = port;
2807363466Sgshapiro				}
280864562Sgshapiro				break;
280990792Sgshapiro#endif /* NETINET6 */
281064562Sgshapiro			}
2811141858Sgshapiro			if (tTd(16, 1))
2812363466Sgshapiro				sm_dprintf("Connecting to [%s].%d...\n",
2813363466Sgshapiro					anynet_ntoa(&addr), ntohs(port));
2814363466Sgshapiro
281538032Speter			i = connect(s, (struct sockaddr *) &addr, addrlen);
281664562Sgshapiro			save_errno = errno;
281738032Speter			if (ev != NULL)
281890792Sgshapiro				sm_clrevent(ev);
281938032Speter			if (i >= 0)
282038032Speter				break;
282138032Speter		}
282238032Speter		else
282364562Sgshapiro			save_errno = errno;
282438032Speter
282594334Sgshapiro		/* couldn't connect.... figure out why */
282694334Sgshapiro		(void) close(s);
282794334Sgshapiro
282838032Speter		/* if running demand-dialed connection, try again */
282990792Sgshapiro		if (DialDelay > 0 && firstconnect &&
283090792Sgshapiro		    bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
283138032Speter		{
283238032Speter			if (tTd(16, 1))
283390792Sgshapiro				sm_dprintf("Connect failed (%s); trying again...\n",
283490792Sgshapiro					   sm_errstring(save_errno));
283590792Sgshapiro			firstconnect = false;
283664562Sgshapiro			(void) sleep(DialDelay);
283738032Speter			continue;
283838032Speter		}
283938032Speter
284090792Sgshapiro		if (LogLevel > 13)
284138032Speter			sm_syslog(LOG_INFO, e->e_id,
2842363466Sgshapiro				  "makeconnection (%s [%s].%d (%d)) failed: %s",
2843363466Sgshapiro				  host, anynet_ntoa(&addr), ntohs(port),
2844363466Sgshapiro				  (int) addr.sa.sa_family,
284590792Sgshapiro				  sm_errstring(save_errno));
284638032Speter
284790792Sgshapiro#if NETINET6
284890792Sgshapironextaddr:
284990792Sgshapiro#endif /* NETINET6 */
285090792Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
285190792Sgshapiro		    (enough == 0 || curtime() < enough))
285238032Speter		{
285338032Speter			if (tTd(16, 1))
285490792Sgshapiro				sm_dprintf("Connect failed (%s); trying new address....\n",
285590792Sgshapiro					   sm_errstring(save_errno));
285638032Speter			switch (addr.sa.sa_family)
285738032Speter			{
285890792Sgshapiro#if NETINET
285938032Speter			  case AF_INET:
286064562Sgshapiro				memmove(&addr.sin.sin_addr,
286164562Sgshapiro					hp->h_addr_list[addrno++],
286264562Sgshapiro					INADDRSZ);
286338032Speter				break;
286490792Sgshapiro#endif /* NETINET */
286538032Speter
286690792Sgshapiro#if NETINET6
286764562Sgshapiro			  case AF_INET6:
286864562Sgshapiro				memmove(&addr.sin6.sin6_addr,
286964562Sgshapiro					hp->h_addr_list[addrno++],
287064562Sgshapiro					IN6ADDRSZ);
287164562Sgshapiro				break;
287290792Sgshapiro#endif /* NETINET6 */
287364562Sgshapiro
287438032Speter			  default:
287564562Sgshapiro				memmove(addr.sa.sa_data,
287664562Sgshapiro					hp->h_addr_list[addrno++],
287738032Speter					hp->h_length);
287838032Speter				break;
287938032Speter			}
288038032Speter			continue;
288138032Speter		}
288264562Sgshapiro		errno = save_errno;
288338032Speter
288490792Sgshapiro#if NETINET6
288564562Sgshapiro		if (family == AF_INET6)
288664562Sgshapiro		{
288764562Sgshapiro			if (tTd(16, 1))
288890792Sgshapiro				sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
288990792Sgshapiro					   sm_errstring(save_errno));
289090792Sgshapiro			v6found = true;
289164562Sgshapiro			family = AF_INET;
2892363466Sgshapiro			FREEHOSTENT(hp, hs);
289364562Sgshapiro			goto v4retry;
289464562Sgshapiro		}
289564562Sgshapiro	v6tempfail:
289690792Sgshapiro#endif /* NETINET6 */
289738032Speter		/* couldn't open connection */
289890792Sgshapiro#if NETINET6
289964562Sgshapiro		/* Don't clobber an already saved errno from v4retry */
290064562Sgshapiro		if (errno > 0)
2901363466Sgshapiro#endif
290264562Sgshapiro			save_errno = errno;
290364562Sgshapiro		if (tTd(16, 1))
290490792Sgshapiro			sm_dprintf("Connect failed (%s)\n",
290590792Sgshapiro				   sm_errstring(save_errno));
290690792Sgshapiro#if XLA
290738032Speter		xla_host_end(host);
2908363466Sgshapiro#endif
290938032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
2910363466Sgshapiro		FREEHOSTENT(hp, hs);
291164562Sgshapiro		errno = save_errno;
2912363466Sgshapiro		OCC_CLOSE;
291338032Speter		return EX_TEMPFAIL;
291438032Speter	}
291538032Speter
2916363466Sgshapiro	FREEHOSTENT(hp, hs);
291771345Sgshapiro
291838032Speter	/* connection ok, put it into canonical form */
291964562Sgshapiro	mci->mci_out = NULL;
292090792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
292190792Sgshapiro				       (void *) &s,
2922132943Sgshapiro				       SM_IO_WRONLY_B, NULL)) == NULL ||
292338032Speter	    (s = dup(s)) < 0 ||
292490792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
292590792Sgshapiro				      (void *) &s,
2926132943Sgshapiro				      SM_IO_RDONLY_B, NULL)) == NULL)
292738032Speter	{
292864562Sgshapiro		save_errno = errno;
292938032Speter		syserr("cannot open SMTP client channel, fd=%d", s);
293038032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
293164562Sgshapiro		if (mci->mci_out != NULL)
293290792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
293364562Sgshapiro		(void) close(s);
293464562Sgshapiro		errno = save_errno;
2935363466Sgshapiro		OCC_CLOSE;
293638032Speter		return EX_TEMPFAIL;
293738032Speter	}
293890792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
293938032Speter
294090792Sgshapiro	/* set {client_flags} */
294190792Sgshapiro	if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
294290792Sgshapiro	{
294390792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
294490792Sgshapiro			  macid("{client_flags}"),
294590792Sgshapiro			  ClientSettings[addr.sa.sa_family].d_mflags);
294690792Sgshapiro	}
294790792Sgshapiro	else
294890792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
294990792Sgshapiro			  macid("{client_flags}"), "");
295090792Sgshapiro
295190792Sgshapiro	/* "add" {client_flags} to bitmap */
295290792Sgshapiro	if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
295390792Sgshapiro	{
295490792Sgshapiro		/* look for just this one flag */
295590792Sgshapiro		setbitn(D_IFNHELO, d_flags);
295690792Sgshapiro	}
295790792Sgshapiro
295864562Sgshapiro	/* find out name for Interface through which we connect */
2959168515Sgshapiro	len = sizeof(addr);
296064562Sgshapiro	if (getsockname(s, &addr.sa, &len) == 0)
296164562Sgshapiro	{
296264562Sgshapiro		char *name;
296364562Sgshapiro
2964363466Sgshapiro		if (!isloopback(addr))
2965363466Sgshapiro		{
2966363466Sgshapiro			char familystr[5];
296764562Sgshapiro
2968363466Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
2969363466Sgshapiro				macid("{if_addr_out}"), anynet_ntoa(&addr));
2970363466Sgshapiro			(void) sm_snprintf(familystr, sizeof(familystr), "%d",
2971363466Sgshapiro				addr.sa.sa_family);
2972363466Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
2973363466Sgshapiro				macid("{if_family_out}"), familystr);
2974363466Sgshapiro		}
2975363466Sgshapiro		else
2976363466Sgshapiro		{
2977363466Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
2978363466Sgshapiro				macid("{if_addr_out}"), NULL);
2979363466Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
2980363466Sgshapiro				macid("{if_family_out}"), NULL);
2981363466Sgshapiro		}
2982363466Sgshapiro
298364562Sgshapiro		name = hostnamebyanyaddr(&addr);
298490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
298590792Sgshapiro			macid("{if_name_out}"), name);
298664562Sgshapiro		if (LogLevel > 11)
298764562Sgshapiro		{
298864562Sgshapiro			/* log connection information */
298964562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
299064562Sgshapiro				  "SMTP outgoing connect on %.40s", name);
299164562Sgshapiro		}
299264562Sgshapiro		if (bitnset(D_IFNHELO, d_flags))
299364562Sgshapiro		{
299464562Sgshapiro			if (name[0] != '[' && strchr(name, '.') != NULL)
299564562Sgshapiro				mci->mci_heloname = newstr(name);
299664562Sgshapiro		}
299764562Sgshapiro	}
299864562Sgshapiro	else
299964562Sgshapiro	{
300090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
300190792Sgshapiro			macid("{if_name_out}"), NULL);
300290792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
300390792Sgshapiro			macid("{if_addr_out}"), NULL);
300490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
300590792Sgshapiro			macid("{if_family_out}"), NULL);
300664562Sgshapiro	}
3007132943Sgshapiro
3008132943Sgshapiro	/* Use the configured HeloName as appropriate */
3009132943Sgshapiro	if (HeloName != NULL && HeloName[0] != '\0')
3010223067Sgshapiro	{
3011363466Sgshapiro		SM_FREE(mci->mci_heloname);
3012132943Sgshapiro		mci->mci_heloname = newstr(HeloName);
3013223067Sgshapiro	}
3014132943Sgshapiro
301538032Speter	mci_setstat(mci, EX_OK, NULL, NULL);
301664562Sgshapiro	return EX_OK;
301738032Speter}
301864562Sgshapiro
301964562Sgshapirostatic void
3020141858Sgshapiroconnecttimeout(ignore)
3021141858Sgshapiro	int ignore;
302264562Sgshapiro{
302377349Sgshapiro	/*
302477349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
302577349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
302677349Sgshapiro	**	DOING.
302777349Sgshapiro	*/
302877349Sgshapiro
302964562Sgshapiro	errno = ETIMEDOUT;
303064562Sgshapiro	longjmp(CtxConnectTimeout, 1);
303164562Sgshapiro}
303290792Sgshapiro/*
303364562Sgshapiro**  MAKECONNECTION_DS -- make a connection to a domain socket.
303464562Sgshapiro**
303564562Sgshapiro**	Parameters:
303664562Sgshapiro**		mux_path -- the path of the socket to connect to.
303764562Sgshapiro**		mci -- a pointer to the mail connection information
303864562Sgshapiro**			structure to be filled in.
303964562Sgshapiro**
304064562Sgshapiro**	Returns:
304164562Sgshapiro**		An exit code telling whether the connection could be
304264562Sgshapiro**			made and if not why not.
304364562Sgshapiro**
304464562Sgshapiro**	Side Effects:
304564562Sgshapiro**		none.
304664562Sgshapiro*/
304764562Sgshapiro
304890792Sgshapiro#if NETUNIX
304990792Sgshapiroint
305090792Sgshapiromakeconnection_ds(mux_path, mci)
305164562Sgshapiro	char *mux_path;
305264562Sgshapiro	register MCI *mci;
305364562Sgshapiro{
305464562Sgshapiro	int sock;
305564562Sgshapiro	int rval, save_errno;
305664562Sgshapiro	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK;
305764562Sgshapiro	struct sockaddr_un unix_addr;
305864562Sgshapiro
305964562Sgshapiro	/* if not safe, don't connect */
306064562Sgshapiro	rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName,
306164562Sgshapiro			sff, S_IRUSR|S_IWUSR, NULL);
306264562Sgshapiro
306364562Sgshapiro	if (rval != 0)
306464562Sgshapiro	{
3065132943Sgshapiro		syserr("makeconnection_ds: unsafe domain socket %s",
3066132943Sgshapiro			mux_path);
306764562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL);
306864562Sgshapiro		errno = rval;
306964562Sgshapiro		return EX_TEMPFAIL;
307064562Sgshapiro	}
307164562Sgshapiro
307264562Sgshapiro	/* prepare address structure */
3073168515Sgshapiro	memset(&unix_addr, '\0', sizeof(unix_addr));
307464562Sgshapiro	unix_addr.sun_family = AF_UNIX;
307564562Sgshapiro
3076168515Sgshapiro	if (strlen(mux_path) >= sizeof(unix_addr.sun_path))
307764562Sgshapiro	{
3078132943Sgshapiro		syserr("makeconnection_ds: domain socket name %s too long",
3079132943Sgshapiro			mux_path);
308090792Sgshapiro
308190792Sgshapiro		/* XXX why TEMPFAIL but 5.x.y ? */
308264562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
308364562Sgshapiro		errno = ENAMETOOLONG;
308464562Sgshapiro		return EX_UNAVAILABLE;
308564562Sgshapiro	}
308690792Sgshapiro	(void) sm_strlcpy(unix_addr.sun_path, mux_path,
3087168515Sgshapiro			  sizeof(unix_addr.sun_path));
308864562Sgshapiro
308964562Sgshapiro	/* initialize domain socket */
309064562Sgshapiro	sock = socket(AF_UNIX, SOCK_STREAM, 0);
309164562Sgshapiro	if (sock == -1)
309264562Sgshapiro	{
309364562Sgshapiro		save_errno = errno;
3094132943Sgshapiro		syserr("makeconnection_ds: could not create domain socket %s",
3095132943Sgshapiro			mux_path);
309664562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
309764562Sgshapiro		errno = save_errno;
309864562Sgshapiro		return EX_TEMPFAIL;
309964562Sgshapiro	}
310064562Sgshapiro
310164562Sgshapiro	/* connect to server */
310264562Sgshapiro	if (connect(sock, (struct sockaddr *) &unix_addr,
310364562Sgshapiro		    sizeof(unix_addr)) == -1)
310464562Sgshapiro	{
310564562Sgshapiro		save_errno = errno;
310664562Sgshapiro		syserr("Could not connect to socket %s", mux_path);
310764562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
310864562Sgshapiro		(void) close(sock);
310964562Sgshapiro		errno = save_errno;
311064562Sgshapiro		return EX_TEMPFAIL;
311164562Sgshapiro	}
311264562Sgshapiro
311364562Sgshapiro	/* connection ok, put it into canonical form */
311464562Sgshapiro	mci->mci_out = NULL;
311590792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
3116132943Sgshapiro				       (void *) &sock, SM_IO_WRONLY_B, NULL))
311790792Sgshapiro					== NULL
311890792Sgshapiro	    || (sock = dup(sock)) < 0 ||
311990792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
3120132943Sgshapiro				      (void *) &sock, SM_IO_RDONLY_B, NULL))
312190792Sgshapiro					== NULL)
312264562Sgshapiro	{
312364562Sgshapiro		save_errno = errno;
312464562Sgshapiro		syserr("cannot open SMTP client channel, fd=%d", sock);
312564562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
312664562Sgshapiro		if (mci->mci_out != NULL)
312790792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
312864562Sgshapiro		(void) close(sock);
312964562Sgshapiro		errno = save_errno;
313064562Sgshapiro		return EX_TEMPFAIL;
313164562Sgshapiro	}
313290792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
313364562Sgshapiro
313464562Sgshapiro	mci_setstat(mci, EX_OK, NULL, NULL);
313564562Sgshapiro	errno = 0;
313664562Sgshapiro	return EX_OK;
313764562Sgshapiro}
313890792Sgshapiro#endif /* NETUNIX */
313990792Sgshapiro/*
314090792Sgshapiro**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
314177349Sgshapiro**
314277349Sgshapiro**	Parameters:
314390792Sgshapiro**		none.
314477349Sgshapiro**
314577349Sgshapiro**	Returns:
314677349Sgshapiro**		none.
314777349Sgshapiro**
314877349Sgshapiro**	Side Effects:
314990792Sgshapiro**		closes control socket, exits.
315077349Sgshapiro*/
315177349Sgshapiro
315290792Sgshapirovoid
315390792Sgshapiroshutdown_daemon()
315477349Sgshapiro{
315590792Sgshapiro	int i;
315690792Sgshapiro	char *reason;
315777349Sgshapiro
315890792Sgshapiro	sm_allsignals(true);
315990792Sgshapiro
316090792Sgshapiro	reason = ShutdownRequest;
316190792Sgshapiro	ShutdownRequest = NULL;
316290792Sgshapiro	PendingSignal = 0;
316390792Sgshapiro
3164132943Sgshapiro	if (LogLevel > 9)
3165132943Sgshapiro		sm_syslog(LOG_INFO, CurEnv->e_id, "stopping daemon, reason=%s",
316690792Sgshapiro			  reason == NULL ? "implicit call" : reason);
316790792Sgshapiro
316890792Sgshapiro	FileName = NULL;
316990792Sgshapiro	closecontrolsocket(true);
317090792Sgshapiro#if XLA
317190792Sgshapiro	xla_all_end();
3172363466Sgshapiro#endif
317390792Sgshapiro
317490792Sgshapiro	for (i = 0; i < NDaemons; i++)
317590792Sgshapiro	{
317690792Sgshapiro		if (Daemons[i].d_socket >= 0)
317790792Sgshapiro		{
317890792Sgshapiro			(void) close(Daemons[i].d_socket);
317990792Sgshapiro			Daemons[i].d_socket = -1;
318090792Sgshapiro
3181285229Sgshapiro#if NETUNIX
318290792Sgshapiro			/* Remove named sockets */
318390792Sgshapiro			if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
318490792Sgshapiro			{
318590792Sgshapiro				int rval;
318690792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
318790792Sgshapiro
318890792Sgshapiro				/* if not safe, don't use it */
318990792Sgshapiro				rval = safefile(Daemons[i].d_addr.sunix.sun_path,
319090792Sgshapiro						RunAsUid, RunAsGid,
319190792Sgshapiro						RunAsUserName, sff,
319290792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
319390792Sgshapiro				if (rval == 0 &&
319490792Sgshapiro				    unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
319590792Sgshapiro				{
319690792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
319790792Sgshapiro						  "Could not remove daemon %s socket: %s: %s",
319890792Sgshapiro						  Daemons[i].d_name,
319990792Sgshapiro						  Daemons[i].d_addr.sunix.sun_path,
320090792Sgshapiro						  sm_errstring(errno));
320190792Sgshapiro				}
320290792Sgshapiro			}
3203285229Sgshapiro#endif /* NETUNIX */
320490792Sgshapiro		}
320590792Sgshapiro	}
320690792Sgshapiro
320790792Sgshapiro	finis(false, true, EX_OK);
320877349Sgshapiro}
320990792Sgshapiro/*
321077349Sgshapiro**  RESTART_DAEMON -- Performs a clean restart of the daemon
321177349Sgshapiro**
321277349Sgshapiro**	Parameters:
321377349Sgshapiro**		none.
321477349Sgshapiro**
321577349Sgshapiro**	Returns:
321677349Sgshapiro**		none.
321777349Sgshapiro**
321877349Sgshapiro**	Side Effects:
321977349Sgshapiro**		restarts the daemon or exits if restart fails.
322077349Sgshapiro*/
322177349Sgshapiro
322280785Sgshapiro/* Make a non-DFL/IGN signal a noop */
322380785Sgshapiro#define SM_NOOP_SIGNAL(sig, old)				\
322480785Sgshapirodo								\
322580785Sgshapiro{								\
322690792Sgshapiro	(old) = sm_signal((sig), sm_signal_noop);		\
322780785Sgshapiro	if ((old) == SIG_IGN || (old) == SIG_DFL)		\
322890792Sgshapiro		(void) sm_signal((sig), (old));			\
322980785Sgshapiro} while (0)
323080785Sgshapiro
323190792Sgshapirovoid
323277349Sgshapirorestart_daemon()
323377349Sgshapiro{
323490792Sgshapiro	bool drop;
323577349Sgshapiro	int save_errno;
323677349Sgshapiro	char *reason;
323780785Sgshapiro	sigfunc_t ignore, oalrm, ousr1;
323877349Sgshapiro	extern int DtableSize;
323977349Sgshapiro
324080785Sgshapiro	/* clear the events to turn off SIGALRMs */
324190792Sgshapiro	sm_clear_events();
324290792Sgshapiro	sm_allsignals(true);
324377349Sgshapiro
324477349Sgshapiro	reason = RestartRequest;
324577349Sgshapiro	RestartRequest = NULL;
324677349Sgshapiro	PendingSignal = 0;
324777349Sgshapiro
324877349Sgshapiro	if (SaveArgv[0][0] != '/')
324977349Sgshapiro	{
325077349Sgshapiro		if (LogLevel > 3)
325177349Sgshapiro			sm_syslog(LOG_INFO, NOQID,
325277349Sgshapiro				  "could not restart: need full path");
325390792Sgshapiro		finis(false, true, EX_OSFILE);
325490792Sgshapiro		/* NOTREACHED */
325577349Sgshapiro	}
325677349Sgshapiro	if (LogLevel > 3)
325777349Sgshapiro		sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
325877349Sgshapiro			  SaveArgv[0],
325977349Sgshapiro			  reason == NULL ? "implicit call" : reason);
326077349Sgshapiro
326190792Sgshapiro	closecontrolsocket(true);
326298121Sgshapiro#if SM_CONF_SHM
326398121Sgshapiro	cleanup_shm(DaemonPid == getpid());
3264363466Sgshapiro#endif
326590792Sgshapiro
3266132943Sgshapiro	/* close locked pid file */
3267132943Sgshapiro	close_sendmail_pid();
3268132943Sgshapiro
326990792Sgshapiro	/*
327090792Sgshapiro	**  Want to drop to the user who started the process in all cases
327190792Sgshapiro	**  *but* when running as "smmsp" for the clientmqueue queue run
327290792Sgshapiro	**  daemon.  In that case, UseMSP will be true, RunAsUid should not
327390792Sgshapiro	**  be root, and RealUid should be either 0 or RunAsUid.
327490792Sgshapiro	*/
327590792Sgshapiro
327690792Sgshapiro	drop = !(UseMSP && RunAsUid != 0 &&
327790792Sgshapiro		 (RealUid == 0 || RealUid == RunAsUid));
327890792Sgshapiro
327990792Sgshapiro	if (drop_privileges(drop) != EX_OK)
328077349Sgshapiro	{
328177349Sgshapiro		if (LogLevel > 0)
328277349Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
328390792Sgshapiro				  "could not drop privileges: %s",
328490792Sgshapiro				  sm_errstring(errno));
328590792Sgshapiro		finis(false, true, EX_OSERR);
328690792Sgshapiro		/* NOTREACHED */
328777349Sgshapiro	}
328877349Sgshapiro
3289132943Sgshapiro	sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
329077349Sgshapiro
329180785Sgshapiro	/*
329280785Sgshapiro	**  Need to allow signals before execve() to make them "harmless".
329380785Sgshapiro	**  However, the default action can be "terminate", so it isn't
329480785Sgshapiro	**  really harmless.  Setting signals to IGN will cause them to be
329580785Sgshapiro	**  ignored in the new process to, so that isn't a good alternative.
329680785Sgshapiro	*/
329780785Sgshapiro
329880785Sgshapiro	SM_NOOP_SIGNAL(SIGALRM, oalrm);
329980785Sgshapiro	SM_NOOP_SIGNAL(SIGCHLD, ignore);
330080785Sgshapiro	SM_NOOP_SIGNAL(SIGHUP, ignore);
330180785Sgshapiro	SM_NOOP_SIGNAL(SIGINT, ignore);
330280785Sgshapiro	SM_NOOP_SIGNAL(SIGPIPE, ignore);
330380785Sgshapiro	SM_NOOP_SIGNAL(SIGTERM, ignore);
330480785Sgshapiro#ifdef SIGUSR1
330580785Sgshapiro	SM_NOOP_SIGNAL(SIGUSR1, ousr1);
3306363466Sgshapiro#endif
330794334Sgshapiro
330894334Sgshapiro	/* Turn back on signals */
330990792Sgshapiro	sm_allsignals(false);
331077349Sgshapiro
331177349Sgshapiro	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
331277349Sgshapiro	save_errno = errno;
331377349Sgshapiro
331480785Sgshapiro	/* block signals again and restore needed signals */
331590792Sgshapiro	sm_allsignals(true);
331680785Sgshapiro
331780785Sgshapiro	/* For finis() events */
331890792Sgshapiro	(void) sm_signal(SIGALRM, oalrm);
331980785Sgshapiro
332080785Sgshapiro#ifdef SIGUSR1
332180785Sgshapiro	/* For debugging finis() */
332290792Sgshapiro	(void) sm_signal(SIGUSR1, ousr1);
3323363466Sgshapiro#endif
332477349Sgshapiro
332577349Sgshapiro	errno = save_errno;
332677349Sgshapiro	if (LogLevel > 0)
332790792Sgshapiro		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
332890792Sgshapiro			  SaveArgv[0], sm_errstring(errno));
332990792Sgshapiro	finis(false, true, EX_OSFILE);
333090792Sgshapiro	/* NOTREACHED */
333177349Sgshapiro}
333290792Sgshapiro/*
333338032Speter**  MYHOSTNAME -- return the name of this host.
333438032Speter**
333538032Speter**	Parameters:
333638032Speter**		hostbuf -- a place to return the name of this host.
333738032Speter**		size -- the size of hostbuf.
333838032Speter**
333938032Speter**	Returns:
334038032Speter**		A list of aliases for this host.
334138032Speter**
334238032Speter**	Side Effects:
334338032Speter**		Adds numeric codes to $=w.
334438032Speter*/
334538032Speter
334638032Speterstruct hostent *
334738032Spetermyhostname(hostbuf, size)
334838032Speter	char hostbuf[];
334938032Speter	int size;
335038032Speter{
335138032Speter	register struct hostent *hp;
335238032Speter
335373188Sgshapiro	if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
335490792Sgshapiro		(void) sm_strlcpy(hostbuf, "localhost", size);
335564562Sgshapiro	hp = sm_gethostbyname(hostbuf, InetMode);
335690792Sgshapiro#if NETINET && NETINET6
335780785Sgshapiro	if (hp == NULL && InetMode == AF_INET6)
335880785Sgshapiro	{
335980785Sgshapiro		/*
336080785Sgshapiro		**  It's possible that this IPv6 enabled machine doesn't
336180785Sgshapiro		**  actually have any IPv6 interfaces and, therefore, no
336280785Sgshapiro		**  IPv6 addresses.  Fall back to AF_INET.
336380785Sgshapiro		*/
336480785Sgshapiro
336580785Sgshapiro		hp = sm_gethostbyname(hostbuf, AF_INET);
336680785Sgshapiro	}
336790792Sgshapiro#endif /* NETINET && NETINET6 */
336838032Speter	if (hp == NULL)
336938032Speter		return NULL;
337038032Speter	if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
337164562Sgshapiro		(void) cleanstrcpy(hostbuf, hp->h_name, size);
337264562Sgshapiro
337390792Sgshapiro#if NETINFO
337464562Sgshapiro	if (strchr(hostbuf, '.') == NULL)
337538032Speter	{
337664562Sgshapiro		char *domainname;
337764562Sgshapiro
337864562Sgshapiro		domainname = ni_propval("/locations", NULL, "resolver",
337964562Sgshapiro					"domain", '\0');
338064562Sgshapiro		if (domainname != NULL &&
338164562Sgshapiro		    strlen(domainname) + strlen(hostbuf) + 1 < size)
338290792Sgshapiro			(void) sm_strlcat2(hostbuf, ".", domainname, size);
338338032Speter	}
338490792Sgshapiro#endif /* NETINFO */
338538032Speter
338638032Speter	/*
338738032Speter	**  If there is still no dot in the name, try looking for a
338838032Speter	**  dotted alias.
338938032Speter	*/
339038032Speter
339138032Speter	if (strchr(hostbuf, '.') == NULL)
339238032Speter	{
339338032Speter		char **ha;
339438032Speter
339564562Sgshapiro		for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
339638032Speter		{
339738032Speter			if (strchr(*ha, '.') != NULL)
339838032Speter			{
339964562Sgshapiro				(void) cleanstrcpy(hostbuf, *ha, size - 1);
340038032Speter				hostbuf[size - 1] = '\0';
340138032Speter				break;
340238032Speter			}
340338032Speter		}
340438032Speter	}
340538032Speter
340638032Speter	/*
340738032Speter	**  If _still_ no dot, wait for a while and try again -- it is
340838032Speter	**  possible that some service is starting up.  This can result
340938032Speter	**  in excessive delays if the system is badly configured, but
341038032Speter	**  there really isn't a way around that, particularly given that
341138032Speter	**  the config file hasn't been read at this point.
341238032Speter	**  All in all, a bit of a mess.
341338032Speter	*/
341438032Speter
341538032Speter	if (strchr(hostbuf, '.') == NULL &&
3416363466Sgshapiro	    getcanonname(hostbuf, size, true, NULL) == HOST_NOTFOUND)
341738032Speter	{
3418182352Sgshapiro		sm_syslog(LocalDaemon ? LOG_WARNING : LOG_CRIT, NOQID,
341964562Sgshapiro			  "My unqualified host name (%s) unknown; sleeping for retry",
342064562Sgshapiro			  hostbuf);
342138032Speter		message("My unqualified host name (%s) unknown; sleeping for retry",
342238032Speter			hostbuf);
342364562Sgshapiro		(void) sleep(60);
3424363466Sgshapiro		if (getcanonname(hostbuf, size, true, NULL) == HOST_NOTFOUND)
342538032Speter		{
3426182352Sgshapiro			sm_syslog(LocalDaemon ? LOG_WARNING : LOG_ALERT, NOQID,
342764562Sgshapiro				  "unable to qualify my own domain name (%s) -- using short name",
342864562Sgshapiro				  hostbuf);
342938032Speter			message("WARNING: unable to qualify my own domain name (%s) -- using short name",
343038032Speter				hostbuf);
343138032Speter		}
343238032Speter	}
343364562Sgshapiro	return hp;
343438032Speter}
343590792Sgshapiro/*
343638032Speter**  ADDRCMP -- compare two host addresses
343738032Speter**
343838032Speter**	Parameters:
343938032Speter**		hp -- hostent structure for the first address
344038032Speter**		ha -- actual first address
344138032Speter**		sa -- second address
344238032Speter**
344338032Speter**	Returns:
344438032Speter**		0 -- if ha and sa match
344538032Speter**		else -- they don't match
344638032Speter*/
344738032Speter
344864562Sgshapirostatic int
344938032Speteraddrcmp(hp, ha, sa)
345038032Speter	struct hostent *hp;
345138032Speter	char *ha;
345238032Speter	SOCKADDR *sa;
345338032Speter{
345490792Sgshapiro#if NETINET6
345590792Sgshapiro	unsigned char *a;
3456363466Sgshapiro#endif
345764562Sgshapiro
345838032Speter	switch (sa->sa.sa_family)
345938032Speter	{
346090792Sgshapiro#if NETINET
346138032Speter	  case AF_INET:
346238032Speter		if (hp->h_addrtype == AF_INET)
346364562Sgshapiro			return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
346438032Speter		break;
3465363466Sgshapiro#endif
346638032Speter
346790792Sgshapiro#if NETINET6
346864562Sgshapiro	  case AF_INET6:
346990792Sgshapiro		a = (unsigned char *) &sa->sin6.sin6_addr;
347064562Sgshapiro
347164562Sgshapiro		/* Straight binary comparison */
347264562Sgshapiro		if (hp->h_addrtype == AF_INET6)
347364562Sgshapiro			return memcmp(ha, a, IN6ADDRSZ);
347464562Sgshapiro
347564562Sgshapiro		/* If IPv4-mapped IPv6 address, compare the IPv4 section */
347664562Sgshapiro		if (hp->h_addrtype == AF_INET &&
347764562Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
347864562Sgshapiro			return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
347964562Sgshapiro		break;
348090792Sgshapiro#endif /* NETINET6 */
348138032Speter	}
348238032Speter	return -1;
348338032Speter}
348490792Sgshapiro/*
348564562Sgshapiro**  GETAUTHINFO -- get the real host name associated with a file descriptor
348638032Speter**
348738032Speter**	Uses RFC1413 protocol to try to get info from the other end.
348838032Speter**
348938032Speter**	Parameters:
349038032Speter**		fd -- the descriptor
349190792Sgshapiro**		may_be_forged -- an outage that is set to true if the
349238032Speter**			forward lookup of RealHostName does not match
349390792Sgshapiro**			RealHostAddr; set to false if they do match.
349438032Speter**
349538032Speter**	Returns:
349638032Speter**		The user@host information associated with this descriptor.
349738032Speter*/
349838032Speter
349938032Speterstatic jmp_buf	CtxAuthTimeout;
350038032Speter
350138032Speterstatic void
3502141858Sgshapiroauthtimeout(ignore)
3503141858Sgshapiro	int ignore;
350438032Speter{
350577349Sgshapiro	/*
350677349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
350777349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
350877349Sgshapiro	**	DOING.
350977349Sgshapiro	*/
351077349Sgshapiro
351177349Sgshapiro	errno = ETIMEDOUT;
351238032Speter	longjmp(CtxAuthTimeout, 1);
351338032Speter}
351438032Speter
351538032Speterchar *
351638032Spetergetauthinfo(fd, may_be_forged)
351738032Speter	int fd;
351838032Speter	bool *may_be_forged;
351938032Speter{
352090792Sgshapiro	unsigned short SM_NONVOLATILE port = 0;
352138032Speter	SOCKADDR_LEN_T falen;
352238032Speter	register char *volatile p = NULL;
352338032Speter	SOCKADDR la;
352438032Speter	SOCKADDR_LEN_T lalen;
352590792Sgshapiro#ifndef NO_GETSERVBYNAME
352638032Speter	register struct servent *sp;
352790792Sgshapiro# if NETINET
352890792Sgshapiro	static unsigned short port4 = 0;
3529363466Sgshapiro# endif
353090792Sgshapiro# if NETINET6
353190792Sgshapiro	static unsigned short port6 = 0;
3532363466Sgshapiro# endif
353390792Sgshapiro#endif /* ! NO_GETSERVBYNAME */
353438032Speter	volatile int s;
353538032Speter	int i = 0;
353690792Sgshapiro	size_t len;
353790792Sgshapiro	SM_EVENT *ev;
353838032Speter	int nleft;
353938032Speter	struct hostent *hp;
354038032Speter	char *ostype = NULL;
354138032Speter	char **ha;
354238032Speter	char ibuf[MAXNAME + 1];
3543110560Sgshapiro	static char hbuf[MAXNAME + MAXAUTHINFO + 11];
354438032Speter
3545285229Sgshapiro	*may_be_forged = true;
3546168515Sgshapiro	falen = sizeof(RealHostAddr);
354738032Speter	if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
354838032Speter	    falen <= 0 || RealHostAddr.sa.sa_family == 0)
354938032Speter	{
355064562Sgshapiro		if (i < 0)
355164562Sgshapiro		{
355264562Sgshapiro			/*
355364562Sgshapiro			**  ENOTSOCK is OK: bail on anything else, but reset
355464562Sgshapiro			**  errno in this case, so a mis-report doesn't
355564562Sgshapiro			**  happen later.
355664562Sgshapiro			*/
355790792Sgshapiro
355864562Sgshapiro			if (errno != ENOTSOCK)
355964562Sgshapiro				return NULL;
356064562Sgshapiro			errno = 0;
356164562Sgshapiro		}
3562285229Sgshapiro
3563285229Sgshapiro		*may_be_forged = false;
3564168515Sgshapiro		(void) sm_strlcpyn(hbuf, sizeof(hbuf), 2, RealUserName,
356590792Sgshapiro				   "@localhost");
356638032Speter		if (tTd(9, 1))
356790792Sgshapiro			sm_dprintf("getauthinfo: %s\n", hbuf);
356838032Speter		return hbuf;
356938032Speter	}
357038032Speter
357138032Speter	if (RealHostName == NULL)
357238032Speter	{
357338032Speter		/* translate that to a host name */
357438032Speter		RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
357538032Speter		if (strlen(RealHostName) > MAXNAME)
357690792Sgshapiro			RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
357738032Speter	}
357838032Speter
357938032Speter	/* cross check RealHostName with forward DNS lookup */
3580285229Sgshapiro	if (anynet_ntoa(&RealHostAddr)[0] == '[' ||
3581285229Sgshapiro	    RealHostName[0] == '[')
3582285229Sgshapiro		*may_be_forged = false;
3583285229Sgshapiro	else
358438032Speter	{
358580785Sgshapiro		int family;
358680785Sgshapiro
358780785Sgshapiro		family = RealHostAddr.sa.sa_family;
358890792Sgshapiro#if NETINET6 && NEEDSGETIPNODE
358980785Sgshapiro		/*
359080785Sgshapiro		**  If RealHostAddr is an IPv6 connection with an
359180785Sgshapiro		**  IPv4-mapped address, we need RealHostName's IPv4
359280785Sgshapiro		**  address(es) for addrcmp() to compare against
359380785Sgshapiro		**  RealHostAddr.
359480785Sgshapiro		**
359580785Sgshapiro		**  Actually, we only need to do this for systems
359680785Sgshapiro		**  which NEEDSGETIPNODE since the real getipnodebyname()
359780785Sgshapiro		**  already does V4MAPPED address via the AI_V4MAPPEDCFG
359880785Sgshapiro		**  flag.  A better fix to this problem is to add this
359980785Sgshapiro		**  functionality to our stub getipnodebyname().
360080785Sgshapiro		*/
360180785Sgshapiro
360280785Sgshapiro		if (family == AF_INET6 &&
360380785Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
360480785Sgshapiro			family = AF_INET;
360590792Sgshapiro#endif /* NETINET6 && NEEDSGETIPNODE */
360680785Sgshapiro
360738032Speter		/* try to match the reverse against the forward lookup */
360880785Sgshapiro		hp = sm_gethostbyname(RealHostName, family);
3609285229Sgshapiro		if (hp != NULL)
3610120256Sgshapiro		{
361138032Speter			for (ha = hp->h_addr_list; *ha != NULL; ha++)
361290792Sgshapiro			{
361338032Speter				if (addrcmp(hp, *ha, &RealHostAddr) == 0)
3614285229Sgshapiro				{
3615285229Sgshapiro					*may_be_forged = false;
361638032Speter					break;
3617285229Sgshapiro				}
361890792Sgshapiro			}
3619363466Sgshapiro			FREEHOSTENT(hp, NULL);
362038032Speter		}
362138032Speter	}
362238032Speter
362338032Speter	if (TimeOuts.to_ident == 0)
362438032Speter		goto noident;
362538032Speter
3626168515Sgshapiro	lalen = sizeof(la);
362764562Sgshapiro	switch (RealHostAddr.sa.sa_family)
362838032Speter	{
362990792Sgshapiro#if NETINET
363064562Sgshapiro	  case AF_INET:
363164562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
363264562Sgshapiro		    lalen <= 0 ||
363364562Sgshapiro		    la.sa.sa_family != AF_INET)
363464562Sgshapiro		{
363564562Sgshapiro			/* no ident info */
363664562Sgshapiro			goto noident;
363764562Sgshapiro		}
363864562Sgshapiro		port = RealHostAddr.sin.sin_port;
363938032Speter
364064562Sgshapiro		/* create ident query */
3641168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
364264562Sgshapiro				ntohs(RealHostAddr.sin.sin_port),
364364562Sgshapiro				ntohs(la.sin.sin_port));
364438032Speter
364564562Sgshapiro		/* create local address */
364664562Sgshapiro		la.sin.sin_port = 0;
364738032Speter
364864562Sgshapiro		/* create foreign address */
364990792Sgshapiro# ifdef NO_GETSERVBYNAME
365038032Speter		RealHostAddr.sin.sin_port = htons(113);
365190792Sgshapiro# else /* NO_GETSERVBYNAME */
365290792Sgshapiro
365390792Sgshapiro		/*
365490792Sgshapiro		**  getservbyname() consumes about 5% of the time
365590792Sgshapiro		**  when receiving a small message (almost all of the time
365690792Sgshapiro		**  spent in this routine).
365790792Sgshapiro		**  Hence we store the port in a static variable
365890792Sgshapiro		**  to save this time.
365990792Sgshapiro		**  The portnumber shouldn't change very often...
366090792Sgshapiro		**  This code makes the assumption that the port number
366190792Sgshapiro		**  is not 0.
366290792Sgshapiro		*/
366390792Sgshapiro
366490792Sgshapiro		if (port4 == 0)
366590792Sgshapiro		{
366690792Sgshapiro			sp = getservbyname("auth", "tcp");
366790792Sgshapiro			if (sp != NULL)
366890792Sgshapiro				port4 = sp->s_port;
366990792Sgshapiro			else
367090792Sgshapiro				port4 = htons(113);
367190792Sgshapiro		}
367290792Sgshapiro		RealHostAddr.sin.sin_port = port4;
367364562Sgshapiro		break;
367490792Sgshapiro# endif /* NO_GETSERVBYNAME */
367590792Sgshapiro#endif /* NETINET */
367638032Speter
367790792Sgshapiro#if NETINET6
367864562Sgshapiro	  case AF_INET6:
367964562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
368064562Sgshapiro		    lalen <= 0 ||
368164562Sgshapiro		    la.sa.sa_family != AF_INET6)
368264562Sgshapiro		{
368364562Sgshapiro			/* no ident info */
368464562Sgshapiro			goto noident;
368564562Sgshapiro		}
368664562Sgshapiro		port = RealHostAddr.sin6.sin6_port;
368764562Sgshapiro
368864562Sgshapiro		/* create ident query */
3689168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
369064562Sgshapiro				ntohs(RealHostAddr.sin6.sin6_port),
369164562Sgshapiro				ntohs(la.sin6.sin6_port));
369264562Sgshapiro
369364562Sgshapiro		/* create local address */
369464562Sgshapiro		la.sin6.sin6_port = 0;
369564562Sgshapiro
369664562Sgshapiro		/* create foreign address */
369790792Sgshapiro# ifdef NO_GETSERVBYNAME
369864562Sgshapiro		RealHostAddr.sin6.sin6_port = htons(113);
369990792Sgshapiro# else /* NO_GETSERVBYNAME */
370090792Sgshapiro		if (port6 == 0)
370190792Sgshapiro		{
370290792Sgshapiro			sp = getservbyname("auth", "tcp");
370390792Sgshapiro			if (sp != NULL)
370490792Sgshapiro				port6 = sp->s_port;
370590792Sgshapiro			else
370690792Sgshapiro				port6 = htons(113);
370790792Sgshapiro		}
370890792Sgshapiro		RealHostAddr.sin6.sin6_port = port6;
370964562Sgshapiro		break;
371090792Sgshapiro# endif /* NO_GETSERVBYNAME */
371190792Sgshapiro#endif /* NETINET6 */
371264562Sgshapiro	  default:
371364562Sgshapiro		/* no ident info */
371464562Sgshapiro		goto noident;
371564562Sgshapiro	}
371664562Sgshapiro
371738032Speter	s = -1;
371838032Speter	if (setjmp(CtxAuthTimeout) != 0)
371938032Speter	{
372038032Speter		if (s >= 0)
372138032Speter			(void) close(s);
372238032Speter		goto noident;
372338032Speter	}
372438032Speter
372538032Speter	/* put a timeout around the whole thing */
372690792Sgshapiro	ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
372738032Speter
372838032Speter	/* connect to foreign IDENT server using same address as SMTP socket */
372964562Sgshapiro	s = socket(la.sa.sa_family, SOCK_STREAM, 0);
373038032Speter	if (s < 0)
373138032Speter	{
373290792Sgshapiro		sm_clrevent(ev);
373338032Speter		goto noident;
373438032Speter	}
373564562Sgshapiro	if (bind(s, &la.sa, lalen) < 0 ||
373664562Sgshapiro	    connect(s, &RealHostAddr.sa, lalen) < 0)
373738032Speter		goto closeident;
373838032Speter
373938032Speter	if (tTd(9, 10))
374090792Sgshapiro		sm_dprintf("getauthinfo: sent %s", ibuf);
374138032Speter
374238032Speter	/* send query */
374338032Speter	if (write(s, ibuf, strlen(ibuf)) < 0)
374438032Speter		goto closeident;
374538032Speter
374638032Speter	/* get result */
374738032Speter	p = &ibuf[0];
3748168515Sgshapiro	nleft = sizeof(ibuf) - 1;
374938032Speter	while ((i = read(s, p, nleft)) > 0)
375038032Speter	{
3751125820Sgshapiro		char *s;
3752125820Sgshapiro
375338032Speter		p += i;
375438032Speter		nleft -= i;
375538032Speter		*p = '\0';
3756125820Sgshapiro		if ((s = strchr(ibuf, '\n')) != NULL)
3757125820Sgshapiro		{
3758125820Sgshapiro			if (p > s + 1)
3759125820Sgshapiro			{
3760125820Sgshapiro				p = s + 1;
3761125820Sgshapiro				*p = '\0';
3762125820Sgshapiro			}
376338032Speter			break;
3764125820Sgshapiro		}
3765125820Sgshapiro		if (nleft <= 0)
3766125820Sgshapiro			break;
376738032Speter	}
376838032Speter	(void) close(s);
376990792Sgshapiro	sm_clrevent(ev);
377038032Speter	if (i < 0 || p == &ibuf[0])
377138032Speter		goto noident;
377238032Speter
3773111823Sgshapiro	if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r')
377438032Speter		p--;
377538032Speter	*++p = '\0';
377638032Speter
377738032Speter	if (tTd(9, 3))
377890792Sgshapiro		sm_dprintf("getauthinfo:  got %s\n", ibuf);
377938032Speter
378038032Speter	/* parse result */
378138032Speter	p = strchr(ibuf, ':');
378238032Speter	if (p == NULL)
378338032Speter	{
378438032Speter		/* malformed response */
378538032Speter		goto noident;
378638032Speter	}
378738032Speter	while (isascii(*++p) && isspace(*p))
378838032Speter		continue;
378990792Sgshapiro	if (sm_strncasecmp(p, "userid", 6) != 0)
379038032Speter	{
379138032Speter		/* presumably an error string */
379238032Speter		goto noident;
379338032Speter	}
379438032Speter	p += 6;
3795363466Sgshapiro	while (SM_ISSPACE(*p))
379638032Speter		p++;
379738032Speter	if (*p++ != ':')
379838032Speter	{
379938032Speter		/* either useridxx or malformed response */
380038032Speter		goto noident;
380138032Speter	}
380238032Speter
380338032Speter	/* p now points to the OSTYPE field */
3804363466Sgshapiro	while (SM_ISSPACE(*p))
380538032Speter		p++;
380638032Speter	ostype = p;
380738032Speter	p = strchr(p, ':');
380838032Speter	if (p == NULL)
380938032Speter	{
381038032Speter		/* malformed response */
381138032Speter		goto noident;
381238032Speter	}
381338032Speter	else
381438032Speter	{
381538032Speter		char *charset;
381638032Speter
381738032Speter		*p = '\0';
381838032Speter		charset = strchr(ostype, ',');
381938032Speter		if (charset != NULL)
382038032Speter			*charset = '\0';
382138032Speter	}
382238032Speter
382338032Speter	/* 1413 says don't do this -- but it's broken otherwise */
382438032Speter	while (isascii(*++p) && isspace(*p))
382538032Speter		continue;
382638032Speter
382738032Speter	/* p now points to the authenticated name -- copy carefully */
382890792Sgshapiro	if (sm_strncasecmp(ostype, "other", 5) == 0 &&
382938032Speter	    (ostype[5] == ' ' || ostype[5] == '\0'))
383038032Speter	{
3831168515Sgshapiro		(void) sm_strlcpy(hbuf, "IDENT:", sizeof(hbuf));
3832110560Sgshapiro		cleanstrcpy(&hbuf[6], p, MAXAUTHINFO);
383338032Speter	}
383438032Speter	else
3835110560Sgshapiro		cleanstrcpy(hbuf, p, MAXAUTHINFO);
383690792Sgshapiro	len = strlen(hbuf);
3837168515Sgshapiro	(void) sm_strlcpyn(&hbuf[len], sizeof(hbuf) - len, 2, "@",
383890792Sgshapiro			   RealHostName == NULL ? "localhost" : RealHostName);
383938032Speter	goto postident;
384038032Speter
384138032Spetercloseident:
384238032Speter	(void) close(s);
384390792Sgshapiro	sm_clrevent(ev);
384438032Speter
384538032Speternoident:
384664562Sgshapiro	/* put back the original incoming port */
384764562Sgshapiro	switch (RealHostAddr.sa.sa_family)
384864562Sgshapiro	{
384990792Sgshapiro#if NETINET
385064562Sgshapiro	  case AF_INET:
385164562Sgshapiro		if (port > 0)
385264562Sgshapiro			RealHostAddr.sin.sin_port = port;
385364562Sgshapiro		break;
385490792Sgshapiro#endif /* NETINET */
385564562Sgshapiro
385690792Sgshapiro#if NETINET6
385764562Sgshapiro	  case AF_INET6:
385864562Sgshapiro		if (port > 0)
385964562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
386064562Sgshapiro		break;
386190792Sgshapiro#endif /* NETINET6 */
386264562Sgshapiro	}
386364562Sgshapiro
386438032Speter	if (RealHostName == NULL)
386538032Speter	{
386638032Speter		if (tTd(9, 1))
386790792Sgshapiro			sm_dprintf("getauthinfo: NULL\n");
386838032Speter		return NULL;
386938032Speter	}
3870168515Sgshapiro	(void) sm_strlcpy(hbuf, RealHostName, sizeof(hbuf));
387138032Speter
387238032Speterpostident:
387390792Sgshapiro#if IP_SRCROUTE
387490792Sgshapiro# ifndef GET_IPOPT_DST
387590792Sgshapiro#  define GET_IPOPT_DST(dst)	(dst)
3876363466Sgshapiro# endif
387738032Speter	/*
387838032Speter	**  Extract IP source routing information.
387938032Speter	**
388038032Speter	**	Format of output for a connection from site a through b
388138032Speter	**	through c to d:
388238032Speter	**		loose:      @site-c@site-b:site-a
388338032Speter	**		strict:	   !@site-c@site-b:site-a
388438032Speter	**
388538032Speter	**	o - pointer within ipopt_list structure.
388638032Speter	**	q - pointer within ls/ss rr route data
388738032Speter	**	p - pointer to hbuf
388838032Speter	*/
388938032Speter
389038032Speter	if (RealHostAddr.sa.sa_family == AF_INET)
389138032Speter	{
389238032Speter		SOCKOPT_LEN_T ipoptlen;
389338032Speter		int j;
389490792Sgshapiro		unsigned char *q;
389590792Sgshapiro		unsigned char *o;
389638032Speter		int l;
389764562Sgshapiro		struct IPOPTION ipopt;
389838032Speter
3899168515Sgshapiro		ipoptlen = sizeof(ipopt);
390038032Speter		if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
390138032Speter			       (char *) &ipopt, &ipoptlen) < 0)
390238032Speter			goto noipsr;
390338032Speter		if (ipoptlen == 0)
390438032Speter			goto noipsr;
390590792Sgshapiro		o = (unsigned char *) ipopt.IP_LIST;
390690792Sgshapiro		while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
390738032Speter		{
390838032Speter			switch (*o)
390938032Speter			{
391064562Sgshapiro			  case IPOPT_EOL:
391138032Speter				o = NULL;
391238032Speter				break;
391338032Speter
391438032Speter			  case IPOPT_NOP:
391538032Speter				o++;
391638032Speter				break;
391738032Speter
391838032Speter			  case IPOPT_SSRR:
391938032Speter			  case IPOPT_LSRR:
392038032Speter				/*
392138032Speter				**  Source routing.
392238032Speter				**	o[0] is the option type (loose/strict).
392338032Speter				**	o[1] is the length of this option,
392438032Speter				**		including option type and
392538032Speter				**		length.
392638032Speter				**	o[2] is the pointer into the route
392738032Speter				**		data.
392838032Speter				**	o[3] begins the route data.
392938032Speter				*/
393038032Speter
393138032Speter				p = &hbuf[strlen(hbuf)];
3932168515Sgshapiro				l = sizeof(hbuf) - (hbuf - p) - 6;
393390792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(hbuf, p),
393490792Sgshapiro					" [%s@%.*s",
393590792Sgshapiro					*o == IPOPT_SSRR ? "!" : "",
393690792Sgshapiro					l > 240 ? 120 : l / 2,
393790792Sgshapiro					inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
393838032Speter				i = strlen(p);
393938032Speter				p += i;
394038032Speter				l -= strlen(p);
394138032Speter
394238032Speter				j = o[1] / sizeof(struct in_addr) - 1;
394338032Speter
394438032Speter				/* q skips length and router pointer to data */
394538032Speter				q = &o[3];
394638032Speter				for ( ; j >= 0; j--)
394738032Speter				{
394864562Sgshapiro					struct in_addr addr;
394964562Sgshapiro
395038032Speter					memcpy(&addr, q, sizeof(addr));
395190792Sgshapiro					(void) sm_snprintf(p,
395290792Sgshapiro						SPACELEFT(hbuf, p),
395390792Sgshapiro						"%c%.*s",
395490792Sgshapiro						j != 0 ? '@' : ':',
395590792Sgshapiro						l > 240 ? 120 :
395690792Sgshapiro							j == 0 ? l : l / 2,
395790792Sgshapiro						inet_ntoa(addr));
395838032Speter					i = strlen(p);
395938032Speter					p += i;
396038032Speter					l -= i + 1;
396164562Sgshapiro					q += sizeof(struct in_addr);
396238032Speter				}
396338032Speter				o += o[1];
396438032Speter				break;
396538032Speter
396638032Speter			  default:
396738032Speter				/* Skip over option */
396838032Speter				o += o[1];
396938032Speter				break;
397038032Speter			}
397138032Speter		}
397290792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
397338032Speter		goto postipsr;
397438032Speter	}
397538032Speter
397638032Speternoipsr:
397790792Sgshapiro#endif /* IP_SRCROUTE */
397838032Speter	if (RealHostName != NULL && RealHostName[0] != '[')
397938032Speter	{
398038032Speter		p = &hbuf[strlen(hbuf)];
398190792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
398290792Sgshapiro				   anynet_ntoa(&RealHostAddr));
398338032Speter	}
398438032Speter	if (*may_be_forged)
398538032Speter	{
398638032Speter		p = &hbuf[strlen(hbuf)];
398790792Sgshapiro		(void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
398890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
398990792Sgshapiro			  macid("{client_resolve}"), "FORGED");
399038032Speter	}
399138032Speter
399290792Sgshapiro#if IP_SRCROUTE
399338032Speterpostipsr:
399490792Sgshapiro#endif /* IP_SRCROUTE */
399564562Sgshapiro
399664562Sgshapiro	/* put back the original incoming port */
399764562Sgshapiro	switch (RealHostAddr.sa.sa_family)
399864562Sgshapiro	{
399990792Sgshapiro#if NETINET
400064562Sgshapiro	  case AF_INET:
400164562Sgshapiro		if (port > 0)
400264562Sgshapiro			RealHostAddr.sin.sin_port = port;
400364562Sgshapiro		break;
400490792Sgshapiro#endif /* NETINET */
400564562Sgshapiro
400690792Sgshapiro#if NETINET6
400764562Sgshapiro	  case AF_INET6:
400864562Sgshapiro		if (port > 0)
400964562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
401064562Sgshapiro		break;
401190792Sgshapiro#endif /* NETINET6 */
401264562Sgshapiro	}
401364562Sgshapiro
401490792Sgshapiro	if (tTd(9, 1))
401590792Sgshapiro		sm_dprintf("getauthinfo: %s\n", hbuf);
401638032Speter	return hbuf;
401738032Speter}
401890792Sgshapiro/*
401938032Speter**  HOST_MAP_LOOKUP -- turn a hostname into canonical form
402038032Speter**
402138032Speter**	Parameters:
402238032Speter**		map -- a pointer to this map.
402338032Speter**		name -- the (presumably unqualified) hostname.
402438032Speter**		av -- unused -- for compatibility with other mapping
402538032Speter**			functions.
402638032Speter**		statp -- an exit status (out parameter) -- set to
402738032Speter**			EX_TEMPFAIL if the name server is unavailable.
402838032Speter**
402938032Speter**	Returns:
403038032Speter**		The mapping, if found.
403138032Speter**		NULL if no mapping found.
403238032Speter**
403338032Speter**	Side Effects:
403438032Speter**		Looks up the host specified in hbuf.  If it is not
403538032Speter**		the canonical name for that host, return the canonical
403638032Speter**		name (unless MF_MATCHONLY is set, which will cause the
403738032Speter**		status only to be returned).
403838032Speter*/
403938032Speter
404038032Speterchar *
404138032Speterhost_map_lookup(map, name, av, statp)
404238032Speter	MAP *map;
404338032Speter	char *name;
404438032Speter	char **av;
404538032Speter	int *statp;
404638032Speter{
404738032Speter	register struct hostent *hp;
404890792Sgshapiro#if NETINET
404938032Speter	struct in_addr in_addr;
4050363466Sgshapiro#endif
405190792Sgshapiro#if NETINET6
405264562Sgshapiro	struct in6_addr in6_addr;
4053363466Sgshapiro#endif
405464562Sgshapiro	char *cp, *ans = NULL;
405538032Speter	register STAB *s;
405690792Sgshapiro	time_t now;
405790792Sgshapiro#if NAMED_BIND
405890792Sgshapiro	time_t SM_NONVOLATILE retrans = 0;
405990792Sgshapiro	int SM_NONVOLATILE retry = 0;
4060363466Sgshapiro#endif
406138032Speter	char hbuf[MAXNAME + 1];
406238032Speter
406338032Speter	/*
406438032Speter	**  See if we have already looked up this name.  If so, just
406590792Sgshapiro	**  return it (unless expired).
406638032Speter	*/
406738032Speter
406890792Sgshapiro	now = curtime();
406938032Speter	s = stab(name, ST_NAMECANON, ST_ENTER);
407090792Sgshapiro	if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
407190792Sgshapiro	    s->s_namecanon.nc_exp >= now)
407238032Speter	{
407338032Speter		if (tTd(9, 1))
407490792Sgshapiro			sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
407590792Sgshapiro				    name,
407690792Sgshapiro				    s->s_namecanon.nc_cname == NULL
407738032Speter					? "NULL"
407838032Speter					: s->s_namecanon.nc_cname);
407938032Speter		errno = s->s_namecanon.nc_errno;
408073188Sgshapiro		SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
408138032Speter		*statp = s->s_namecanon.nc_stat;
408238032Speter		if (*statp == EX_TEMPFAIL)
408338032Speter		{
408438032Speter			CurEnv->e_status = "4.4.3";
408538032Speter			message("851 %s: Name server timeout",
408638032Speter				shortenstring(name, 33));
408738032Speter		}
408838032Speter		if (*statp != EX_OK)
408938032Speter			return NULL;
409038032Speter		if (s->s_namecanon.nc_cname == NULL)
409138032Speter		{
4092132943Sgshapiro			syserr("host_map_lookup(%s): bogus NULL cache entry, errno=%d, h_errno=%d",
409364562Sgshapiro			       name,
409464562Sgshapiro			       s->s_namecanon.nc_errno,
409564562Sgshapiro			       s->s_namecanon.nc_herrno);
409638032Speter			return NULL;
409738032Speter		}
4098363466Sgshapiro		if (bitset(NCF_SECURE, s->s_namecanon.nc_flags))
4099363466Sgshapiro			map->map_mflags |= MF_SECURE;
4100363466Sgshapiro		else
4101363466Sgshapiro			map->map_mflags &= ~MF_SECURE;
410238032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
410338032Speter			cp = map_rewrite(map, name, strlen(name), NULL);
410438032Speter		else
410538032Speter			cp = map_rewrite(map,
410638032Speter					 s->s_namecanon.nc_cname,
410738032Speter					 strlen(s->s_namecanon.nc_cname),
410838032Speter					 av);
410938032Speter		return cp;
411038032Speter	}
411138032Speter
411238032Speter	/*
411338032Speter	**  If we are running without a regular network connection (usually
411438032Speter	**  dial-on-demand) and we are just queueing, we want to avoid DNS
411538032Speter	**  lookups because those could try to connect to a server.
411638032Speter	*/
411738032Speter
411864562Sgshapiro	if (CurEnv->e_sendmode == SM_DEFER &&
411964562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
412038032Speter	{
412138032Speter		if (tTd(9, 1))
412290792Sgshapiro			sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
412338032Speter		*statp = EX_TEMPFAIL;
412438032Speter		return NULL;
412538032Speter	}
412638032Speter
412738032Speter	/*
412838032Speter	**  If first character is a bracket, then it is an address
412938032Speter	**  lookup.  Address is copied into a temporary buffer to
413038032Speter	**  strip the brackets and to preserve name if address is
413138032Speter	**  unknown.
413238032Speter	*/
413338032Speter
413464562Sgshapiro	if (tTd(9, 1))
413590792Sgshapiro		sm_dprintf("host_map_lookup(%s) => ", name);
413690792Sgshapiro#if NAMED_BIND
413790792Sgshapiro	if (map->map_timeout > 0)
413890792Sgshapiro	{
413990792Sgshapiro		retrans = _res.retrans;
414090792Sgshapiro		_res.retrans = map->map_timeout;
414190792Sgshapiro	}
414290792Sgshapiro	if (map->map_retry > 0)
414390792Sgshapiro	{
414490792Sgshapiro		retry = _res.retry;
414590792Sgshapiro		_res.retry = map->map_retry;
414690792Sgshapiro	}
414790792Sgshapiro#endif /* NAMED_BIND */
414890792Sgshapiro
414990792Sgshapiro	/* set default TTL */
415090792Sgshapiro	s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
415138032Speter	if (*name != '[')
415238032Speter	{
4153363466Sgshapiro		int ttl, r;
415490792Sgshapiro
4155168515Sgshapiro		(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
4156363466Sgshapiro
4157363466Sgshapiro		r = getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl);
4158363466Sgshapiro		if (r != HOST_NOTFOUND)
415990792Sgshapiro		{
416064562Sgshapiro			ans = hbuf;
416190792Sgshapiro			if (ttl > 0)
416290792Sgshapiro				s->s_namecanon.nc_exp = now + SM_MIN(ttl,
416390792Sgshapiro								SM_DEFAULT_TTL);
4164363466Sgshapiro
4165363466Sgshapiro			if (HOST_SECURE == r)
4166363466Sgshapiro			{
4167363466Sgshapiro				s->s_namecanon.nc_flags |= NCF_SECURE;
4168363466Sgshapiro				map->map_mflags |= MF_SECURE;
4169363466Sgshapiro			}
4170363466Sgshapiro			else
4171363466Sgshapiro			{
4172363466Sgshapiro				s->s_namecanon.nc_flags &= ~NCF_SECURE;
4173363466Sgshapiro				map->map_mflags &= ~MF_SECURE;
4174363466Sgshapiro			}
417590792Sgshapiro		}
417664562Sgshapiro	}
417764562Sgshapiro	else
417864562Sgshapiro	{
417964562Sgshapiro		if ((cp = strchr(name, ']')) == NULL)
418071345Sgshapiro		{
418171345Sgshapiro			if (tTd(9, 1))
418290792Sgshapiro				sm_dprintf("FAILED\n");
418364562Sgshapiro			return NULL;
418471345Sgshapiro		}
418564562Sgshapiro		*cp = '\0';
418664562Sgshapiro
418764562Sgshapiro		hp = NULL;
4188363466Sgshapiro
4189363466Sgshapiro		/* should this be considered secure? */
4190363466Sgshapiro		map->map_mflags &= ~MF_SECURE;
419190792Sgshapiro#if NETINET
419264562Sgshapiro		if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE)
419364562Sgshapiro			hp = sm_gethostbyaddr((char *)&in_addr,
419464562Sgshapiro					      INADDRSZ, AF_INET);
419590792Sgshapiro#endif /* NETINET */
419690792Sgshapiro#if NETINET6
419764562Sgshapiro		if (hp == NULL &&
419890792Sgshapiro		    anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
419964562Sgshapiro			hp = sm_gethostbyaddr((char *)&in6_addr,
420064562Sgshapiro					      IN6ADDRSZ, AF_INET6);
420190792Sgshapiro#endif /* NETINET6 */
420264562Sgshapiro		*cp = ']';
420364562Sgshapiro
420464562Sgshapiro		if (hp != NULL)
420538032Speter		{
420664562Sgshapiro			/* found a match -- copy out */
420790792Sgshapiro			ans = denlstring((char *) hp->h_name, true, true);
420890792Sgshapiro#if NETINET6
420990792Sgshapiro			if (ans == hp->h_name)
421090792Sgshapiro			{
421190792Sgshapiro				static char n[MAXNAME + 1];
421290792Sgshapiro
421390792Sgshapiro				/* hp->h_name is about to disappear */
4214168515Sgshapiro				(void) sm_strlcpy(n, ans, sizeof(n));
421590792Sgshapiro				ans = n;
421690792Sgshapiro			}
4217363466Sgshapiro			FREEHOSTENT(hp, NULL);
421890792Sgshapiro#endif /* NETINET6 */
421938032Speter		}
422064562Sgshapiro	}
422190792Sgshapiro#if NAMED_BIND
422290792Sgshapiro	if (map->map_timeout > 0)
422390792Sgshapiro		_res.retrans = retrans;
422490792Sgshapiro	if (map->map_retry > 0)
422590792Sgshapiro		_res.retry = retry;
422690792Sgshapiro#endif /* NAMED_BIND */
422738032Speter
422864562Sgshapiro	s->s_namecanon.nc_flags |= NCF_VALID;	/* will be soon */
422938032Speter
423064562Sgshapiro	/* Found an answer */
423164562Sgshapiro	if (ans != NULL)
423264562Sgshapiro	{
423364562Sgshapiro		s->s_namecanon.nc_stat = *statp = EX_OK;
423490792Sgshapiro		if (s->s_namecanon.nc_cname != NULL)
423590792Sgshapiro			sm_free(s->s_namecanon.nc_cname);
423690792Sgshapiro		s->s_namecanon.nc_cname = sm_strdup_x(ans);
423764562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
423864562Sgshapiro			cp = map_rewrite(map, name, strlen(name), NULL);
423964562Sgshapiro		else
424064562Sgshapiro			cp = map_rewrite(map, ans, strlen(ans), av);
424171345Sgshapiro		if (tTd(9, 1))
424290792Sgshapiro			sm_dprintf("FOUND %s\n", ans);
424364562Sgshapiro		return cp;
424438032Speter	}
424538032Speter
424664562Sgshapiro
424764562Sgshapiro	/* No match found */
424838032Speter	s->s_namecanon.nc_errno = errno;
424990792Sgshapiro#if NAMED_BIND
425038032Speter	s->s_namecanon.nc_herrno = h_errno;
425164562Sgshapiro	if (tTd(9, 1))
425290792Sgshapiro		sm_dprintf("FAIL (%d)\n", h_errno);
425364562Sgshapiro	switch (h_errno)
425438032Speter	{
425564562Sgshapiro	  case TRY_AGAIN:
425664562Sgshapiro		if (UseNameServer)
425764562Sgshapiro		{
425864562Sgshapiro			CurEnv->e_status = "4.4.3";
425964562Sgshapiro			message("851 %s: Name server timeout",
426064562Sgshapiro				shortenstring(name, 33));
426164562Sgshapiro		}
426264562Sgshapiro		*statp = EX_TEMPFAIL;
426364562Sgshapiro		break;
426464562Sgshapiro
426564562Sgshapiro	  case HOST_NOT_FOUND:
426664562Sgshapiro	  case NO_DATA:
426764562Sgshapiro		*statp = EX_NOHOST;
426864562Sgshapiro		break;
426964562Sgshapiro
427064562Sgshapiro	  case NO_RECOVERY:
427164562Sgshapiro		*statp = EX_SOFTWARE;
427264562Sgshapiro		break;
427364562Sgshapiro
427464562Sgshapiro	  default:
427564562Sgshapiro		*statp = EX_UNAVAILABLE;
427664562Sgshapiro		break;
427738032Speter	}
427890792Sgshapiro#else /* NAMED_BIND */
427964562Sgshapiro	if (tTd(9, 1))
428090792Sgshapiro		sm_dprintf("FAIL\n");
428164562Sgshapiro	*statp = EX_NOHOST;
428290792Sgshapiro#endif /* NAMED_BIND */
428364562Sgshapiro	s->s_namecanon.nc_stat = *statp;
428464562Sgshapiro	return NULL;
428538032Speter}
428638032Speter/*
428790792Sgshapiro**  HOST_MAP_INIT -- initialize host class structures
428838032Speter**
428938032Speter**	Parameters:
429090792Sgshapiro**		map -- a pointer to this map.
429190792Sgshapiro**		args -- argument string.
429238032Speter**
429338032Speter**	Returns:
429490792Sgshapiro**		true.
429538032Speter*/
429638032Speter
429738032Speterbool
429838032Speterhost_map_init(map, args)
429938032Speter	MAP *map;
430038032Speter	char *args;
430138032Speter{
430238032Speter	register char *p = args;
430338032Speter
430438032Speter	for (;;)
430538032Speter	{
4306363466Sgshapiro		while (SM_ISSPACE(*p))
430738032Speter			p++;
430838032Speter		if (*p != '-')
430938032Speter			break;
431038032Speter		switch (*++p)
431138032Speter		{
431238032Speter		  case 'a':
431338032Speter			map->map_app = ++p;
431438032Speter			break;
431538032Speter
431638032Speter		  case 'T':
431738032Speter			map->map_tapp = ++p;
431838032Speter			break;
431938032Speter
432038032Speter		  case 'm':
432138032Speter			map->map_mflags |= MF_MATCHONLY;
432238032Speter			break;
432338032Speter
432438032Speter		  case 't':
432538032Speter			map->map_mflags |= MF_NODEFER;
432638032Speter			break;
432764562Sgshapiro
432864562Sgshapiro		  case 'S':	/* only for consistency */
432964562Sgshapiro			map->map_spacesub = *++p;
433064562Sgshapiro			break;
433164562Sgshapiro
433264562Sgshapiro		  case 'D':
433364562Sgshapiro			map->map_mflags |= MF_DEFER;
433464562Sgshapiro			break;
433590792Sgshapiro
433690792Sgshapiro		  case 'd':
433790792Sgshapiro			{
433890792Sgshapiro				char *h;
433990792Sgshapiro
434090792Sgshapiro				while (isascii(*++p) && isspace(*p))
434190792Sgshapiro					continue;
434290792Sgshapiro				h = strchr(p, ' ');
434390792Sgshapiro				if (h != NULL)
434490792Sgshapiro					*h = '\0';
434590792Sgshapiro				map->map_timeout = convtime(p, 's');
434690792Sgshapiro				if (h != NULL)
434790792Sgshapiro					*h = ' ';
434890792Sgshapiro			}
434990792Sgshapiro			break;
435090792Sgshapiro
435190792Sgshapiro		  case 'r':
435290792Sgshapiro			while (isascii(*++p) && isspace(*p))
435390792Sgshapiro				continue;
435490792Sgshapiro			map->map_retry = atoi(p);
435590792Sgshapiro			break;
435638032Speter		}
4357363466Sgshapiro		while (*p != '\0' && !(SM_ISSPACE(*p)))
435838032Speter			p++;
435938032Speter		if (*p != '\0')
436038032Speter			*p++ = '\0';
436138032Speter	}
436238032Speter	if (map->map_app != NULL)
436338032Speter		map->map_app = newstr(map->map_app);
436438032Speter	if (map->map_tapp != NULL)
436538032Speter		map->map_tapp = newstr(map->map_tapp);
436690792Sgshapiro	return true;
436738032Speter}
436890792Sgshapiro
436964562Sgshapiro#if NETINET6
437064562Sgshapiro/*
437164562Sgshapiro**  ANYNET_NTOP -- convert an IPv6 network address to printable form.
437264562Sgshapiro**
437364562Sgshapiro**	Parameters:
437464562Sgshapiro**		s6a -- a pointer to an in6_addr structure.
437564562Sgshapiro**		dst -- buffer to store result in
437664562Sgshapiro**		dst_len -- size of dst buffer
437764562Sgshapiro**
437864562Sgshapiro**	Returns:
437964562Sgshapiro**		A printable version of that structure.
438064562Sgshapiro*/
438190792Sgshapiro
438264562Sgshapirochar *
438364562Sgshapiroanynet_ntop(s6a, dst, dst_len)
438464562Sgshapiro	struct in6_addr *s6a;
438564562Sgshapiro	char *dst;
438664562Sgshapiro	size_t dst_len;
438764562Sgshapiro{
438864562Sgshapiro	register char *ap;
438964562Sgshapiro
439064562Sgshapiro	if (IN6_IS_ADDR_V4MAPPED(s6a))
439164562Sgshapiro		ap = (char *) inet_ntop(AF_INET,
439264562Sgshapiro					&s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
439364562Sgshapiro					dst, dst_len);
439464562Sgshapiro	else
439590792Sgshapiro	{
439690792Sgshapiro		char *d;
439790792Sgshapiro		size_t sz;
439890792Sgshapiro
439990792Sgshapiro		/* Save pointer to beginning of string */
440090792Sgshapiro		d = dst;
440190792Sgshapiro
440290792Sgshapiro		/* Add IPv6: protocol tag */
440390792Sgshapiro		sz = sm_strlcpy(dst, "IPv6:", dst_len);
440490792Sgshapiro		if (sz >= dst_len)
440590792Sgshapiro			return NULL;
440690792Sgshapiro		dst += sz;
440790792Sgshapiro		dst_len -= sz;
4408285229Sgshapiro		if (UseCompressedIPv6Addresses)
4409285229Sgshapiro			ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
4410285229Sgshapiro		else
4411285229Sgshapiro			ap = sm_inet6_ntop(s6a, dst, dst_len);
441290792Sgshapiro		/* Restore pointer to beginning of string */
441390792Sgshapiro		if (ap != NULL)
441490792Sgshapiro			ap = d;
441590792Sgshapiro	}
441664562Sgshapiro	return ap;
441764562Sgshapiro}
441890792Sgshapiro
441990792Sgshapiro/*
442090792Sgshapiro**  ANYNET_PTON -- convert printed form to network address.
442190792Sgshapiro**
442290792Sgshapiro**	Wrapper for inet_pton() which handles IPv6: labels.
442390792Sgshapiro**
442490792Sgshapiro**	Parameters:
442590792Sgshapiro**		family -- address family
442690792Sgshapiro**		src -- string
442790792Sgshapiro**		dst -- destination address structure
442890792Sgshapiro**
442990792Sgshapiro**	Returns:
443090792Sgshapiro**		1 if the address was valid
4431363466Sgshapiro**		0 if the address wasn't parsable
443290792Sgshapiro**		-1 if error
443390792Sgshapiro*/
443490792Sgshapiro
443590792Sgshapiroint
443690792Sgshapiroanynet_pton(family, src, dst)
443790792Sgshapiro	int family;
443890792Sgshapiro	const char *src;
443990792Sgshapiro	void *dst;
444090792Sgshapiro{
444190792Sgshapiro	if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
444290792Sgshapiro		src += 5;
444390792Sgshapiro	return inet_pton(family, src, dst);
444490792Sgshapiro}
444564562Sgshapiro#endif /* NETINET6 */
444690792Sgshapiro/*
444738032Speter**  ANYNET_NTOA -- convert a network address to printable form.
444838032Speter**
444938032Speter**	Parameters:
445038032Speter**		sap -- a pointer to a sockaddr structure.
445138032Speter**
445238032Speter**	Returns:
445338032Speter**		A printable version of that sockaddr.
445438032Speter*/
445538032Speter
445638032Speter#ifdef USE_SOCK_STREAM
445738032Speter
445864562Sgshapiro# if NETLINK
445964562Sgshapiro#  include <net/if_dl.h>
4460363466Sgshapiro# endif
446138032Speter
446238032Speterchar *
446338032Speteranynet_ntoa(sap)
446438032Speter	register SOCKADDR *sap;
446538032Speter{
446638032Speter	register char *bp;
446738032Speter	register char *ap;
446838032Speter	int l;
446938032Speter	static char buf[100];
447038032Speter
447138032Speter	/* check for null/zero family */
447238032Speter	if (sap == NULL)
447338032Speter		return "NULLADDR";
447438032Speter	if (sap->sa.sa_family == 0)
447538032Speter		return "0";
447638032Speter
447738032Speter	switch (sap->sa.sa_family)
447838032Speter	{
447964562Sgshapiro# if NETUNIX
448038032Speter	  case AF_UNIX:
448164562Sgshapiro		if (sap->sunix.sun_path[0] != '\0')
4482168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "[UNIX: %.64s]",
448390792Sgshapiro					   sap->sunix.sun_path);
448464562Sgshapiro		else
4485168515Sgshapiro			(void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof(buf));
448638032Speter		return buf;
448764562Sgshapiro# endif /* NETUNIX */
448838032Speter
448964562Sgshapiro# if NETINET
449038032Speter	  case AF_INET:
449164562Sgshapiro		return (char *) inet_ntoa(sap->sin.sin_addr);
4492363466Sgshapiro# endif
449338032Speter
449464562Sgshapiro# if NETINET6
449564562Sgshapiro	  case AF_INET6:
4496168515Sgshapiro		ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof(buf));
449764562Sgshapiro		if (ap != NULL)
449864562Sgshapiro			return ap;
449964562Sgshapiro		break;
450064562Sgshapiro# endif /* NETINET6 */
450164562Sgshapiro
450264562Sgshapiro# if NETLINK
450338032Speter	  case AF_LINK:
4504168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[LINK: %s]",
450590792Sgshapiro				   link_ntoa((struct sockaddr_dl *) &sap->sa));
450638032Speter		return buf;
450764562Sgshapiro# endif /* NETLINK */
450838032Speter	  default:
450938032Speter		/* this case is needed when nothing is #defined */
451038032Speter		/* in order to keep the switch syntactically correct */
451138032Speter		break;
451238032Speter	}
451338032Speter
451438032Speter	/* unknown family -- just dump bytes */
4515168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family);
451638032Speter	bp = &buf[strlen(buf)];
451738032Speter	ap = sap->sa.sa_data;
4518168515Sgshapiro	for (l = sizeof(sap->sa.sa_data); --l >= 0; )
451938032Speter	{
452090792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
452190792Sgshapiro				   *ap++ & 0377);
452238032Speter		bp += 3;
452338032Speter	}
452438032Speter	*--bp = '\0';
452538032Speter	return buf;
452638032Speter}
452790792Sgshapiro/*
452838032Speter**  HOSTNAMEBYANYADDR -- return name of host based on address
452938032Speter**
453038032Speter**	Parameters:
453138032Speter**		sap -- SOCKADDR pointer
453238032Speter**
453338032Speter**	Returns:
453438032Speter**		text representation of host name.
453538032Speter**
453638032Speter**	Side Effects:
453738032Speter**		none.
453838032Speter*/
453938032Speter
454038032Speterchar *
454138032Speterhostnamebyanyaddr(sap)
454238032Speter	register SOCKADDR *sap;
454338032Speter{
454438032Speter	register struct hostent *hp;
454564562Sgshapiro# if NAMED_BIND
454638032Speter	int saveretry;
4547363466Sgshapiro# endif
454864562Sgshapiro# if NETINET6
454964562Sgshapiro	struct in6_addr in6_addr;
455064562Sgshapiro# endif /* NETINET6 */
455138032Speter
455264562Sgshapiro# if NAMED_BIND
455338032Speter	/* shorten name server timeout to avoid higher level timeouts */
455438032Speter	saveretry = _res.retry;
455564562Sgshapiro	if (_res.retry * _res.retrans > 20)
455664562Sgshapiro		_res.retry = 20 / _res.retrans;
4557244833Sgshapiro	if (_res.retry == 0)
4558244833Sgshapiro		_res.retry = 1;
455964562Sgshapiro# endif /* NAMED_BIND */
456038032Speter
456138032Speter	switch (sap->sa.sa_family)
456238032Speter	{
456364562Sgshapiro# if NETINET
456438032Speter	  case AF_INET:
456538032Speter		hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
456690792Sgshapiro				      INADDRSZ, AF_INET);
456738032Speter		break;
456864562Sgshapiro# endif /* NETINET */
456938032Speter
457064562Sgshapiro# if NETINET6
457164562Sgshapiro	  case AF_INET6:
457264562Sgshapiro		hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
457390792Sgshapiro				      IN6ADDRSZ, AF_INET6);
457464562Sgshapiro		break;
457564562Sgshapiro# endif /* NETINET6 */
457664562Sgshapiro
457764562Sgshapiro# if NETISO
457838032Speter	  case AF_ISO:
457938032Speter		hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
4580168515Sgshapiro				      sizeof(sap->siso.siso_addr), AF_ISO);
458138032Speter		break;
458264562Sgshapiro# endif /* NETISO */
458338032Speter
458464562Sgshapiro# if NETUNIX
458538032Speter	  case AF_UNIX:
458638032Speter		hp = NULL;
458738032Speter		break;
458864562Sgshapiro# endif /* NETUNIX */
458938032Speter
459038032Speter	  default:
4591168515Sgshapiro		hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof(sap->sa.sa_data),
459290792Sgshapiro				      sap->sa.sa_family);
459338032Speter		break;
459438032Speter	}
459538032Speter
459664562Sgshapiro# if NAMED_BIND
459738032Speter	_res.retry = saveretry;
4598363466Sgshapiro# endif
459938032Speter
460064562Sgshapiro# if NETINET || NETINET6
460164562Sgshapiro	if (hp != NULL && hp->h_name[0] != '['
460264562Sgshapiro#  if NETINET6
460364562Sgshapiro	    && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1
460464562Sgshapiro#  endif /* NETINET6 */
460564562Sgshapiro#  if NETINET
460664562Sgshapiro	    && inet_addr(hp->h_name) == INADDR_NONE
4607363466Sgshapiro#  endif
460864562Sgshapiro	    )
460971345Sgshapiro	{
461071345Sgshapiro		char *name;
461171345Sgshapiro
461290792Sgshapiro		name = denlstring((char *) hp->h_name, true, true);
461390792Sgshapiro#  if NETINET6
461471345Sgshapiro		if (name == hp->h_name)
461571345Sgshapiro		{
461671345Sgshapiro			static char n[MAXNAME + 1];
461771345Sgshapiro
461871345Sgshapiro			/* Copy the string, hp->h_name is about to disappear */
4619168515Sgshapiro			(void) sm_strlcpy(n, name, sizeof(n));
462071345Sgshapiro			name = n;
462171345Sgshapiro		}
4622363466Sgshapiro		FREEHOSTENT(hp, NULL);
462390792Sgshapiro#  endif /* NETINET6 */
462471345Sgshapiro		return name;
462571345Sgshapiro	}
462664562Sgshapiro# endif /* NETINET || NETINET6 */
462771345Sgshapiro
4628363466Sgshapiro	FREEHOSTENT(hp, NULL);
462971345Sgshapiro
463064562Sgshapiro# if NETUNIX
463164562Sgshapiro	if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
463238032Speter		return "localhost";
4633363466Sgshapiro# endif
463438032Speter	{
463538032Speter		static char buf[203];
463638032Speter
4637168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[%.200s]",
463890792Sgshapiro				   anynet_ntoa(sap));
463938032Speter		return buf;
464038032Speter	}
464138032Speter}
464264562Sgshapiro#endif /* USE_SOCK_STREAM */
4643