daemon.c revision 203004
138032Speter/*
2203004Sgshapiro * Copyright (c) 1998-2007, 2009 Sendmail, 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
17203004SgshapiroSM_RCSID("@(#)$Id: daemon.c,v 8.683 2009/12/18 01:12:40 ca Exp $")
1864562Sgshapiro
1938032Speter#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
2038032Speter# define USE_SOCK_STREAM	1
2164562Sgshapiro#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
2238032Speter
2390792Sgshapiro#if defined(USE_SOCK_STREAM)
2464562Sgshapiro# if NETINET || NETINET6
2564562Sgshapiro#  include <arpa/inet.h>
2664562Sgshapiro# endif /* NETINET || NETINET6 */
2738032Speter# if NAMED_BIND
2838032Speter#  ifndef NO_DATA
2938032Speter#   define NO_DATA	NO_ADDRESS
3064562Sgshapiro#  endif /* ! NO_DATA */
3164562Sgshapiro# endif /* NAMED_BIND */
3290792Sgshapiro#endif /* defined(USE_SOCK_STREAM) */
3338032Speter
3490792Sgshapiro#if STARTTLS
3590792Sgshapiro#  include <openssl/rand.h>
3690792Sgshapiro#endif /* STARTTLS */
3738032Speter
38157001Sgshapiro#include <sm/time.h>
3966494Sgshapiro
4090792Sgshapiro#if IP_SRCROUTE && NETINET
4190792Sgshapiro# include <netinet/in_systm.h>
4290792Sgshapiro# include <netinet/ip.h>
4390792Sgshapiro# if HAS_IN_H
4490792Sgshapiro#  include <netinet/in.h>
4590792Sgshapiro#  ifndef IPOPTION
4690792Sgshapiro#   define IPOPTION	ip_opts
4790792Sgshapiro#   define IP_LIST	ip_opts
4890792Sgshapiro#   define IP_DST	ip_dst
4990792Sgshapiro#  endif /* ! IPOPTION */
5090792Sgshapiro# else /* HAS_IN_H */
5190792Sgshapiro#  include <netinet/ip_var.h>
5290792Sgshapiro#  ifndef IPOPTION
5390792Sgshapiro#   define IPOPTION	ipoption
5490792Sgshapiro#   define IP_LIST	ipopt_list
5590792Sgshapiro#   define IP_DST	ipopt_dst
5690792Sgshapiro#  endif /* ! IPOPTION */
5790792Sgshapiro# endif /* HAS_IN_H */
5890792Sgshapiro#endif /* IP_SRCROUTE && NETINET */
5938032Speter
6090792Sgshapiro#include <sm/fdset.h>
6138032Speter
62168515Sgshapiro#define DAEMON_C 1
63168515Sgshapiro#include <daemon.h>
6464562Sgshapiro
65141858Sgshapirostatic void		connecttimeout __P((int));
6690792Sgshapirostatic int		opendaemonsocket __P((DAEMON_T *, bool));
6790792Sgshapirostatic unsigned short	setupdaemon __P((SOCKADDR *));
6890792Sgshapirostatic void		getrequests_checkdiskspace __P((ENVELOPE *e));
69141858Sgshapirostatic void		setsockaddroptions __P((char *, DAEMON_T *));
70141858Sgshapirostatic void		printdaemonflags __P((DAEMON_T *));
71141858Sgshapirostatic int		addr_family __P((char *));
72141858Sgshapirostatic int		addrcmp __P((struct hostent *, char *, SOCKADDR *));
73141858Sgshapirostatic void		authtimeout __P((int));
7464562Sgshapiro
7538032Speter/*
7638032Speter**  DAEMON.C -- routines to use when running as a daemon.
7738032Speter**
7838032Speter**	This entire file is highly dependent on the 4.2 BSD
7938032Speter**	interprocess communication primitives.  No attempt has
8038032Speter**	been made to make this file portable to Version 7,
8138032Speter**	Version 6, MPX files, etc.  If you should try such a
8238032Speter**	thing yourself, I recommend chucking the entire file
8338032Speter**	and starting from scratch.  Basic semantics are:
8438032Speter**
8538032Speter**	getrequests(e)
8638032Speter**		Opens a port and initiates a connection.
8738032Speter**		Returns in a child.  Must set InChannel and
8838032Speter**		OutChannel appropriately.
8938032Speter**	clrdaemon()
9038032Speter**		Close any open files associated with getting
9138032Speter**		the connection; this is used when running the queue,
9238032Speter**		etc., to avoid having extra file descriptors during
9338032Speter**		the queue run and to avoid confusing the network
9438032Speter**		code (if it cares).
9590792Sgshapiro**	makeconnection(host, port, mci, e, enough)
9638032Speter**		Make a connection to the named host on the given
9790792Sgshapiro**		port. Returns zero on success, else an exit status
9890792Sgshapiro**		describing the error.
9938032Speter**	host_map_lookup(map, hbuf, avp, pstat)
10038032Speter**		Convert the entry in hbuf into a canonical form.
10138032Speter*/
10264562Sgshapiro
10390792Sgshapirostatic int	NDaemons = 0;			/* actual number of daemons */
10464562Sgshapiro
10590792Sgshapirostatic time_t	NextDiskSpaceCheck = 0;
10664562Sgshapiro
10790792Sgshapiro/*
10838032Speter**  GETREQUESTS -- open mail IPC port and get requests.
10938032Speter**
11038032Speter**	Parameters:
11138032Speter**		e -- the current envelope.
11238032Speter**
11338032Speter**	Returns:
11464562Sgshapiro**		pointer to flags.
11538032Speter**
11638032Speter**	Side Effects:
11738032Speter**		Waits until some interesting activity occurs.  When
11838032Speter**		it does, a child is created to process it, and the
11938032Speter**		parent waits for completion.  Return from this
12038032Speter**		routine is always in the child.  The file pointers
12138032Speter**		"InChannel" and "OutChannel" should be set to point
12238032Speter**		to the communication channel.
12390792Sgshapiro**		May restart persistent queue runners if they have ended
12490792Sgshapiro**		for some reason.
12538032Speter*/
12638032Speter
12764562SgshapiroBITMAP256 *
12838032Spetergetrequests(e)
12938032Speter	ENVELOPE *e;
13038032Speter{
13138032Speter	int t;
13264562Sgshapiro	int idx, curdaemon = -1;
13364562Sgshapiro	int i, olddaemon = 0;
13490792Sgshapiro#if XDEBUG
13538032Speter	bool j_has_dot;
13690792Sgshapiro#endif /* XDEBUG */
13742575Speter	char status[MAXLINE];
13864562Sgshapiro	SOCKADDR sa;
139168515Sgshapiro	SOCKADDR_LEN_T len = sizeof(sa);
14094334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
14194334Sgshapiro	time_t lastrun;
14294334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
14364562Sgshapiro# if NETUNIX
14442575Speter	extern int ControlSocket;
14564562Sgshapiro# endif /* NETUNIX */
14664562Sgshapiro	extern ENVELOPE BlankEnvelope;
14738032Speter
14838032Speter
149125820Sgshapiro	/* initialize data for function that generates queue ids */
150125820Sgshapiro	init_qid_alg();
15190792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
15238032Speter	{
15364562Sgshapiro		Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
15490792Sgshapiro		Daemons[idx].d_firsttime = true;
15564562Sgshapiro		Daemons[idx].d_refuse_connections_until = (time_t) 0;
15638032Speter	}
15771345Sgshapiro
15838032Speter	/*
15938032Speter	**  Try to actually open the connection.
16038032Speter	*/
16138032Speter
16238032Speter	if (tTd(15, 1))
16364562Sgshapiro	{
16490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
16571345Sgshapiro		{
16690792Sgshapiro			sm_dprintf("getrequests: daemon %s: port %d\n",
16790792Sgshapiro				   Daemons[idx].d_name,
16890792Sgshapiro				   ntohs(Daemons[idx].d_port));
16971345Sgshapiro		}
17064562Sgshapiro	}
17138032Speter
17238032Speter	/* get a socket for the SMTP connection */
17390792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
17490792Sgshapiro		Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
17538032Speter
17642575Speter	if (opencontrolsocket() < 0)
17742575Speter		sm_syslog(LOG_WARNING, NOQID,
17843730Speter			  "daemon could not open control socket %s: %s",
17990792Sgshapiro			  ControlSocketName, sm_errstring(errno));
18042575Speter
18190792Sgshapiro	/* If there are any queue runners released reapchild() co-ord's */
18290792Sgshapiro	(void) sm_signal(SIGCHLD, reapchild);
18338032Speter
18490792Sgshapiro	/* write the pid to file, command line args to syslog */
18564562Sgshapiro	log_sendmail_pid(e);
18638032Speter
18790792Sgshapiro#if XDEBUG
18838032Speter	{
18938032Speter		char jbuf[MAXHOSTNAMELEN];
19038032Speter
191168515Sgshapiro		expand("\201j", jbuf, sizeof(jbuf), e);
19238032Speter		j_has_dot = strchr(jbuf, '.') != NULL;
19338032Speter	}
19490792Sgshapiro#endif /* XDEBUG */
19538032Speter
19642575Speter	/* Add parent process as first item */
197132943Sgshapiro	proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1, NULL);
19842575Speter
19938032Speter	if (tTd(15, 1))
20064562Sgshapiro	{
20190792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
202203004Sgshapiro			sm_dprintf("getrequests: daemon %s: socket %d\n",
20364562Sgshapiro				Daemons[idx].d_name,
20464562Sgshapiro				Daemons[idx].d_socket);
20564562Sgshapiro	}
20638032Speter
20738032Speter	for (;;)
20838032Speter	{
20938032Speter		register pid_t pid;
21038032Speter		auto SOCKADDR_LEN_T lotherend;
21190792Sgshapiro		bool timedout = false;
21290792Sgshapiro		bool control = false;
21364562Sgshapiro		int save_errno;
21438032Speter		int pipefd[2];
21590792Sgshapiro		time_t now;
21690792Sgshapiro#if STARTTLS
21766494Sgshapiro		long seed;
21890792Sgshapiro#endif /* STARTTLS */
21938032Speter
22038032Speter		/* see if we are rejecting connections */
22190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
222120256Sgshapiro		CHECK_RESTART;
22364562Sgshapiro
22490792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
22571345Sgshapiro		{
22690792Sgshapiro			/*
22790792Sgshapiro			**  XXX do this call outside the loop?
22890792Sgshapiro			**	no: refuse_connections may sleep().
22990792Sgshapiro			*/
23071345Sgshapiro
23190792Sgshapiro			now = curtime();
23290792Sgshapiro			if (now < Daemons[idx].d_refuse_connections_until)
23364562Sgshapiro				continue;
23490792Sgshapiro			if (bitnset(D_DISABLE, Daemons[idx].d_flags))
23590792Sgshapiro				continue;
236168515Sgshapiro			if (refuseconnections(e, idx, curdaemon == idx))
23738032Speter			{
23864562Sgshapiro				if (Daemons[idx].d_socket >= 0)
23942575Speter				{
24071345Sgshapiro					/* close socket so peer fails quickly */
24171345Sgshapiro					(void) close(Daemons[idx].d_socket);
24271345Sgshapiro					Daemons[idx].d_socket = -1;
24342575Speter				}
24442575Speter
24542575Speter				/* refuse connections for next 15 seconds */
24690792Sgshapiro				Daemons[idx].d_refuse_connections_until = now + 15;
24738032Speter			}
24864562Sgshapiro			else if (Daemons[idx].d_socket < 0 ||
24964562Sgshapiro				 Daemons[idx].d_firsttime)
25042575Speter			{
25190792Sgshapiro				if (!Daemons[idx].d_firsttime && LogLevel > 8)
25271345Sgshapiro					sm_syslog(LOG_INFO, NOQID,
25371345Sgshapiro						"accepting connections again for daemon %s",
25471345Sgshapiro						Daemons[idx].d_name);
25564562Sgshapiro
25671345Sgshapiro				/* arrange to (re)open the socket if needed */
25790792Sgshapiro				(void) opendaemonsocket(&Daemons[idx], false);
25890792Sgshapiro				Daemons[idx].d_firsttime = false;
25942575Speter			}
26038032Speter		}
26138032Speter
26277349Sgshapiro		/* May have been sleeping above, check again */
263120256Sgshapiro		CHECK_RESTART;
264132943Sgshapiro
26590792Sgshapiro		getrequests_checkdiskspace(e);
26671345Sgshapiro
26790792Sgshapiro#if XDEBUG
26838032Speter		/* check for disaster */
26938032Speter		{
27038032Speter			char jbuf[MAXHOSTNAMELEN];
27138032Speter
272168515Sgshapiro			expand("\201j", jbuf, sizeof(jbuf), e);
27338032Speter			if (!wordinclass(jbuf, 'w'))
27438032Speter			{
27538032Speter				dumpstate("daemon lost $j");
27638032Speter				sm_syslog(LOG_ALERT, NOQID,
27764562Sgshapiro					  "daemon process doesn't have $j in $=w; see syslog");
27838032Speter				abort();
27938032Speter			}
28038032Speter			else if (j_has_dot && strchr(jbuf, '.') == NULL)
28138032Speter			{
28238032Speter				dumpstate("daemon $j lost dot");
28338032Speter				sm_syslog(LOG_ALERT, NOQID,
28464562Sgshapiro					  "daemon process $j lost dot; see syslog");
28538032Speter				abort();
28638032Speter			}
28738032Speter		}
28890792Sgshapiro#endif /* XDEBUG */
28938032Speter
29090792Sgshapiro#if 0
29138032Speter		/*
29238032Speter		**  Andrew Sun <asun@ieps-sun.ml.com> claims that this will
29338032Speter		**  fix the SVr4 problem.  But it seems to have gone away,
29438032Speter		**  so is it worth doing this?
29538032Speter		*/
29638032Speter
29742575Speter		if (DaemonSocket >= 0 &&
29890792Sgshapiro		    SetNonBlocking(DaemonSocket, false) < 0)
29938032Speter			log an error here;
30090792Sgshapiro#endif /* 0 */
30190792Sgshapiro		(void) sm_releasesignal(SIGALRM);
30264562Sgshapiro
30338032Speter		for (;;)
30438032Speter		{
30590792Sgshapiro			bool setproc = false;
30642575Speter			int highest = -1;
30738032Speter			fd_set readfds;
30838032Speter			struct timeval timeout;
30938032Speter
310120256Sgshapiro			CHECK_RESTART;
31138032Speter			FD_ZERO(&readfds);
31290792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
31342575Speter			{
31464562Sgshapiro				/* wait for a connection */
31564562Sgshapiro				if (Daemons[idx].d_socket >= 0)
31664562Sgshapiro				{
31771345Sgshapiro					if (!setproc &&
31871345Sgshapiro					    !bitnset(D_ETRNONLY,
31971345Sgshapiro						     Daemons[idx].d_flags))
32064562Sgshapiro					{
32190792Sgshapiro						sm_setproctitle(true, e,
32264562Sgshapiro								"accepting connections");
32390792Sgshapiro						setproc = true;
32464562Sgshapiro					}
32564562Sgshapiro					if (Daemons[idx].d_socket > highest)
32664562Sgshapiro						highest = Daemons[idx].d_socket;
32790792Sgshapiro					SM_FD_SET(Daemons[idx].d_socket,
32890792Sgshapiro						  &readfds);
32964562Sgshapiro				}
33042575Speter			}
33164562Sgshapiro
33290792Sgshapiro#if NETUNIX
33342575Speter			if (ControlSocket >= 0)
33442575Speter			{
33542575Speter				if (ControlSocket > highest)
33642575Speter					highest = ControlSocket;
33790792Sgshapiro				SM_FD_SET(ControlSocket, &readfds);
33842575Speter			}
33990792Sgshapiro#endif /* NETUNIX */
34064562Sgshapiro
34177349Sgshapiro			timeout.tv_sec = 5;
34238032Speter			timeout.tv_usec = 0;
34338032Speter
34442575Speter			t = select(highest + 1, FDSET_CAST &readfds,
34564562Sgshapiro				   NULL, NULL, &timeout);
34642575Speter
34777349Sgshapiro			/* Did someone signal while waiting? */
348120256Sgshapiro			CHECK_RESTART;
34971345Sgshapiro
35090792Sgshapiro			curdaemon = -1;
35190792Sgshapiro			if (doqueuerun())
35294334Sgshapiro			{
35390792Sgshapiro				(void) runqueue(true, false, false, false);
35494334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
35594334Sgshapiro				lastrun = now;
35694334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
35794334Sgshapiro			}
35894334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
359157001Sgshapiro			else if (CheckQueueRunners > 0 && QueueIntvl > 0 &&
360157001Sgshapiro				 lastrun + QueueIntvl + CheckQueueRunners < now)
36194334Sgshapiro			{
36271345Sgshapiro
36394334Sgshapiro				/*
36494334Sgshapiro				**  set lastrun unconditionally to avoid
36594334Sgshapiro				**  calling checkqueuerunner() all the time.
36694334Sgshapiro				**  That's also why we currently ignore the
36794334Sgshapiro				**  result of the function call.
36894334Sgshapiro				*/
36994334Sgshapiro
37094334Sgshapiro				(void) checkqueuerunner();
37194334Sgshapiro				lastrun = now;
37294334Sgshapiro			}
37394334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
37494334Sgshapiro
37542575Speter			if (t <= 0)
37642575Speter			{
37790792Sgshapiro				timedout = true;
37842575Speter				break;
37942575Speter			}
38038032Speter
38190792Sgshapiro			control = false;
38238032Speter			errno = 0;
38364562Sgshapiro
38464562Sgshapiro			/* look "round-robin" for an active socket */
38590792Sgshapiro			if ((idx = olddaemon + 1) >= NDaemons)
38664562Sgshapiro				idx = 0;
38790792Sgshapiro			for (i = 0; i < NDaemons; i++)
38842575Speter			{
38964562Sgshapiro				if (Daemons[idx].d_socket >= 0 &&
39090792Sgshapiro				    SM_FD_ISSET(Daemons[idx].d_socket,
39190792Sgshapiro						&readfds))
39264562Sgshapiro				{
39364562Sgshapiro					lotherend = Daemons[idx].d_socksize;
39473188Sgshapiro					memset(&RealHostAddr, '\0',
395168515Sgshapiro					       sizeof(RealHostAddr));
39664562Sgshapiro					t = accept(Daemons[idx].d_socket,
39764562Sgshapiro						   (struct sockaddr *)&RealHostAddr,
39864562Sgshapiro						   &lotherend);
39973188Sgshapiro
40073188Sgshapiro					/*
40173188Sgshapiro					**  If remote side closes before
40273188Sgshapiro					**  accept() finishes, sockaddr
40373188Sgshapiro					**  might not be fully filled in.
40473188Sgshapiro					*/
40573188Sgshapiro
40673188Sgshapiro					if (t >= 0 &&
40773188Sgshapiro					    (lotherend == 0 ||
40873188Sgshapiro# ifdef BSD4_4_SOCKADDR
40973188Sgshapiro					     RealHostAddr.sa.sa_len == 0 ||
41073188Sgshapiro# endif /* BSD4_4_SOCKADDR */
41173188Sgshapiro					     RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
41273188Sgshapiro					{
41373188Sgshapiro						(void) close(t);
41473188Sgshapiro						t = -1;
41573188Sgshapiro						errno = EINVAL;
41673188Sgshapiro					}
41764562Sgshapiro					olddaemon = curdaemon = idx;
41864562Sgshapiro					break;
41964562Sgshapiro				}
42090792Sgshapiro				if (++idx >= NDaemons)
42164562Sgshapiro					idx = 0;
42242575Speter			}
42390792Sgshapiro#if NETUNIX
42464562Sgshapiro			if (curdaemon == -1 && ControlSocket >= 0 &&
42590792Sgshapiro			    SM_FD_ISSET(ControlSocket, &readfds))
42642575Speter			{
42742575Speter				struct sockaddr_un sa_un;
42842575Speter
429168515Sgshapiro				lotherend = sizeof(sa_un);
430168515Sgshapiro				memset(&sa_un, '\0', sizeof(sa_un));
43142575Speter				t = accept(ControlSocket,
43242575Speter					   (struct sockaddr *)&sa_un,
43342575Speter					   &lotherend);
43473188Sgshapiro
43573188Sgshapiro				/*
43673188Sgshapiro				**  If remote side closes before
43773188Sgshapiro				**  accept() finishes, sockaddr
43873188Sgshapiro				**  might not be fully filled in.
43973188Sgshapiro				*/
44073188Sgshapiro
44173188Sgshapiro				if (t >= 0 &&
44273188Sgshapiro				    (lotherend == 0 ||
44373188Sgshapiro# ifdef BSD4_4_SOCKADDR
44473188Sgshapiro				     sa_un.sun_len == 0 ||
44573188Sgshapiro# endif /* BSD4_4_SOCKADDR */
44673188Sgshapiro				     sa_un.sun_family != AF_UNIX))
44773188Sgshapiro				{
44873188Sgshapiro					(void) close(t);
44973188Sgshapiro					t = -1;
45073188Sgshapiro					errno = EINVAL;
45173188Sgshapiro				}
45273188Sgshapiro				if (t >= 0)
45390792Sgshapiro					control = true;
45442575Speter			}
45590792Sgshapiro#else /* NETUNIX */
45671345Sgshapiro			if (curdaemon == -1)
45771345Sgshapiro			{
45871345Sgshapiro				/* No daemon to service */
45971345Sgshapiro				continue;
46071345Sgshapiro			}
46190792Sgshapiro#endif /* NETUNIX */
46238032Speter			if (t >= 0 || errno != EINTR)
46338032Speter				break;
46438032Speter		}
46542575Speter		if (timedout)
46642575Speter		{
46790792Sgshapiro			timedout = false;
46842575Speter			continue;
46942575Speter		}
47064562Sgshapiro		save_errno = errno;
47190792Sgshapiro		(void) sm_blocksignal(SIGALRM);
47238032Speter		if (t < 0)
47338032Speter		{
47464562Sgshapiro			errno = save_errno;
475132943Sgshapiro
476132943Sgshapiro			/* let's ignore these temporary errors */
477132943Sgshapiro			if (save_errno == EINTR
478132943Sgshapiro#ifdef EAGAIN
479132943Sgshapiro			    || save_errno == EAGAIN
480132943Sgshapiro#endif /* EAGAIN */
481132943Sgshapiro#ifdef ECONNABORTED
482132943Sgshapiro			    || save_errno == ECONNABORTED
483132943Sgshapiro#endif /* ECONNABORTED */
484132943Sgshapiro#ifdef EWOULDBLOCK
485132943Sgshapiro			    || save_errno == EWOULDBLOCK
486132943Sgshapiro#endif /* EWOULDBLOCK */
487132943Sgshapiro			   )
488132943Sgshapiro				continue;
489132943Sgshapiro
49038032Speter			syserr("getrequests: accept");
49138032Speter
492159609Sgshapiro			if (curdaemon >= 0)
493159609Sgshapiro			{
494159609Sgshapiro				/* arrange to re-open socket next time around */
495159609Sgshapiro				(void) close(Daemons[curdaemon].d_socket);
496159609Sgshapiro				Daemons[curdaemon].d_socket = -1;
49790792Sgshapiro#if SO_REUSEADDR_IS_BROKEN
498159609Sgshapiro				/*
499159609Sgshapiro				**  Give time for bound socket to be released.
500159609Sgshapiro				**  This creates a denial-of-service if you can
501159609Sgshapiro				**  force accept() to fail on affected systems.
502159609Sgshapiro				*/
50364562Sgshapiro
504159609Sgshapiro				Daemons[curdaemon].d_refuse_connections_until =
505159609Sgshapiro					curtime() + 15;
50690792Sgshapiro#endif /* SO_REUSEADDR_IS_BROKEN */
507159609Sgshapiro			}
50838032Speter			continue;
50938032Speter		}
51038032Speter
51164562Sgshapiro		if (!control)
51264562Sgshapiro		{
51364562Sgshapiro			/* set some daemon related macros */
51464562Sgshapiro			switch (Daemons[curdaemon].d_addr.sa.sa_family)
51564562Sgshapiro			{
51664562Sgshapiro			  case AF_UNSPEC:
51790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
51890792Sgshapiro					macid("{daemon_family}"), "unspec");
51964562Sgshapiro				break;
52090792Sgshapiro#if _FFR_DAEMON_NETUNIX
52190792Sgshapiro# if NETUNIX
52290792Sgshapiro			  case AF_UNIX:
52390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
52490792Sgshapiro					macid("{daemon_family}"), "local");
52590792Sgshapiro				break;
52690792Sgshapiro# endif /* NETUNIX */
52790792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
52890792Sgshapiro#if NETINET
52964562Sgshapiro			  case AF_INET:
53090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
53190792Sgshapiro					macid("{daemon_family}"), "inet");
53264562Sgshapiro				break;
53390792Sgshapiro#endif /* NETINET */
53490792Sgshapiro#if NETINET6
53564562Sgshapiro			  case AF_INET6:
53690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
53790792Sgshapiro					macid("{daemon_family}"), "inet6");
53864562Sgshapiro				break;
53990792Sgshapiro#endif /* NETINET6 */
54090792Sgshapiro#if NETISO
54164562Sgshapiro			  case AF_ISO:
54290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54390792Sgshapiro					macid("{daemon_family}"), "iso");
54464562Sgshapiro				break;
54590792Sgshapiro#endif /* NETISO */
54690792Sgshapiro#if NETNS
54764562Sgshapiro			  case AF_NS:
54890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54990792Sgshapiro					macid("{daemon_family}"), "ns");
55064562Sgshapiro				break;
55190792Sgshapiro#endif /* NETNS */
55290792Sgshapiro#if NETX25
55364562Sgshapiro			  case AF_CCITT:
55490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
55590792Sgshapiro					macid("{daemon_family}"), "x.25");
55664562Sgshapiro				break;
55790792Sgshapiro#endif /* NETX25 */
55864562Sgshapiro			}
55990792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
56090792Sgshapiro				macid("{daemon_name}"),
56190792Sgshapiro				Daemons[curdaemon].d_name);
56264562Sgshapiro			if (Daemons[curdaemon].d_mflags != NULL)
56390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56490792Sgshapiro					macid("{daemon_flags}"),
56590792Sgshapiro					Daemons[curdaemon].d_mflags);
56664562Sgshapiro			else
56790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56890792Sgshapiro					macid("{daemon_flags}"), "");
56964562Sgshapiro		}
57064562Sgshapiro
57138032Speter		/*
572132943Sgshapiro		**  If connection rate is exceeded here, connection shall be
573132943Sgshapiro		**  refused later by a new call after fork() by the
574132943Sgshapiro		**  validate_connection() function. Closing the connection
575132943Sgshapiro		**  at this point violates RFC 2821.
576132943Sgshapiro		**  Do NOT remove this call, its side effects are needed.
577132943Sgshapiro		*/
578132943Sgshapiro
579132943Sgshapiro		connection_rate_check(&RealHostAddr, NULL);
580132943Sgshapiro
581132943Sgshapiro		/*
58238032Speter		**  Create a subprocess to process the mail.
58338032Speter		*/
58438032Speter
58538032Speter		if (tTd(15, 2))
58690792Sgshapiro			sm_dprintf("getrequests: forking (fd = %d)\n", t);
58738032Speter
58838032Speter		/*
58990792Sgshapiro		**  Advance state of PRNG.
59090792Sgshapiro		**  This is necessary because otherwise all child processes
59164562Sgshapiro		**  will produce the same PRN sequence and hence the selection
59264562Sgshapiro		**  of a queue directory (and other things, e.g., MX selection)
59364562Sgshapiro		**  are not "really" random.
59464562Sgshapiro		*/
59590792Sgshapiro#if STARTTLS
59690792Sgshapiro		/* XXX get some better "random" data? */
59766494Sgshapiro		seed = get_random();
59890792Sgshapiro		RAND_seed((void *) &NextDiskSpaceCheck,
599168515Sgshapiro			  sizeof(NextDiskSpaceCheck));
600168515Sgshapiro		RAND_seed((void *) &now, sizeof(now));
601168515Sgshapiro		RAND_seed((void *) &seed, sizeof(seed));
60290792Sgshapiro#else /* STARTTLS */
60364562Sgshapiro		(void) get_random();
60490792Sgshapiro#endif /* STARTTLS */
60564562Sgshapiro
60690792Sgshapiro#if NAMED_BIND
60764562Sgshapiro		/*
608132943Sgshapiro		**  Update MX records for FallbackMX.
60990792Sgshapiro		**  Let's hope this is fast otherwise we screw up the
61090792Sgshapiro		**  response time.
61190792Sgshapiro		*/
61290792Sgshapiro
613132943Sgshapiro		if (FallbackMX != NULL)
614132943Sgshapiro			(void) getfallbackmxrr(FallbackMX);
61590792Sgshapiro#endif /* NAMED_BIND */
61690792Sgshapiro
617110560Sgshapiro		if (tTd(93, 100))
618110560Sgshapiro		{
619110560Sgshapiro			/* don't fork, handle connection in this process */
620110560Sgshapiro			pid = 0;
62138032Speter			pipefd[0] = pipefd[1] = -1;
622110560Sgshapiro		}
623110560Sgshapiro		else
624110560Sgshapiro		{
625110560Sgshapiro			/*
626110560Sgshapiro			**  Create a pipe to keep the child from writing to
627110560Sgshapiro			**  the socket until after the parent has closed
628110560Sgshapiro			**  it.  Otherwise the parent may hang if the child
629110560Sgshapiro			**  has closed it first.
630110560Sgshapiro			*/
63138032Speter
632110560Sgshapiro			if (pipe(pipefd) < 0)
633110560Sgshapiro				pipefd[0] = pipefd[1] = -1;
634110560Sgshapiro
635110560Sgshapiro			(void) sm_blocksignal(SIGCHLD);
636110560Sgshapiro			pid = fork();
637110560Sgshapiro			if (pid < 0)
63838032Speter			{
639110560Sgshapiro				syserr("daemon: cannot fork");
640110560Sgshapiro				if (pipefd[0] != -1)
641110560Sgshapiro				{
642110560Sgshapiro					(void) close(pipefd[0]);
643110560Sgshapiro					(void) close(pipefd[1]);
644110560Sgshapiro				}
645110560Sgshapiro				(void) sm_releasesignal(SIGCHLD);
646110560Sgshapiro				(void) sleep(10);
647110560Sgshapiro				(void) close(t);
648110560Sgshapiro				continue;
64938032Speter			}
65038032Speter		}
65190792Sgshapiro
65238032Speter		if (pid == 0)
65338032Speter		{
65438032Speter			char *p;
65590792Sgshapiro			SM_FILE_T *inchannel, *outchannel = NULL;
65638032Speter
65738032Speter			/*
65838032Speter			**  CHILD -- return to caller.
65938032Speter			**	Collect verified idea of sending host.
66038032Speter			**	Verify calling user id if possible here.
66138032Speter			*/
66238032Speter
66377349Sgshapiro			/* Reset global flags */
66477349Sgshapiro			RestartRequest = NULL;
66590792Sgshapiro			RestartWorkGroup = false;
66677349Sgshapiro			ShutdownRequest = NULL;
66777349Sgshapiro			PendingSignal = 0;
66890792Sgshapiro			CurrentPid = getpid();
669132943Sgshapiro			close_sendmail_pid();
67077349Sgshapiro
67190792Sgshapiro			(void) sm_releasesignal(SIGALRM);
67290792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
67390792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
67490792Sgshapiro			(void) sm_signal(SIGHUP, SIG_DFL);
67590792Sgshapiro			(void) sm_signal(SIGTERM, intsig);
67677349Sgshapiro
67790792Sgshapiro			/* turn on profiling */
67890792Sgshapiro			/* SM_PROF(0); */
67990792Sgshapiro
68090792Sgshapiro			/*
68190792Sgshapiro			**  Initialize exception stack and default exception
68290792Sgshapiro			**  handler for child process.
68390792Sgshapiro			*/
68490792Sgshapiro
68590792Sgshapiro			sm_exc_newthread(fatal_error);
68690792Sgshapiro
68764562Sgshapiro			if (!control)
68864562Sgshapiro			{
68990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
69090792Sgshapiro					macid("{daemon_addr}"),
69190792Sgshapiro					anynet_ntoa(&Daemons[curdaemon].d_addr));
692168515Sgshapiro				(void) sm_snprintf(status, sizeof(status), "%d",
69364562Sgshapiro						ntohs(Daemons[curdaemon].d_port));
69490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
69590792Sgshapiro					macid("{daemon_port}"), status);
69664562Sgshapiro			}
69764562Sgshapiro
69890792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
69964562Sgshapiro			{
70064562Sgshapiro				if (Daemons[idx].d_socket >= 0)
70164562Sgshapiro					(void) close(Daemons[idx].d_socket);
70280785Sgshapiro				Daemons[idx].d_socket = -1;
70364562Sgshapiro			}
70442575Speter			clrcontrol();
70538032Speter
70664562Sgshapiro			/* Avoid SMTP daemon actions if control command */
70764562Sgshapiro			if (control)
70864562Sgshapiro			{
70964562Sgshapiro				/* Add control socket process */
71090792Sgshapiro				proc_list_add(CurrentPid,
71190792Sgshapiro					      "console socket child",
712132943Sgshapiro					      PROC_CONTROL_CHILD, 0, -1, NULL);
71364562Sgshapiro			}
71464562Sgshapiro			else
71564562Sgshapiro			{
71664562Sgshapiro				proc_list_clear();
71742575Speter
71890792Sgshapiro				/* clean up background delivery children */
71990792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
72090792Sgshapiro
72164562Sgshapiro				/* Add parent process as first child item */
72290792Sgshapiro				proc_list_add(CurrentPid, "daemon child",
723132943Sgshapiro					      PROC_DAEMON_CHILD, 0, -1, NULL);
72464562Sgshapiro				/* don't schedule queue runs if ETRN */
72564562Sgshapiro				QueueIntvl = 0;
726168515Sgshapiro
727168515Sgshapiro				/*
728168515Sgshapiro				**  Hack: override global variables if
729168515Sgshapiro				**	the corresponding DaemonPortOption
730168515Sgshapiro				**	is set.
731168515Sgshapiro				*/
732147078Sgshapiro#if _FFR_SS_PER_DAEMON
733147078Sgshapiro				if (Daemons[curdaemon].d_supersafe !=
734168515Sgshapiro				    DPO_NOTSET)
735168515Sgshapiro					SuperSafe = Daemons[curdaemon].
736168515Sgshapiro								d_supersafe;
737147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
738147078Sgshapiro				if (Daemons[curdaemon].d_dm != DM_NOTSET)
739147078Sgshapiro					set_delivery_mode(
740147078Sgshapiro						Daemons[curdaemon].d_dm, e);
74138032Speter
742168515Sgshapiro				if (Daemons[curdaemon].d_refuseLA !=
743168515Sgshapiro				    DPO_NOTSET)
744168515Sgshapiro					RefuseLA = Daemons[curdaemon].
745168515Sgshapiro								d_refuseLA;
746168515Sgshapiro				if (Daemons[curdaemon].d_queueLA != DPO_NOTSET)
747168515Sgshapiro					QueueLA = Daemons[curdaemon].d_queueLA;
748168515Sgshapiro				if (Daemons[curdaemon].d_delayLA != DPO_NOTSET)
749168515Sgshapiro					DelayLA = Daemons[curdaemon].d_delayLA;
750168515Sgshapiro				if (Daemons[curdaemon].d_maxchildren !=
751168515Sgshapiro				    DPO_NOTSET)
752168515Sgshapiro					MaxChildren = Daemons[curdaemon].
753168515Sgshapiro								d_maxchildren;
754168515Sgshapiro
75590792Sgshapiro				sm_setproctitle(true, e, "startup with %s",
75664562Sgshapiro						anynet_ntoa(&RealHostAddr));
75764562Sgshapiro			}
75864562Sgshapiro
75938032Speter			if (pipefd[0] != -1)
76038032Speter			{
76138032Speter				auto char c;
76238032Speter
76338032Speter				/*
76438032Speter				**  Wait for the parent to close the write end
76538032Speter				**  of the pipe, which we will see as an EOF.
76638032Speter				**  This guarantees that we won't write to the
76738032Speter				**  socket until after the parent has closed
76838032Speter				**  the pipe.
76938032Speter				*/
77038032Speter
77138032Speter				/* close the write end of the pipe */
77238032Speter				(void) close(pipefd[1]);
77338032Speter
77438032Speter				/* we shouldn't be interrupted, but ... */
77538032Speter				while (read(pipefd[0], &c, 1) < 0 &&
77638032Speter				       errno == EINTR)
77738032Speter					continue;
77838032Speter				(void) close(pipefd[0]);
77938032Speter			}
78038032Speter
78164562Sgshapiro			/* control socket processing */
78264562Sgshapiro			if (control)
78364562Sgshapiro			{
78464562Sgshapiro				control_command(t, e);
78564562Sgshapiro				/* NOTREACHED */
78664562Sgshapiro				exit(EX_SOFTWARE);
78764562Sgshapiro			}
78864562Sgshapiro
78938032Speter			/* determine host name */
79038032Speter			p = hostnamebyanyaddr(&RealHostAddr);
79190792Sgshapiro			if (strlen(p) > MAXNAME) /* XXX  - 1 ? */
79238032Speter				p[MAXNAME] = '\0';
79338032Speter			RealHostName = newstr(p);
79464562Sgshapiro			if (RealHostName[0] == '[')
79564562Sgshapiro			{
79690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
79790792Sgshapiro					macid("{client_resolve}"),
79890792Sgshapiro					h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
79964562Sgshapiro			}
80064562Sgshapiro			else
801132943Sgshapiro			{
80290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
803132943Sgshapiro					  macid("{client_resolve}"), "OK");
804132943Sgshapiro			}
80590792Sgshapiro			sm_setproctitle(true, e, "startup with %s", p);
80694334Sgshapiro			markstats(e, NULL, STATS_CONNECT);
80738032Speter
80890792Sgshapiro			if ((inchannel = sm_io_open(SmFtStdiofd,
80990792Sgshapiro						    SM_TIME_DEFAULT,
81090792Sgshapiro						    (void *) &t,
811132943Sgshapiro						    SM_IO_RDONLY_B,
81290792Sgshapiro						    NULL)) == NULL ||
81338032Speter			    (t = dup(t)) < 0 ||
81490792Sgshapiro			    (outchannel = sm_io_open(SmFtStdiofd,
81590792Sgshapiro						     SM_TIME_DEFAULT,
81690792Sgshapiro						     (void *) &t,
817132943Sgshapiro						     SM_IO_WRONLY_B,
81890792Sgshapiro						     NULL)) == NULL)
81938032Speter			{
82090792Sgshapiro				syserr("cannot open SMTP server channel, fd=%d",
82190792Sgshapiro					t);
82290792Sgshapiro				finis(false, true, EX_OK);
82338032Speter			}
82490792Sgshapiro			sm_io_automode(inchannel, outchannel);
82538032Speter
82638032Speter			InChannel = inchannel;
82738032Speter			OutChannel = outchannel;
82890792Sgshapiro			DisConnected = false;
82938032Speter
83090792Sgshapiro#if XLA
83138032Speter			if (!xla_host_ok(RealHostName))
83238032Speter			{
83364562Sgshapiro				message("421 4.4.5 Too many SMTP sessions for this host");
83490792Sgshapiro				finis(false, true, EX_OK);
83538032Speter			}
83690792Sgshapiro#endif /* XLA */
83764562Sgshapiro			/* find out name for interface of connection */
83890792Sgshapiro			if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
83990792Sgshapiro						      NULL), &sa.sa, &len) == 0)
84064562Sgshapiro			{
84164562Sgshapiro				p = hostnamebyanyaddr(&sa);
84264562Sgshapiro				if (tTd(15, 9))
84390792Sgshapiro					sm_dprintf("getreq: got name %s\n", p);
84490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
84590792Sgshapiro					macid("{if_name}"), p);
84664562Sgshapiro
84790792Sgshapiro				/*
84890792Sgshapiro				**  Do this only if it is not the loopback
84990792Sgshapiro				**  interface.
85090792Sgshapiro				*/
85190792Sgshapiro
85264562Sgshapiro				if (!isloopback(sa))
85364562Sgshapiro				{
85490792Sgshapiro					char *addr;
85590792Sgshapiro					char family[5];
85690792Sgshapiro
85790792Sgshapiro					addr = anynet_ntoa(&sa);
85890792Sgshapiro					(void) sm_snprintf(family,
85990792Sgshapiro						sizeof(family),
86090792Sgshapiro						"%d", sa.sa.sa_family);
86190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
86290792Sgshapiro						A_TEMP,
86390792Sgshapiro						macid("{if_addr}"), addr);
86490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
86590792Sgshapiro						A_TEMP,
86690792Sgshapiro						macid("{if_family}"), family);
86764562Sgshapiro					if (tTd(15, 7))
86890792Sgshapiro						sm_dprintf("getreq: got addr %s and family %s\n",
86990792Sgshapiro							addr, family);
87064562Sgshapiro				}
87164562Sgshapiro				else
87264562Sgshapiro				{
87390792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
87490792Sgshapiro						A_PERM,
87590792Sgshapiro						macid("{if_addr}"), NULL);
87690792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
87790792Sgshapiro						A_PERM,
87890792Sgshapiro						macid("{if_family}"), NULL);
87964562Sgshapiro				}
88064562Sgshapiro			}
88164562Sgshapiro			else
88264562Sgshapiro			{
88364562Sgshapiro				if (tTd(15, 7))
88490792Sgshapiro					sm_dprintf("getreq: getsockname failed\n");
88590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
88690792Sgshapiro					macid("{if_name}"), NULL);
88790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
88890792Sgshapiro					macid("{if_addr}"), NULL);
88990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
89090792Sgshapiro					macid("{if_family}"), NULL);
89164562Sgshapiro			}
89238032Speter			break;
89338032Speter		}
89438032Speter
89538032Speter		/* parent -- keep track of children */
89664562Sgshapiro		if (control)
89764562Sgshapiro		{
898168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
89990792Sgshapiro					   "control socket server child");
900132943Sgshapiro			proc_list_add(pid, status, PROC_CONTROL, 0, -1, NULL);
90164562Sgshapiro		}
90264562Sgshapiro		else
90364562Sgshapiro		{
904168515Sgshapiro			(void) sm_snprintf(status, sizeof(status),
90590792Sgshapiro					   "SMTP server child for %s",
90690792Sgshapiro					   anynet_ntoa(&RealHostAddr));
907132943Sgshapiro			proc_list_add(pid, status, PROC_DAEMON, 0, -1,
908132943Sgshapiro					&RealHostAddr);
90964562Sgshapiro		}
91090792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
91138032Speter
91238032Speter		/* close the read end of the synchronization pipe */
91338032Speter		if (pipefd[0] != -1)
91464562Sgshapiro		{
91538032Speter			(void) close(pipefd[0]);
91664562Sgshapiro			pipefd[0] = -1;
91764562Sgshapiro		}
91838032Speter
91938032Speter		/* close the port so that others will hang (for a while) */
92038032Speter		(void) close(t);
92138032Speter
92238032Speter		/* release the child by closing the read end of the sync pipe */
92338032Speter		if (pipefd[1] != -1)
92464562Sgshapiro		{
92538032Speter			(void) close(pipefd[1]);
92664562Sgshapiro			pipefd[1] = -1;
92764562Sgshapiro		}
92838032Speter	}
92990792Sgshapiro	if (tTd(15, 2))
93090792Sgshapiro		sm_dprintf("getreq: returning\n");
93164562Sgshapiro
93290792Sgshapiro#if MILTER
93390792Sgshapiro	/* set the filters for this daemon */
93490792Sgshapiro	if (Daemons[curdaemon].d_inputfilterlist != NULL)
93590792Sgshapiro	{
93690792Sgshapiro		for (i = 0;
937110560Sgshapiro		     (i < MAXFILTERS &&
938110560Sgshapiro		      Daemons[curdaemon].d_inputfilters[i] != NULL);
93990792Sgshapiro		     i++)
94090792Sgshapiro		{
94190792Sgshapiro			InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
94290792Sgshapiro		}
94390792Sgshapiro		if (i < MAXFILTERS)
94490792Sgshapiro			InputFilters[i] = NULL;
94590792Sgshapiro	}
94690792Sgshapiro#endif /* MILTER */
94764562Sgshapiro	return &Daemons[curdaemon].d_flags;
94838032Speter}
94990792Sgshapiro
95090792Sgshapiro/*
95190792Sgshapiro**  GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
95290792Sgshapiro**
95390792Sgshapiro**	Parameters:
95490792Sgshapiro**		e -- envelope.
95590792Sgshapiro**
95690792Sgshapiro**	Returns:
95790792Sgshapiro**		none.
95890792Sgshapiro**
95990792Sgshapiro**	Side Effects:
96090792Sgshapiro**		Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
96190792Sgshapiro*/
96290792Sgshapiro
96390792Sgshapirostatic void
96490792Sgshapirogetrequests_checkdiskspace(e)
96590792Sgshapiro	ENVELOPE *e;
96690792Sgshapiro{
96790792Sgshapiro	bool logged = false;
96890792Sgshapiro	int idx;
96990792Sgshapiro	time_t now;
97090792Sgshapiro
97190792Sgshapiro	now = curtime();
97290792Sgshapiro	if (now < NextDiskSpaceCheck)
97390792Sgshapiro		return;
97490792Sgshapiro
97590792Sgshapiro	/* Check if there is available disk space in all queue groups. */
97690792Sgshapiro	if (!enoughdiskspace(0, NULL))
97790792Sgshapiro	{
97890792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
97990792Sgshapiro		{
98090792Sgshapiro			if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
98190792Sgshapiro				continue;
98290792Sgshapiro
98390792Sgshapiro			/* log only if not logged before */
98490792Sgshapiro			if (!logged)
98590792Sgshapiro			{
98690792Sgshapiro				if (LogLevel > 8)
98790792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
98890792Sgshapiro						  "rejecting new messages: min free: %ld",
98990792Sgshapiro						  MinBlocksFree);
99090792Sgshapiro				sm_setproctitle(true, e,
99190792Sgshapiro						"rejecting new messages: min free: %ld",
99290792Sgshapiro						MinBlocksFree);
99390792Sgshapiro				logged = true;
99490792Sgshapiro			}
99590792Sgshapiro			setbitn(D_ETRNONLY, Daemons[idx].d_flags);
99690792Sgshapiro		}
99790792Sgshapiro	}
99890792Sgshapiro	else
99990792Sgshapiro	{
100090792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
100190792Sgshapiro		{
100290792Sgshapiro			if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
100390792Sgshapiro				continue;
100490792Sgshapiro
100590792Sgshapiro			/* log only if not logged before */
100690792Sgshapiro			if (!logged)
100790792Sgshapiro			{
100890792Sgshapiro				if (LogLevel > 8)
100990792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
101090792Sgshapiro						  "accepting new messages (again)");
101190792Sgshapiro				logged = true;
101290792Sgshapiro			}
101390792Sgshapiro
101490792Sgshapiro			/* title will be set later */
101590792Sgshapiro			clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
101690792Sgshapiro		}
101790792Sgshapiro	}
101890792Sgshapiro
101990792Sgshapiro	/* only check disk space once a minute */
102090792Sgshapiro	NextDiskSpaceCheck = now + 60;
102190792Sgshapiro}
102290792Sgshapiro
102390792Sgshapiro/*
102464562Sgshapiro**  OPENDAEMONSOCKET -- open SMTP socket
102538032Speter**
102664562Sgshapiro**	Deals with setting all appropriate options.
102738032Speter**
102838032Speter**	Parameters:
102964562Sgshapiro**		d -- the structure for the daemon to open.
103038032Speter**		firsttime -- set if this is the initial open.
103138032Speter**
103238032Speter**	Returns:
103338032Speter**		Size in bytes of the daemon socket addr.
103438032Speter**
103538032Speter**	Side Effects:
103638032Speter**		Leaves DaemonSocket set to the open socket.
103738032Speter**		Exits if the socket cannot be created.
103838032Speter*/
103938032Speter
104090792Sgshapiro#define MAXOPENTRIES	10	/* maximum number of tries to open connection */
104138032Speter
104264562Sgshapirostatic int
104364562Sgshapiroopendaemonsocket(d, firsttime)
104490792Sgshapiro	DAEMON_T *d;
104538032Speter	bool firsttime;
104638032Speter{
104738032Speter	int on = 1;
104864562Sgshapiro	int fdflags;
104964562Sgshapiro	SOCKADDR_LEN_T socksize = 0;
105038032Speter	int ntries = 0;
105164562Sgshapiro	int save_errno;
105238032Speter
105338032Speter	if (tTd(15, 2))
105490792Sgshapiro		sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
105538032Speter
105638032Speter	do
105738032Speter	{
105838032Speter		if (ntries > 0)
105964562Sgshapiro			(void) sleep(5);
106064562Sgshapiro		if (firsttime || d->d_socket < 0)
106138032Speter		{
106290792Sgshapiro#if _FFR_DAEMON_NETUNIX
106390792Sgshapiro# if NETUNIX
106490792Sgshapiro			if (d->d_addr.sa.sa_family == AF_UNIX)
106590792Sgshapiro			{
106690792Sgshapiro				int rval;
106790792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
106890792Sgshapiro
106990792Sgshapiro				/* if not safe, don't use it */
107090792Sgshapiro				rval = safefile(d->d_addr.sunix.sun_path,
107190792Sgshapiro						RunAsUid, RunAsGid,
107290792Sgshapiro						RunAsUserName, sff,
107390792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
107490792Sgshapiro				if (rval != 0)
107590792Sgshapiro				{
107690792Sgshapiro					save_errno = errno;
107790792Sgshapiro					syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
107890792Sgshapiro					       d->d_name,
107990792Sgshapiro					       d->d_addr.sunix.sun_path);
108090792Sgshapiro					goto fail;
108190792Sgshapiro				}
108290792Sgshapiro
108390792Sgshapiro				/* Don't try to overtake an existing socket */
108490792Sgshapiro				(void) unlink(d->d_addr.sunix.sun_path);
108590792Sgshapiro			}
108690792Sgshapiro# endif /* NETUNIX */
108790792Sgshapiro#endif /* _FFR_DOMAIN_NETUNIX */
108864562Sgshapiro			d->d_socket = socket(d->d_addr.sa.sa_family,
108964562Sgshapiro					     SOCK_STREAM, 0);
109064562Sgshapiro			if (d->d_socket < 0)
109138032Speter			{
109264562Sgshapiro				save_errno = errno;
109390792Sgshapiro				syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
109490792Sgshapiro				       d->d_name);
109590792Sgshapiro			  fail:
109690792Sgshapiro				if (bitnset(D_OPTIONAL, d->d_flags) &&
109790792Sgshapiro				    (!transienterror(save_errno) ||
109890792Sgshapiro				     ntries >= MAXOPENTRIES - 1))
109990792Sgshapiro				{
110090792Sgshapiro					syserr("opendaemonsocket: daemon %s: optional socket disabled",
110190792Sgshapiro					       d->d_name);
110290792Sgshapiro					setbitn(D_DISABLE, d->d_flags);
110390792Sgshapiro					d->d_socket = -1;
110490792Sgshapiro					return -1;
110590792Sgshapiro				}
110638032Speter			  severe:
110738032Speter				if (LogLevel > 0)
110838032Speter					sm_syslog(LOG_ALERT, NOQID,
110990792Sgshapiro						  "daemon %s: problem creating SMTP socket",
111090792Sgshapiro						  d->d_name);
111164562Sgshapiro				d->d_socket = -1;
111238032Speter				continue;
111338032Speter			}
111438032Speter
1115110560Sgshapiro			if (SM_FD_SETSIZE > 0 && d->d_socket >= SM_FD_SETSIZE)
1116110560Sgshapiro			{
1117110560Sgshapiro				save_errno = EINVAL;
1118110560Sgshapiro				syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large",
1119110560Sgshapiro				       d->d_name, d->d_socket);
1120110560Sgshapiro				goto fail;
1121110560Sgshapiro			}
1122110560Sgshapiro
112338032Speter			/* turn on network debugging? */
112438032Speter			if (tTd(15, 101))
112564562Sgshapiro				(void) setsockopt(d->d_socket, SOL_SOCKET,
112638032Speter						  SO_DEBUG, (char *)&on,
1127168515Sgshapiro						  sizeof(on));
112838032Speter
112964562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1130168515Sgshapiro					  SO_REUSEADDR, (char *)&on, sizeof(on));
113164562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
1132168515Sgshapiro					  SO_KEEPALIVE, (char *)&on, sizeof(on));
113338032Speter
113490792Sgshapiro#ifdef SO_RCVBUF
113564562Sgshapiro			if (d->d_tcprcvbufsize > 0)
113638032Speter			{
113764562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
113838032Speter					       SO_RCVBUF,
113964562Sgshapiro					       (char *) &d->d_tcprcvbufsize,
114064562Sgshapiro					       sizeof(d->d_tcprcvbufsize)) < 0)
114164562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
114238032Speter			}
114390792Sgshapiro#endif /* SO_RCVBUF */
114490792Sgshapiro#ifdef SO_SNDBUF
114564562Sgshapiro			if (d->d_tcpsndbufsize > 0)
114664562Sgshapiro			{
114764562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
114864562Sgshapiro					       SO_SNDBUF,
114964562Sgshapiro					       (char *) &d->d_tcpsndbufsize,
115064562Sgshapiro					       sizeof(d->d_tcpsndbufsize)) < 0)
115164562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
115264562Sgshapiro			}
115390792Sgshapiro#endif /* SO_SNDBUF */
115438032Speter
115564562Sgshapiro			if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
115664562Sgshapiro			    fcntl(d->d_socket, F_SETFD,
115764562Sgshapiro				  fdflags | FD_CLOEXEC) == -1)
115838032Speter			{
115964562Sgshapiro				save_errno = errno;
116064562Sgshapiro				syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
116164562Sgshapiro				       d->d_name,
116264562Sgshapiro				       fdflags == -1 ? "get" : "set",
116390792Sgshapiro				       sm_errstring(save_errno));
116464562Sgshapiro				(void) close(d->d_socket);
116564562Sgshapiro				goto severe;
116664562Sgshapiro			}
116764562Sgshapiro
116864562Sgshapiro			switch (d->d_addr.sa.sa_family)
116964562Sgshapiro			{
117090792Sgshapiro#if _FFR_DAEMON_NETUNIX
117190792Sgshapiro# ifdef NETUNIX
117290792Sgshapiro			  case AF_UNIX:
1173168515Sgshapiro				socksize = sizeof(d->d_addr.sunix);
117490792Sgshapiro				break;
117590792Sgshapiro# endif /* NETUNIX */
117690792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
117790792Sgshapiro#if NETINET
117838032Speter			  case AF_INET:
1179168515Sgshapiro				socksize = sizeof(d->d_addr.sin);
118038032Speter				break;
118190792Sgshapiro#endif /* NETINET */
118238032Speter
118390792Sgshapiro#if NETINET6
118464562Sgshapiro			  case AF_INET6:
1185168515Sgshapiro				socksize = sizeof(d->d_addr.sin6);
118664562Sgshapiro				break;
118790792Sgshapiro#endif /* NETINET6 */
118864562Sgshapiro
118990792Sgshapiro#if NETISO
119038032Speter			  case AF_ISO:
1191168515Sgshapiro				socksize = sizeof(d->d_addr.siso);
119238032Speter				break;
119390792Sgshapiro#endif /* NETISO */
119438032Speter
119538032Speter			  default:
1196168515Sgshapiro				socksize = sizeof(d->d_addr);
119738032Speter				break;
119838032Speter			}
119938032Speter
120064562Sgshapiro			if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
120138032Speter			{
120238032Speter				/* probably another daemon already */
120364562Sgshapiro				save_errno = errno;
120464562Sgshapiro				syserr("opendaemonsocket: daemon %s: cannot bind",
120564562Sgshapiro				       d->d_name);
120664562Sgshapiro				(void) close(d->d_socket);
120790792Sgshapiro				goto fail;
120838032Speter			}
120938032Speter		}
121064562Sgshapiro		if (!firsttime &&
121164562Sgshapiro		    listen(d->d_socket, d->d_listenqueue) < 0)
121238032Speter		{
121364562Sgshapiro			save_errno = errno;
121464562Sgshapiro			syserr("opendaemonsocket: daemon %s: cannot listen",
121564562Sgshapiro			       d->d_name);
121664562Sgshapiro			(void) close(d->d_socket);
121738032Speter			goto severe;
121838032Speter		}
121938032Speter		return socksize;
122064562Sgshapiro	} while (ntries++ < MAXOPENTRIES && transienterror(save_errno));
122164562Sgshapiro	syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting",
122264562Sgshapiro	       d->d_name);
122364562Sgshapiro	/* NOTREACHED */
122438032Speter	return -1;  /* avoid compiler warning on IRIX */
122538032Speter}
122690792Sgshapiro/*
122764562Sgshapiro**  SETUPDAEMON -- setup socket for daemon
122864562Sgshapiro**
122964562Sgshapiro**	Parameters:
123064562Sgshapiro**		daemonaddr -- socket for daemon
123164562Sgshapiro**
123264562Sgshapiro**	Returns:
123364562Sgshapiro**		port number on which daemon should run
123464562Sgshapiro**
123564562Sgshapiro*/
123690792Sgshapiro
123790792Sgshapirostatic unsigned short
123864562Sgshapirosetupdaemon(daemonaddr)
123964562Sgshapiro	SOCKADDR *daemonaddr;
124064562Sgshapiro{
124190792Sgshapiro	unsigned short port;
124264562Sgshapiro
124364562Sgshapiro	/*
124464562Sgshapiro	**  Set up the address for the mailer.
124564562Sgshapiro	*/
124664562Sgshapiro
124764562Sgshapiro	if (daemonaddr->sa.sa_family == AF_UNSPEC)
124864562Sgshapiro	{
1249168515Sgshapiro		memset(daemonaddr, '\0', sizeof(*daemonaddr));
125090792Sgshapiro#if NETINET
125164562Sgshapiro		daemonaddr->sa.sa_family = AF_INET;
125290792Sgshapiro#endif /* NETINET */
125364562Sgshapiro	}
125464562Sgshapiro
125564562Sgshapiro	switch (daemonaddr->sa.sa_family)
125664562Sgshapiro	{
125790792Sgshapiro#if NETINET
125864562Sgshapiro	  case AF_INET:
125964562Sgshapiro		if (daemonaddr->sin.sin_addr.s_addr == 0)
1260182352Sgshapiro			daemonaddr->sin.sin_addr.s_addr =
1261182352Sgshapiro			    LocalDaemon ? htonl(INADDR_LOOPBACK) : INADDR_ANY;
126264562Sgshapiro		port = daemonaddr->sin.sin_port;
126364562Sgshapiro		break;
126490792Sgshapiro#endif /* NETINET */
126564562Sgshapiro
126690792Sgshapiro#if NETINET6
126764562Sgshapiro	  case AF_INET6:
126864562Sgshapiro		if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr))
1269182352Sgshapiro			daemonaddr->sin6.sin6_addr =
1270182352Sgshapiro			    LocalDaemon ? in6addr_loopback : in6addr_any;
127164562Sgshapiro		port = daemonaddr->sin6.sin6_port;
127264562Sgshapiro		break;
127390792Sgshapiro#endif /* NETINET6 */
127464562Sgshapiro
127564562Sgshapiro	  default:
127664562Sgshapiro		/* unknown protocol */
127764562Sgshapiro		port = 0;
127864562Sgshapiro		break;
127964562Sgshapiro	}
128064562Sgshapiro	if (port == 0)
128164562Sgshapiro	{
128290792Sgshapiro#ifdef NO_GETSERVBYNAME
128364562Sgshapiro		port = htons(25);
128490792Sgshapiro#else /* NO_GETSERVBYNAME */
128564562Sgshapiro		{
128664562Sgshapiro			register struct servent *sp;
128764562Sgshapiro
128864562Sgshapiro			sp = getservbyname("smtp", "tcp");
128964562Sgshapiro			if (sp == NULL)
129064562Sgshapiro			{
129164562Sgshapiro				syserr("554 5.3.5 service \"smtp\" unknown");
129264562Sgshapiro				port = htons(25);
129364562Sgshapiro			}
129464562Sgshapiro			else
129564562Sgshapiro				port = sp->s_port;
129664562Sgshapiro		}
129790792Sgshapiro#endif /* NO_GETSERVBYNAME */
129864562Sgshapiro	}
129964562Sgshapiro
130064562Sgshapiro	switch (daemonaddr->sa.sa_family)
130164562Sgshapiro	{
130290792Sgshapiro#if NETINET
130364562Sgshapiro	  case AF_INET:
130464562Sgshapiro		daemonaddr->sin.sin_port = port;
130564562Sgshapiro		break;
130690792Sgshapiro#endif /* NETINET */
130764562Sgshapiro
130890792Sgshapiro#if NETINET6
130964562Sgshapiro	  case AF_INET6:
131064562Sgshapiro		daemonaddr->sin6.sin6_port = port;
131164562Sgshapiro		break;
131290792Sgshapiro#endif /* NETINET6 */
131364562Sgshapiro
131464562Sgshapiro	  default:
131564562Sgshapiro		/* unknown protocol */
131664562Sgshapiro		break;
131764562Sgshapiro	}
131890792Sgshapiro	return port;
131964562Sgshapiro}
132090792Sgshapiro/*
132138032Speter**  CLRDAEMON -- reset the daemon connection
132238032Speter**
132338032Speter**	Parameters:
132438032Speter**		none.
132538032Speter**
132638032Speter**	Returns:
132738032Speter**		none.
132838032Speter**
132938032Speter**	Side Effects:
133038032Speter**		releases any resources used by the passive daemon.
133138032Speter*/
133238032Speter
133338032Spetervoid
133438032Speterclrdaemon()
133538032Speter{
133664562Sgshapiro	int i;
133764562Sgshapiro
133890792Sgshapiro	for (i = 0; i < NDaemons; i++)
133964562Sgshapiro	{
134064562Sgshapiro		if (Daemons[i].d_socket >= 0)
134164562Sgshapiro			(void) close(Daemons[i].d_socket);
134264562Sgshapiro		Daemons[i].d_socket = -1;
134364562Sgshapiro	}
134438032Speter}
134590792Sgshapiro
134690792Sgshapiro/*
134790792Sgshapiro**  GETMODIFIERS -- get modifier flags
134890792Sgshapiro**
134990792Sgshapiro**	Parameters:
135090792Sgshapiro**		v -- the modifiers (input text line).
135190792Sgshapiro**		modifiers -- pointer to flag field to represent modifiers.
135290792Sgshapiro**
135390792Sgshapiro**	Returns:
135490792Sgshapiro**		(xallocat()ed) string representation of modifiers.
135590792Sgshapiro**
135690792Sgshapiro**	Side Effects:
135790792Sgshapiro**		fills in modifiers.
135890792Sgshapiro*/
135990792Sgshapiro
136090792Sgshapirochar *
136190792Sgshapirogetmodifiers(v, modifiers)
136290792Sgshapiro	char *v;
136390792Sgshapiro	BITMAP256 modifiers;
136490792Sgshapiro{
136590792Sgshapiro	int l;
136690792Sgshapiro	char *h, *f, *flags;
136790792Sgshapiro
136890792Sgshapiro	/* maximum length of flags: upper case Option -> "OO " */
136990792Sgshapiro	l = 3 * strlen(v) + 3;
137090792Sgshapiro
137190792Sgshapiro	/* is someone joking? */
137290792Sgshapiro	if (l < 0 || l > 256)
137390792Sgshapiro	{
137490792Sgshapiro		if (LogLevel > 2)
137590792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
137690792Sgshapiro				  "getmodifiers too long, ignored");
137790792Sgshapiro		return NULL;
137890792Sgshapiro	}
137990792Sgshapiro	flags = xalloc(l);
138090792Sgshapiro	f = flags;
138190792Sgshapiro	clrbitmap(modifiers);
138290792Sgshapiro	for (h = v; *h != '\0'; h++)
138390792Sgshapiro	{
138490792Sgshapiro		if (isascii(*h) && !isspace(*h) && isprint(*h))
138590792Sgshapiro		{
138690792Sgshapiro			setbitn(*h, modifiers);
138790792Sgshapiro			if (flags != f)
138890792Sgshapiro				*flags++ = ' ';
138990792Sgshapiro			*flags++ = *h;
139090792Sgshapiro			if (isupper(*h))
139190792Sgshapiro				*flags++ = *h;
139290792Sgshapiro		}
139390792Sgshapiro	}
139490792Sgshapiro	*flags++ = '\0';
139590792Sgshapiro	return f;
139690792Sgshapiro}
139790792Sgshapiro
139890792Sgshapiro/*
139990792Sgshapiro**  CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
140090792Sgshapiro**
140190792Sgshapiro**	Parameters:
140290792Sgshapiro**		flag -- the flag to test.
140390792Sgshapiro**
140490792Sgshapiro**	Returns:
140590792Sgshapiro**		true iff all daemons have set flag.
140690792Sgshapiro*/
140790792Sgshapiro
140890792Sgshapirobool
140990792Sgshapirochkdaemonmodifiers(flag)
141090792Sgshapiro	int flag;
141190792Sgshapiro{
141290792Sgshapiro	int i;
141390792Sgshapiro
141490792Sgshapiro	for (i = 0; i < NDaemons; i++)
141590792Sgshapiro		if (!bitnset((char) flag, Daemons[i].d_flags))
141690792Sgshapiro			return false;
141790792Sgshapiro	return true;
141890792Sgshapiro}
141990792Sgshapiro
142090792Sgshapiro/*
142164562Sgshapiro**  SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
142238032Speter**
142338032Speter**	Parameters:
142438032Speter**		p -- the options line.
142564562Sgshapiro**		d -- the daemon structure to fill in.
142638032Speter**
142738032Speter**	Returns:
142838032Speter**		none.
142938032Speter*/
143038032Speter
143164562Sgshapirostatic void
143264562Sgshapirosetsockaddroptions(p, d)
1433141858Sgshapiro	char *p;
143490792Sgshapiro	DAEMON_T *d;
143538032Speter{
143690792Sgshapiro#if NETISO
143771345Sgshapiro	short portno;
143890792Sgshapiro#endif /* NETISO */
143971345Sgshapiro	char *port = NULL;
144071345Sgshapiro	char *addr = NULL;
144138032Speter
144290792Sgshapiro#if NETINET
144364562Sgshapiro	if (d->d_addr.sa.sa_family == AF_UNSPEC)
144464562Sgshapiro		d->d_addr.sa.sa_family = AF_INET;
144590792Sgshapiro#endif /* NETINET */
1446157001Sgshapiro#if _FFR_SS_PER_DAEMON
1447168515Sgshapiro	d->d_supersafe = DPO_NOTSET;
1448157001Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
1449157001Sgshapiro	d->d_dm = DM_NOTSET;
1450168515Sgshapiro	d->d_refuseLA = DPO_NOTSET;
1451168515Sgshapiro	d->d_queueLA = DPO_NOTSET;
1452168515Sgshapiro	d->d_delayLA = DPO_NOTSET;
1453168515Sgshapiro	d->d_maxchildren = DPO_NOTSET;
145464562Sgshapiro
145538032Speter	while (p != NULL)
145638032Speter	{
145738032Speter		register char *f;
145838032Speter		register char *v;
145938032Speter
146038032Speter		while (isascii(*p) && isspace(*p))
146138032Speter			p++;
146238032Speter		if (*p == '\0')
146338032Speter			break;
146438032Speter		f = p;
146538032Speter		p = strchr(p, ',');
146638032Speter		if (p != NULL)
146738032Speter			*p++ = '\0';
146838032Speter		v = strchr(f, '=');
146938032Speter		if (v == NULL)
147038032Speter			continue;
147138032Speter		while (isascii(*++v) && isspace(*v))
147238032Speter			continue;
147338032Speter
147438032Speter		switch (*f)
147538032Speter		{
1476147078Sgshapiro		  case 'A':		/* address */
1477168515Sgshapiro#if !_FFR_DPO_CS
1478168515Sgshapiro		  case 'a':
1479168515Sgshapiro#endif /* !_FFR_DPO_CS */
1480147078Sgshapiro			addr = v;
1481147078Sgshapiro			break;
1482147078Sgshapiro
1483168515Sgshapiro		  case 'c':
1484168515Sgshapiro			d->d_maxchildren = atoi(v);
1485168515Sgshapiro			break;
1486168515Sgshapiro
1487147078Sgshapiro		  case 'D':		/* DeliveryMode */
1488147078Sgshapiro			switch (*v)
1489147078Sgshapiro			{
1490147078Sgshapiro			  case SM_QUEUE:
1491147078Sgshapiro			  case SM_DEFER:
1492147078Sgshapiro			  case SM_DELIVER:
1493157001Sgshapiro			  case SM_FORK:
1494147078Sgshapiro				d->d_dm = *v;
1495147078Sgshapiro				break;
1496147078Sgshapiro			  default:
1497147078Sgshapiro				syserr("554 5.3.5 Unknown delivery mode %c",
1498147078Sgshapiro					*v);
1499147078Sgshapiro				break;
1500147078Sgshapiro			}
1501147078Sgshapiro			break;
1502147078Sgshapiro
1503168515Sgshapiro		  case 'd':		/* delayLA */
1504168515Sgshapiro			d->d_delayLA = atoi(v);
1505168515Sgshapiro			break;
1506168515Sgshapiro
150738032Speter		  case 'F':		/* address family */
1508168515Sgshapiro#if !_FFR_DPO_CS
1509168515Sgshapiro		  case 'f':
1510168515Sgshapiro#endif /* !_FFR_DPO_CS */
151138032Speter			if (isascii(*v) && isdigit(*v))
151264562Sgshapiro				d->d_addr.sa.sa_family = atoi(v);
151390792Sgshapiro#if _FFR_DAEMON_NETUNIX
151490792Sgshapiro# ifdef NETUNIX
151590792Sgshapiro			else if (sm_strcasecmp(v, "unix") == 0 ||
151690792Sgshapiro				 sm_strcasecmp(v, "local") == 0)
151790792Sgshapiro				d->d_addr.sa.sa_family = AF_UNIX;
151890792Sgshapiro# endif /* NETUNIX */
151990792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
152090792Sgshapiro#if NETINET
152190792Sgshapiro			else if (sm_strcasecmp(v, "inet") == 0)
152264562Sgshapiro				d->d_addr.sa.sa_family = AF_INET;
152390792Sgshapiro#endif /* NETINET */
152490792Sgshapiro#if NETINET6
152590792Sgshapiro			else if (sm_strcasecmp(v, "inet6") == 0)
152664562Sgshapiro				d->d_addr.sa.sa_family = AF_INET6;
152790792Sgshapiro#endif /* NETINET6 */
152890792Sgshapiro#if NETISO
152990792Sgshapiro			else if (sm_strcasecmp(v, "iso") == 0)
153064562Sgshapiro				d->d_addr.sa.sa_family = AF_ISO;
153190792Sgshapiro#endif /* NETISO */
153290792Sgshapiro#if NETNS
153390792Sgshapiro			else if (sm_strcasecmp(v, "ns") == 0)
153464562Sgshapiro				d->d_addr.sa.sa_family = AF_NS;
153590792Sgshapiro#endif /* NETNS */
153690792Sgshapiro#if NETX25
153790792Sgshapiro			else if (sm_strcasecmp(v, "x.25") == 0)
153864562Sgshapiro				d->d_addr.sa.sa_family = AF_CCITT;
153990792Sgshapiro#endif /* NETX25 */
154038032Speter			else
154164562Sgshapiro				syserr("554 5.3.5 Unknown address family %s in Family=option",
154264562Sgshapiro				       v);
154338032Speter			break;
154438032Speter
154590792Sgshapiro#if MILTER
154690792Sgshapiro		  case 'I':
1547168515Sgshapiro# if !_FFR_DPO_CS
1548168515Sgshapiro		  case 'i':
1549168515Sgshapiro# endif /* !_FFR_DPO_CS */
155090792Sgshapiro			d->d_inputfilterlist = v;
155190792Sgshapiro			break;
155290792Sgshapiro#endif /* MILTER */
155390792Sgshapiro
155438032Speter		  case 'L':		/* listen queue size */
1555168515Sgshapiro#if !_FFR_DPO_CS
1556168515Sgshapiro		  case 'l':
1557168515Sgshapiro#endif /* !_FFR_DPO_CS */
155864562Sgshapiro			d->d_listenqueue = atoi(v);
155938032Speter			break;
156038032Speter
156164562Sgshapiro		  case 'M':		/* modifiers (flags) */
1562168515Sgshapiro#if !_FFR_DPO_CS
1563168515Sgshapiro		  case 'm':
1564168515Sgshapiro#endif /* !_FFR_DPO_CS */
156590792Sgshapiro			d->d_mflags = getmodifiers(v, d->d_flags);
156664562Sgshapiro			break;
156764562Sgshapiro
1568147078Sgshapiro		  case 'N':		/* name */
1569168515Sgshapiro#if !_FFR_DPO_CS
1570168515Sgshapiro		  case 'n':
1571168515Sgshapiro#endif /* !_FFR_DPO_CS */
1572147078Sgshapiro			d->d_name = v;
157338032Speter			break;
157438032Speter
1575147078Sgshapiro		  case 'P':		/* port */
1576168515Sgshapiro#if !_FFR_DPO_CS
1577168515Sgshapiro		  case 'p':
1578168515Sgshapiro#endif /* !_FFR_DPO_CS */
1579147078Sgshapiro			port = v;
1580147078Sgshapiro			break;
1581147078Sgshapiro
1582168515Sgshapiro		  case 'q':
1583168515Sgshapiro			d->d_queueLA = atoi(v);
1584168515Sgshapiro			break;
1585168515Sgshapiro
158638032Speter		  case 'R':		/* receive buffer size */
158764562Sgshapiro			d->d_tcprcvbufsize = atoi(v);
158838032Speter			break;
158938032Speter
1590168515Sgshapiro		  case 'r':
1591168515Sgshapiro			d->d_refuseLA = atoi(v);
1592168515Sgshapiro			break;
1593168515Sgshapiro
1594147078Sgshapiro		  case 'S':		/* send buffer size */
1595168515Sgshapiro#if !_FFR_DPO_CS
1596168515Sgshapiro		  case 's':
1597168515Sgshapiro#endif /* !_FFR_DPO_CS */
1598147078Sgshapiro			d->d_tcpsndbufsize = atoi(v);
159964562Sgshapiro			break;
160064562Sgshapiro
1601147078Sgshapiro#if _FFR_SS_PER_DAEMON
1602147078Sgshapiro		  case 'T':		/* SuperSafe */
1603147078Sgshapiro			if (tolower(*v) == 'i')
1604147078Sgshapiro				d->d_supersafe = SAFE_INTERACTIVE;
1605147078Sgshapiro			else if (tolower(*v) == 'p')
1606147078Sgshapiro# if MILTER
1607147078Sgshapiro				d->d_supersafe = SAFE_REALLY_POSTMILTER;
1608147078Sgshapiro# else /* MILTER */
1609147078Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1610147078Sgshapiro					"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
1611147078Sgshapiro# endif /* MILTER */
1612147078Sgshapiro			else
1613147078Sgshapiro				d->d_supersafe = atobool(v) ? SAFE_REALLY
1614147078Sgshapiro							: SAFE_NO;
1615147078Sgshapiro			break;
1616147078Sgshapiro#endif /* _FFR_SS_PER_DAEMON */
1617147078Sgshapiro
161838032Speter		  default:
161964562Sgshapiro			syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
162064562Sgshapiro			       f);
162138032Speter		}
162238032Speter	}
162371345Sgshapiro
162471345Sgshapiro	/* Check addr and port after finding family */
162571345Sgshapiro	if (addr != NULL)
162671345Sgshapiro	{
162771345Sgshapiro		switch (d->d_addr.sa.sa_family)
162871345Sgshapiro		{
162990792Sgshapiro#if _FFR_DAEMON_NETUNIX
163090792Sgshapiro# if NETUNIX
163190792Sgshapiro		  case AF_UNIX:
163290792Sgshapiro			if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
163390792Sgshapiro			{
163490792Sgshapiro				errno = ENAMETOOLONG;
163590792Sgshapiro				syserr("setsockaddroptions: domain socket name too long: %s > %d",
163690792Sgshapiro				       addr, sizeof(d->d_addr.sunix.sun_path));
163790792Sgshapiro				break;
163890792Sgshapiro			}
163990792Sgshapiro
164090792Sgshapiro			/* file safety check done in opendaemonsocket() */
164190792Sgshapiro			(void) memset(&d->d_addr.sunix.sun_path, '\0',
164290792Sgshapiro				      sizeof(d->d_addr.sunix.sun_path));
164390792Sgshapiro			(void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
164490792Sgshapiro					  addr,
164590792Sgshapiro					  sizeof(d->d_addr.sunix.sun_path));
164690792Sgshapiro			break;
164790792Sgshapiro# endif /* NETUNIX */
164890792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
164990792Sgshapiro#if NETINET
165071345Sgshapiro		  case AF_INET:
165171345Sgshapiro			if (!isascii(*addr) || !isdigit(*addr) ||
165290792Sgshapiro			    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
165390792Sgshapiro			     == INADDR_NONE))
165471345Sgshapiro			{
165571345Sgshapiro				register struct hostent *hp;
165671345Sgshapiro
165771345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET);
165871345Sgshapiro				if (hp == NULL)
165971345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
166071345Sgshapiro					       addr);
166171345Sgshapiro				else
166271345Sgshapiro				{
166371345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
166471345Sgshapiro					       hp->h_addrtype != AF_INET)
166571345Sgshapiro						hp->h_addr_list++;
166671345Sgshapiro					if (*(hp->h_addr_list) == NULL)
166771345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
166871345Sgshapiro						       addr);
166971345Sgshapiro					else
167071345Sgshapiro						memmove(&d->d_addr.sin.sin_addr,
167171345Sgshapiro							*(hp->h_addr_list),
167271345Sgshapiro							INADDRSZ);
167390792Sgshapiro# if NETINET6
167471345Sgshapiro					freehostent(hp);
167571345Sgshapiro					hp = NULL;
167690792Sgshapiro# endif /* NETINET6 */
167771345Sgshapiro				}
167871345Sgshapiro			}
167971345Sgshapiro			break;
168090792Sgshapiro#endif /* NETINET */
168171345Sgshapiro
168290792Sgshapiro#if NETINET6
168371345Sgshapiro		  case AF_INET6:
168490792Sgshapiro			if (anynet_pton(AF_INET6, addr,
168590792Sgshapiro					&d->d_addr.sin6.sin6_addr) != 1)
168671345Sgshapiro			{
168771345Sgshapiro				register struct hostent *hp;
168871345Sgshapiro
168971345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET6);
169071345Sgshapiro				if (hp == NULL)
169171345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
169271345Sgshapiro					       addr);
169371345Sgshapiro				else
169471345Sgshapiro				{
169571345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
169671345Sgshapiro					       hp->h_addrtype != AF_INET6)
169771345Sgshapiro						hp->h_addr_list++;
169871345Sgshapiro					if (*(hp->h_addr_list) == NULL)
169971345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
170071345Sgshapiro						       addr);
170171345Sgshapiro					else
170271345Sgshapiro						memmove(&d->d_addr.sin6.sin6_addr,
170371345Sgshapiro							*(hp->h_addr_list),
170471345Sgshapiro							IN6ADDRSZ);
170571345Sgshapiro					freehostent(hp);
170671345Sgshapiro					hp = NULL;
170771345Sgshapiro				}
170871345Sgshapiro			}
170971345Sgshapiro			break;
171090792Sgshapiro#endif /* NETINET6 */
171171345Sgshapiro
171271345Sgshapiro		  default:
171371345Sgshapiro			syserr("554 5.3.5 address= option unsupported for family %d",
171471345Sgshapiro			       d->d_addr.sa.sa_family);
171571345Sgshapiro			break;
171671345Sgshapiro		}
171771345Sgshapiro	}
171871345Sgshapiro
171971345Sgshapiro	if (port != NULL)
172071345Sgshapiro	{
172171345Sgshapiro		switch (d->d_addr.sa.sa_family)
172271345Sgshapiro		{
172390792Sgshapiro#if NETINET
172471345Sgshapiro		  case AF_INET:
172571345Sgshapiro			if (isascii(*port) && isdigit(*port))
172690792Sgshapiro				d->d_addr.sin.sin_port = htons((unsigned short)
172790792Sgshapiro						     atoi((const char *) port));
172871345Sgshapiro			else
172971345Sgshapiro			{
173090792Sgshapiro# ifdef NO_GETSERVBYNAME
173171345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
173271345Sgshapiro				       port);
173390792Sgshapiro# else /* NO_GETSERVBYNAME */
173471345Sgshapiro				register struct servent *sp;
173571345Sgshapiro
173671345Sgshapiro				sp = getservbyname(port, "tcp");
173771345Sgshapiro				if (sp == NULL)
173871345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
173971345Sgshapiro					       port);
174071345Sgshapiro				else
174171345Sgshapiro					d->d_addr.sin.sin_port = sp->s_port;
174290792Sgshapiro# endif /* NO_GETSERVBYNAME */
174371345Sgshapiro			}
174471345Sgshapiro			break;
174590792Sgshapiro#endif /* NETINET */
174671345Sgshapiro
174790792Sgshapiro#if NETINET6
174871345Sgshapiro		  case AF_INET6:
174971345Sgshapiro			if (isascii(*port) && isdigit(*port))
175090792Sgshapiro				d->d_addr.sin6.sin6_port = htons((unsigned short)
175190792Sgshapiro								  atoi(port));
175271345Sgshapiro			else
175371345Sgshapiro			{
175490792Sgshapiro# ifdef NO_GETSERVBYNAME
175571345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
175671345Sgshapiro				       port);
175790792Sgshapiro# else /* NO_GETSERVBYNAME */
175871345Sgshapiro				register struct servent *sp;
175971345Sgshapiro
176071345Sgshapiro				sp = getservbyname(port, "tcp");
176171345Sgshapiro				if (sp == NULL)
176271345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
176371345Sgshapiro					       port);
176471345Sgshapiro				else
176571345Sgshapiro					d->d_addr.sin6.sin6_port = sp->s_port;
176690792Sgshapiro# endif /* NO_GETSERVBYNAME */
176771345Sgshapiro			}
176871345Sgshapiro			break;
176990792Sgshapiro#endif /* NETINET6 */
177071345Sgshapiro
177190792Sgshapiro#if NETISO
177271345Sgshapiro		  case AF_ISO:
177371345Sgshapiro			/* assume two byte transport selector */
177471345Sgshapiro			if (isascii(*port) && isdigit(*port))
177590792Sgshapiro				portno = htons((unsigned short) atoi(port));
177671345Sgshapiro			else
177771345Sgshapiro			{
177890792Sgshapiro# ifdef NO_GETSERVBYNAME
177971345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
178071345Sgshapiro				       port);
178190792Sgshapiro# else /* NO_GETSERVBYNAME */
178271345Sgshapiro				register struct servent *sp;
178371345Sgshapiro
178471345Sgshapiro				sp = getservbyname(port, "tcp");
178571345Sgshapiro				if (sp == NULL)
178671345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
178771345Sgshapiro					       port);
178871345Sgshapiro				else
178971345Sgshapiro					portno = sp->s_port;
179090792Sgshapiro# endif /* NO_GETSERVBYNAME */
179171345Sgshapiro			}
179271345Sgshapiro			memmove(TSEL(&d->d_addr.siso),
179371345Sgshapiro				(char *) &portno, 2);
179471345Sgshapiro			break;
179590792Sgshapiro#endif /* NETISO */
179671345Sgshapiro
179771345Sgshapiro		  default:
179871345Sgshapiro			syserr("554 5.3.5 Port= option unsupported for family %d",
179971345Sgshapiro			       d->d_addr.sa.sa_family);
180071345Sgshapiro			break;
180171345Sgshapiro		}
180271345Sgshapiro	}
180338032Speter}
180490792Sgshapiro/*
180564562Sgshapiro**  SETDAEMONOPTIONS -- set options for running the MTA daemon
180638032Speter**
180738032Speter**	Parameters:
180864562Sgshapiro**		p -- the options line.
180964562Sgshapiro**
181064562Sgshapiro**	Returns:
181190792Sgshapiro**		true if successful, false otherwise.
181290792Sgshapiro**
181390792Sgshapiro**	Side Effects:
181490792Sgshapiro**		increments number of daemons.
181564562Sgshapiro*/
181664562Sgshapiro
181790792Sgshapiro#define DEF_LISTENQUEUE	10
181890792Sgshapiro
181998841Sgshapirostruct dflags
182098841Sgshapiro{
182198841Sgshapiro	char	*d_name;
182298841Sgshapiro	int	d_flag;
182398841Sgshapiro};
182498841Sgshapiro
182598841Sgshapirostatic struct dflags	DaemonFlags[] =
182698841Sgshapiro{
182798841Sgshapiro	{ "AUTHREQ",		D_AUTHREQ	},
182898841Sgshapiro	{ "BINDIF",		D_BINDIF	},
182998841Sgshapiro	{ "CANONREQ",		D_CANONREQ	},
183098841Sgshapiro	{ "IFNHELO",		D_IFNHELO	},
183198841Sgshapiro	{ "FQMAIL",		D_FQMAIL	},
183298841Sgshapiro	{ "FQRCPT",		D_FQRCPT	},
183398841Sgshapiro	{ "SMTPS",		D_SMTPS		},
183498841Sgshapiro	{ "UNQUALOK",		D_UNQUALOK	},
183598841Sgshapiro	{ "NOAUTH",		D_NOAUTH	},
183698841Sgshapiro	{ "NOCANON",		D_NOCANON	},
183798841Sgshapiro	{ "NOETRN",		D_NOETRN	},
183898841Sgshapiro	{ "NOTLS",		D_NOTLS		},
183998841Sgshapiro	{ "ETRNONLY",		D_ETRNONLY	},
184098841Sgshapiro	{ "OPTIONAL",		D_OPTIONAL	},
184198841Sgshapiro	{ "DISABLE",		D_DISABLE	},
184298841Sgshapiro	{ "ISSET",		D_ISSET		},
184398841Sgshapiro	{ NULL,			0		}
184498841Sgshapiro};
184598841Sgshapiro
184698841Sgshapirostatic void
184798841Sgshapiroprintdaemonflags(d)
184898841Sgshapiro	DAEMON_T *d;
184998841Sgshapiro{
185098841Sgshapiro	register struct dflags *df;
185198841Sgshapiro	bool first = true;
185298841Sgshapiro
185398841Sgshapiro	for (df = DaemonFlags; df->d_name != NULL; df++)
185498841Sgshapiro	{
185598841Sgshapiro		if (!bitnset(df->d_flag, d->d_flags))
185698841Sgshapiro			continue;
185798841Sgshapiro		if (first)
1858132943Sgshapiro			sm_dprintf("<%s", df->d_name);
185998841Sgshapiro		else
1860132943Sgshapiro			sm_dprintf(",%s", df->d_name);
186198841Sgshapiro		first = false;
186298841Sgshapiro	}
186398841Sgshapiro	if (!first)
1864132943Sgshapiro		sm_dprintf(">");
186598841Sgshapiro}
186698841Sgshapiro
186764562Sgshapirobool
186864562Sgshapirosetdaemonoptions(p)
186964562Sgshapiro	register char *p;
187064562Sgshapiro{
187190792Sgshapiro	if (NDaemons >= MAXDAEMONS)
187290792Sgshapiro		return false;
187390792Sgshapiro	Daemons[NDaemons].d_socket = -1;
187490792Sgshapiro	Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
187590792Sgshapiro	clrbitmap(Daemons[NDaemons].d_flags);
187690792Sgshapiro	setsockaddroptions(p, &Daemons[NDaemons]);
187764562Sgshapiro
187890792Sgshapiro#if MILTER
187990792Sgshapiro	if (Daemons[NDaemons].d_inputfilterlist != NULL)
188090792Sgshapiro		Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
188190792Sgshapiro#endif /* MILTER */
188290792Sgshapiro
188390792Sgshapiro	if (Daemons[NDaemons].d_name != NULL)
188490792Sgshapiro		Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
188564562Sgshapiro	else
188664562Sgshapiro	{
188764562Sgshapiro		char num[30];
188864562Sgshapiro
1889168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Daemon%d", NDaemons);
189090792Sgshapiro		Daemons[NDaemons].d_name = newstr(num);
189164562Sgshapiro	}
189264562Sgshapiro
189364562Sgshapiro	if (tTd(37, 1))
189464562Sgshapiro	{
189590792Sgshapiro		sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
189698841Sgshapiro		printdaemonflags(&Daemons[NDaemons]);
189790792Sgshapiro		sm_dprintf("\n");
189864562Sgshapiro	}
189990792Sgshapiro	++NDaemons;
190090792Sgshapiro	return true;
190164562Sgshapiro}
190290792Sgshapiro/*
190364562Sgshapiro**  INITDAEMON -- initialize daemon if not yet done.
190464562Sgshapiro**
190564562Sgshapiro**	Parameters:
190664562Sgshapiro**		none
190764562Sgshapiro**
190864562Sgshapiro**	Returns:
190964562Sgshapiro**		none
191064562Sgshapiro**
191164562Sgshapiro**	Side Effects:
191264562Sgshapiro**		initializes structure for one daemon.
191364562Sgshapiro*/
191490792Sgshapiro
191564562Sgshapirovoid
191664562Sgshapiroinitdaemon()
191764562Sgshapiro{
191890792Sgshapiro	if (NDaemons == 0)
191964562Sgshapiro	{
192090792Sgshapiro		Daemons[NDaemons].d_socket = -1;
192190792Sgshapiro		Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
192290792Sgshapiro		Daemons[NDaemons].d_name = "Daemon0";
192390792Sgshapiro		NDaemons = 1;
192464562Sgshapiro	}
192564562Sgshapiro}
192690792Sgshapiro/*
192764562Sgshapiro**  SETCLIENTOPTIONS -- set options for running the client
192864562Sgshapiro**
192964562Sgshapiro**	Parameters:
193064562Sgshapiro**		p -- the options line.
193164562Sgshapiro**
193264562Sgshapiro**	Returns:
193364562Sgshapiro**		none.
193464562Sgshapiro*/
193564562Sgshapiro
193690792Sgshapirostatic DAEMON_T	ClientSettings[AF_MAX + 1];
193764562Sgshapiro
193864562Sgshapirovoid
193964562Sgshapirosetclientoptions(p)
194064562Sgshapiro	register char *p;
194164562Sgshapiro{
194290792Sgshapiro	int family;
194390792Sgshapiro	DAEMON_T d;
194464562Sgshapiro
1945168515Sgshapiro	memset(&d, '\0', sizeof(d));
194664562Sgshapiro	setsockaddroptions(p, &d);
194764562Sgshapiro
194864562Sgshapiro	/* grab what we need */
194990792Sgshapiro	family = d.d_addr.sa.sa_family;
195090792Sgshapiro	STRUCTCOPY(d, ClientSettings[family]);
195190792Sgshapiro	setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
195290792Sgshapiro	if (d.d_name != NULL)
195390792Sgshapiro		ClientSettings[family].d_name = newstr(d.d_name);
195464562Sgshapiro	else
195590792Sgshapiro	{
195690792Sgshapiro		char num[30];
195790792Sgshapiro
1958168515Sgshapiro		(void) sm_snprintf(num, sizeof(num), "Client%d", family);
195990792Sgshapiro		ClientSettings[family].d_name = newstr(num);
196090792Sgshapiro	}
196164562Sgshapiro}
196290792Sgshapiro/*
196364562Sgshapiro**  ADDR_FAMILY -- determine address family from address
196464562Sgshapiro**
196564562Sgshapiro**	Parameters:
196664562Sgshapiro**		addr -- the string representation of the address
196764562Sgshapiro**
196864562Sgshapiro**	Returns:
196964562Sgshapiro**		AF_INET, AF_INET6 or AF_UNSPEC
197064562Sgshapiro**
197164562Sgshapiro**	Side Effects:
197264562Sgshapiro**		none.
197364562Sgshapiro*/
197464562Sgshapiro
197564562Sgshapirostatic int
197664562Sgshapiroaddr_family(addr)
197764562Sgshapiro	char *addr;
197864562Sgshapiro{
197990792Sgshapiro#if NETINET6
198064562Sgshapiro	SOCKADDR clt_addr;
198190792Sgshapiro#endif /* NETINET6 */
198264562Sgshapiro
198390792Sgshapiro#if NETINET
198464562Sgshapiro	if (inet_addr(addr) != INADDR_NONE)
198564562Sgshapiro	{
198664562Sgshapiro		if (tTd(16, 9))
198790792Sgshapiro			sm_dprintf("addr_family(%s): INET\n", addr);
198864562Sgshapiro		return AF_INET;
198964562Sgshapiro	}
199090792Sgshapiro#endif /* NETINET */
199190792Sgshapiro#if NETINET6
199290792Sgshapiro	if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
199364562Sgshapiro	{
199464562Sgshapiro		if (tTd(16, 9))
199590792Sgshapiro			sm_dprintf("addr_family(%s): INET6\n", addr);
199664562Sgshapiro		return AF_INET6;
199764562Sgshapiro	}
199890792Sgshapiro#endif /* NETINET6 */
199990792Sgshapiro#if _FFR_DAEMON_NETUNIX
200090792Sgshapiro# if NETUNIX
200190792Sgshapiro	if (*addr == '/')
200290792Sgshapiro	{
200390792Sgshapiro		if (tTd(16, 9))
200490792Sgshapiro			sm_dprintf("addr_family(%s): LOCAL\n", addr);
200590792Sgshapiro		return AF_UNIX;
200690792Sgshapiro	}
200790792Sgshapiro# endif /* NETUNIX */
200890792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
200964562Sgshapiro	if (tTd(16, 9))
201090792Sgshapiro		sm_dprintf("addr_family(%s): UNSPEC\n", addr);
201164562Sgshapiro	return AF_UNSPEC;
201264562Sgshapiro}
201390792Sgshapiro
201490792Sgshapiro/*
201590792Sgshapiro**  CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
201690792Sgshapiro**
201790792Sgshapiro**	Parameters:
201890792Sgshapiro**		flag -- the flag to test.
201990792Sgshapiro**
202090792Sgshapiro**	Returns:
202190792Sgshapiro**		true iff all configured clients have set the flag.
202290792Sgshapiro*/
202390792Sgshapiro
202490792Sgshapirobool
202590792Sgshapirochkclientmodifiers(flag)
202690792Sgshapiro	int flag;
202790792Sgshapiro{
202890792Sgshapiro	int i;
202990792Sgshapiro	bool flagisset;
203090792Sgshapiro
203190792Sgshapiro	flagisset = false;
203290792Sgshapiro	for (i = 0; i < AF_MAX; i++)
203390792Sgshapiro	{
203490792Sgshapiro		if (bitnset(D_ISSET, ClientSettings[i].d_flags))
203590792Sgshapiro		{
203690792Sgshapiro			if (!bitnset((char) flag, ClientSettings[i].d_flags))
203790792Sgshapiro				return false;
203890792Sgshapiro			flagisset = true;
203990792Sgshapiro		}
204090792Sgshapiro	}
204190792Sgshapiro	return flagisset;
204290792Sgshapiro}
204390792Sgshapiro
204490792Sgshapiro#if MILTER
204590792Sgshapiro/*
204690792Sgshapiro**  SETUP_DAEMON_FILTERS -- Parse per-socket filters
204790792Sgshapiro**
204890792Sgshapiro**	Parameters:
204990792Sgshapiro**		none
205090792Sgshapiro**
205190792Sgshapiro**	Returns:
205290792Sgshapiro**		none
205390792Sgshapiro*/
205490792Sgshapiro
205590792Sgshapirovoid
205690792Sgshapirosetup_daemon_milters()
205790792Sgshapiro{
205890792Sgshapiro	int idx;
205990792Sgshapiro
206090792Sgshapiro	if (OpMode == MD_SMTP)
206190792Sgshapiro	{
206290792Sgshapiro		/* no need to configure the daemons */
206390792Sgshapiro		return;
206490792Sgshapiro	}
206590792Sgshapiro
206690792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
206790792Sgshapiro	{
206890792Sgshapiro		if (Daemons[idx].d_inputfilterlist != NULL)
206990792Sgshapiro		{
207090792Sgshapiro			milter_config(Daemons[idx].d_inputfilterlist,
207190792Sgshapiro				      Daemons[idx].d_inputfilters,
207290792Sgshapiro				      MAXFILTERS);
207390792Sgshapiro		}
207490792Sgshapiro	}
207590792Sgshapiro}
207690792Sgshapiro#endif /* MILTER */
207790792Sgshapiro/*
207864562Sgshapiro**  MAKECONNECTION -- make a connection to an SMTP socket on a machine.
207964562Sgshapiro**
208064562Sgshapiro**	Parameters:
208138032Speter**		host -- the name of the host.
208238032Speter**		port -- the port number to connect to.
208338032Speter**		mci -- a pointer to the mail connection information
208438032Speter**			structure to be filled in.
208538032Speter**		e -- the current envelope.
208690792Sgshapiro**		enough -- time at which to stop further connection attempts.
208790792Sgshapiro**			(0 means no limit)
208838032Speter**
208938032Speter**	Returns:
209038032Speter**		An exit code telling whether the connection could be
209138032Speter**			made and if not why not.
209238032Speter**
209338032Speter**	Side Effects:
209438032Speter**		none.
209538032Speter*/
209638032Speter
209738032Speterstatic jmp_buf	CtxConnectTimeout;
209838032Speter
209938032SpeterSOCKADDR	CurHostAddr;		/* address of current host */
210038032Speter
210138032Speterint
210290792Sgshapiromakeconnection(host, port, mci, e, enough)
210338032Speter	char *host;
210490792Sgshapiro	volatile unsigned int port;
210538032Speter	register MCI *mci;
210638032Speter	ENVELOPE *e;
210790792Sgshapiro	time_t enough;
210838032Speter{
210938032Speter	register volatile int addrno = 0;
211090792Sgshapiro	volatile int s;
211190792Sgshapiro	register struct hostent *volatile hp = (struct hostent *) NULL;
211238032Speter	SOCKADDR addr;
211364562Sgshapiro	SOCKADDR clt_addr;
211464562Sgshapiro	int save_errno = 0;
211564562Sgshapiro	volatile SOCKADDR_LEN_T addrlen;
2116159609Sgshapiro	volatile bool firstconnect = true;
211790792Sgshapiro	SM_EVENT *volatile ev = NULL;
211890792Sgshapiro#if NETINET6
211990792Sgshapiro	volatile bool v6found = false;
212090792Sgshapiro#endif /* NETINET6 */
212164562Sgshapiro	volatile int family = InetMode;
212264562Sgshapiro	SOCKADDR_LEN_T len;
212364562Sgshapiro	volatile SOCKADDR_LEN_T socksize = 0;
212464562Sgshapiro	volatile bool clt_bind;
212564562Sgshapiro	BITMAP256 d_flags;
212664562Sgshapiro	char *p;
212764562Sgshapiro	extern ENVELOPE BlankEnvelope;
212838032Speter
212990792Sgshapiro	/* retranslate {daemon_flags} into bitmap */
213064562Sgshapiro	clrbitmap(d_flags);
213190792Sgshapiro	if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL)
213264562Sgshapiro	{
213364562Sgshapiro		for (; *p != '\0'; p++)
213464562Sgshapiro		{
213564562Sgshapiro			if (!(isascii(*p) && isspace(*p)))
213671345Sgshapiro				setbitn(bitidx(*p), d_flags);
213764562Sgshapiro		}
213864562Sgshapiro	}
213964562Sgshapiro
214090792Sgshapiro#if NETINET6
214164562Sgshapiro v4retry:
214290792Sgshapiro#endif /* NETINET6 */
214390792Sgshapiro	clt_bind = false;
214464562Sgshapiro
214564562Sgshapiro	/* Set up the address for outgoing connection. */
214664562Sgshapiro	if (bitnset(D_BINDIF, d_flags) &&
214790792Sgshapiro	    (p = macvalue(macid("{if_addr}"), e)) != NULL &&
214873188Sgshapiro	    *p != '\0')
214964562Sgshapiro	{
215090792Sgshapiro#if NETINET6
215164562Sgshapiro		char p6[INET6_ADDRSTRLEN];
215290792Sgshapiro#endif /* NETINET6 */
215364562Sgshapiro
2154168515Sgshapiro		memset(&clt_addr, '\0', sizeof(clt_addr));
215564562Sgshapiro
215664562Sgshapiro		/* infer the address family from the address itself */
215764562Sgshapiro		clt_addr.sa.sa_family = addr_family(p);
215864562Sgshapiro		switch (clt_addr.sa.sa_family)
215964562Sgshapiro		{
216090792Sgshapiro#if NETINET
216164562Sgshapiro		  case AF_INET:
216273188Sgshapiro			clt_addr.sin.sin_addr.s_addr = inet_addr(p);
216373188Sgshapiro			if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
2164203004Sgshapiro			    clt_addr.sin.sin_addr.s_addr !=
2165203004Sgshapiro				htonl(INADDR_LOOPBACK))
216664562Sgshapiro			{
216790792Sgshapiro				clt_bind = true;
2168168515Sgshapiro				socksize = sizeof(struct sockaddr_in);
216964562Sgshapiro			}
217064562Sgshapiro			break;
217190792Sgshapiro#endif /* NETINET */
217264562Sgshapiro
217390792Sgshapiro#if NETINET6
217464562Sgshapiro		  case AF_INET6:
217564562Sgshapiro			if (inet_addr(p) != INADDR_NONE)
2176168515Sgshapiro				(void) sm_snprintf(p6, sizeof(p6),
217790792Sgshapiro						   "IPv6:::ffff:%s", p);
217864562Sgshapiro			else
2179168515Sgshapiro				(void) sm_strlcpy(p6, p, sizeof(p6));
218090792Sgshapiro			if (anynet_pton(AF_INET6, p6,
218190792Sgshapiro					&clt_addr.sin6.sin6_addr) == 1 &&
218273188Sgshapiro			    !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
218364562Sgshapiro			{
218490792Sgshapiro				clt_bind = true;
2185168515Sgshapiro				socksize = sizeof(struct sockaddr_in6);
218664562Sgshapiro			}
218764562Sgshapiro			break;
218890792Sgshapiro#endif /* NETINET6 */
218964562Sgshapiro
219090792Sgshapiro#if 0
219164562Sgshapiro		  default:
219264562Sgshapiro			syserr("554 5.3.5 Address= option unsupported for family %d",
219364562Sgshapiro			       clt_addr.sa.sa_family);
219464562Sgshapiro			break;
219590792Sgshapiro#endif /* 0 */
219664562Sgshapiro		}
219764562Sgshapiro		if (clt_bind)
219864562Sgshapiro			family = clt_addr.sa.sa_family;
219964562Sgshapiro	}
220090792Sgshapiro
220190792Sgshapiro	/* D_BINDIF not set or not available, fallback to ClientPortOptions */
220290792Sgshapiro	if (!clt_bind)
220364562Sgshapiro	{
220490792Sgshapiro		STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
220564562Sgshapiro		switch (clt_addr.sa.sa_family)
220664562Sgshapiro		{
220790792Sgshapiro#if NETINET
220864562Sgshapiro		  case AF_INET:
220964562Sgshapiro			if (clt_addr.sin.sin_addr.s_addr == 0)
2210182352Sgshapiro				clt_addr.sin.sin_addr.s_addr = LocalDaemon ?
2211182352Sgshapiro					htonl(INADDR_LOOPBACK) : INADDR_ANY;
221264562Sgshapiro			else
221390792Sgshapiro				clt_bind = true;
221464562Sgshapiro			if (clt_addr.sin.sin_port != 0)
221590792Sgshapiro				clt_bind = true;
2216168515Sgshapiro			socksize = sizeof(struct sockaddr_in);
221764562Sgshapiro			break;
221890792Sgshapiro#endif /* NETINET */
221990792Sgshapiro#if NETINET6
222064562Sgshapiro		  case AF_INET6:
222164562Sgshapiro			if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
2222182352Sgshapiro				clt_addr.sin6.sin6_addr = LocalDaemon ?
2223182352Sgshapiro					in6addr_loopback : in6addr_any;
222464562Sgshapiro			else
222590792Sgshapiro				clt_bind = true;
2226168515Sgshapiro			socksize = sizeof(struct sockaddr_in6);
222764562Sgshapiro			if (clt_addr.sin6.sin6_port != 0)
222890792Sgshapiro				clt_bind = true;
222964562Sgshapiro			break;
223090792Sgshapiro#endif /* NETINET6 */
223190792Sgshapiro#if NETISO
223264562Sgshapiro		  case AF_ISO:
2233168515Sgshapiro			socksize = sizeof(clt_addr.siso);
223490792Sgshapiro			clt_bind = true;
223564562Sgshapiro			break;
223690792Sgshapiro#endif /* NETISO */
223764562Sgshapiro		  default:
223864562Sgshapiro			break;
223964562Sgshapiro		}
224064562Sgshapiro	}
224164562Sgshapiro
224238032Speter	/*
224338032Speter	**  Set up the address for the mailer.
224438032Speter	**	Accept "[a.b.c.d]" syntax for host name.
224538032Speter	*/
224638032Speter
224773188Sgshapiro	SM_SET_H_ERRNO(0);
224838032Speter	errno = 0;
2249168515Sgshapiro	memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
2250168515Sgshapiro	memset(&addr, '\0', sizeof(addr));
225138032Speter	SmtpPhase = mci->mci_phase = "initial connection";
225238032Speter	CurHostName = host;
225338032Speter
225438032Speter	if (host[0] == '[')
225538032Speter	{
225664562Sgshapiro		p = strchr(host, ']');
225738032Speter		if (p != NULL)
225838032Speter		{
225990792Sgshapiro#if NETINET
226064562Sgshapiro			unsigned long hid = INADDR_NONE;
226190792Sgshapiro#endif /* NETINET */
226290792Sgshapiro#if NETINET6
226364562Sgshapiro			struct sockaddr_in6 hid6;
226490792Sgshapiro#endif /* NETINET6 */
226564562Sgshapiro
226638032Speter			*p = '\0';
226790792Sgshapiro#if NETINET6
2268168515Sgshapiro			memset(&hid6, '\0', sizeof(hid6));
226990792Sgshapiro#endif /* NETINET6 */
227090792Sgshapiro#if NETINET
227164562Sgshapiro			if (family == AF_INET &&
227264562Sgshapiro			    (hid = inet_addr(&host[1])) != INADDR_NONE)
227338032Speter			{
227464562Sgshapiro				addr.sin.sin_family = AF_INET;
227564562Sgshapiro				addr.sin.sin_addr.s_addr = hid;
227664562Sgshapiro			}
227764562Sgshapiro			else
227890792Sgshapiro#endif /* NETINET */
227990792Sgshapiro#if NETINET6
228064562Sgshapiro			if (family == AF_INET6 &&
228190792Sgshapiro			    anynet_pton(AF_INET6, &host[1],
228290792Sgshapiro					&hid6.sin6_addr) == 1)
228364562Sgshapiro			{
228464562Sgshapiro				addr.sin6.sin6_family = AF_INET6;
228564562Sgshapiro				addr.sin6.sin6_addr = hid6.sin6_addr;
228664562Sgshapiro			}
228764562Sgshapiro			else
228890792Sgshapiro#endif /* NETINET6 */
228964562Sgshapiro			{
229038032Speter				/* try it as a host name (avoid MX lookup) */
229164562Sgshapiro				hp = sm_gethostbyname(&host[1], family);
229238032Speter				if (hp == NULL && p[-1] == '.')
229338032Speter				{
229490792Sgshapiro#if NAMED_BIND
229538032Speter					int oldopts = _res.options;
229638032Speter
229738032Speter					_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
229890792Sgshapiro#endif /* NAMED_BIND */
229938032Speter					p[-1] = '\0';
230064562Sgshapiro					hp = sm_gethostbyname(&host[1],
230164562Sgshapiro							      family);
230238032Speter					p[-1] = '.';
230390792Sgshapiro#if NAMED_BIND
230438032Speter					_res.options = oldopts;
230590792Sgshapiro#endif /* NAMED_BIND */
230638032Speter				}
230738032Speter				*p = ']';
230838032Speter				goto gothostent;
230938032Speter			}
231038032Speter			*p = ']';
231138032Speter		}
231238032Speter		if (p == NULL)
231338032Speter		{
231438032Speter			extern char MsgBuf[];
231538032Speter
231664562Sgshapiro			usrerrenh("5.1.2",
231764562Sgshapiro				  "553 Invalid numeric domain spec \"%s\"",
231864562Sgshapiro				  host);
231938032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
232064562Sgshapiro			errno = EINVAL;
232138032Speter			return EX_NOHOST;
232238032Speter		}
232338032Speter	}
232438032Speter	else
232538032Speter	{
232638032Speter		/* contortion to get around SGI cc complaints */
232738032Speter		{
232864562Sgshapiro			p = &host[strlen(host) - 1];
232964562Sgshapiro			hp = sm_gethostbyname(host, family);
233038032Speter			if (hp == NULL && *p == '.')
233138032Speter			{
233290792Sgshapiro#if NAMED_BIND
233338032Speter				int oldopts = _res.options;
233438032Speter
233538032Speter				_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
233690792Sgshapiro#endif /* NAMED_BIND */
233738032Speter				*p = '\0';
233864562Sgshapiro				hp = sm_gethostbyname(host, family);
233938032Speter				*p = '.';
234090792Sgshapiro#if NAMED_BIND
234138032Speter				_res.options = oldopts;
234290792Sgshapiro#endif /* NAMED_BIND */
234338032Speter			}
234438032Speter		}
234538032Spetergothostent:
2346203004Sgshapiro		if (hp == NULL || hp->h_addr == NULL)
234738032Speter		{
234890792Sgshapiro#if NAMED_BIND
234938032Speter			/* check for name server timeouts */
235090792Sgshapiro# if NETINET6
235190792Sgshapiro			if (WorkAroundBrokenAAAA && family == AF_INET6 &&
235290792Sgshapiro			    errno == ETIMEDOUT)
235338032Speter			{
235490792Sgshapiro				/*
235590792Sgshapiro				**  An attempt with family AF_INET may
235690792Sgshapiro				**  succeed By skipping the next section
235790792Sgshapiro				**  of code, we will try AF_INET before
235890792Sgshapiro				**  failing.
235990792Sgshapiro				*/
236090792Sgshapiro
236190792Sgshapiro				if (tTd(16, 10))
236290792Sgshapiro					sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
236338032Speter			}
236490792Sgshapiro			else
236590792Sgshapiro# endif /* NETINET6 */
236690792Sgshapiro			{
236790792Sgshapiro				if (errno == ETIMEDOUT ||
2368168515Sgshapiro# if _FFR_GETHBN_ExFILE
2369168515Sgshapiro#  ifdef EMFILE
2370168515Sgshapiro				   errno == EMFILE ||
2371168515Sgshapiro#  endif /* EMFILE */
2372168515Sgshapiro#  ifdef ENFILE
2373168515Sgshapiro				   errno == ENFILE ||
2374168515Sgshapiro#  endif /* ENFILE */
2375168515Sgshapiro# endif /* _FFR_GETHBN_ExFILE */
237690792Sgshapiro				    h_errno == TRY_AGAIN ||
237790792Sgshapiro				    (errno == ECONNREFUSED && UseNameServer))
237890792Sgshapiro				{
237990792Sgshapiro					save_errno = errno;
238090792Sgshapiro					mci_setstat(mci, EX_TEMPFAIL,
238190792Sgshapiro						    "4.4.3", NULL);
238290792Sgshapiro					errno = save_errno;
238390792Sgshapiro					return EX_TEMPFAIL;
238490792Sgshapiro				}
238590792Sgshapiro			}
238690792Sgshapiro#endif /* NAMED_BIND */
238790792Sgshapiro#if NETINET6
238864562Sgshapiro			/*
238964562Sgshapiro			**  Try v6 first, then fall back to v4.
239064562Sgshapiro			**  If we found a v6 address, but no v4
239164562Sgshapiro			**  addresses, then TEMPFAIL.
239264562Sgshapiro			*/
239364562Sgshapiro
239464562Sgshapiro			if (family == AF_INET6)
239564562Sgshapiro			{
239664562Sgshapiro				family = AF_INET;
239764562Sgshapiro				goto v4retry;
239864562Sgshapiro			}
239964562Sgshapiro			if (v6found)
240064562Sgshapiro				goto v6tempfail;
240190792Sgshapiro#endif /* NETINET6 */
240264562Sgshapiro			save_errno = errno;
240338032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
240464562Sgshapiro			errno = save_errno;
240564562Sgshapiro			return EX_NOHOST;
240638032Speter		}
240738032Speter		addr.sa.sa_family = hp->h_addrtype;
240838032Speter		switch (hp->h_addrtype)
240938032Speter		{
241090792Sgshapiro#if NETINET
241138032Speter		  case AF_INET:
241264562Sgshapiro			memmove(&addr.sin.sin_addr,
241364562Sgshapiro				hp->h_addr,
241438032Speter				INADDRSZ);
241538032Speter			break;
241690792Sgshapiro#endif /* NETINET */
241738032Speter
241890792Sgshapiro#if NETINET6
241964562Sgshapiro		  case AF_INET6:
242064562Sgshapiro			memmove(&addr.sin6.sin6_addr,
242164562Sgshapiro				hp->h_addr,
242264562Sgshapiro				IN6ADDRSZ);
242364562Sgshapiro			break;
242490792Sgshapiro#endif /* NETINET6 */
242564562Sgshapiro
242638032Speter		  default:
2427168515Sgshapiro			if (hp->h_length > sizeof(addr.sa.sa_data))
242838032Speter			{
242938032Speter				syserr("makeconnection: long sa_data: family %d len %d",
243038032Speter					hp->h_addrtype, hp->h_length);
243138032Speter				mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
243264562Sgshapiro				errno = EINVAL;
243338032Speter				return EX_NOHOST;
243438032Speter			}
243590792Sgshapiro			memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
243638032Speter			break;
243738032Speter		}
243838032Speter		addrno = 1;
243938032Speter	}
244038032Speter
244138032Speter	/*
244238032Speter	**  Determine the port number.
244338032Speter	*/
244438032Speter
244538032Speter	if (port == 0)
244638032Speter	{
244790792Sgshapiro#ifdef NO_GETSERVBYNAME
244864562Sgshapiro		port = htons(25);
244990792Sgshapiro#else /* NO_GETSERVBYNAME */
245038032Speter		register struct servent *sp = getservbyname("smtp", "tcp");
245138032Speter
245238032Speter		if (sp == NULL)
245338032Speter		{
245438032Speter			if (LogLevel > 2)
245538032Speter				sm_syslog(LOG_ERR, NOQID,
245664562Sgshapiro					  "makeconnection: service \"smtp\" unknown");
245738032Speter			port = htons(25);
245838032Speter		}
245938032Speter		else
246038032Speter			port = sp->s_port;
246190792Sgshapiro#endif /* NO_GETSERVBYNAME */
246238032Speter	}
246338032Speter
246490792Sgshapiro#if NETINET6
246590792Sgshapiro	if (addr.sa.sa_family == AF_INET6 &&
246690792Sgshapiro	    IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
246790792Sgshapiro	    ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
246890792Sgshapiro	{
246990792Sgshapiro		/*
247090792Sgshapiro		**  Ignore mapped IPv4 address since
247190792Sgshapiro		**  there is a ClientPortOptions setting
247290792Sgshapiro		**  for IPv4.
247390792Sgshapiro		*/
247490792Sgshapiro
247590792Sgshapiro		goto nextaddr;
247690792Sgshapiro	}
247790792Sgshapiro#endif /* NETINET6 */
247890792Sgshapiro
247938032Speter	switch (addr.sa.sa_family)
248038032Speter	{
248190792Sgshapiro#if NETINET
248238032Speter	  case AF_INET:
248338032Speter		addr.sin.sin_port = port;
2484168515Sgshapiro		addrlen = sizeof(struct sockaddr_in);
248538032Speter		break;
248690792Sgshapiro#endif /* NETINET */
248738032Speter
248890792Sgshapiro#if NETINET6
248964562Sgshapiro	  case AF_INET6:
249064562Sgshapiro		addr.sin6.sin6_port = port;
2491168515Sgshapiro		addrlen = sizeof(struct sockaddr_in6);
249264562Sgshapiro		break;
249390792Sgshapiro#endif /* NETINET6 */
249464562Sgshapiro
249590792Sgshapiro#if NETISO
249638032Speter	  case AF_ISO:
249738032Speter		/* assume two byte transport selector */
249864562Sgshapiro		memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
2499168515Sgshapiro		addrlen = sizeof(struct sockaddr_iso);
250038032Speter		break;
250190792Sgshapiro#endif /* NETISO */
250238032Speter
250338032Speter	  default:
250438032Speter		syserr("Can't connect to address family %d", addr.sa.sa_family);
250538032Speter		mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
250664562Sgshapiro		errno = EINVAL;
250790792Sgshapiro#if NETINET6
250871345Sgshapiro		if (hp != NULL)
250971345Sgshapiro			freehostent(hp);
251090792Sgshapiro#endif /* NETINET6 */
251164562Sgshapiro		return EX_NOHOST;
251238032Speter	}
251338032Speter
251438032Speter	/*
251538032Speter	**  Try to actually open the connection.
251638032Speter	*/
251738032Speter
251890792Sgshapiro#if XLA
251938032Speter	/* if too many connections, don't bother trying */
252038032Speter	if (!xla_noqueue_ok(host))
252171345Sgshapiro	{
252290792Sgshapiro# if NETINET6
252371345Sgshapiro		if (hp != NULL)
252471345Sgshapiro			freehostent(hp);
252590792Sgshapiro# endif /* NETINET6 */
252638032Speter		return EX_TEMPFAIL;
252771345Sgshapiro	}
252890792Sgshapiro#endif /* XLA */
252938032Speter
253038032Speter	for (;;)
253138032Speter	{
253238032Speter		if (tTd(16, 1))
253390792Sgshapiro			sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
253490792Sgshapiro				   host, anynet_ntoa(&addr), ntohs(port),
253590792Sgshapiro				   (int) addr.sa.sa_family);
253638032Speter
253738032Speter		/* save for logging */
253838032Speter		CurHostAddr = addr;
253938032Speter
254090792Sgshapiro#if HASRRESVPORT
254138032Speter		if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
254238032Speter		{
254338032Speter			int rport = IPPORT_RESERVED - 1;
254438032Speter
254538032Speter			s = rresvport(&rport);
254638032Speter		}
254738032Speter		else
254890792Sgshapiro#endif /* HASRRESVPORT */
254938032Speter		{
255090792Sgshapiro			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
255138032Speter		}
255238032Speter		if (s < 0)
255338032Speter		{
255464562Sgshapiro			save_errno = errno;
255538032Speter			syserr("makeconnection: cannot create socket");
255690792Sgshapiro#if XLA
255738032Speter			xla_host_end(host);
255890792Sgshapiro#endif /* XLA */
255938032Speter			mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
256090792Sgshapiro#if NETINET6
256171345Sgshapiro			if (hp != NULL)
256271345Sgshapiro				freehostent(hp);
256390792Sgshapiro#endif /* NETINET6 */
256464562Sgshapiro			errno = save_errno;
256538032Speter			return EX_TEMPFAIL;
256638032Speter		}
256738032Speter
256890792Sgshapiro#ifdef SO_SNDBUF
256990792Sgshapiro		if (ClientSettings[family].d_tcpsndbufsize > 0)
257038032Speter		{
257138032Speter			if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
257290792Sgshapiro				       (char *) &ClientSettings[family].d_tcpsndbufsize,
257390792Sgshapiro				       sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
257438032Speter				syserr("makeconnection: setsockopt(SO_SNDBUF)");
257538032Speter		}
257690792Sgshapiro#endif /* SO_SNDBUF */
257790792Sgshapiro#ifdef SO_RCVBUF
257890792Sgshapiro		if (ClientSettings[family].d_tcprcvbufsize > 0)
257964562Sgshapiro		{
258064562Sgshapiro			if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
258190792Sgshapiro				       (char *) &ClientSettings[family].d_tcprcvbufsize,
258290792Sgshapiro				       sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
258364562Sgshapiro				syserr("makeconnection: setsockopt(SO_RCVBUF)");
258464562Sgshapiro		}
258590792Sgshapiro#endif /* SO_RCVBUF */
258638032Speter
258738032Speter		if (tTd(16, 1))
258890792Sgshapiro			sm_dprintf("makeconnection: fd=%d\n", s);
258938032Speter
259038032Speter		/* turn on network debugging? */
259138032Speter		if (tTd(16, 101))
259238032Speter		{
259338032Speter			int on = 1;
259464562Sgshapiro
259538032Speter			(void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
2596168515Sgshapiro					  (char *)&on, sizeof(on));
259738032Speter		}
259890792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
259990792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
260090792Sgshapiro		errno = 0;		/* for debugging */
260138032Speter
260264562Sgshapiro		if (clt_bind)
260364562Sgshapiro		{
260464562Sgshapiro			int on = 1;
260564562Sgshapiro
260664562Sgshapiro			switch (clt_addr.sa.sa_family)
260764562Sgshapiro			{
260890792Sgshapiro#if NETINET
260964562Sgshapiro			  case AF_INET:
261064562Sgshapiro				if (clt_addr.sin.sin_port != 0)
261164562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
261264562Sgshapiro							  SO_REUSEADDR,
261364562Sgshapiro							  (char *) &on,
2614168515Sgshapiro							  sizeof(on));
261564562Sgshapiro				break;
261690792Sgshapiro#endif /* NETINET */
261764562Sgshapiro
261890792Sgshapiro#if NETINET6
261964562Sgshapiro			  case AF_INET6:
262064562Sgshapiro				if (clt_addr.sin6.sin6_port != 0)
262164562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
262264562Sgshapiro							  SO_REUSEADDR,
262364562Sgshapiro							  (char *) &on,
2624168515Sgshapiro							  sizeof(on));
262564562Sgshapiro				break;
262690792Sgshapiro#endif /* NETINET6 */
262764562Sgshapiro			}
262864562Sgshapiro
262964562Sgshapiro			if (bind(s, &clt_addr.sa, socksize) < 0)
263064562Sgshapiro			{
263164562Sgshapiro				save_errno = errno;
263264562Sgshapiro				(void) close(s);
263364562Sgshapiro				errno = save_errno;
263464562Sgshapiro				syserr("makeconnection: cannot bind socket [%s]",
263564562Sgshapiro				       anynet_ntoa(&clt_addr));
263690792Sgshapiro#if NETINET6
263771345Sgshapiro				if (hp != NULL)
263871345Sgshapiro					freehostent(hp);
263990792Sgshapiro#endif /* NETINET6 */
264064562Sgshapiro				errno = save_errno;
264164562Sgshapiro				return EX_TEMPFAIL;
264264562Sgshapiro			}
264364562Sgshapiro		}
264464562Sgshapiro
264538032Speter		/*
264638032Speter		**  Linux seems to hang in connect for 90 minutes (!!!).
264738032Speter		**  Time out the connect to avoid this problem.
264838032Speter		*/
264938032Speter
265038032Speter		if (setjmp(CtxConnectTimeout) == 0)
265138032Speter		{
265238032Speter			int i;
265338032Speter
265438032Speter			if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
265590792Sgshapiro				ev = sm_setevent(TimeOuts.to_iconnect,
265690792Sgshapiro						 connecttimeout, 0);
265738032Speter			else if (TimeOuts.to_connect != 0)
265890792Sgshapiro				ev = sm_setevent(TimeOuts.to_connect,
265990792Sgshapiro						 connecttimeout, 0);
266038032Speter			else
266138032Speter				ev = NULL;
266238032Speter
266364562Sgshapiro			switch (ConnectOnlyTo.sa.sa_family)
266464562Sgshapiro			{
266590792Sgshapiro#if NETINET
266664562Sgshapiro			  case AF_INET:
266764562Sgshapiro				addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
266864562Sgshapiro				break;
266990792Sgshapiro#endif /* NETINET */
267064562Sgshapiro
267190792Sgshapiro#if NETINET6
267264562Sgshapiro			  case AF_INET6:
267364562Sgshapiro				memmove(&addr.sin6.sin6_addr,
267464562Sgshapiro					&ConnectOnlyTo.sin6.sin6_addr,
267564562Sgshapiro					IN6ADDRSZ);
267664562Sgshapiro				break;
267790792Sgshapiro#endif /* NETINET6 */
267864562Sgshapiro			}
2679141858Sgshapiro			if (tTd(16, 1))
2680141858Sgshapiro				sm_dprintf("Connecting to [%s]...\n", anynet_ntoa(&addr));
268138032Speter			i = connect(s, (struct sockaddr *) &addr, addrlen);
268264562Sgshapiro			save_errno = errno;
268338032Speter			if (ev != NULL)
268490792Sgshapiro				sm_clrevent(ev);
268538032Speter			if (i >= 0)
268638032Speter				break;
268738032Speter		}
268838032Speter		else
268964562Sgshapiro			save_errno = errno;
269038032Speter
269194334Sgshapiro		/* couldn't connect.... figure out why */
269294334Sgshapiro		(void) close(s);
269394334Sgshapiro
269438032Speter		/* if running demand-dialed connection, try again */
269590792Sgshapiro		if (DialDelay > 0 && firstconnect &&
269690792Sgshapiro		    bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
269738032Speter		{
269838032Speter			if (tTd(16, 1))
269990792Sgshapiro				sm_dprintf("Connect failed (%s); trying again...\n",
270090792Sgshapiro					   sm_errstring(save_errno));
270190792Sgshapiro			firstconnect = false;
270264562Sgshapiro			(void) sleep(DialDelay);
270338032Speter			continue;
270438032Speter		}
270538032Speter
270690792Sgshapiro		if (LogLevel > 13)
270738032Speter			sm_syslog(LOG_INFO, e->e_id,
270838032Speter				  "makeconnection (%s [%s]) failed: %s",
270938032Speter				  host, anynet_ntoa(&addr),
271090792Sgshapiro				  sm_errstring(save_errno));
271138032Speter
271290792Sgshapiro#if NETINET6
271390792Sgshapironextaddr:
271490792Sgshapiro#endif /* NETINET6 */
271590792Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
271690792Sgshapiro		    (enough == 0 || curtime() < enough))
271738032Speter		{
271838032Speter			if (tTd(16, 1))
271990792Sgshapiro				sm_dprintf("Connect failed (%s); trying new address....\n",
272090792Sgshapiro					   sm_errstring(save_errno));
272138032Speter			switch (addr.sa.sa_family)
272238032Speter			{
272390792Sgshapiro#if NETINET
272438032Speter			  case AF_INET:
272564562Sgshapiro				memmove(&addr.sin.sin_addr,
272664562Sgshapiro					hp->h_addr_list[addrno++],
272764562Sgshapiro					INADDRSZ);
272838032Speter				break;
272990792Sgshapiro#endif /* NETINET */
273038032Speter
273190792Sgshapiro#if NETINET6
273264562Sgshapiro			  case AF_INET6:
273364562Sgshapiro				memmove(&addr.sin6.sin6_addr,
273464562Sgshapiro					hp->h_addr_list[addrno++],
273564562Sgshapiro					IN6ADDRSZ);
273664562Sgshapiro				break;
273790792Sgshapiro#endif /* NETINET6 */
273864562Sgshapiro
273938032Speter			  default:
274064562Sgshapiro				memmove(addr.sa.sa_data,
274164562Sgshapiro					hp->h_addr_list[addrno++],
274238032Speter					hp->h_length);
274338032Speter				break;
274438032Speter			}
274538032Speter			continue;
274638032Speter		}
274764562Sgshapiro		errno = save_errno;
274838032Speter
274990792Sgshapiro#if NETINET6
275064562Sgshapiro		if (family == AF_INET6)
275164562Sgshapiro		{
275264562Sgshapiro			if (tTd(16, 1))
275390792Sgshapiro				sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
275490792Sgshapiro					   sm_errstring(save_errno));
275590792Sgshapiro			v6found = true;
275664562Sgshapiro			family = AF_INET;
275771345Sgshapiro			if (hp != NULL)
275871345Sgshapiro			{
275971345Sgshapiro				freehostent(hp);
276071345Sgshapiro				hp = NULL;
276171345Sgshapiro			}
276264562Sgshapiro			goto v4retry;
276364562Sgshapiro		}
276464562Sgshapiro	v6tempfail:
276590792Sgshapiro#endif /* NETINET6 */
276638032Speter		/* couldn't open connection */
276790792Sgshapiro#if NETINET6
276864562Sgshapiro		/* Don't clobber an already saved errno from v4retry */
276964562Sgshapiro		if (errno > 0)
277090792Sgshapiro#endif /* NETINET6 */
277164562Sgshapiro			save_errno = errno;
277264562Sgshapiro		if (tTd(16, 1))
277390792Sgshapiro			sm_dprintf("Connect failed (%s)\n",
277490792Sgshapiro				   sm_errstring(save_errno));
277590792Sgshapiro#if XLA
277638032Speter		xla_host_end(host);
277790792Sgshapiro#endif /* XLA */
277838032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
277990792Sgshapiro#if NETINET6
278071345Sgshapiro		if (hp != NULL)
278171345Sgshapiro			freehostent(hp);
278290792Sgshapiro#endif /* NETINET6 */
278364562Sgshapiro		errno = save_errno;
278438032Speter		return EX_TEMPFAIL;
278538032Speter	}
278638032Speter
278790792Sgshapiro#if NETINET6
278871345Sgshapiro	if (hp != NULL)
278971345Sgshapiro	{
279071345Sgshapiro		freehostent(hp);
279171345Sgshapiro		hp = NULL;
279271345Sgshapiro	}
279390792Sgshapiro#endif /* NETINET6 */
279471345Sgshapiro
279538032Speter	/* connection ok, put it into canonical form */
279664562Sgshapiro	mci->mci_out = NULL;
279790792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
279890792Sgshapiro				       (void *) &s,
2799132943Sgshapiro				       SM_IO_WRONLY_B, NULL)) == NULL ||
280038032Speter	    (s = dup(s)) < 0 ||
280190792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
280290792Sgshapiro				      (void *) &s,
2803132943Sgshapiro				      SM_IO_RDONLY_B, NULL)) == NULL)
280438032Speter	{
280564562Sgshapiro		save_errno = errno;
280638032Speter		syserr("cannot open SMTP client channel, fd=%d", s);
280738032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
280864562Sgshapiro		if (mci->mci_out != NULL)
280990792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
281064562Sgshapiro		(void) close(s);
281164562Sgshapiro		errno = save_errno;
281238032Speter		return EX_TEMPFAIL;
281338032Speter	}
281490792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
281538032Speter
281690792Sgshapiro	/* set {client_flags} */
281790792Sgshapiro	if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
281890792Sgshapiro	{
281990792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
282090792Sgshapiro			  macid("{client_flags}"),
282190792Sgshapiro			  ClientSettings[addr.sa.sa_family].d_mflags);
282290792Sgshapiro	}
282390792Sgshapiro	else
282490792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
282590792Sgshapiro			  macid("{client_flags}"), "");
282690792Sgshapiro
282790792Sgshapiro	/* "add" {client_flags} to bitmap */
282890792Sgshapiro	if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
282990792Sgshapiro	{
283090792Sgshapiro		/* look for just this one flag */
283190792Sgshapiro		setbitn(D_IFNHELO, d_flags);
283290792Sgshapiro	}
283390792Sgshapiro
283464562Sgshapiro	/* find out name for Interface through which we connect */
2835168515Sgshapiro	len = sizeof(addr);
283664562Sgshapiro	if (getsockname(s, &addr.sa, &len) == 0)
283764562Sgshapiro	{
283864562Sgshapiro		char *name;
283990792Sgshapiro		char family[5];
284064562Sgshapiro
284190792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
284290792Sgshapiro			macid("{if_addr_out}"), anynet_ntoa(&addr));
284390792Sgshapiro		(void) sm_snprintf(family, sizeof(family), "%d",
284490792Sgshapiro			addr.sa.sa_family);
284590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
284690792Sgshapiro			macid("{if_family_out}"), family);
284764562Sgshapiro
284864562Sgshapiro		name = hostnamebyanyaddr(&addr);
284990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
285090792Sgshapiro			macid("{if_name_out}"), name);
285164562Sgshapiro		if (LogLevel > 11)
285264562Sgshapiro		{
285364562Sgshapiro			/* log connection information */
285464562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
285564562Sgshapiro				  "SMTP outgoing connect on %.40s", name);
285664562Sgshapiro		}
285764562Sgshapiro		if (bitnset(D_IFNHELO, d_flags))
285864562Sgshapiro		{
285964562Sgshapiro			if (name[0] != '[' && strchr(name, '.') != NULL)
286064562Sgshapiro				mci->mci_heloname = newstr(name);
286164562Sgshapiro		}
286264562Sgshapiro	}
286364562Sgshapiro	else
286464562Sgshapiro	{
286590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
286690792Sgshapiro			macid("{if_name_out}"), NULL);
286790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
286890792Sgshapiro			macid("{if_addr_out}"), NULL);
286990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
287090792Sgshapiro			macid("{if_family_out}"), NULL);
287164562Sgshapiro	}
2872132943Sgshapiro
2873132943Sgshapiro	/* Use the configured HeloName as appropriate */
2874132943Sgshapiro	if (HeloName != NULL && HeloName[0] != '\0')
2875132943Sgshapiro		mci->mci_heloname = newstr(HeloName);
2876132943Sgshapiro
287738032Speter	mci_setstat(mci, EX_OK, NULL, NULL);
287864562Sgshapiro	return EX_OK;
287938032Speter}
288064562Sgshapiro
288164562Sgshapirostatic void
2882141858Sgshapiroconnecttimeout(ignore)
2883141858Sgshapiro	int ignore;
288464562Sgshapiro{
288577349Sgshapiro	/*
288677349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
288777349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
288877349Sgshapiro	**	DOING.
288977349Sgshapiro	*/
289077349Sgshapiro
289164562Sgshapiro	errno = ETIMEDOUT;
289264562Sgshapiro	longjmp(CtxConnectTimeout, 1);
289364562Sgshapiro}
289490792Sgshapiro/*
289564562Sgshapiro**  MAKECONNECTION_DS -- make a connection to a domain socket.
289664562Sgshapiro**
289764562Sgshapiro**	Parameters:
289864562Sgshapiro**		mux_path -- the path of the socket to connect to.
289964562Sgshapiro**		mci -- a pointer to the mail connection information
290064562Sgshapiro**			structure to be filled in.
290164562Sgshapiro**
290264562Sgshapiro**	Returns:
290364562Sgshapiro**		An exit code telling whether the connection could be
290464562Sgshapiro**			made and if not why not.
290564562Sgshapiro**
290664562Sgshapiro**	Side Effects:
290764562Sgshapiro**		none.
290864562Sgshapiro*/
290964562Sgshapiro
291090792Sgshapiro#if NETUNIX
291190792Sgshapiroint
291290792Sgshapiromakeconnection_ds(mux_path, mci)
291364562Sgshapiro	char *mux_path;
291464562Sgshapiro	register MCI *mci;
291564562Sgshapiro{
291664562Sgshapiro	int sock;
291764562Sgshapiro	int rval, save_errno;
291864562Sgshapiro	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK;
291964562Sgshapiro	struct sockaddr_un unix_addr;
292064562Sgshapiro
292164562Sgshapiro	/* if not safe, don't connect */
292264562Sgshapiro	rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName,
292364562Sgshapiro			sff, S_IRUSR|S_IWUSR, NULL);
292464562Sgshapiro
292564562Sgshapiro	if (rval != 0)
292664562Sgshapiro	{
2927132943Sgshapiro		syserr("makeconnection_ds: unsafe domain socket %s",
2928132943Sgshapiro			mux_path);
292964562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL);
293064562Sgshapiro		errno = rval;
293164562Sgshapiro		return EX_TEMPFAIL;
293264562Sgshapiro	}
293364562Sgshapiro
293464562Sgshapiro	/* prepare address structure */
2935168515Sgshapiro	memset(&unix_addr, '\0', sizeof(unix_addr));
293664562Sgshapiro	unix_addr.sun_family = AF_UNIX;
293764562Sgshapiro
2938168515Sgshapiro	if (strlen(mux_path) >= sizeof(unix_addr.sun_path))
293964562Sgshapiro	{
2940132943Sgshapiro		syserr("makeconnection_ds: domain socket name %s too long",
2941132943Sgshapiro			mux_path);
294290792Sgshapiro
294390792Sgshapiro		/* XXX why TEMPFAIL but 5.x.y ? */
294464562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
294564562Sgshapiro		errno = ENAMETOOLONG;
294664562Sgshapiro		return EX_UNAVAILABLE;
294764562Sgshapiro	}
294890792Sgshapiro	(void) sm_strlcpy(unix_addr.sun_path, mux_path,
2949168515Sgshapiro			  sizeof(unix_addr.sun_path));
295064562Sgshapiro
295164562Sgshapiro	/* initialize domain socket */
295264562Sgshapiro	sock = socket(AF_UNIX, SOCK_STREAM, 0);
295364562Sgshapiro	if (sock == -1)
295464562Sgshapiro	{
295564562Sgshapiro		save_errno = errno;
2956132943Sgshapiro		syserr("makeconnection_ds: could not create domain socket %s",
2957132943Sgshapiro			mux_path);
295864562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
295964562Sgshapiro		errno = save_errno;
296064562Sgshapiro		return EX_TEMPFAIL;
296164562Sgshapiro	}
296264562Sgshapiro
296364562Sgshapiro	/* connect to server */
296464562Sgshapiro	if (connect(sock, (struct sockaddr *) &unix_addr,
296564562Sgshapiro		    sizeof(unix_addr)) == -1)
296664562Sgshapiro	{
296764562Sgshapiro		save_errno = errno;
296864562Sgshapiro		syserr("Could not connect to socket %s", mux_path);
296964562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
297064562Sgshapiro		(void) close(sock);
297164562Sgshapiro		errno = save_errno;
297264562Sgshapiro		return EX_TEMPFAIL;
297364562Sgshapiro	}
297464562Sgshapiro
297564562Sgshapiro	/* connection ok, put it into canonical form */
297664562Sgshapiro	mci->mci_out = NULL;
297790792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2978132943Sgshapiro				       (void *) &sock, SM_IO_WRONLY_B, NULL))
297990792Sgshapiro					== NULL
298090792Sgshapiro	    || (sock = dup(sock)) < 0 ||
298190792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2982132943Sgshapiro				      (void *) &sock, SM_IO_RDONLY_B, NULL))
298390792Sgshapiro					== NULL)
298464562Sgshapiro	{
298564562Sgshapiro		save_errno = errno;
298664562Sgshapiro		syserr("cannot open SMTP client channel, fd=%d", sock);
298764562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
298864562Sgshapiro		if (mci->mci_out != NULL)
298990792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
299064562Sgshapiro		(void) close(sock);
299164562Sgshapiro		errno = save_errno;
299264562Sgshapiro		return EX_TEMPFAIL;
299364562Sgshapiro	}
299490792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
299564562Sgshapiro
299664562Sgshapiro	mci_setstat(mci, EX_OK, NULL, NULL);
299764562Sgshapiro	errno = 0;
299864562Sgshapiro	return EX_OK;
299964562Sgshapiro}
300090792Sgshapiro#endif /* NETUNIX */
300190792Sgshapiro/*
300290792Sgshapiro**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
300377349Sgshapiro**
300477349Sgshapiro**	Parameters:
300590792Sgshapiro**		none.
300677349Sgshapiro**
300777349Sgshapiro**	Returns:
300877349Sgshapiro**		none.
300977349Sgshapiro**
301077349Sgshapiro**	Side Effects:
301190792Sgshapiro**		closes control socket, exits.
301277349Sgshapiro*/
301377349Sgshapiro
301490792Sgshapirovoid
301590792Sgshapiroshutdown_daemon()
301677349Sgshapiro{
301790792Sgshapiro	int i;
301890792Sgshapiro	char *reason;
301977349Sgshapiro
302090792Sgshapiro	sm_allsignals(true);
302190792Sgshapiro
302290792Sgshapiro	reason = ShutdownRequest;
302390792Sgshapiro	ShutdownRequest = NULL;
302490792Sgshapiro	PendingSignal = 0;
302590792Sgshapiro
3026132943Sgshapiro	if (LogLevel > 9)
3027132943Sgshapiro		sm_syslog(LOG_INFO, CurEnv->e_id, "stopping daemon, reason=%s",
302890792Sgshapiro			  reason == NULL ? "implicit call" : reason);
302990792Sgshapiro
303090792Sgshapiro	FileName = NULL;
303190792Sgshapiro	closecontrolsocket(true);
303290792Sgshapiro#if XLA
303390792Sgshapiro	xla_all_end();
303490792Sgshapiro#endif /* XLA */
303590792Sgshapiro
303690792Sgshapiro	for (i = 0; i < NDaemons; i++)
303790792Sgshapiro	{
303890792Sgshapiro		if (Daemons[i].d_socket >= 0)
303990792Sgshapiro		{
304090792Sgshapiro			(void) close(Daemons[i].d_socket);
304190792Sgshapiro			Daemons[i].d_socket = -1;
304290792Sgshapiro
304390792Sgshapiro#if _FFR_DAEMON_NETUNIX
304490792Sgshapiro# if NETUNIX
304590792Sgshapiro			/* Remove named sockets */
304690792Sgshapiro			if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
304790792Sgshapiro			{
304890792Sgshapiro				int rval;
304990792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
305090792Sgshapiro
305190792Sgshapiro				/* if not safe, don't use it */
305290792Sgshapiro				rval = safefile(Daemons[i].d_addr.sunix.sun_path,
305390792Sgshapiro						RunAsUid, RunAsGid,
305490792Sgshapiro						RunAsUserName, sff,
305590792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
305690792Sgshapiro				if (rval == 0 &&
305790792Sgshapiro				    unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
305890792Sgshapiro				{
305990792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
306090792Sgshapiro						  "Could not remove daemon %s socket: %s: %s",
306190792Sgshapiro						  Daemons[i].d_name,
306290792Sgshapiro						  Daemons[i].d_addr.sunix.sun_path,
306390792Sgshapiro						  sm_errstring(errno));
306490792Sgshapiro				}
306590792Sgshapiro			}
306690792Sgshapiro# endif /* NETUNIX */
306790792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
306890792Sgshapiro		}
306990792Sgshapiro	}
307090792Sgshapiro
307190792Sgshapiro	finis(false, true, EX_OK);
307277349Sgshapiro}
307390792Sgshapiro/*
307477349Sgshapiro**  RESTART_DAEMON -- Performs a clean restart of the daemon
307577349Sgshapiro**
307677349Sgshapiro**	Parameters:
307777349Sgshapiro**		none.
307877349Sgshapiro**
307977349Sgshapiro**	Returns:
308077349Sgshapiro**		none.
308177349Sgshapiro**
308277349Sgshapiro**	Side Effects:
308377349Sgshapiro**		restarts the daemon or exits if restart fails.
308477349Sgshapiro*/
308577349Sgshapiro
308680785Sgshapiro/* Make a non-DFL/IGN signal a noop */
308780785Sgshapiro#define SM_NOOP_SIGNAL(sig, old)				\
308880785Sgshapirodo								\
308980785Sgshapiro{								\
309090792Sgshapiro	(old) = sm_signal((sig), sm_signal_noop);		\
309180785Sgshapiro	if ((old) == SIG_IGN || (old) == SIG_DFL)		\
309290792Sgshapiro		(void) sm_signal((sig), (old));			\
309380785Sgshapiro} while (0)
309480785Sgshapiro
309590792Sgshapirovoid
309677349Sgshapirorestart_daemon()
309777349Sgshapiro{
309890792Sgshapiro	bool drop;
309977349Sgshapiro	int save_errno;
310077349Sgshapiro	char *reason;
310180785Sgshapiro	sigfunc_t ignore, oalrm, ousr1;
310277349Sgshapiro	extern int DtableSize;
310377349Sgshapiro
310480785Sgshapiro	/* clear the events to turn off SIGALRMs */
310590792Sgshapiro	sm_clear_events();
310690792Sgshapiro	sm_allsignals(true);
310777349Sgshapiro
310877349Sgshapiro	reason = RestartRequest;
310977349Sgshapiro	RestartRequest = NULL;
311077349Sgshapiro	PendingSignal = 0;
311177349Sgshapiro
311277349Sgshapiro	if (SaveArgv[0][0] != '/')
311377349Sgshapiro	{
311477349Sgshapiro		if (LogLevel > 3)
311577349Sgshapiro			sm_syslog(LOG_INFO, NOQID,
311677349Sgshapiro				  "could not restart: need full path");
311790792Sgshapiro		finis(false, true, EX_OSFILE);
311890792Sgshapiro		/* NOTREACHED */
311977349Sgshapiro	}
312077349Sgshapiro	if (LogLevel > 3)
312177349Sgshapiro		sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
312277349Sgshapiro			  SaveArgv[0],
312377349Sgshapiro			  reason == NULL ? "implicit call" : reason);
312477349Sgshapiro
312590792Sgshapiro	closecontrolsocket(true);
312698121Sgshapiro#if SM_CONF_SHM
312798121Sgshapiro	cleanup_shm(DaemonPid == getpid());
312898121Sgshapiro#endif /* SM_CONF_SHM */
312990792Sgshapiro
3130132943Sgshapiro	/* close locked pid file */
3131132943Sgshapiro	close_sendmail_pid();
3132132943Sgshapiro
313390792Sgshapiro	/*
313490792Sgshapiro	**  Want to drop to the user who started the process in all cases
313590792Sgshapiro	**  *but* when running as "smmsp" for the clientmqueue queue run
313690792Sgshapiro	**  daemon.  In that case, UseMSP will be true, RunAsUid should not
313790792Sgshapiro	**  be root, and RealUid should be either 0 or RunAsUid.
313890792Sgshapiro	*/
313990792Sgshapiro
314090792Sgshapiro	drop = !(UseMSP && RunAsUid != 0 &&
314190792Sgshapiro		 (RealUid == 0 || RealUid == RunAsUid));
314290792Sgshapiro
314390792Sgshapiro	if (drop_privileges(drop) != EX_OK)
314477349Sgshapiro	{
314577349Sgshapiro		if (LogLevel > 0)
314677349Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
314790792Sgshapiro				  "could not drop privileges: %s",
314890792Sgshapiro				  sm_errstring(errno));
314990792Sgshapiro		finis(false, true, EX_OSERR);
315090792Sgshapiro		/* NOTREACHED */
315177349Sgshapiro	}
315277349Sgshapiro
3153132943Sgshapiro	sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
315477349Sgshapiro
315580785Sgshapiro	/*
315680785Sgshapiro	**  Need to allow signals before execve() to make them "harmless".
315780785Sgshapiro	**  However, the default action can be "terminate", so it isn't
315880785Sgshapiro	**  really harmless.  Setting signals to IGN will cause them to be
315980785Sgshapiro	**  ignored in the new process to, so that isn't a good alternative.
316080785Sgshapiro	*/
316180785Sgshapiro
316280785Sgshapiro	SM_NOOP_SIGNAL(SIGALRM, oalrm);
316380785Sgshapiro	SM_NOOP_SIGNAL(SIGCHLD, ignore);
316480785Sgshapiro	SM_NOOP_SIGNAL(SIGHUP, ignore);
316580785Sgshapiro	SM_NOOP_SIGNAL(SIGINT, ignore);
316680785Sgshapiro	SM_NOOP_SIGNAL(SIGPIPE, ignore);
316780785Sgshapiro	SM_NOOP_SIGNAL(SIGTERM, ignore);
316880785Sgshapiro#ifdef SIGUSR1
316980785Sgshapiro	SM_NOOP_SIGNAL(SIGUSR1, ousr1);
317080785Sgshapiro#endif /* SIGUSR1 */
317194334Sgshapiro
317294334Sgshapiro	/* Turn back on signals */
317390792Sgshapiro	sm_allsignals(false);
317477349Sgshapiro
317577349Sgshapiro	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
317677349Sgshapiro	save_errno = errno;
317777349Sgshapiro
317880785Sgshapiro	/* block signals again and restore needed signals */
317990792Sgshapiro	sm_allsignals(true);
318080785Sgshapiro
318180785Sgshapiro	/* For finis() events */
318290792Sgshapiro	(void) sm_signal(SIGALRM, oalrm);
318380785Sgshapiro
318480785Sgshapiro#ifdef SIGUSR1
318580785Sgshapiro	/* For debugging finis() */
318690792Sgshapiro	(void) sm_signal(SIGUSR1, ousr1);
318780785Sgshapiro#endif /* SIGUSR1 */
318877349Sgshapiro
318977349Sgshapiro	errno = save_errno;
319077349Sgshapiro	if (LogLevel > 0)
319190792Sgshapiro		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
319290792Sgshapiro			  SaveArgv[0], sm_errstring(errno));
319390792Sgshapiro	finis(false, true, EX_OSFILE);
319490792Sgshapiro	/* NOTREACHED */
319577349Sgshapiro}
319690792Sgshapiro/*
319738032Speter**  MYHOSTNAME -- return the name of this host.
319838032Speter**
319938032Speter**	Parameters:
320038032Speter**		hostbuf -- a place to return the name of this host.
320138032Speter**		size -- the size of hostbuf.
320238032Speter**
320338032Speter**	Returns:
320438032Speter**		A list of aliases for this host.
320538032Speter**
320638032Speter**	Side Effects:
320738032Speter**		Adds numeric codes to $=w.
320838032Speter*/
320938032Speter
321038032Speterstruct hostent *
321138032Spetermyhostname(hostbuf, size)
321238032Speter	char hostbuf[];
321338032Speter	int size;
321438032Speter{
321538032Speter	register struct hostent *hp;
321638032Speter
321773188Sgshapiro	if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
321890792Sgshapiro		(void) sm_strlcpy(hostbuf, "localhost", size);
321964562Sgshapiro	hp = sm_gethostbyname(hostbuf, InetMode);
322090792Sgshapiro#if NETINET && NETINET6
322180785Sgshapiro	if (hp == NULL && InetMode == AF_INET6)
322280785Sgshapiro	{
322380785Sgshapiro		/*
322480785Sgshapiro		**  It's possible that this IPv6 enabled machine doesn't
322580785Sgshapiro		**  actually have any IPv6 interfaces and, therefore, no
322680785Sgshapiro		**  IPv6 addresses.  Fall back to AF_INET.
322780785Sgshapiro		*/
322880785Sgshapiro
322980785Sgshapiro		hp = sm_gethostbyname(hostbuf, AF_INET);
323080785Sgshapiro	}
323190792Sgshapiro#endif /* NETINET && NETINET6 */
323238032Speter	if (hp == NULL)
323338032Speter		return NULL;
323438032Speter	if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
323564562Sgshapiro		(void) cleanstrcpy(hostbuf, hp->h_name, size);
323664562Sgshapiro
323790792Sgshapiro#if NETINFO
323864562Sgshapiro	if (strchr(hostbuf, '.') == NULL)
323938032Speter	{
324064562Sgshapiro		char *domainname;
324164562Sgshapiro
324264562Sgshapiro		domainname = ni_propval("/locations", NULL, "resolver",
324364562Sgshapiro					"domain", '\0');
324464562Sgshapiro		if (domainname != NULL &&
324564562Sgshapiro		    strlen(domainname) + strlen(hostbuf) + 1 < size)
324690792Sgshapiro			(void) sm_strlcat2(hostbuf, ".", domainname, size);
324738032Speter	}
324890792Sgshapiro#endif /* NETINFO */
324938032Speter
325038032Speter	/*
325138032Speter	**  If there is still no dot in the name, try looking for a
325238032Speter	**  dotted alias.
325338032Speter	*/
325438032Speter
325538032Speter	if (strchr(hostbuf, '.') == NULL)
325638032Speter	{
325738032Speter		char **ha;
325838032Speter
325964562Sgshapiro		for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
326038032Speter		{
326138032Speter			if (strchr(*ha, '.') != NULL)
326238032Speter			{
326364562Sgshapiro				(void) cleanstrcpy(hostbuf, *ha, size - 1);
326438032Speter				hostbuf[size - 1] = '\0';
326538032Speter				break;
326638032Speter			}
326738032Speter		}
326838032Speter	}
326938032Speter
327038032Speter	/*
327138032Speter	**  If _still_ no dot, wait for a while and try again -- it is
327238032Speter	**  possible that some service is starting up.  This can result
327338032Speter	**  in excessive delays if the system is badly configured, but
327438032Speter	**  there really isn't a way around that, particularly given that
327538032Speter	**  the config file hasn't been read at this point.
327638032Speter	**  All in all, a bit of a mess.
327738032Speter	*/
327838032Speter
327938032Speter	if (strchr(hostbuf, '.') == NULL &&
328090792Sgshapiro	    !getcanonname(hostbuf, size, true, NULL))
328138032Speter	{
3282182352Sgshapiro		sm_syslog(LocalDaemon ? LOG_WARNING : LOG_CRIT, NOQID,
328364562Sgshapiro			  "My unqualified host name (%s) unknown; sleeping for retry",
328464562Sgshapiro			  hostbuf);
328538032Speter		message("My unqualified host name (%s) unknown; sleeping for retry",
328638032Speter			hostbuf);
328764562Sgshapiro		(void) sleep(60);
328890792Sgshapiro		if (!getcanonname(hostbuf, size, true, NULL))
328938032Speter		{
3290182352Sgshapiro			sm_syslog(LocalDaemon ? LOG_WARNING : LOG_ALERT, NOQID,
329164562Sgshapiro				  "unable to qualify my own domain name (%s) -- using short name",
329264562Sgshapiro				  hostbuf);
329338032Speter			message("WARNING: unable to qualify my own domain name (%s) -- using short name",
329438032Speter				hostbuf);
329538032Speter		}
329638032Speter	}
329764562Sgshapiro	return hp;
329838032Speter}
329990792Sgshapiro/*
330038032Speter**  ADDRCMP -- compare two host addresses
330138032Speter**
330238032Speter**	Parameters:
330338032Speter**		hp -- hostent structure for the first address
330438032Speter**		ha -- actual first address
330538032Speter**		sa -- second address
330638032Speter**
330738032Speter**	Returns:
330838032Speter**		0 -- if ha and sa match
330938032Speter**		else -- they don't match
331038032Speter*/
331138032Speter
331264562Sgshapirostatic int
331338032Speteraddrcmp(hp, ha, sa)
331438032Speter	struct hostent *hp;
331538032Speter	char *ha;
331638032Speter	SOCKADDR *sa;
331738032Speter{
331890792Sgshapiro#if NETINET6
331990792Sgshapiro	unsigned char *a;
332090792Sgshapiro#endif /* NETINET6 */
332164562Sgshapiro
332238032Speter	switch (sa->sa.sa_family)
332338032Speter	{
332490792Sgshapiro#if NETINET
332538032Speter	  case AF_INET:
332638032Speter		if (hp->h_addrtype == AF_INET)
332764562Sgshapiro			return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
332838032Speter		break;
332990792Sgshapiro#endif /* NETINET */
333038032Speter
333190792Sgshapiro#if NETINET6
333264562Sgshapiro	  case AF_INET6:
333390792Sgshapiro		a = (unsigned char *) &sa->sin6.sin6_addr;
333464562Sgshapiro
333564562Sgshapiro		/* Straight binary comparison */
333664562Sgshapiro		if (hp->h_addrtype == AF_INET6)
333764562Sgshapiro			return memcmp(ha, a, IN6ADDRSZ);
333864562Sgshapiro
333964562Sgshapiro		/* If IPv4-mapped IPv6 address, compare the IPv4 section */
334064562Sgshapiro		if (hp->h_addrtype == AF_INET &&
334164562Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
334264562Sgshapiro			return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
334364562Sgshapiro		break;
334490792Sgshapiro#endif /* NETINET6 */
334538032Speter	}
334638032Speter	return -1;
334738032Speter}
334890792Sgshapiro/*
334964562Sgshapiro**  GETAUTHINFO -- get the real host name associated with a file descriptor
335038032Speter**
335138032Speter**	Uses RFC1413 protocol to try to get info from the other end.
335238032Speter**
335338032Speter**	Parameters:
335438032Speter**		fd -- the descriptor
335590792Sgshapiro**		may_be_forged -- an outage that is set to true if the
335638032Speter**			forward lookup of RealHostName does not match
335790792Sgshapiro**			RealHostAddr; set to false if they do match.
335838032Speter**
335938032Speter**	Returns:
336038032Speter**		The user@host information associated with this descriptor.
336138032Speter*/
336238032Speter
336338032Speterstatic jmp_buf	CtxAuthTimeout;
336438032Speter
336538032Speterstatic void
3366141858Sgshapiroauthtimeout(ignore)
3367141858Sgshapiro	int ignore;
336838032Speter{
336977349Sgshapiro	/*
337077349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
337177349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
337277349Sgshapiro	**	DOING.
337377349Sgshapiro	*/
337477349Sgshapiro
337577349Sgshapiro	errno = ETIMEDOUT;
337638032Speter	longjmp(CtxAuthTimeout, 1);
337738032Speter}
337838032Speter
337938032Speterchar *
338038032Spetergetauthinfo(fd, may_be_forged)
338138032Speter	int fd;
338238032Speter	bool *may_be_forged;
338338032Speter{
338490792Sgshapiro	unsigned short SM_NONVOLATILE port = 0;
338538032Speter	SOCKADDR_LEN_T falen;
338638032Speter	register char *volatile p = NULL;
338738032Speter	SOCKADDR la;
338838032Speter	SOCKADDR_LEN_T lalen;
338990792Sgshapiro#ifndef NO_GETSERVBYNAME
339038032Speter	register struct servent *sp;
339190792Sgshapiro# if NETINET
339290792Sgshapiro	static unsigned short port4 = 0;
339390792Sgshapiro# endif /* NETINET */
339490792Sgshapiro# if NETINET6
339590792Sgshapiro	static unsigned short port6 = 0;
339690792Sgshapiro# endif /* NETINET6 */
339790792Sgshapiro#endif /* ! NO_GETSERVBYNAME */
339838032Speter	volatile int s;
339938032Speter	int i = 0;
340090792Sgshapiro	size_t len;
340190792Sgshapiro	SM_EVENT *ev;
340238032Speter	int nleft;
340338032Speter	struct hostent *hp;
340438032Speter	char *ostype = NULL;
340538032Speter	char **ha;
340638032Speter	char ibuf[MAXNAME + 1];
3407110560Sgshapiro	static char hbuf[MAXNAME + MAXAUTHINFO + 11];
340838032Speter
340990792Sgshapiro	*may_be_forged = false;
3410168515Sgshapiro	falen = sizeof(RealHostAddr);
341138032Speter	if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
341238032Speter	    falen <= 0 || RealHostAddr.sa.sa_family == 0)
341338032Speter	{
341464562Sgshapiro		if (i < 0)
341564562Sgshapiro		{
341664562Sgshapiro			/*
341764562Sgshapiro			**  ENOTSOCK is OK: bail on anything else, but reset
341864562Sgshapiro			**  errno in this case, so a mis-report doesn't
341964562Sgshapiro			**  happen later.
342064562Sgshapiro			*/
342190792Sgshapiro
342264562Sgshapiro			if (errno != ENOTSOCK)
342364562Sgshapiro				return NULL;
342464562Sgshapiro			errno = 0;
342564562Sgshapiro		}
3426168515Sgshapiro		(void) sm_strlcpyn(hbuf, sizeof(hbuf), 2, RealUserName,
342790792Sgshapiro				   "@localhost");
342838032Speter		if (tTd(9, 1))
342990792Sgshapiro			sm_dprintf("getauthinfo: %s\n", hbuf);
343038032Speter		return hbuf;
343138032Speter	}
343238032Speter
343338032Speter	if (RealHostName == NULL)
343438032Speter	{
343538032Speter		/* translate that to a host name */
343638032Speter		RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
343738032Speter		if (strlen(RealHostName) > MAXNAME)
343890792Sgshapiro			RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
343938032Speter	}
344038032Speter
344138032Speter	/* cross check RealHostName with forward DNS lookup */
344290792Sgshapiro	if (anynet_ntoa(&RealHostAddr)[0] != '[' &&
344390792Sgshapiro	    RealHostName[0] != '[')
344438032Speter	{
344580785Sgshapiro		int family;
344680785Sgshapiro
344780785Sgshapiro		family = RealHostAddr.sa.sa_family;
344890792Sgshapiro#if NETINET6 && NEEDSGETIPNODE
344980785Sgshapiro		/*
345080785Sgshapiro		**  If RealHostAddr is an IPv6 connection with an
345180785Sgshapiro		**  IPv4-mapped address, we need RealHostName's IPv4
345280785Sgshapiro		**  address(es) for addrcmp() to compare against
345380785Sgshapiro		**  RealHostAddr.
345480785Sgshapiro		**
345580785Sgshapiro		**  Actually, we only need to do this for systems
345680785Sgshapiro		**  which NEEDSGETIPNODE since the real getipnodebyname()
345780785Sgshapiro		**  already does V4MAPPED address via the AI_V4MAPPEDCFG
345880785Sgshapiro		**  flag.  A better fix to this problem is to add this
345980785Sgshapiro		**  functionality to our stub getipnodebyname().
346080785Sgshapiro		*/
346180785Sgshapiro
346280785Sgshapiro		if (family == AF_INET6 &&
346380785Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
346480785Sgshapiro			family = AF_INET;
346590792Sgshapiro#endif /* NETINET6 && NEEDSGETIPNODE */
346680785Sgshapiro
346738032Speter		/* try to match the reverse against the forward lookup */
346880785Sgshapiro		hp = sm_gethostbyname(RealHostName, family);
346938032Speter		if (hp == NULL)
3470120256Sgshapiro		{
3471132943Sgshapiro			/* XXX: Could be a temporary error on forward lookup */
347290792Sgshapiro			*may_be_forged = true;
3473120256Sgshapiro		}
347438032Speter		else
347538032Speter		{
347638032Speter			for (ha = hp->h_addr_list; *ha != NULL; ha++)
347790792Sgshapiro			{
347838032Speter				if (addrcmp(hp, *ha, &RealHostAddr) == 0)
347938032Speter					break;
348090792Sgshapiro			}
348138032Speter			*may_be_forged = *ha == NULL;
348290792Sgshapiro#if NETINET6
348371345Sgshapiro			freehostent(hp);
348471345Sgshapiro			hp = NULL;
348590792Sgshapiro#endif /* NETINET6 */
348638032Speter		}
348738032Speter	}
348838032Speter
348938032Speter	if (TimeOuts.to_ident == 0)
349038032Speter		goto noident;
349138032Speter
3492168515Sgshapiro	lalen = sizeof(la);
349364562Sgshapiro	switch (RealHostAddr.sa.sa_family)
349438032Speter	{
349590792Sgshapiro#if NETINET
349664562Sgshapiro	  case AF_INET:
349764562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
349864562Sgshapiro		    lalen <= 0 ||
349964562Sgshapiro		    la.sa.sa_family != AF_INET)
350064562Sgshapiro		{
350164562Sgshapiro			/* no ident info */
350264562Sgshapiro			goto noident;
350364562Sgshapiro		}
350464562Sgshapiro		port = RealHostAddr.sin.sin_port;
350538032Speter
350664562Sgshapiro		/* create ident query */
3507168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
350864562Sgshapiro				ntohs(RealHostAddr.sin.sin_port),
350964562Sgshapiro				ntohs(la.sin.sin_port));
351038032Speter
351164562Sgshapiro		/* create local address */
351264562Sgshapiro		la.sin.sin_port = 0;
351338032Speter
351464562Sgshapiro		/* create foreign address */
351590792Sgshapiro# ifdef NO_GETSERVBYNAME
351638032Speter		RealHostAddr.sin.sin_port = htons(113);
351790792Sgshapiro# else /* NO_GETSERVBYNAME */
351890792Sgshapiro
351990792Sgshapiro		/*
352090792Sgshapiro		**  getservbyname() consumes about 5% of the time
352190792Sgshapiro		**  when receiving a small message (almost all of the time
352290792Sgshapiro		**  spent in this routine).
352390792Sgshapiro		**  Hence we store the port in a static variable
352490792Sgshapiro		**  to save this time.
352590792Sgshapiro		**  The portnumber shouldn't change very often...
352690792Sgshapiro		**  This code makes the assumption that the port number
352790792Sgshapiro		**  is not 0.
352890792Sgshapiro		*/
352990792Sgshapiro
353090792Sgshapiro		if (port4 == 0)
353190792Sgshapiro		{
353290792Sgshapiro			sp = getservbyname("auth", "tcp");
353390792Sgshapiro			if (sp != NULL)
353490792Sgshapiro				port4 = sp->s_port;
353590792Sgshapiro			else
353690792Sgshapiro				port4 = htons(113);
353790792Sgshapiro		}
353890792Sgshapiro		RealHostAddr.sin.sin_port = port4;
353964562Sgshapiro		break;
354090792Sgshapiro# endif /* NO_GETSERVBYNAME */
354190792Sgshapiro#endif /* NETINET */
354238032Speter
354390792Sgshapiro#if NETINET6
354464562Sgshapiro	  case AF_INET6:
354564562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
354664562Sgshapiro		    lalen <= 0 ||
354764562Sgshapiro		    la.sa.sa_family != AF_INET6)
354864562Sgshapiro		{
354964562Sgshapiro			/* no ident info */
355064562Sgshapiro			goto noident;
355164562Sgshapiro		}
355264562Sgshapiro		port = RealHostAddr.sin6.sin6_port;
355364562Sgshapiro
355464562Sgshapiro		/* create ident query */
3555168515Sgshapiro		(void) sm_snprintf(ibuf, sizeof(ibuf), "%d,%d\r\n",
355664562Sgshapiro				ntohs(RealHostAddr.sin6.sin6_port),
355764562Sgshapiro				ntohs(la.sin6.sin6_port));
355864562Sgshapiro
355964562Sgshapiro		/* create local address */
356064562Sgshapiro		la.sin6.sin6_port = 0;
356164562Sgshapiro
356264562Sgshapiro		/* create foreign address */
356390792Sgshapiro# ifdef NO_GETSERVBYNAME
356464562Sgshapiro		RealHostAddr.sin6.sin6_port = htons(113);
356590792Sgshapiro# else /* NO_GETSERVBYNAME */
356690792Sgshapiro		if (port6 == 0)
356790792Sgshapiro		{
356890792Sgshapiro			sp = getservbyname("auth", "tcp");
356990792Sgshapiro			if (sp != NULL)
357090792Sgshapiro				port6 = sp->s_port;
357190792Sgshapiro			else
357290792Sgshapiro				port6 = htons(113);
357390792Sgshapiro		}
357490792Sgshapiro		RealHostAddr.sin6.sin6_port = port6;
357564562Sgshapiro		break;
357690792Sgshapiro# endif /* NO_GETSERVBYNAME */
357790792Sgshapiro#endif /* NETINET6 */
357864562Sgshapiro	  default:
357964562Sgshapiro		/* no ident info */
358064562Sgshapiro		goto noident;
358164562Sgshapiro	}
358264562Sgshapiro
358338032Speter	s = -1;
358438032Speter	if (setjmp(CtxAuthTimeout) != 0)
358538032Speter	{
358638032Speter		if (s >= 0)
358738032Speter			(void) close(s);
358838032Speter		goto noident;
358938032Speter	}
359038032Speter
359138032Speter	/* put a timeout around the whole thing */
359290792Sgshapiro	ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
359338032Speter
359438032Speter	/* connect to foreign IDENT server using same address as SMTP socket */
359564562Sgshapiro	s = socket(la.sa.sa_family, SOCK_STREAM, 0);
359638032Speter	if (s < 0)
359738032Speter	{
359890792Sgshapiro		sm_clrevent(ev);
359938032Speter		goto noident;
360038032Speter	}
360164562Sgshapiro	if (bind(s, &la.sa, lalen) < 0 ||
360264562Sgshapiro	    connect(s, &RealHostAddr.sa, lalen) < 0)
360338032Speter		goto closeident;
360438032Speter
360538032Speter	if (tTd(9, 10))
360690792Sgshapiro		sm_dprintf("getauthinfo: sent %s", ibuf);
360738032Speter
360838032Speter	/* send query */
360938032Speter	if (write(s, ibuf, strlen(ibuf)) < 0)
361038032Speter		goto closeident;
361138032Speter
361238032Speter	/* get result */
361338032Speter	p = &ibuf[0];
3614168515Sgshapiro	nleft = sizeof(ibuf) - 1;
361538032Speter	while ((i = read(s, p, nleft)) > 0)
361638032Speter	{
3617125820Sgshapiro		char *s;
3618125820Sgshapiro
361938032Speter		p += i;
362038032Speter		nleft -= i;
362138032Speter		*p = '\0';
3622125820Sgshapiro		if ((s = strchr(ibuf, '\n')) != NULL)
3623125820Sgshapiro		{
3624125820Sgshapiro			if (p > s + 1)
3625125820Sgshapiro			{
3626125820Sgshapiro				p = s + 1;
3627125820Sgshapiro				*p = '\0';
3628125820Sgshapiro			}
362938032Speter			break;
3630125820Sgshapiro		}
3631125820Sgshapiro		if (nleft <= 0)
3632125820Sgshapiro			break;
363338032Speter	}
363438032Speter	(void) close(s);
363590792Sgshapiro	sm_clrevent(ev);
363638032Speter	if (i < 0 || p == &ibuf[0])
363738032Speter		goto noident;
363838032Speter
3639111823Sgshapiro	if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r')
364038032Speter		p--;
364138032Speter	*++p = '\0';
364238032Speter
364338032Speter	if (tTd(9, 3))
364490792Sgshapiro		sm_dprintf("getauthinfo:  got %s\n", ibuf);
364538032Speter
364638032Speter	/* parse result */
364738032Speter	p = strchr(ibuf, ':');
364838032Speter	if (p == NULL)
364938032Speter	{
365038032Speter		/* malformed response */
365138032Speter		goto noident;
365238032Speter	}
365338032Speter	while (isascii(*++p) && isspace(*p))
365438032Speter		continue;
365590792Sgshapiro	if (sm_strncasecmp(p, "userid", 6) != 0)
365638032Speter	{
365738032Speter		/* presumably an error string */
365838032Speter		goto noident;
365938032Speter	}
366038032Speter	p += 6;
366138032Speter	while (isascii(*p) && isspace(*p))
366238032Speter		p++;
366338032Speter	if (*p++ != ':')
366438032Speter	{
366538032Speter		/* either useridxx or malformed response */
366638032Speter		goto noident;
366738032Speter	}
366838032Speter
366938032Speter	/* p now points to the OSTYPE field */
367038032Speter	while (isascii(*p) && isspace(*p))
367138032Speter		p++;
367238032Speter	ostype = p;
367338032Speter	p = strchr(p, ':');
367438032Speter	if (p == NULL)
367538032Speter	{
367638032Speter		/* malformed response */
367738032Speter		goto noident;
367838032Speter	}
367938032Speter	else
368038032Speter	{
368138032Speter		char *charset;
368238032Speter
368338032Speter		*p = '\0';
368438032Speter		charset = strchr(ostype, ',');
368538032Speter		if (charset != NULL)
368638032Speter			*charset = '\0';
368738032Speter	}
368838032Speter
368938032Speter	/* 1413 says don't do this -- but it's broken otherwise */
369038032Speter	while (isascii(*++p) && isspace(*p))
369138032Speter		continue;
369238032Speter
369338032Speter	/* p now points to the authenticated name -- copy carefully */
369490792Sgshapiro	if (sm_strncasecmp(ostype, "other", 5) == 0 &&
369538032Speter	    (ostype[5] == ' ' || ostype[5] == '\0'))
369638032Speter	{
3697168515Sgshapiro		(void) sm_strlcpy(hbuf, "IDENT:", sizeof(hbuf));
3698110560Sgshapiro		cleanstrcpy(&hbuf[6], p, MAXAUTHINFO);
369938032Speter	}
370038032Speter	else
3701110560Sgshapiro		cleanstrcpy(hbuf, p, MAXAUTHINFO);
370290792Sgshapiro	len = strlen(hbuf);
3703168515Sgshapiro	(void) sm_strlcpyn(&hbuf[len], sizeof(hbuf) - len, 2, "@",
370490792Sgshapiro			   RealHostName == NULL ? "localhost" : RealHostName);
370538032Speter	goto postident;
370638032Speter
370738032Spetercloseident:
370838032Speter	(void) close(s);
370990792Sgshapiro	sm_clrevent(ev);
371038032Speter
371138032Speternoident:
371264562Sgshapiro	/* put back the original incoming port */
371364562Sgshapiro	switch (RealHostAddr.sa.sa_family)
371464562Sgshapiro	{
371590792Sgshapiro#if NETINET
371664562Sgshapiro	  case AF_INET:
371764562Sgshapiro		if (port > 0)
371864562Sgshapiro			RealHostAddr.sin.sin_port = port;
371964562Sgshapiro		break;
372090792Sgshapiro#endif /* NETINET */
372164562Sgshapiro
372290792Sgshapiro#if NETINET6
372364562Sgshapiro	  case AF_INET6:
372464562Sgshapiro		if (port > 0)
372564562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
372664562Sgshapiro		break;
372790792Sgshapiro#endif /* NETINET6 */
372864562Sgshapiro	}
372964562Sgshapiro
373038032Speter	if (RealHostName == NULL)
373138032Speter	{
373238032Speter		if (tTd(9, 1))
373390792Sgshapiro			sm_dprintf("getauthinfo: NULL\n");
373438032Speter		return NULL;
373538032Speter	}
3736168515Sgshapiro	(void) sm_strlcpy(hbuf, RealHostName, sizeof(hbuf));
373738032Speter
373838032Speterpostident:
373990792Sgshapiro#if IP_SRCROUTE
374090792Sgshapiro# ifndef GET_IPOPT_DST
374190792Sgshapiro#  define GET_IPOPT_DST(dst)	(dst)
374290792Sgshapiro# endif /* ! GET_IPOPT_DST */
374338032Speter	/*
374438032Speter	**  Extract IP source routing information.
374538032Speter	**
374638032Speter	**	Format of output for a connection from site a through b
374738032Speter	**	through c to d:
374838032Speter	**		loose:      @site-c@site-b:site-a
374938032Speter	**		strict:	   !@site-c@site-b:site-a
375038032Speter	**
375138032Speter	**	o - pointer within ipopt_list structure.
375238032Speter	**	q - pointer within ls/ss rr route data
375338032Speter	**	p - pointer to hbuf
375438032Speter	*/
375538032Speter
375638032Speter	if (RealHostAddr.sa.sa_family == AF_INET)
375738032Speter	{
375838032Speter		SOCKOPT_LEN_T ipoptlen;
375938032Speter		int j;
376090792Sgshapiro		unsigned char *q;
376190792Sgshapiro		unsigned char *o;
376238032Speter		int l;
376364562Sgshapiro		struct IPOPTION ipopt;
376438032Speter
3765168515Sgshapiro		ipoptlen = sizeof(ipopt);
376638032Speter		if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
376738032Speter			       (char *) &ipopt, &ipoptlen) < 0)
376838032Speter			goto noipsr;
376938032Speter		if (ipoptlen == 0)
377038032Speter			goto noipsr;
377190792Sgshapiro		o = (unsigned char *) ipopt.IP_LIST;
377290792Sgshapiro		while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
377338032Speter		{
377438032Speter			switch (*o)
377538032Speter			{
377664562Sgshapiro			  case IPOPT_EOL:
377738032Speter				o = NULL;
377838032Speter				break;
377938032Speter
378038032Speter			  case IPOPT_NOP:
378138032Speter				o++;
378238032Speter				break;
378338032Speter
378438032Speter			  case IPOPT_SSRR:
378538032Speter			  case IPOPT_LSRR:
378638032Speter				/*
378738032Speter				**  Source routing.
378838032Speter				**	o[0] is the option type (loose/strict).
378938032Speter				**	o[1] is the length of this option,
379038032Speter				**		including option type and
379138032Speter				**		length.
379238032Speter				**	o[2] is the pointer into the route
379338032Speter				**		data.
379438032Speter				**	o[3] begins the route data.
379538032Speter				*/
379638032Speter
379738032Speter				p = &hbuf[strlen(hbuf)];
3798168515Sgshapiro				l = sizeof(hbuf) - (hbuf - p) - 6;
379990792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(hbuf, p),
380090792Sgshapiro					" [%s@%.*s",
380190792Sgshapiro					*o == IPOPT_SSRR ? "!" : "",
380290792Sgshapiro					l > 240 ? 120 : l / 2,
380390792Sgshapiro					inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
380438032Speter				i = strlen(p);
380538032Speter				p += i;
380638032Speter				l -= strlen(p);
380738032Speter
380838032Speter				j = o[1] / sizeof(struct in_addr) - 1;
380938032Speter
381038032Speter				/* q skips length and router pointer to data */
381138032Speter				q = &o[3];
381238032Speter				for ( ; j >= 0; j--)
381338032Speter				{
381464562Sgshapiro					struct in_addr addr;
381564562Sgshapiro
381638032Speter					memcpy(&addr, q, sizeof(addr));
381790792Sgshapiro					(void) sm_snprintf(p,
381890792Sgshapiro						SPACELEFT(hbuf, p),
381990792Sgshapiro						"%c%.*s",
382090792Sgshapiro						j != 0 ? '@' : ':',
382190792Sgshapiro						l > 240 ? 120 :
382290792Sgshapiro							j == 0 ? l : l / 2,
382390792Sgshapiro						inet_ntoa(addr));
382438032Speter					i = strlen(p);
382538032Speter					p += i;
382638032Speter					l -= i + 1;
382764562Sgshapiro					q += sizeof(struct in_addr);
382838032Speter				}
382938032Speter				o += o[1];
383038032Speter				break;
383138032Speter
383238032Speter			  default:
383338032Speter				/* Skip over option */
383438032Speter				o += o[1];
383538032Speter				break;
383638032Speter			}
383738032Speter		}
383890792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
383938032Speter		goto postipsr;
384038032Speter	}
384138032Speter
384238032Speternoipsr:
384390792Sgshapiro#endif /* IP_SRCROUTE */
384438032Speter	if (RealHostName != NULL && RealHostName[0] != '[')
384538032Speter	{
384638032Speter		p = &hbuf[strlen(hbuf)];
384790792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
384890792Sgshapiro				   anynet_ntoa(&RealHostAddr));
384938032Speter	}
385038032Speter	if (*may_be_forged)
385138032Speter	{
385238032Speter		p = &hbuf[strlen(hbuf)];
385390792Sgshapiro		(void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
385490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
385590792Sgshapiro			  macid("{client_resolve}"), "FORGED");
385638032Speter	}
385738032Speter
385890792Sgshapiro#if IP_SRCROUTE
385938032Speterpostipsr:
386090792Sgshapiro#endif /* IP_SRCROUTE */
386164562Sgshapiro
386264562Sgshapiro	/* put back the original incoming port */
386364562Sgshapiro	switch (RealHostAddr.sa.sa_family)
386464562Sgshapiro	{
386590792Sgshapiro#if NETINET
386664562Sgshapiro	  case AF_INET:
386764562Sgshapiro		if (port > 0)
386864562Sgshapiro			RealHostAddr.sin.sin_port = port;
386964562Sgshapiro		break;
387090792Sgshapiro#endif /* NETINET */
387164562Sgshapiro
387290792Sgshapiro#if NETINET6
387364562Sgshapiro	  case AF_INET6:
387464562Sgshapiro		if (port > 0)
387564562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
387664562Sgshapiro		break;
387790792Sgshapiro#endif /* NETINET6 */
387864562Sgshapiro	}
387964562Sgshapiro
388090792Sgshapiro	if (tTd(9, 1))
388190792Sgshapiro		sm_dprintf("getauthinfo: %s\n", hbuf);
388238032Speter	return hbuf;
388338032Speter}
388490792Sgshapiro/*
388538032Speter**  HOST_MAP_LOOKUP -- turn a hostname into canonical form
388638032Speter**
388738032Speter**	Parameters:
388838032Speter**		map -- a pointer to this map.
388938032Speter**		name -- the (presumably unqualified) hostname.
389038032Speter**		av -- unused -- for compatibility with other mapping
389138032Speter**			functions.
389238032Speter**		statp -- an exit status (out parameter) -- set to
389338032Speter**			EX_TEMPFAIL if the name server is unavailable.
389438032Speter**
389538032Speter**	Returns:
389638032Speter**		The mapping, if found.
389738032Speter**		NULL if no mapping found.
389838032Speter**
389938032Speter**	Side Effects:
390038032Speter**		Looks up the host specified in hbuf.  If it is not
390138032Speter**		the canonical name for that host, return the canonical
390238032Speter**		name (unless MF_MATCHONLY is set, which will cause the
390338032Speter**		status only to be returned).
390438032Speter*/
390538032Speter
390638032Speterchar *
390738032Speterhost_map_lookup(map, name, av, statp)
390838032Speter	MAP *map;
390938032Speter	char *name;
391038032Speter	char **av;
391138032Speter	int *statp;
391238032Speter{
391338032Speter	register struct hostent *hp;
391490792Sgshapiro#if NETINET
391538032Speter	struct in_addr in_addr;
391690792Sgshapiro#endif /* NETINET */
391790792Sgshapiro#if NETINET6
391864562Sgshapiro	struct in6_addr in6_addr;
391990792Sgshapiro#endif /* NETINET6 */
392064562Sgshapiro	char *cp, *ans = NULL;
392138032Speter	register STAB *s;
392290792Sgshapiro	time_t now;
392390792Sgshapiro#if NAMED_BIND
392490792Sgshapiro	time_t SM_NONVOLATILE retrans = 0;
392590792Sgshapiro	int SM_NONVOLATILE retry = 0;
392690792Sgshapiro#endif /* NAMED_BIND */
392738032Speter	char hbuf[MAXNAME + 1];
392838032Speter
392938032Speter	/*
393038032Speter	**  See if we have already looked up this name.  If so, just
393190792Sgshapiro	**  return it (unless expired).
393238032Speter	*/
393338032Speter
393490792Sgshapiro	now = curtime();
393538032Speter	s = stab(name, ST_NAMECANON, ST_ENTER);
393690792Sgshapiro	if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
393790792Sgshapiro	    s->s_namecanon.nc_exp >= now)
393838032Speter	{
393938032Speter		if (tTd(9, 1))
394090792Sgshapiro			sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
394190792Sgshapiro				    name,
394290792Sgshapiro				    s->s_namecanon.nc_cname == NULL
394338032Speter					? "NULL"
394438032Speter					: s->s_namecanon.nc_cname);
394538032Speter		errno = s->s_namecanon.nc_errno;
394673188Sgshapiro		SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
394738032Speter		*statp = s->s_namecanon.nc_stat;
394838032Speter		if (*statp == EX_TEMPFAIL)
394938032Speter		{
395038032Speter			CurEnv->e_status = "4.4.3";
395138032Speter			message("851 %s: Name server timeout",
395238032Speter				shortenstring(name, 33));
395338032Speter		}
395438032Speter		if (*statp != EX_OK)
395538032Speter			return NULL;
395638032Speter		if (s->s_namecanon.nc_cname == NULL)
395738032Speter		{
3958132943Sgshapiro			syserr("host_map_lookup(%s): bogus NULL cache entry, errno=%d, h_errno=%d",
395964562Sgshapiro			       name,
396064562Sgshapiro			       s->s_namecanon.nc_errno,
396164562Sgshapiro			       s->s_namecanon.nc_herrno);
396238032Speter			return NULL;
396338032Speter		}
396438032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
396538032Speter			cp = map_rewrite(map, name, strlen(name), NULL);
396638032Speter		else
396738032Speter			cp = map_rewrite(map,
396838032Speter					 s->s_namecanon.nc_cname,
396938032Speter					 strlen(s->s_namecanon.nc_cname),
397038032Speter					 av);
397138032Speter		return cp;
397238032Speter	}
397338032Speter
397438032Speter	/*
397538032Speter	**  If we are running without a regular network connection (usually
397638032Speter	**  dial-on-demand) and we are just queueing, we want to avoid DNS
397738032Speter	**  lookups because those could try to connect to a server.
397838032Speter	*/
397938032Speter
398064562Sgshapiro	if (CurEnv->e_sendmode == SM_DEFER &&
398164562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
398238032Speter	{
398338032Speter		if (tTd(9, 1))
398490792Sgshapiro			sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
398538032Speter		*statp = EX_TEMPFAIL;
398638032Speter		return NULL;
398738032Speter	}
398838032Speter
398938032Speter	/*
399038032Speter	**  If first character is a bracket, then it is an address
399138032Speter	**  lookup.  Address is copied into a temporary buffer to
399238032Speter	**  strip the brackets and to preserve name if address is
399338032Speter	**  unknown.
399438032Speter	*/
399538032Speter
399664562Sgshapiro	if (tTd(9, 1))
399790792Sgshapiro		sm_dprintf("host_map_lookup(%s) => ", name);
399890792Sgshapiro#if NAMED_BIND
399990792Sgshapiro	if (map->map_timeout > 0)
400090792Sgshapiro	{
400190792Sgshapiro		retrans = _res.retrans;
400290792Sgshapiro		_res.retrans = map->map_timeout;
400390792Sgshapiro	}
400490792Sgshapiro	if (map->map_retry > 0)
400590792Sgshapiro	{
400690792Sgshapiro		retry = _res.retry;
400790792Sgshapiro		_res.retry = map->map_retry;
400890792Sgshapiro	}
400990792Sgshapiro#endif /* NAMED_BIND */
401090792Sgshapiro
401190792Sgshapiro	/* set default TTL */
401290792Sgshapiro	s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
401338032Speter	if (*name != '[')
401438032Speter	{
401590792Sgshapiro		int ttl;
401690792Sgshapiro
4017168515Sgshapiro		(void) sm_strlcpy(hbuf, name, sizeof(hbuf));
4018168515Sgshapiro		if (getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl))
401990792Sgshapiro		{
402064562Sgshapiro			ans = hbuf;
402190792Sgshapiro			if (ttl > 0)
402290792Sgshapiro				s->s_namecanon.nc_exp = now + SM_MIN(ttl,
402390792Sgshapiro								SM_DEFAULT_TTL);
402490792Sgshapiro		}
402564562Sgshapiro	}
402664562Sgshapiro	else
402764562Sgshapiro	{
402864562Sgshapiro		if ((cp = strchr(name, ']')) == NULL)
402971345Sgshapiro		{
403071345Sgshapiro			if (tTd(9, 1))
403190792Sgshapiro				sm_dprintf("FAILED\n");
403264562Sgshapiro			return NULL;
403371345Sgshapiro		}
403464562Sgshapiro		*cp = '\0';
403564562Sgshapiro
403664562Sgshapiro		hp = NULL;
403790792Sgshapiro#if NETINET
403864562Sgshapiro		if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE)
403964562Sgshapiro			hp = sm_gethostbyaddr((char *)&in_addr,
404064562Sgshapiro					      INADDRSZ, AF_INET);
404190792Sgshapiro#endif /* NETINET */
404290792Sgshapiro#if NETINET6
404364562Sgshapiro		if (hp == NULL &&
404490792Sgshapiro		    anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
404564562Sgshapiro			hp = sm_gethostbyaddr((char *)&in6_addr,
404664562Sgshapiro					      IN6ADDRSZ, AF_INET6);
404790792Sgshapiro#endif /* NETINET6 */
404864562Sgshapiro		*cp = ']';
404964562Sgshapiro
405064562Sgshapiro		if (hp != NULL)
405138032Speter		{
405264562Sgshapiro			/* found a match -- copy out */
405390792Sgshapiro			ans = denlstring((char *) hp->h_name, true, true);
405490792Sgshapiro#if NETINET6
405590792Sgshapiro			if (ans == hp->h_name)
405690792Sgshapiro			{
405790792Sgshapiro				static char n[MAXNAME + 1];
405890792Sgshapiro
405990792Sgshapiro				/* hp->h_name is about to disappear */
4060168515Sgshapiro				(void) sm_strlcpy(n, ans, sizeof(n));
406190792Sgshapiro				ans = n;
406290792Sgshapiro			}
406371345Sgshapiro			freehostent(hp);
406471345Sgshapiro			hp = NULL;
406590792Sgshapiro#endif /* NETINET6 */
406638032Speter		}
406764562Sgshapiro	}
406890792Sgshapiro#if NAMED_BIND
406990792Sgshapiro	if (map->map_timeout > 0)
407090792Sgshapiro		_res.retrans = retrans;
407190792Sgshapiro	if (map->map_retry > 0)
407290792Sgshapiro		_res.retry = retry;
407390792Sgshapiro#endif /* NAMED_BIND */
407438032Speter
407564562Sgshapiro	s->s_namecanon.nc_flags |= NCF_VALID;	/* will be soon */
407638032Speter
407764562Sgshapiro	/* Found an answer */
407864562Sgshapiro	if (ans != NULL)
407964562Sgshapiro	{
408064562Sgshapiro		s->s_namecanon.nc_stat = *statp = EX_OK;
408190792Sgshapiro		if (s->s_namecanon.nc_cname != NULL)
408290792Sgshapiro			sm_free(s->s_namecanon.nc_cname);
408390792Sgshapiro		s->s_namecanon.nc_cname = sm_strdup_x(ans);
408464562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
408564562Sgshapiro			cp = map_rewrite(map, name, strlen(name), NULL);
408664562Sgshapiro		else
408764562Sgshapiro			cp = map_rewrite(map, ans, strlen(ans), av);
408871345Sgshapiro		if (tTd(9, 1))
408990792Sgshapiro			sm_dprintf("FOUND %s\n", ans);
409064562Sgshapiro		return cp;
409138032Speter	}
409238032Speter
409364562Sgshapiro
409464562Sgshapiro	/* No match found */
409538032Speter	s->s_namecanon.nc_errno = errno;
409690792Sgshapiro#if NAMED_BIND
409738032Speter	s->s_namecanon.nc_herrno = h_errno;
409864562Sgshapiro	if (tTd(9, 1))
409990792Sgshapiro		sm_dprintf("FAIL (%d)\n", h_errno);
410064562Sgshapiro	switch (h_errno)
410138032Speter	{
410264562Sgshapiro	  case TRY_AGAIN:
410364562Sgshapiro		if (UseNameServer)
410464562Sgshapiro		{
410564562Sgshapiro			CurEnv->e_status = "4.4.3";
410664562Sgshapiro			message("851 %s: Name server timeout",
410764562Sgshapiro				shortenstring(name, 33));
410864562Sgshapiro		}
410964562Sgshapiro		*statp = EX_TEMPFAIL;
411064562Sgshapiro		break;
411164562Sgshapiro
411264562Sgshapiro	  case HOST_NOT_FOUND:
411364562Sgshapiro	  case NO_DATA:
411464562Sgshapiro		*statp = EX_NOHOST;
411564562Sgshapiro		break;
411664562Sgshapiro
411764562Sgshapiro	  case NO_RECOVERY:
411864562Sgshapiro		*statp = EX_SOFTWARE;
411964562Sgshapiro		break;
412064562Sgshapiro
412164562Sgshapiro	  default:
412264562Sgshapiro		*statp = EX_UNAVAILABLE;
412364562Sgshapiro		break;
412438032Speter	}
412590792Sgshapiro#else /* NAMED_BIND */
412664562Sgshapiro	if (tTd(9, 1))
412790792Sgshapiro		sm_dprintf("FAIL\n");
412864562Sgshapiro	*statp = EX_NOHOST;
412990792Sgshapiro#endif /* NAMED_BIND */
413064562Sgshapiro	s->s_namecanon.nc_stat = *statp;
413164562Sgshapiro	return NULL;
413238032Speter}
413338032Speter/*
413490792Sgshapiro**  HOST_MAP_INIT -- initialize host class structures
413538032Speter**
413638032Speter**	Parameters:
413790792Sgshapiro**		map -- a pointer to this map.
413890792Sgshapiro**		args -- argument string.
413938032Speter**
414038032Speter**	Returns:
414190792Sgshapiro**		true.
414238032Speter*/
414338032Speter
414438032Speterbool
414538032Speterhost_map_init(map, args)
414638032Speter	MAP *map;
414738032Speter	char *args;
414838032Speter{
414938032Speter	register char *p = args;
415038032Speter
415138032Speter	for (;;)
415238032Speter	{
415338032Speter		while (isascii(*p) && isspace(*p))
415438032Speter			p++;
415538032Speter		if (*p != '-')
415638032Speter			break;
415738032Speter		switch (*++p)
415838032Speter		{
415938032Speter		  case 'a':
416038032Speter			map->map_app = ++p;
416138032Speter			break;
416238032Speter
416338032Speter		  case 'T':
416438032Speter			map->map_tapp = ++p;
416538032Speter			break;
416638032Speter
416738032Speter		  case 'm':
416838032Speter			map->map_mflags |= MF_MATCHONLY;
416938032Speter			break;
417038032Speter
417138032Speter		  case 't':
417238032Speter			map->map_mflags |= MF_NODEFER;
417338032Speter			break;
417464562Sgshapiro
417564562Sgshapiro		  case 'S':	/* only for consistency */
417664562Sgshapiro			map->map_spacesub = *++p;
417764562Sgshapiro			break;
417864562Sgshapiro
417964562Sgshapiro		  case 'D':
418064562Sgshapiro			map->map_mflags |= MF_DEFER;
418164562Sgshapiro			break;
418290792Sgshapiro
418390792Sgshapiro		  case 'd':
418490792Sgshapiro			{
418590792Sgshapiro				char *h;
418690792Sgshapiro
418790792Sgshapiro				while (isascii(*++p) && isspace(*p))
418890792Sgshapiro					continue;
418990792Sgshapiro				h = strchr(p, ' ');
419090792Sgshapiro				if (h != NULL)
419190792Sgshapiro					*h = '\0';
419290792Sgshapiro				map->map_timeout = convtime(p, 's');
419390792Sgshapiro				if (h != NULL)
419490792Sgshapiro					*h = ' ';
419590792Sgshapiro			}
419690792Sgshapiro			break;
419790792Sgshapiro
419890792Sgshapiro		  case 'r':
419990792Sgshapiro			while (isascii(*++p) && isspace(*p))
420090792Sgshapiro				continue;
420190792Sgshapiro			map->map_retry = atoi(p);
420290792Sgshapiro			break;
420338032Speter		}
420438032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
420538032Speter			p++;
420638032Speter		if (*p != '\0')
420738032Speter			*p++ = '\0';
420838032Speter	}
420938032Speter	if (map->map_app != NULL)
421038032Speter		map->map_app = newstr(map->map_app);
421138032Speter	if (map->map_tapp != NULL)
421238032Speter		map->map_tapp = newstr(map->map_tapp);
421390792Sgshapiro	return true;
421438032Speter}
421590792Sgshapiro
421664562Sgshapiro#if NETINET6
421764562Sgshapiro/*
421864562Sgshapiro**  ANYNET_NTOP -- convert an IPv6 network address to printable form.
421964562Sgshapiro**
422064562Sgshapiro**	Parameters:
422164562Sgshapiro**		s6a -- a pointer to an in6_addr structure.
422264562Sgshapiro**		dst -- buffer to store result in
422364562Sgshapiro**		dst_len -- size of dst buffer
422464562Sgshapiro**
422564562Sgshapiro**	Returns:
422664562Sgshapiro**		A printable version of that structure.
422764562Sgshapiro*/
422890792Sgshapiro
422964562Sgshapirochar *
423064562Sgshapiroanynet_ntop(s6a, dst, dst_len)
423164562Sgshapiro	struct in6_addr *s6a;
423264562Sgshapiro	char *dst;
423364562Sgshapiro	size_t dst_len;
423464562Sgshapiro{
423564562Sgshapiro	register char *ap;
423664562Sgshapiro
423764562Sgshapiro	if (IN6_IS_ADDR_V4MAPPED(s6a))
423864562Sgshapiro		ap = (char *) inet_ntop(AF_INET,
423964562Sgshapiro					&s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
424064562Sgshapiro					dst, dst_len);
424164562Sgshapiro	else
424290792Sgshapiro	{
424390792Sgshapiro		char *d;
424490792Sgshapiro		size_t sz;
424590792Sgshapiro
424690792Sgshapiro		/* Save pointer to beginning of string */
424790792Sgshapiro		d = dst;
424890792Sgshapiro
424990792Sgshapiro		/* Add IPv6: protocol tag */
425090792Sgshapiro		sz = sm_strlcpy(dst, "IPv6:", dst_len);
425190792Sgshapiro		if (sz >= dst_len)
425290792Sgshapiro			return NULL;
425390792Sgshapiro		dst += sz;
425490792Sgshapiro		dst_len -= sz;
425564562Sgshapiro		ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
425690792Sgshapiro
425790792Sgshapiro		/* Restore pointer to beginning of string */
425890792Sgshapiro		if (ap != NULL)
425990792Sgshapiro			ap = d;
426090792Sgshapiro	}
426164562Sgshapiro	return ap;
426264562Sgshapiro}
426390792Sgshapiro
426490792Sgshapiro/*
426590792Sgshapiro**  ANYNET_PTON -- convert printed form to network address.
426690792Sgshapiro**
426790792Sgshapiro**	Wrapper for inet_pton() which handles IPv6: labels.
426890792Sgshapiro**
426990792Sgshapiro**	Parameters:
427090792Sgshapiro**		family -- address family
427190792Sgshapiro**		src -- string
427290792Sgshapiro**		dst -- destination address structure
427390792Sgshapiro**
427490792Sgshapiro**	Returns:
427590792Sgshapiro**		1 if the address was valid
427690792Sgshapiro**		0 if the address wasn't parseable
427790792Sgshapiro**		-1 if error
427890792Sgshapiro*/
427990792Sgshapiro
428090792Sgshapiroint
428190792Sgshapiroanynet_pton(family, src, dst)
428290792Sgshapiro	int family;
428390792Sgshapiro	const char *src;
428490792Sgshapiro	void *dst;
428590792Sgshapiro{
428690792Sgshapiro	if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
428790792Sgshapiro		src += 5;
428890792Sgshapiro	return inet_pton(family, src, dst);
428990792Sgshapiro}
429064562Sgshapiro#endif /* NETINET6 */
429190792Sgshapiro/*
429238032Speter**  ANYNET_NTOA -- convert a network address to printable form.
429338032Speter**
429438032Speter**	Parameters:
429538032Speter**		sap -- a pointer to a sockaddr structure.
429638032Speter**
429738032Speter**	Returns:
429838032Speter**		A printable version of that sockaddr.
429938032Speter*/
430038032Speter
430138032Speter#ifdef USE_SOCK_STREAM
430238032Speter
430364562Sgshapiro# if NETLINK
430464562Sgshapiro#  include <net/if_dl.h>
430564562Sgshapiro# endif /* NETLINK */
430638032Speter
430738032Speterchar *
430838032Speteranynet_ntoa(sap)
430938032Speter	register SOCKADDR *sap;
431038032Speter{
431138032Speter	register char *bp;
431238032Speter	register char *ap;
431338032Speter	int l;
431438032Speter	static char buf[100];
431538032Speter
431638032Speter	/* check for null/zero family */
431738032Speter	if (sap == NULL)
431838032Speter		return "NULLADDR";
431938032Speter	if (sap->sa.sa_family == 0)
432038032Speter		return "0";
432138032Speter
432238032Speter	switch (sap->sa.sa_family)
432338032Speter	{
432464562Sgshapiro# if NETUNIX
432538032Speter	  case AF_UNIX:
432664562Sgshapiro		if (sap->sunix.sun_path[0] != '\0')
4327168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "[UNIX: %.64s]",
432890792Sgshapiro					   sap->sunix.sun_path);
432964562Sgshapiro		else
4330168515Sgshapiro			(void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof(buf));
433138032Speter		return buf;
433264562Sgshapiro# endif /* NETUNIX */
433338032Speter
433464562Sgshapiro# if NETINET
433538032Speter	  case AF_INET:
433664562Sgshapiro		return (char *) inet_ntoa(sap->sin.sin_addr);
433764562Sgshapiro# endif /* NETINET */
433838032Speter
433964562Sgshapiro# if NETINET6
434064562Sgshapiro	  case AF_INET6:
4341168515Sgshapiro		ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof(buf));
434264562Sgshapiro		if (ap != NULL)
434364562Sgshapiro			return ap;
434464562Sgshapiro		break;
434564562Sgshapiro# endif /* NETINET6 */
434664562Sgshapiro
434764562Sgshapiro# if NETLINK
434838032Speter	  case AF_LINK:
4349168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[LINK: %s]",
435090792Sgshapiro				   link_ntoa((struct sockaddr_dl *) &sap->sa));
435138032Speter		return buf;
435264562Sgshapiro# endif /* NETLINK */
435338032Speter	  default:
435438032Speter		/* this case is needed when nothing is #defined */
435538032Speter		/* in order to keep the switch syntactically correct */
435638032Speter		break;
435738032Speter	}
435838032Speter
435938032Speter	/* unknown family -- just dump bytes */
4360168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family);
436138032Speter	bp = &buf[strlen(buf)];
436238032Speter	ap = sap->sa.sa_data;
4363168515Sgshapiro	for (l = sizeof(sap->sa.sa_data); --l >= 0; )
436438032Speter	{
436590792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
436690792Sgshapiro				   *ap++ & 0377);
436738032Speter		bp += 3;
436838032Speter	}
436938032Speter	*--bp = '\0';
437038032Speter	return buf;
437138032Speter}
437290792Sgshapiro/*
437338032Speter**  HOSTNAMEBYANYADDR -- return name of host based on address
437438032Speter**
437538032Speter**	Parameters:
437638032Speter**		sap -- SOCKADDR pointer
437738032Speter**
437838032Speter**	Returns:
437938032Speter**		text representation of host name.
438038032Speter**
438138032Speter**	Side Effects:
438238032Speter**		none.
438338032Speter*/
438438032Speter
438538032Speterchar *
438638032Speterhostnamebyanyaddr(sap)
438738032Speter	register SOCKADDR *sap;
438838032Speter{
438938032Speter	register struct hostent *hp;
439064562Sgshapiro# if NAMED_BIND
439138032Speter	int saveretry;
439264562Sgshapiro# endif /* NAMED_BIND */
439364562Sgshapiro# if NETINET6
439464562Sgshapiro	struct in6_addr in6_addr;
439564562Sgshapiro# endif /* NETINET6 */
439638032Speter
439764562Sgshapiro# if NAMED_BIND
439838032Speter	/* shorten name server timeout to avoid higher level timeouts */
439938032Speter	saveretry = _res.retry;
440064562Sgshapiro	if (_res.retry * _res.retrans > 20)
440164562Sgshapiro		_res.retry = 20 / _res.retrans;
440264562Sgshapiro# endif /* NAMED_BIND */
440338032Speter
440438032Speter	switch (sap->sa.sa_family)
440538032Speter	{
440664562Sgshapiro# if NETINET
440738032Speter	  case AF_INET:
440838032Speter		hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
440990792Sgshapiro				      INADDRSZ, AF_INET);
441038032Speter		break;
441164562Sgshapiro# endif /* NETINET */
441238032Speter
441364562Sgshapiro# if NETINET6
441464562Sgshapiro	  case AF_INET6:
441564562Sgshapiro		hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
441690792Sgshapiro				      IN6ADDRSZ, AF_INET6);
441764562Sgshapiro		break;
441864562Sgshapiro# endif /* NETINET6 */
441964562Sgshapiro
442064562Sgshapiro# if NETISO
442138032Speter	  case AF_ISO:
442238032Speter		hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
4423168515Sgshapiro				      sizeof(sap->siso.siso_addr), AF_ISO);
442438032Speter		break;
442564562Sgshapiro# endif /* NETISO */
442638032Speter
442764562Sgshapiro# if NETUNIX
442838032Speter	  case AF_UNIX:
442938032Speter		hp = NULL;
443038032Speter		break;
443164562Sgshapiro# endif /* NETUNIX */
443238032Speter
443338032Speter	  default:
4434168515Sgshapiro		hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof(sap->sa.sa_data),
443590792Sgshapiro				      sap->sa.sa_family);
443638032Speter		break;
443738032Speter	}
443838032Speter
443964562Sgshapiro# if NAMED_BIND
444038032Speter	_res.retry = saveretry;
444164562Sgshapiro# endif /* NAMED_BIND */
444238032Speter
444364562Sgshapiro# if NETINET || NETINET6
444464562Sgshapiro	if (hp != NULL && hp->h_name[0] != '['
444564562Sgshapiro#  if NETINET6
444664562Sgshapiro	    && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1
444764562Sgshapiro#  endif /* NETINET6 */
444864562Sgshapiro#  if NETINET
444964562Sgshapiro	    && inet_addr(hp->h_name) == INADDR_NONE
445064562Sgshapiro#  endif /* NETINET */
445164562Sgshapiro	    )
445271345Sgshapiro	{
445371345Sgshapiro		char *name;
445471345Sgshapiro
445590792Sgshapiro		name = denlstring((char *) hp->h_name, true, true);
445690792Sgshapiro#  if NETINET6
445771345Sgshapiro		if (name == hp->h_name)
445871345Sgshapiro		{
445971345Sgshapiro			static char n[MAXNAME + 1];
446071345Sgshapiro
446171345Sgshapiro			/* Copy the string, hp->h_name is about to disappear */
4462168515Sgshapiro			(void) sm_strlcpy(n, name, sizeof(n));
446371345Sgshapiro			name = n;
446471345Sgshapiro		}
446571345Sgshapiro		freehostent(hp);
446690792Sgshapiro#  endif /* NETINET6 */
446771345Sgshapiro		return name;
446871345Sgshapiro	}
446964562Sgshapiro# endif /* NETINET || NETINET6 */
447071345Sgshapiro
447190792Sgshapiro# if NETINET6
447271345Sgshapiro	if (hp != NULL)
447371345Sgshapiro	{
447471345Sgshapiro		freehostent(hp);
447571345Sgshapiro		hp = NULL;
447671345Sgshapiro	}
447790792Sgshapiro# endif /* NETINET6 */
447871345Sgshapiro
447964562Sgshapiro# if NETUNIX
448064562Sgshapiro	if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
448138032Speter		return "localhost";
448264562Sgshapiro# endif /* NETUNIX */
448338032Speter	{
448438032Speter		static char buf[203];
448538032Speter
4486168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "[%.200s]",
448790792Sgshapiro				   anynet_ntoa(sap));
448838032Speter		return buf;
448938032Speter	}
449038032Speter}
449164562Sgshapiro#endif /* USE_SOCK_STREAM */
4492