daemon.c revision 98121
138032Speter/*
294334Sgshapiro * Copyright (c) 1998-2002 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>
1538032Speter
1698121SgshapiroSM_RCSID("@(#)$Id: daemon.c,v 8.612 2002/05/02 19:40:52 ca Exp $")
1764562Sgshapiro
1838032Speter#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
1938032Speter# define USE_SOCK_STREAM	1
2064562Sgshapiro#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
2138032Speter
2290792Sgshapiro#if defined(USE_SOCK_STREAM)
2364562Sgshapiro# if NETINET || NETINET6
2464562Sgshapiro#  include <arpa/inet.h>
2564562Sgshapiro# endif /* NETINET || NETINET6 */
2638032Speter# if NAMED_BIND
2738032Speter#  ifndef NO_DATA
2838032Speter#   define NO_DATA	NO_ADDRESS
2964562Sgshapiro#  endif /* ! NO_DATA */
3064562Sgshapiro# endif /* NAMED_BIND */
3190792Sgshapiro#endif /* defined(USE_SOCK_STREAM) */
3238032Speter
3390792Sgshapiro#if STARTTLS
3490792Sgshapiro#  include <openssl/rand.h>
3590792Sgshapiro#endif /* STARTTLS */
3638032Speter
3790792Sgshapiro#include <sys/time.h>
3866494Sgshapiro
3990792Sgshapiro#if IP_SRCROUTE && NETINET
4090792Sgshapiro# include <netinet/in_systm.h>
4190792Sgshapiro# include <netinet/ip.h>
4290792Sgshapiro# if HAS_IN_H
4390792Sgshapiro#  include <netinet/in.h>
4490792Sgshapiro#  ifndef IPOPTION
4590792Sgshapiro#   define IPOPTION	ip_opts
4690792Sgshapiro#   define IP_LIST	ip_opts
4790792Sgshapiro#   define IP_DST	ip_dst
4890792Sgshapiro#  endif /* ! IPOPTION */
4990792Sgshapiro# else /* HAS_IN_H */
5090792Sgshapiro#  include <netinet/ip_var.h>
5190792Sgshapiro#  ifndef IPOPTION
5290792Sgshapiro#   define IPOPTION	ipoption
5390792Sgshapiro#   define IP_LIST	ipopt_list
5490792Sgshapiro#   define IP_DST	ipopt_dst
5590792Sgshapiro#  endif /* ! IPOPTION */
5690792Sgshapiro# endif /* HAS_IN_H */
5790792Sgshapiro#endif /* IP_SRCROUTE && NETINET */
5838032Speter
5990792Sgshapiro#include <sm/fdset.h>
6038032Speter
6190792Sgshapiro/* structure to describe a daemon or a client */
6264562Sgshapirostruct daemon
6364562Sgshapiro{
6464562Sgshapiro	int		d_socket;	/* fd for socket */
6564562Sgshapiro	SOCKADDR	d_addr;		/* socket for incoming */
6690792Sgshapiro	unsigned short	d_port;		/* port number */
6764562Sgshapiro	int		d_listenqueue;	/* size of listen queue */
6864562Sgshapiro	int		d_tcprcvbufsize;	/* size of TCP receive buffer */
6964562Sgshapiro	int		d_tcpsndbufsize;	/* size of TCP send buffer */
7064562Sgshapiro	time_t		d_refuse_connections_until;
7164562Sgshapiro	bool		d_firsttime;
7264562Sgshapiro	int		d_socksize;
7364562Sgshapiro	BITMAP256	d_flags;	/* flags; see sendmail.h */
7464562Sgshapiro	char		*d_mflags;	/* flags for use in macro */
7564562Sgshapiro	char		*d_name;	/* user-supplied name */
7690792Sgshapiro#if MILTER
7790792Sgshapiro# if _FFR_MILTER_PERDAEMON
7890792Sgshapiro	char		*d_inputfilterlist;
7990792Sgshapiro	struct milter	*d_inputfilters[MAXFILTERS];
8090792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
8190792Sgshapiro#endif /* MILTER */
8264562Sgshapiro};
8364562Sgshapiro
8464562Sgshapirotypedef struct daemon DAEMON_T;
8564562Sgshapiro
8690792Sgshapirostatic void		connecttimeout __P((void));
8790792Sgshapirostatic int		opendaemonsocket __P((DAEMON_T *, bool));
8890792Sgshapirostatic unsigned short	setupdaemon __P((SOCKADDR *));
8990792Sgshapirostatic void		getrequests_checkdiskspace __P((ENVELOPE *e));
9064562Sgshapiro
9138032Speter/*
9238032Speter**  DAEMON.C -- routines to use when running as a daemon.
9338032Speter**
9438032Speter**	This entire file is highly dependent on the 4.2 BSD
9538032Speter**	interprocess communication primitives.  No attempt has
9638032Speter**	been made to make this file portable to Version 7,
9738032Speter**	Version 6, MPX files, etc.  If you should try such a
9838032Speter**	thing yourself, I recommend chucking the entire file
9938032Speter**	and starting from scratch.  Basic semantics are:
10038032Speter**
10138032Speter**	getrequests(e)
10238032Speter**		Opens a port and initiates a connection.
10338032Speter**		Returns in a child.  Must set InChannel and
10438032Speter**		OutChannel appropriately.
10538032Speter**	clrdaemon()
10638032Speter**		Close any open files associated with getting
10738032Speter**		the connection; this is used when running the queue,
10838032Speter**		etc., to avoid having extra file descriptors during
10938032Speter**		the queue run and to avoid confusing the network
11038032Speter**		code (if it cares).
11190792Sgshapiro**	makeconnection(host, port, mci, e, enough)
11238032Speter**		Make a connection to the named host on the given
11390792Sgshapiro**		port. Returns zero on success, else an exit status
11490792Sgshapiro**		describing the error.
11538032Speter**	host_map_lookup(map, hbuf, avp, pstat)
11638032Speter**		Convert the entry in hbuf into a canonical form.
11738032Speter*/
11864562Sgshapiro
11964562Sgshapirostatic DAEMON_T	Daemons[MAXDAEMONS];
12090792Sgshapirostatic int	NDaemons = 0;			/* actual number of daemons */
12164562Sgshapiro
12290792Sgshapirostatic time_t	NextDiskSpaceCheck = 0;
12364562Sgshapiro
12490792Sgshapiro/*
12538032Speter**  GETREQUESTS -- open mail IPC port and get requests.
12638032Speter**
12738032Speter**	Parameters:
12838032Speter**		e -- the current envelope.
12938032Speter**
13038032Speter**	Returns:
13164562Sgshapiro**		pointer to flags.
13238032Speter**
13338032Speter**	Side Effects:
13438032Speter**		Waits until some interesting activity occurs.  When
13538032Speter**		it does, a child is created to process it, and the
13638032Speter**		parent waits for completion.  Return from this
13738032Speter**		routine is always in the child.  The file pointers
13838032Speter**		"InChannel" and "OutChannel" should be set to point
13938032Speter**		to the communication channel.
14090792Sgshapiro**		May restart persistent queue runners if they have ended
14190792Sgshapiro**		for some reason.
14238032Speter*/
14338032Speter
14464562SgshapiroBITMAP256 *
14538032Spetergetrequests(e)
14638032Speter	ENVELOPE *e;
14738032Speter{
14838032Speter	int t;
14964562Sgshapiro	int idx, curdaemon = -1;
15064562Sgshapiro	int i, olddaemon = 0;
15190792Sgshapiro#if XDEBUG
15238032Speter	bool j_has_dot;
15390792Sgshapiro#endif /* XDEBUG */
15442575Speter	char status[MAXLINE];
15564562Sgshapiro	SOCKADDR sa;
15664562Sgshapiro	SOCKADDR_LEN_T len = sizeof sa;
15794334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
15894334Sgshapiro	time_t lastrun;
15994334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
16064562Sgshapiro# if NETUNIX
16142575Speter	extern int ControlSocket;
16264562Sgshapiro# endif /* NETUNIX */
16364562Sgshapiro	extern ENVELOPE BlankEnvelope;
16490792Sgshapiro	extern bool refuseconnections __P((char *, ENVELOPE *, int, bool));
16538032Speter
16638032Speter
16790792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
16838032Speter	{
16964562Sgshapiro		Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
17090792Sgshapiro		Daemons[idx].d_firsttime = true;
17164562Sgshapiro		Daemons[idx].d_refuse_connections_until = (time_t) 0;
17238032Speter	}
17371345Sgshapiro
17438032Speter	/*
17538032Speter	**  Try to actually open the connection.
17638032Speter	*/
17738032Speter
17838032Speter	if (tTd(15, 1))
17964562Sgshapiro	{
18090792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
18171345Sgshapiro		{
18290792Sgshapiro			sm_dprintf("getrequests: daemon %s: port %d\n",
18390792Sgshapiro				   Daemons[idx].d_name,
18490792Sgshapiro				   ntohs(Daemons[idx].d_port));
18571345Sgshapiro		}
18664562Sgshapiro	}
18738032Speter
18838032Speter	/* get a socket for the SMTP connection */
18990792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
19090792Sgshapiro		Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
19138032Speter
19242575Speter	if (opencontrolsocket() < 0)
19342575Speter		sm_syslog(LOG_WARNING, NOQID,
19443730Speter			  "daemon could not open control socket %s: %s",
19590792Sgshapiro			  ControlSocketName, sm_errstring(errno));
19642575Speter
19790792Sgshapiro	/* If there are any queue runners released reapchild() co-ord's */
19890792Sgshapiro	(void) sm_signal(SIGCHLD, reapchild);
19938032Speter
20090792Sgshapiro	/* write the pid to file, command line args to syslog */
20164562Sgshapiro	log_sendmail_pid(e);
20238032Speter
20390792Sgshapiro#if XDEBUG
20438032Speter	{
20538032Speter		char jbuf[MAXHOSTNAMELEN];
20638032Speter
20738032Speter		expand("\201j", jbuf, sizeof jbuf, e);
20838032Speter		j_has_dot = strchr(jbuf, '.') != NULL;
20938032Speter	}
21090792Sgshapiro#endif /* XDEBUG */
21138032Speter
21242575Speter	/* Add parent process as first item */
21390792Sgshapiro	proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1);
21442575Speter
21538032Speter	if (tTd(15, 1))
21664562Sgshapiro	{
21790792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
21890792Sgshapiro			sm_dprintf("getrequests: daemon %s: %d\n",
21964562Sgshapiro				Daemons[idx].d_name,
22064562Sgshapiro				Daemons[idx].d_socket);
22164562Sgshapiro	}
22238032Speter
22338032Speter	for (;;)
22438032Speter	{
22538032Speter		register pid_t pid;
22638032Speter		auto SOCKADDR_LEN_T lotherend;
22790792Sgshapiro		bool timedout = false;
22890792Sgshapiro		bool control = false;
22964562Sgshapiro		int save_errno;
23038032Speter		int pipefd[2];
23190792Sgshapiro		time_t now;
23290792Sgshapiro#if STARTTLS
23366494Sgshapiro		long seed;
23490792Sgshapiro#endif /* STARTTLS */
23538032Speter
23638032Speter		/* see if we are rejecting connections */
23790792Sgshapiro		(void) sm_blocksignal(SIGALRM);
23864562Sgshapiro
23977349Sgshapiro		if (ShutdownRequest != NULL)
24077349Sgshapiro			shutdown_daemon();
24177349Sgshapiro		else if (RestartRequest != NULL)
24277349Sgshapiro			restart_daemon();
24390792Sgshapiro		else if (RestartWorkGroup)
24490792Sgshapiro			restart_marked_work_groups();
24577349Sgshapiro
24690792Sgshapiro		for (idx = 0; idx < NDaemons; idx++)
24771345Sgshapiro		{
24890792Sgshapiro			/*
24990792Sgshapiro			**  XXX do this call outside the loop?
25090792Sgshapiro			**	no: refuse_connections may sleep().
25190792Sgshapiro			*/
25271345Sgshapiro
25390792Sgshapiro			now = curtime();
25490792Sgshapiro			if (now < Daemons[idx].d_refuse_connections_until)
25564562Sgshapiro				continue;
25690792Sgshapiro			if (bitnset(D_DISABLE, Daemons[idx].d_flags))
25790792Sgshapiro				continue;
25890792Sgshapiro			if (refuseconnections(Daemons[idx].d_name, e, idx,
25990792Sgshapiro					      curdaemon == idx))
26038032Speter			{
26164562Sgshapiro				if (Daemons[idx].d_socket >= 0)
26242575Speter				{
26371345Sgshapiro					/* close socket so peer fails quickly */
26471345Sgshapiro					(void) close(Daemons[idx].d_socket);
26571345Sgshapiro					Daemons[idx].d_socket = -1;
26642575Speter				}
26742575Speter
26842575Speter				/* refuse connections for next 15 seconds */
26990792Sgshapiro				Daemons[idx].d_refuse_connections_until = now + 15;
27038032Speter			}
27164562Sgshapiro			else if (Daemons[idx].d_socket < 0 ||
27264562Sgshapiro				 Daemons[idx].d_firsttime)
27342575Speter			{
27490792Sgshapiro				if (!Daemons[idx].d_firsttime && LogLevel > 8)
27571345Sgshapiro					sm_syslog(LOG_INFO, NOQID,
27671345Sgshapiro						"accepting connections again for daemon %s",
27771345Sgshapiro						Daemons[idx].d_name);
27864562Sgshapiro
27971345Sgshapiro				/* arrange to (re)open the socket if needed */
28090792Sgshapiro				(void) opendaemonsocket(&Daemons[idx], false);
28190792Sgshapiro				Daemons[idx].d_firsttime = false;
28242575Speter			}
28338032Speter		}
28438032Speter
28577349Sgshapiro		/* May have been sleeping above, check again */
28677349Sgshapiro		if (ShutdownRequest != NULL)
28777349Sgshapiro			shutdown_daemon();
28877349Sgshapiro		else if (RestartRequest != NULL)
28977349Sgshapiro			restart_daemon();
29090792Sgshapiro		else if (RestartWorkGroup)
29190792Sgshapiro			restart_marked_work_groups();
29277349Sgshapiro
29390792Sgshapiro		getrequests_checkdiskspace(e);
29471345Sgshapiro
29590792Sgshapiro#if XDEBUG
29638032Speter		/* check for disaster */
29738032Speter		{
29838032Speter			char jbuf[MAXHOSTNAMELEN];
29938032Speter
30038032Speter			expand("\201j", jbuf, sizeof jbuf, e);
30138032Speter			if (!wordinclass(jbuf, 'w'))
30238032Speter			{
30338032Speter				dumpstate("daemon lost $j");
30438032Speter				sm_syslog(LOG_ALERT, NOQID,
30564562Sgshapiro					  "daemon process doesn't have $j in $=w; see syslog");
30638032Speter				abort();
30738032Speter			}
30838032Speter			else if (j_has_dot && strchr(jbuf, '.') == NULL)
30938032Speter			{
31038032Speter				dumpstate("daemon $j lost dot");
31138032Speter				sm_syslog(LOG_ALERT, NOQID,
31264562Sgshapiro					  "daemon process $j lost dot; see syslog");
31338032Speter				abort();
31438032Speter			}
31538032Speter		}
31690792Sgshapiro#endif /* XDEBUG */
31738032Speter
31890792Sgshapiro#if 0
31938032Speter		/*
32038032Speter		**  Andrew Sun <asun@ieps-sun.ml.com> claims that this will
32138032Speter		**  fix the SVr4 problem.  But it seems to have gone away,
32238032Speter		**  so is it worth doing this?
32338032Speter		*/
32438032Speter
32542575Speter		if (DaemonSocket >= 0 &&
32690792Sgshapiro		    SetNonBlocking(DaemonSocket, false) < 0)
32738032Speter			log an error here;
32890792Sgshapiro#endif /* 0 */
32990792Sgshapiro		(void) sm_releasesignal(SIGALRM);
33064562Sgshapiro
33138032Speter		for (;;)
33238032Speter		{
33390792Sgshapiro			bool setproc = false;
33442575Speter			int highest = -1;
33538032Speter			fd_set readfds;
33638032Speter			struct timeval timeout;
33738032Speter
33877349Sgshapiro			if (ShutdownRequest != NULL)
33977349Sgshapiro				shutdown_daemon();
34077349Sgshapiro			else if (RestartRequest != NULL)
34177349Sgshapiro				restart_daemon();
34290792Sgshapiro			else if (RestartWorkGroup)
34390792Sgshapiro				restart_marked_work_groups();
34477349Sgshapiro
34538032Speter			FD_ZERO(&readfds);
34642575Speter
34790792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
34842575Speter			{
34964562Sgshapiro				/* wait for a connection */
35064562Sgshapiro				if (Daemons[idx].d_socket >= 0)
35164562Sgshapiro				{
35271345Sgshapiro					if (!setproc &&
35371345Sgshapiro					    !bitnset(D_ETRNONLY,
35471345Sgshapiro						     Daemons[idx].d_flags))
35564562Sgshapiro					{
35690792Sgshapiro						sm_setproctitle(true, e,
35764562Sgshapiro								"accepting connections");
35890792Sgshapiro						setproc = true;
35964562Sgshapiro					}
36064562Sgshapiro					if (Daemons[idx].d_socket > highest)
36164562Sgshapiro						highest = Daemons[idx].d_socket;
36290792Sgshapiro					SM_FD_SET(Daemons[idx].d_socket,
36390792Sgshapiro						  &readfds);
36464562Sgshapiro				}
36542575Speter			}
36664562Sgshapiro
36790792Sgshapiro#if NETUNIX
36842575Speter			if (ControlSocket >= 0)
36942575Speter			{
37042575Speter				if (ControlSocket > highest)
37142575Speter					highest = ControlSocket;
37290792Sgshapiro				SM_FD_SET(ControlSocket, &readfds);
37342575Speter			}
37490792Sgshapiro#endif /* NETUNIX */
37564562Sgshapiro
37677349Sgshapiro			timeout.tv_sec = 5;
37738032Speter			timeout.tv_usec = 0;
37838032Speter
37942575Speter			t = select(highest + 1, FDSET_CAST &readfds,
38064562Sgshapiro				   NULL, NULL, &timeout);
38142575Speter
38277349Sgshapiro			/* Did someone signal while waiting? */
38377349Sgshapiro			if (ShutdownRequest != NULL)
38477349Sgshapiro				shutdown_daemon();
38577349Sgshapiro			else if (RestartRequest != NULL)
38677349Sgshapiro				restart_daemon();
38790792Sgshapiro			else if (RestartWorkGroup)
38890792Sgshapiro				restart_marked_work_groups();
38971345Sgshapiro
39071345Sgshapiro
39177349Sgshapiro
39290792Sgshapiro			curdaemon = -1;
39390792Sgshapiro			if (doqueuerun())
39494334Sgshapiro			{
39590792Sgshapiro				(void) runqueue(true, false, false, false);
39694334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
39794334Sgshapiro				lastrun = now;
39894334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
39994334Sgshapiro			}
40094334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
40194334Sgshapiro			else if (QueueIntvl > 0 &&
40294334Sgshapiro				 lastrun + QueueIntvl + 60 < now)
40394334Sgshapiro			{
40471345Sgshapiro
40594334Sgshapiro				/*
40694334Sgshapiro				**  set lastrun unconditionally to avoid
40794334Sgshapiro				**  calling checkqueuerunner() all the time.
40894334Sgshapiro				**  That's also why we currently ignore the
40994334Sgshapiro				**  result of the function call.
41094334Sgshapiro				*/
41194334Sgshapiro
41294334Sgshapiro				(void) checkqueuerunner();
41394334Sgshapiro				lastrun = now;
41494334Sgshapiro			}
41594334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
41694334Sgshapiro
41742575Speter			if (t <= 0)
41842575Speter			{
41990792Sgshapiro				timedout = true;
42042575Speter				break;
42142575Speter			}
42238032Speter
42390792Sgshapiro			control = false;
42438032Speter			errno = 0;
42564562Sgshapiro
42664562Sgshapiro			/* look "round-robin" for an active socket */
42790792Sgshapiro			if ((idx = olddaemon + 1) >= NDaemons)
42864562Sgshapiro				idx = 0;
42990792Sgshapiro			for (i = 0; i < NDaemons; i++)
43042575Speter			{
43164562Sgshapiro				if (Daemons[idx].d_socket >= 0 &&
43290792Sgshapiro				    SM_FD_ISSET(Daemons[idx].d_socket,
43390792Sgshapiro						&readfds))
43464562Sgshapiro				{
43564562Sgshapiro					lotherend = Daemons[idx].d_socksize;
43673188Sgshapiro					memset(&RealHostAddr, '\0',
43773188Sgshapiro					       sizeof RealHostAddr);
43864562Sgshapiro					t = accept(Daemons[idx].d_socket,
43964562Sgshapiro						   (struct sockaddr *)&RealHostAddr,
44064562Sgshapiro						   &lotherend);
44173188Sgshapiro
44273188Sgshapiro					/*
44373188Sgshapiro					**  If remote side closes before
44473188Sgshapiro					**  accept() finishes, sockaddr
44573188Sgshapiro					**  might not be fully filled in.
44673188Sgshapiro					*/
44773188Sgshapiro
44873188Sgshapiro					if (t >= 0 &&
44973188Sgshapiro					    (lotherend == 0 ||
45073188Sgshapiro# ifdef BSD4_4_SOCKADDR
45173188Sgshapiro					     RealHostAddr.sa.sa_len == 0 ||
45273188Sgshapiro# endif /* BSD4_4_SOCKADDR */
45373188Sgshapiro					     RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
45473188Sgshapiro					{
45573188Sgshapiro						(void) close(t);
45673188Sgshapiro						t = -1;
45773188Sgshapiro						errno = EINVAL;
45873188Sgshapiro					}
45964562Sgshapiro					olddaemon = curdaemon = idx;
46064562Sgshapiro					break;
46164562Sgshapiro				}
46290792Sgshapiro				if (++idx >= NDaemons)
46364562Sgshapiro					idx = 0;
46442575Speter			}
46590792Sgshapiro#if NETUNIX
46664562Sgshapiro			if (curdaemon == -1 && ControlSocket >= 0 &&
46790792Sgshapiro			    SM_FD_ISSET(ControlSocket, &readfds))
46842575Speter			{
46942575Speter				struct sockaddr_un sa_un;
47042575Speter
47142575Speter				lotherend = sizeof sa_un;
47273188Sgshapiro				memset(&sa_un, '\0', sizeof sa_un);
47342575Speter				t = accept(ControlSocket,
47442575Speter					   (struct sockaddr *)&sa_un,
47542575Speter					   &lotherend);
47673188Sgshapiro
47773188Sgshapiro				/*
47873188Sgshapiro				**  If remote side closes before
47973188Sgshapiro				**  accept() finishes, sockaddr
48073188Sgshapiro				**  might not be fully filled in.
48173188Sgshapiro				*/
48273188Sgshapiro
48373188Sgshapiro				if (t >= 0 &&
48473188Sgshapiro				    (lotherend == 0 ||
48573188Sgshapiro# ifdef BSD4_4_SOCKADDR
48673188Sgshapiro				     sa_un.sun_len == 0 ||
48773188Sgshapiro# endif /* BSD4_4_SOCKADDR */
48873188Sgshapiro				     sa_un.sun_family != AF_UNIX))
48973188Sgshapiro				{
49073188Sgshapiro					(void) close(t);
49173188Sgshapiro					t = -1;
49273188Sgshapiro					errno = EINVAL;
49373188Sgshapiro				}
49473188Sgshapiro				if (t >= 0)
49590792Sgshapiro					control = true;
49642575Speter			}
49790792Sgshapiro#else /* NETUNIX */
49871345Sgshapiro			if (curdaemon == -1)
49971345Sgshapiro			{
50071345Sgshapiro				/* No daemon to service */
50171345Sgshapiro				continue;
50271345Sgshapiro			}
50390792Sgshapiro#endif /* NETUNIX */
50438032Speter			if (t >= 0 || errno != EINTR)
50538032Speter				break;
50638032Speter		}
50742575Speter		if (timedout)
50842575Speter		{
50990792Sgshapiro			timedout = false;
51042575Speter			continue;
51142575Speter		}
51264562Sgshapiro		save_errno = errno;
51390792Sgshapiro		(void) sm_blocksignal(SIGALRM);
51438032Speter		if (t < 0)
51538032Speter		{
51664562Sgshapiro			errno = save_errno;
51738032Speter			syserr("getrequests: accept");
51838032Speter
51938032Speter			/* arrange to re-open the socket next time around */
52064562Sgshapiro			(void) close(Daemons[curdaemon].d_socket);
52164562Sgshapiro			Daemons[curdaemon].d_socket = -1;
52290792Sgshapiro#if SO_REUSEADDR_IS_BROKEN
52364562Sgshapiro			/*
52464562Sgshapiro			**  Give time for bound socket to be released.
52564562Sgshapiro			**  This creates a denial-of-service if you can
52664562Sgshapiro			**  force accept() to fail on affected systems.
52764562Sgshapiro			*/
52864562Sgshapiro
52990792Sgshapiro			Daemons[curdaemon].d_refuse_connections_until = curtime() + 15;
53090792Sgshapiro#endif /* SO_REUSEADDR_IS_BROKEN */
53138032Speter			continue;
53238032Speter		}
53338032Speter
53464562Sgshapiro		if (!control)
53564562Sgshapiro		{
53664562Sgshapiro			/* set some daemon related macros */
53764562Sgshapiro			switch (Daemons[curdaemon].d_addr.sa.sa_family)
53864562Sgshapiro			{
53964562Sgshapiro			  case AF_UNSPEC:
54090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54190792Sgshapiro					macid("{daemon_family}"), "unspec");
54264562Sgshapiro				break;
54390792Sgshapiro#if _FFR_DAEMON_NETUNIX
54490792Sgshapiro# if NETUNIX
54590792Sgshapiro			  case AF_UNIX:
54690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
54790792Sgshapiro					macid("{daemon_family}"), "local");
54890792Sgshapiro				break;
54990792Sgshapiro# endif /* NETUNIX */
55090792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
55190792Sgshapiro#if NETINET
55264562Sgshapiro			  case AF_INET:
55390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
55490792Sgshapiro					macid("{daemon_family}"), "inet");
55564562Sgshapiro				break;
55690792Sgshapiro#endif /* NETINET */
55790792Sgshapiro#if NETINET6
55864562Sgshapiro			  case AF_INET6:
55990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56090792Sgshapiro					macid("{daemon_family}"), "inet6");
56164562Sgshapiro				break;
56290792Sgshapiro#endif /* NETINET6 */
56390792Sgshapiro#if NETISO
56464562Sgshapiro			  case AF_ISO:
56590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
56690792Sgshapiro					macid("{daemon_family}"), "iso");
56764562Sgshapiro				break;
56890792Sgshapiro#endif /* NETISO */
56990792Sgshapiro#if NETNS
57064562Sgshapiro			  case AF_NS:
57190792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
57290792Sgshapiro					macid("{daemon_family}"), "ns");
57364562Sgshapiro				break;
57490792Sgshapiro#endif /* NETNS */
57590792Sgshapiro#if NETX25
57664562Sgshapiro			  case AF_CCITT:
57790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
57890792Sgshapiro					macid("{daemon_family}"), "x.25");
57964562Sgshapiro				break;
58090792Sgshapiro#endif /* NETX25 */
58164562Sgshapiro			}
58290792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
58390792Sgshapiro				macid("{daemon_name}"),
58490792Sgshapiro				Daemons[curdaemon].d_name);
58564562Sgshapiro			if (Daemons[curdaemon].d_mflags != NULL)
58690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
58790792Sgshapiro					macid("{daemon_flags}"),
58890792Sgshapiro					Daemons[curdaemon].d_mflags);
58964562Sgshapiro			else
59090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
59190792Sgshapiro					macid("{daemon_flags}"), "");
59264562Sgshapiro		}
59364562Sgshapiro
59438032Speter		/*
59538032Speter		**  Create a subprocess to process the mail.
59638032Speter		*/
59738032Speter
59838032Speter		if (tTd(15, 2))
59990792Sgshapiro			sm_dprintf("getrequests: forking (fd = %d)\n", t);
60038032Speter
60138032Speter		/*
60290792Sgshapiro		**  Advance state of PRNG.
60390792Sgshapiro		**  This is necessary because otherwise all child processes
60464562Sgshapiro		**  will produce the same PRN sequence and hence the selection
60564562Sgshapiro		**  of a queue directory (and other things, e.g., MX selection)
60664562Sgshapiro		**  are not "really" random.
60764562Sgshapiro		*/
60890792Sgshapiro#if STARTTLS
60990792Sgshapiro		/* XXX get some better "random" data? */
61066494Sgshapiro		seed = get_random();
61190792Sgshapiro		RAND_seed((void *) &NextDiskSpaceCheck,
61290792Sgshapiro			  sizeof NextDiskSpaceCheck);
61390792Sgshapiro		RAND_seed((void *) &now, sizeof now);
61466494Sgshapiro		RAND_seed((void *) &seed, sizeof seed);
61590792Sgshapiro#else /* STARTTLS */
61664562Sgshapiro		(void) get_random();
61790792Sgshapiro#endif /* STARTTLS */
61864562Sgshapiro
61990792Sgshapiro#if NAMED_BIND
62064562Sgshapiro		/*
62190792Sgshapiro		**  Update MX records for FallBackMX.
62290792Sgshapiro		**  Let's hope this is fast otherwise we screw up the
62390792Sgshapiro		**  response time.
62490792Sgshapiro		*/
62590792Sgshapiro
62690792Sgshapiro		if (FallBackMX != NULL)
62790792Sgshapiro			(void) getfallbackmxrr(FallBackMX);
62890792Sgshapiro#endif /* NAMED_BIND */
62990792Sgshapiro
63090792Sgshapiro#if !PROFILING
63190792Sgshapiro		/*
63238032Speter		**  Create a pipe to keep the child from writing to the
63338032Speter		**  socket until after the parent has closed it.  Otherwise
63438032Speter		**  the parent may hang if the child has closed it first.
63538032Speter		*/
63638032Speter
63738032Speter		if (pipe(pipefd) < 0)
63838032Speter			pipefd[0] = pipefd[1] = -1;
63938032Speter
64090792Sgshapiro		(void) sm_blocksignal(SIGCHLD);
64138032Speter		pid = fork();
64238032Speter		if (pid < 0)
64338032Speter		{
64438032Speter			syserr("daemon: cannot fork");
64538032Speter			if (pipefd[0] != -1)
64638032Speter			{
64738032Speter				(void) close(pipefd[0]);
64838032Speter				(void) close(pipefd[1]);
64938032Speter			}
65090792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
65164562Sgshapiro			(void) sleep(10);
65238032Speter			(void) close(t);
65338032Speter			continue;
65438032Speter		}
65590792Sgshapiro
65690792Sgshapiro#else /* !PROFILING */
65771345Sgshapiro		pid = 0;
65890792Sgshapiro#endif /* !PROFILING */
65938032Speter
66038032Speter		if (pid == 0)
66138032Speter		{
66238032Speter			char *p;
66390792Sgshapiro			SM_FILE_T *inchannel, *outchannel = NULL;
66438032Speter
66538032Speter			/*
66638032Speter			**  CHILD -- return to caller.
66738032Speter			**	Collect verified idea of sending host.
66838032Speter			**	Verify calling user id if possible here.
66938032Speter			*/
67038032Speter
67177349Sgshapiro			/* Reset global flags */
67277349Sgshapiro			RestartRequest = NULL;
67390792Sgshapiro			RestartWorkGroup = false;
67477349Sgshapiro			ShutdownRequest = NULL;
67577349Sgshapiro			PendingSignal = 0;
67690792Sgshapiro			CurrentPid = getpid();
67777349Sgshapiro
67890792Sgshapiro			(void) sm_releasesignal(SIGALRM);
67990792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
68090792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
68190792Sgshapiro			(void) sm_signal(SIGHUP, SIG_DFL);
68290792Sgshapiro			(void) sm_signal(SIGTERM, intsig);
68377349Sgshapiro
68490792Sgshapiro			/* turn on profiling */
68590792Sgshapiro			/* SM_PROF(0); */
68690792Sgshapiro
68790792Sgshapiro			/*
68890792Sgshapiro			**  Initialize exception stack and default exception
68990792Sgshapiro			**  handler for child process.
69090792Sgshapiro			*/
69190792Sgshapiro
69290792Sgshapiro			sm_exc_newthread(fatal_error);
69390792Sgshapiro
69464562Sgshapiro			if (!control)
69564562Sgshapiro			{
69690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
69790792Sgshapiro					macid("{daemon_addr}"),
69890792Sgshapiro					anynet_ntoa(&Daemons[curdaemon].d_addr));
69990792Sgshapiro				(void) sm_snprintf(status, sizeof status, "%d",
70064562Sgshapiro						ntohs(Daemons[curdaemon].d_port));
70190792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
70290792Sgshapiro					macid("{daemon_port}"), status);
70364562Sgshapiro			}
70464562Sgshapiro
70590792Sgshapiro			for (idx = 0; idx < NDaemons; idx++)
70664562Sgshapiro			{
70764562Sgshapiro				if (Daemons[idx].d_socket >= 0)
70864562Sgshapiro					(void) close(Daemons[idx].d_socket);
70980785Sgshapiro				Daemons[idx].d_socket = -1;
71064562Sgshapiro			}
71142575Speter			clrcontrol();
71238032Speter
71364562Sgshapiro			/* Avoid SMTP daemon actions if control command */
71464562Sgshapiro			if (control)
71564562Sgshapiro			{
71664562Sgshapiro				/* Add control socket process */
71790792Sgshapiro				proc_list_add(CurrentPid,
71890792Sgshapiro					      "console socket child",
71990792Sgshapiro					      PROC_CONTROL_CHILD, 0, -1);
72064562Sgshapiro			}
72164562Sgshapiro			else
72264562Sgshapiro			{
72364562Sgshapiro				proc_list_clear();
72442575Speter
72590792Sgshapiro				/* clean up background delivery children */
72690792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
72790792Sgshapiro
72864562Sgshapiro				/* Add parent process as first child item */
72990792Sgshapiro				proc_list_add(CurrentPid, "daemon child",
73090792Sgshapiro					      PROC_DAEMON_CHILD, 0, -1);
73138032Speter
73264562Sgshapiro				/* don't schedule queue runs if ETRN */
73364562Sgshapiro				QueueIntvl = 0;
73438032Speter
73590792Sgshapiro				sm_setproctitle(true, e, "startup with %s",
73664562Sgshapiro						anynet_ntoa(&RealHostAddr));
73764562Sgshapiro			}
73864562Sgshapiro
73990792Sgshapiro#if !PROFILING
74038032Speter			if (pipefd[0] != -1)
74138032Speter			{
74238032Speter				auto char c;
74338032Speter
74438032Speter				/*
74538032Speter				**  Wait for the parent to close the write end
74638032Speter				**  of the pipe, which we will see as an EOF.
74738032Speter				**  This guarantees that we won't write to the
74838032Speter				**  socket until after the parent has closed
74938032Speter				**  the pipe.
75038032Speter				*/
75138032Speter
75238032Speter				/* close the write end of the pipe */
75338032Speter				(void) close(pipefd[1]);
75438032Speter
75538032Speter				/* we shouldn't be interrupted, but ... */
75638032Speter				while (read(pipefd[0], &c, 1) < 0 &&
75738032Speter				       errno == EINTR)
75838032Speter					continue;
75938032Speter				(void) close(pipefd[0]);
76038032Speter			}
76190792Sgshapiro#endif /* !PROFILING */
76238032Speter
76364562Sgshapiro			/* control socket processing */
76464562Sgshapiro			if (control)
76564562Sgshapiro			{
76664562Sgshapiro				control_command(t, e);
76764562Sgshapiro				/* NOTREACHED */
76864562Sgshapiro				exit(EX_SOFTWARE);
76964562Sgshapiro			}
77064562Sgshapiro
77138032Speter			/* determine host name */
77238032Speter			p = hostnamebyanyaddr(&RealHostAddr);
77390792Sgshapiro			if (strlen(p) > MAXNAME) /* XXX  - 1 ? */
77438032Speter				p[MAXNAME] = '\0';
77538032Speter			RealHostName = newstr(p);
77664562Sgshapiro			if (RealHostName[0] == '[')
77764562Sgshapiro			{
77890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
77990792Sgshapiro					macid("{client_resolve}"),
78090792Sgshapiro					h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
78164562Sgshapiro			}
78264562Sgshapiro			else
78390792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
78490792Sgshapiro					macid("{client_resolve}"), "OK");
78590792Sgshapiro			sm_setproctitle(true, e, "startup with %s", p);
78694334Sgshapiro			markstats(e, NULL, STATS_CONNECT);
78738032Speter
78890792Sgshapiro			if ((inchannel = sm_io_open(SmFtStdiofd,
78990792Sgshapiro						    SM_TIME_DEFAULT,
79090792Sgshapiro						    (void *) &t,
79190792Sgshapiro						    SM_IO_RDONLY,
79290792Sgshapiro						    NULL)) == NULL ||
79338032Speter			    (t = dup(t)) < 0 ||
79490792Sgshapiro			    (outchannel = sm_io_open(SmFtStdiofd,
79590792Sgshapiro						     SM_TIME_DEFAULT,
79690792Sgshapiro						     (void *) &t,
79790792Sgshapiro						     SM_IO_WRONLY,
79890792Sgshapiro						     NULL)) == NULL)
79938032Speter			{
80090792Sgshapiro				syserr("cannot open SMTP server channel, fd=%d",
80190792Sgshapiro					t);
80290792Sgshapiro				finis(false, true, EX_OK);
80338032Speter			}
80490792Sgshapiro			sm_io_automode(inchannel, outchannel);
80538032Speter
80638032Speter			InChannel = inchannel;
80738032Speter			OutChannel = outchannel;
80890792Sgshapiro			DisConnected = false;
80938032Speter
81090792Sgshapiro#if XLA
81138032Speter			if (!xla_host_ok(RealHostName))
81238032Speter			{
81364562Sgshapiro				message("421 4.4.5 Too many SMTP sessions for this host");
81490792Sgshapiro				finis(false, true, EX_OK);
81538032Speter			}
81690792Sgshapiro#endif /* XLA */
81764562Sgshapiro			/* find out name for interface of connection */
81890792Sgshapiro			if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
81990792Sgshapiro						      NULL), &sa.sa, &len) == 0)
82064562Sgshapiro			{
82164562Sgshapiro				p = hostnamebyanyaddr(&sa);
82264562Sgshapiro				if (tTd(15, 9))
82390792Sgshapiro					sm_dprintf("getreq: got name %s\n", p);
82490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
82590792Sgshapiro					macid("{if_name}"), p);
82664562Sgshapiro
82790792Sgshapiro				/*
82890792Sgshapiro				**  Do this only if it is not the loopback
82990792Sgshapiro				**  interface.
83090792Sgshapiro				*/
83190792Sgshapiro
83264562Sgshapiro				if (!isloopback(sa))
83364562Sgshapiro				{
83490792Sgshapiro					char *addr;
83590792Sgshapiro					char family[5];
83690792Sgshapiro
83790792Sgshapiro					addr = anynet_ntoa(&sa);
83890792Sgshapiro					(void) sm_snprintf(family,
83990792Sgshapiro						sizeof(family),
84090792Sgshapiro						"%d", sa.sa.sa_family);
84190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
84290792Sgshapiro						A_TEMP,
84390792Sgshapiro						macid("{if_addr}"), addr);
84490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
84590792Sgshapiro						A_TEMP,
84690792Sgshapiro						macid("{if_family}"), family);
84764562Sgshapiro					if (tTd(15, 7))
84890792Sgshapiro						sm_dprintf("getreq: got addr %s and family %s\n",
84990792Sgshapiro							addr, family);
85064562Sgshapiro				}
85164562Sgshapiro				else
85264562Sgshapiro				{
85390792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
85490792Sgshapiro						A_PERM,
85590792Sgshapiro						macid("{if_addr}"), NULL);
85690792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
85790792Sgshapiro						A_PERM,
85890792Sgshapiro						macid("{if_family}"), NULL);
85964562Sgshapiro				}
86064562Sgshapiro			}
86164562Sgshapiro			else
86264562Sgshapiro			{
86364562Sgshapiro				if (tTd(15, 7))
86490792Sgshapiro					sm_dprintf("getreq: getsockname failed\n");
86590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
86690792Sgshapiro					macid("{if_name}"), NULL);
86790792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
86890792Sgshapiro					macid("{if_addr}"), NULL);
86990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
87090792Sgshapiro					macid("{if_family}"), NULL);
87164562Sgshapiro			}
87238032Speter			break;
87338032Speter		}
87438032Speter
87538032Speter		/* parent -- keep track of children */
87664562Sgshapiro		if (control)
87764562Sgshapiro		{
87890792Sgshapiro			(void) sm_snprintf(status, sizeof status,
87990792Sgshapiro					   "control socket server child");
88090792Sgshapiro			proc_list_add(pid, status, PROC_CONTROL, 0, -1);
88164562Sgshapiro		}
88264562Sgshapiro		else
88364562Sgshapiro		{
88490792Sgshapiro			(void) sm_snprintf(status, sizeof status,
88590792Sgshapiro					   "SMTP server child for %s",
88690792Sgshapiro					   anynet_ntoa(&RealHostAddr));
88790792Sgshapiro			proc_list_add(pid, status, PROC_DAEMON, 0, -1);
88864562Sgshapiro		}
88990792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
89038032Speter
89138032Speter		/* close the read end of the synchronization pipe */
89238032Speter		if (pipefd[0] != -1)
89364562Sgshapiro		{
89438032Speter			(void) close(pipefd[0]);
89564562Sgshapiro			pipefd[0] = -1;
89664562Sgshapiro		}
89738032Speter
89838032Speter		/* close the port so that others will hang (for a while) */
89938032Speter		(void) close(t);
90038032Speter
90138032Speter		/* release the child by closing the read end of the sync pipe */
90238032Speter		if (pipefd[1] != -1)
90364562Sgshapiro		{
90438032Speter			(void) close(pipefd[1]);
90564562Sgshapiro			pipefd[1] = -1;
90664562Sgshapiro		}
90738032Speter	}
90890792Sgshapiro	if (tTd(15, 2))
90990792Sgshapiro		sm_dprintf("getreq: returning\n");
91064562Sgshapiro
91190792Sgshapiro#if MILTER
91290792Sgshapiro# if _FFR_MILTER_PERDAEMON
91390792Sgshapiro	/* set the filters for this daemon */
91490792Sgshapiro	if (Daemons[curdaemon].d_inputfilterlist != NULL)
91590792Sgshapiro	{
91690792Sgshapiro		for (i = 0;
91790792Sgshapiro		     (Daemons[curdaemon].d_inputfilters[i] != NULL &&
91890792Sgshapiro		      i < MAXFILTERS);
91990792Sgshapiro		     i++)
92090792Sgshapiro		{
92190792Sgshapiro			InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
92290792Sgshapiro		}
92390792Sgshapiro		if (i < MAXFILTERS)
92490792Sgshapiro			InputFilters[i] = NULL;
92590792Sgshapiro	}
92690792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
92790792Sgshapiro#endif /* MILTER */
92864562Sgshapiro	return &Daemons[curdaemon].d_flags;
92938032Speter}
93090792Sgshapiro
93190792Sgshapiro/*
93290792Sgshapiro**  GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
93390792Sgshapiro**
93490792Sgshapiro**	Parameters:
93590792Sgshapiro**		e -- envelope.
93690792Sgshapiro**
93790792Sgshapiro**	Returns:
93890792Sgshapiro**		none.
93990792Sgshapiro**
94090792Sgshapiro**	Side Effects:
94190792Sgshapiro**		Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
94290792Sgshapiro*/
94390792Sgshapiro
94490792Sgshapirostatic void
94590792Sgshapirogetrequests_checkdiskspace(e)
94690792Sgshapiro	ENVELOPE *e;
94790792Sgshapiro{
94890792Sgshapiro	bool logged = false;
94990792Sgshapiro	int idx;
95090792Sgshapiro	time_t now;
95190792Sgshapiro
95290792Sgshapiro	now = curtime();
95390792Sgshapiro	if (now < NextDiskSpaceCheck)
95490792Sgshapiro		return;
95590792Sgshapiro
95690792Sgshapiro	/* Check if there is available disk space in all queue groups. */
95790792Sgshapiro	if (!enoughdiskspace(0, NULL))
95890792Sgshapiro	{
95990792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
96090792Sgshapiro		{
96190792Sgshapiro			if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
96290792Sgshapiro				continue;
96390792Sgshapiro
96490792Sgshapiro			/* log only if not logged before */
96590792Sgshapiro			if (!logged)
96690792Sgshapiro			{
96790792Sgshapiro				if (LogLevel > 8)
96890792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
96990792Sgshapiro						  "rejecting new messages: min free: %ld",
97090792Sgshapiro						  MinBlocksFree);
97190792Sgshapiro				sm_setproctitle(true, e,
97290792Sgshapiro						"rejecting new messages: min free: %ld",
97390792Sgshapiro						MinBlocksFree);
97490792Sgshapiro				logged = true;
97590792Sgshapiro			}
97690792Sgshapiro			setbitn(D_ETRNONLY, Daemons[idx].d_flags);
97790792Sgshapiro		}
97890792Sgshapiro	}
97990792Sgshapiro	else
98090792Sgshapiro	{
98190792Sgshapiro		for (idx = 0; idx < NDaemons; ++idx)
98290792Sgshapiro		{
98390792Sgshapiro			if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
98490792Sgshapiro				continue;
98590792Sgshapiro
98690792Sgshapiro			/* log only if not logged before */
98790792Sgshapiro			if (!logged)
98890792Sgshapiro			{
98990792Sgshapiro				if (LogLevel > 8)
99090792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
99190792Sgshapiro						  "accepting new messages (again)");
99290792Sgshapiro				logged = true;
99390792Sgshapiro			}
99490792Sgshapiro
99590792Sgshapiro			/* title will be set later */
99690792Sgshapiro			clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
99790792Sgshapiro		}
99890792Sgshapiro	}
99990792Sgshapiro
100090792Sgshapiro	/* only check disk space once a minute */
100190792Sgshapiro	NextDiskSpaceCheck = now + 60;
100290792Sgshapiro}
100390792Sgshapiro
100490792Sgshapiro/*
100564562Sgshapiro**  OPENDAEMONSOCKET -- open SMTP socket
100638032Speter**
100764562Sgshapiro**	Deals with setting all appropriate options.
100838032Speter**
100938032Speter**	Parameters:
101064562Sgshapiro**		d -- the structure for the daemon to open.
101138032Speter**		firsttime -- set if this is the initial open.
101238032Speter**
101338032Speter**	Returns:
101438032Speter**		Size in bytes of the daemon socket addr.
101538032Speter**
101638032Speter**	Side Effects:
101738032Speter**		Leaves DaemonSocket set to the open socket.
101838032Speter**		Exits if the socket cannot be created.
101938032Speter*/
102038032Speter
102190792Sgshapiro#define MAXOPENTRIES	10	/* maximum number of tries to open connection */
102238032Speter
102364562Sgshapirostatic int
102464562Sgshapiroopendaemonsocket(d, firsttime)
102590792Sgshapiro	DAEMON_T *d;
102638032Speter	bool firsttime;
102738032Speter{
102838032Speter	int on = 1;
102964562Sgshapiro	int fdflags;
103064562Sgshapiro	SOCKADDR_LEN_T socksize = 0;
103138032Speter	int ntries = 0;
103264562Sgshapiro	int save_errno;
103338032Speter
103438032Speter	if (tTd(15, 2))
103590792Sgshapiro		sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
103638032Speter
103738032Speter	do
103838032Speter	{
103938032Speter		if (ntries > 0)
104064562Sgshapiro			(void) sleep(5);
104164562Sgshapiro		if (firsttime || d->d_socket < 0)
104238032Speter		{
104390792Sgshapiro#if _FFR_DAEMON_NETUNIX
104490792Sgshapiro# if NETUNIX
104590792Sgshapiro			if (d->d_addr.sa.sa_family == AF_UNIX)
104690792Sgshapiro			{
104790792Sgshapiro				int rval;
104890792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
104990792Sgshapiro
105090792Sgshapiro				/* if not safe, don't use it */
105190792Sgshapiro				rval = safefile(d->d_addr.sunix.sun_path,
105290792Sgshapiro						RunAsUid, RunAsGid,
105390792Sgshapiro						RunAsUserName, sff,
105490792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
105590792Sgshapiro				if (rval != 0)
105690792Sgshapiro				{
105790792Sgshapiro					save_errno = errno;
105890792Sgshapiro					syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
105990792Sgshapiro					       d->d_name,
106090792Sgshapiro					       d->d_addr.sunix.sun_path);
106190792Sgshapiro					goto fail;
106290792Sgshapiro				}
106390792Sgshapiro
106490792Sgshapiro				/* Don't try to overtake an existing socket */
106590792Sgshapiro				(void) unlink(d->d_addr.sunix.sun_path);
106690792Sgshapiro			}
106790792Sgshapiro# endif /* NETUNIX */
106890792Sgshapiro#endif /* _FFR_DOMAIN_NETUNIX */
106964562Sgshapiro			d->d_socket = socket(d->d_addr.sa.sa_family,
107064562Sgshapiro					     SOCK_STREAM, 0);
107164562Sgshapiro			if (d->d_socket < 0)
107238032Speter			{
107364562Sgshapiro				save_errno = errno;
107490792Sgshapiro				syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
107590792Sgshapiro				       d->d_name);
107690792Sgshapiro			  fail:
107790792Sgshapiro				if (bitnset(D_OPTIONAL, d->d_flags) &&
107890792Sgshapiro				    (!transienterror(save_errno) ||
107990792Sgshapiro				     ntries >= MAXOPENTRIES - 1))
108090792Sgshapiro				{
108190792Sgshapiro					syserr("opendaemonsocket: daemon %s: optional socket disabled",
108290792Sgshapiro					       d->d_name);
108390792Sgshapiro					setbitn(D_DISABLE, d->d_flags);
108490792Sgshapiro					d->d_socket = -1;
108590792Sgshapiro					return -1;
108690792Sgshapiro				}
108738032Speter			  severe:
108838032Speter				if (LogLevel > 0)
108938032Speter					sm_syslog(LOG_ALERT, NOQID,
109090792Sgshapiro						  "daemon %s: problem creating SMTP socket",
109190792Sgshapiro						  d->d_name);
109264562Sgshapiro				d->d_socket = -1;
109338032Speter				continue;
109438032Speter			}
109538032Speter
109638032Speter			/* turn on network debugging? */
109738032Speter			if (tTd(15, 101))
109864562Sgshapiro				(void) setsockopt(d->d_socket, SOL_SOCKET,
109938032Speter						  SO_DEBUG, (char *)&on,
110038032Speter						  sizeof on);
110138032Speter
110264562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
110338032Speter					  SO_REUSEADDR, (char *)&on, sizeof on);
110464562Sgshapiro			(void) setsockopt(d->d_socket, SOL_SOCKET,
110538032Speter					  SO_KEEPALIVE, (char *)&on, sizeof on);
110638032Speter
110790792Sgshapiro#ifdef SO_RCVBUF
110864562Sgshapiro			if (d->d_tcprcvbufsize > 0)
110938032Speter			{
111064562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
111138032Speter					       SO_RCVBUF,
111264562Sgshapiro					       (char *) &d->d_tcprcvbufsize,
111364562Sgshapiro					       sizeof(d->d_tcprcvbufsize)) < 0)
111464562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_RCVBUF)", d->d_name);
111538032Speter			}
111690792Sgshapiro#endif /* SO_RCVBUF */
111790792Sgshapiro#ifdef SO_SNDBUF
111864562Sgshapiro			if (d->d_tcpsndbufsize > 0)
111964562Sgshapiro			{
112064562Sgshapiro				if (setsockopt(d->d_socket, SOL_SOCKET,
112164562Sgshapiro					       SO_SNDBUF,
112264562Sgshapiro					       (char *) &d->d_tcpsndbufsize,
112364562Sgshapiro					       sizeof(d->d_tcpsndbufsize)) < 0)
112464562Sgshapiro					syserr("opendaemonsocket: daemon %s: setsockopt(SO_SNDBUF)", d->d_name);
112564562Sgshapiro			}
112690792Sgshapiro#endif /* SO_SNDBUF */
112738032Speter
112864562Sgshapiro			if ((fdflags = fcntl(d->d_socket, F_GETFD, 0)) == -1 ||
112964562Sgshapiro			    fcntl(d->d_socket, F_SETFD,
113064562Sgshapiro				  fdflags | FD_CLOEXEC) == -1)
113138032Speter			{
113264562Sgshapiro				save_errno = errno;
113364562Sgshapiro				syserr("opendaemonsocket: daemon %s: failed to %s close-on-exec flag: %s",
113464562Sgshapiro				       d->d_name,
113564562Sgshapiro				       fdflags == -1 ? "get" : "set",
113690792Sgshapiro				       sm_errstring(save_errno));
113764562Sgshapiro				(void) close(d->d_socket);
113864562Sgshapiro				goto severe;
113964562Sgshapiro			}
114064562Sgshapiro
114164562Sgshapiro			switch (d->d_addr.sa.sa_family)
114264562Sgshapiro			{
114390792Sgshapiro#if _FFR_DAEMON_NETUNIX
114490792Sgshapiro# ifdef NETUNIX
114590792Sgshapiro			  case AF_UNIX:
114690792Sgshapiro				socksize = sizeof d->d_addr.sunix;
114790792Sgshapiro				break;
114890792Sgshapiro# endif /* NETUNIX */
114990792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
115090792Sgshapiro#if NETINET
115138032Speter			  case AF_INET:
115264562Sgshapiro				socksize = sizeof d->d_addr.sin;
115338032Speter				break;
115490792Sgshapiro#endif /* NETINET */
115538032Speter
115690792Sgshapiro#if NETINET6
115764562Sgshapiro			  case AF_INET6:
115864562Sgshapiro				socksize = sizeof d->d_addr.sin6;
115964562Sgshapiro				break;
116090792Sgshapiro#endif /* NETINET6 */
116164562Sgshapiro
116290792Sgshapiro#if NETISO
116338032Speter			  case AF_ISO:
116464562Sgshapiro				socksize = sizeof d->d_addr.siso;
116538032Speter				break;
116690792Sgshapiro#endif /* NETISO */
116738032Speter
116838032Speter			  default:
116964562Sgshapiro				socksize = sizeof d->d_addr;
117038032Speter				break;
117138032Speter			}
117238032Speter
117364562Sgshapiro			if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
117438032Speter			{
117538032Speter				/* probably another daemon already */
117664562Sgshapiro				save_errno = errno;
117764562Sgshapiro				syserr("opendaemonsocket: daemon %s: cannot bind",
117864562Sgshapiro				       d->d_name);
117964562Sgshapiro				(void) close(d->d_socket);
118090792Sgshapiro				goto fail;
118138032Speter			}
118238032Speter		}
118364562Sgshapiro		if (!firsttime &&
118464562Sgshapiro		    listen(d->d_socket, d->d_listenqueue) < 0)
118538032Speter		{
118664562Sgshapiro			save_errno = errno;
118764562Sgshapiro			syserr("opendaemonsocket: daemon %s: cannot listen",
118864562Sgshapiro			       d->d_name);
118964562Sgshapiro			(void) close(d->d_socket);
119038032Speter			goto severe;
119138032Speter		}
119238032Speter		return socksize;
119364562Sgshapiro	} while (ntries++ < MAXOPENTRIES && transienterror(save_errno));
119464562Sgshapiro	syserr("!opendaemonsocket: daemon %s: server SMTP socket wedged: exiting",
119564562Sgshapiro	       d->d_name);
119664562Sgshapiro	/* NOTREACHED */
119738032Speter	return -1;  /* avoid compiler warning on IRIX */
119838032Speter}
119990792Sgshapiro/*
120064562Sgshapiro**  SETUPDAEMON -- setup socket for daemon
120164562Sgshapiro**
120264562Sgshapiro**	Parameters:
120364562Sgshapiro**		daemonaddr -- socket for daemon
120464562Sgshapiro**
120564562Sgshapiro**	Returns:
120664562Sgshapiro**		port number on which daemon should run
120764562Sgshapiro**
120864562Sgshapiro*/
120990792Sgshapiro
121090792Sgshapirostatic unsigned short
121164562Sgshapirosetupdaemon(daemonaddr)
121264562Sgshapiro	SOCKADDR *daemonaddr;
121364562Sgshapiro{
121490792Sgshapiro	unsigned short port;
121564562Sgshapiro
121664562Sgshapiro	/*
121764562Sgshapiro	**  Set up the address for the mailer.
121864562Sgshapiro	*/
121964562Sgshapiro
122064562Sgshapiro	if (daemonaddr->sa.sa_family == AF_UNSPEC)
122164562Sgshapiro	{
122264562Sgshapiro		memset(daemonaddr, '\0', sizeof *daemonaddr);
122390792Sgshapiro#if NETINET
122464562Sgshapiro		daemonaddr->sa.sa_family = AF_INET;
122590792Sgshapiro#endif /* NETINET */
122664562Sgshapiro	}
122764562Sgshapiro
122864562Sgshapiro	switch (daemonaddr->sa.sa_family)
122964562Sgshapiro	{
123090792Sgshapiro#if NETINET
123164562Sgshapiro	  case AF_INET:
123264562Sgshapiro		if (daemonaddr->sin.sin_addr.s_addr == 0)
123364562Sgshapiro			daemonaddr->sin.sin_addr.s_addr = INADDR_ANY;
123464562Sgshapiro		port = daemonaddr->sin.sin_port;
123564562Sgshapiro		break;
123690792Sgshapiro#endif /* NETINET */
123764562Sgshapiro
123890792Sgshapiro#if NETINET6
123964562Sgshapiro	  case AF_INET6:
124064562Sgshapiro		if (IN6_IS_ADDR_UNSPECIFIED(&daemonaddr->sin6.sin6_addr))
124164562Sgshapiro			daemonaddr->sin6.sin6_addr = in6addr_any;
124264562Sgshapiro		port = daemonaddr->sin6.sin6_port;
124364562Sgshapiro		break;
124490792Sgshapiro#endif /* NETINET6 */
124564562Sgshapiro
124664562Sgshapiro	  default:
124764562Sgshapiro		/* unknown protocol */
124864562Sgshapiro		port = 0;
124964562Sgshapiro		break;
125064562Sgshapiro	}
125164562Sgshapiro	if (port == 0)
125264562Sgshapiro	{
125390792Sgshapiro#ifdef NO_GETSERVBYNAME
125464562Sgshapiro		port = htons(25);
125590792Sgshapiro#else /* NO_GETSERVBYNAME */
125664562Sgshapiro		{
125764562Sgshapiro			register struct servent *sp;
125864562Sgshapiro
125964562Sgshapiro			sp = getservbyname("smtp", "tcp");
126064562Sgshapiro			if (sp == NULL)
126164562Sgshapiro			{
126264562Sgshapiro				syserr("554 5.3.5 service \"smtp\" unknown");
126364562Sgshapiro				port = htons(25);
126464562Sgshapiro			}
126564562Sgshapiro			else
126664562Sgshapiro				port = sp->s_port;
126764562Sgshapiro		}
126890792Sgshapiro#endif /* NO_GETSERVBYNAME */
126964562Sgshapiro	}
127064562Sgshapiro
127164562Sgshapiro	switch (daemonaddr->sa.sa_family)
127264562Sgshapiro	{
127390792Sgshapiro#if NETINET
127464562Sgshapiro	  case AF_INET:
127564562Sgshapiro		daemonaddr->sin.sin_port = port;
127664562Sgshapiro		break;
127790792Sgshapiro#endif /* NETINET */
127864562Sgshapiro
127990792Sgshapiro#if NETINET6
128064562Sgshapiro	  case AF_INET6:
128164562Sgshapiro		daemonaddr->sin6.sin6_port = port;
128264562Sgshapiro		break;
128390792Sgshapiro#endif /* NETINET6 */
128464562Sgshapiro
128564562Sgshapiro	  default:
128664562Sgshapiro		/* unknown protocol */
128764562Sgshapiro		break;
128864562Sgshapiro	}
128990792Sgshapiro	return port;
129064562Sgshapiro}
129190792Sgshapiro/*
129238032Speter**  CLRDAEMON -- reset the daemon connection
129338032Speter**
129438032Speter**	Parameters:
129538032Speter**		none.
129638032Speter**
129738032Speter**	Returns:
129838032Speter**		none.
129938032Speter**
130038032Speter**	Side Effects:
130138032Speter**		releases any resources used by the passive daemon.
130238032Speter*/
130338032Speter
130438032Spetervoid
130538032Speterclrdaemon()
130638032Speter{
130764562Sgshapiro	int i;
130864562Sgshapiro
130990792Sgshapiro	for (i = 0; i < NDaemons; i++)
131064562Sgshapiro	{
131164562Sgshapiro		if (Daemons[i].d_socket >= 0)
131264562Sgshapiro			(void) close(Daemons[i].d_socket);
131364562Sgshapiro		Daemons[i].d_socket = -1;
131464562Sgshapiro	}
131538032Speter}
131690792Sgshapiro
131790792Sgshapiro/*
131890792Sgshapiro**  GETMODIFIERS -- get modifier flags
131990792Sgshapiro**
132090792Sgshapiro**	Parameters:
132190792Sgshapiro**		v -- the modifiers (input text line).
132290792Sgshapiro**		modifiers -- pointer to flag field to represent modifiers.
132390792Sgshapiro**
132490792Sgshapiro**	Returns:
132590792Sgshapiro**		(xallocat()ed) string representation of modifiers.
132690792Sgshapiro**
132790792Sgshapiro**	Side Effects:
132890792Sgshapiro**		fills in modifiers.
132990792Sgshapiro*/
133090792Sgshapiro
133190792Sgshapirochar *
133290792Sgshapirogetmodifiers(v, modifiers)
133390792Sgshapiro	char *v;
133490792Sgshapiro	BITMAP256 modifiers;
133590792Sgshapiro{
133690792Sgshapiro	int l;
133790792Sgshapiro	char *h, *f, *flags;
133890792Sgshapiro
133990792Sgshapiro	/* maximum length of flags: upper case Option -> "OO " */
134090792Sgshapiro	l = 3 * strlen(v) + 3;
134190792Sgshapiro
134290792Sgshapiro	/* is someone joking? */
134390792Sgshapiro	if (l < 0 || l > 256)
134490792Sgshapiro	{
134590792Sgshapiro		if (LogLevel > 2)
134690792Sgshapiro			sm_syslog(LOG_ERR, NOQID,
134790792Sgshapiro				  "getmodifiers too long, ignored");
134890792Sgshapiro		return NULL;
134990792Sgshapiro	}
135090792Sgshapiro	flags = xalloc(l);
135190792Sgshapiro	f = flags;
135290792Sgshapiro	clrbitmap(modifiers);
135390792Sgshapiro	for (h = v; *h != '\0'; h++)
135490792Sgshapiro	{
135590792Sgshapiro		if (isascii(*h) && !isspace(*h) && isprint(*h))
135690792Sgshapiro		{
135790792Sgshapiro			setbitn(*h, modifiers);
135890792Sgshapiro			if (flags != f)
135990792Sgshapiro				*flags++ = ' ';
136090792Sgshapiro			*flags++ = *h;
136190792Sgshapiro			if (isupper(*h))
136290792Sgshapiro				*flags++ = *h;
136390792Sgshapiro		}
136490792Sgshapiro	}
136590792Sgshapiro	*flags++ = '\0';
136690792Sgshapiro	return f;
136790792Sgshapiro}
136890792Sgshapiro
136990792Sgshapiro/*
137090792Sgshapiro**  CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
137190792Sgshapiro**
137290792Sgshapiro**	Parameters:
137390792Sgshapiro**		flag -- the flag to test.
137490792Sgshapiro**
137590792Sgshapiro**	Returns:
137690792Sgshapiro**		true iff all daemons have set flag.
137790792Sgshapiro*/
137890792Sgshapiro
137990792Sgshapirobool
138090792Sgshapirochkdaemonmodifiers(flag)
138190792Sgshapiro	int flag;
138290792Sgshapiro{
138390792Sgshapiro	int i;
138490792Sgshapiro
138590792Sgshapiro	for (i = 0; i < NDaemons; i++)
138690792Sgshapiro		if (!bitnset((char) flag, Daemons[i].d_flags))
138790792Sgshapiro			return false;
138890792Sgshapiro	return true;
138990792Sgshapiro}
139090792Sgshapiro
139190792Sgshapiro/*
139264562Sgshapiro**  SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
139338032Speter**
139438032Speter**	Parameters:
139538032Speter**		p -- the options line.
139664562Sgshapiro**		d -- the daemon structure to fill in.
139738032Speter**
139838032Speter**	Returns:
139938032Speter**		none.
140038032Speter*/
140138032Speter
140264562Sgshapirostatic void
140364562Sgshapirosetsockaddroptions(p, d)
140438032Speter	register char *p;
140590792Sgshapiro	DAEMON_T *d;
140638032Speter{
140790792Sgshapiro#if NETISO
140871345Sgshapiro	short portno;
140990792Sgshapiro#endif /* NETISO */
141071345Sgshapiro	char *port = NULL;
141171345Sgshapiro	char *addr = NULL;
141238032Speter
141390792Sgshapiro#if NETINET
141464562Sgshapiro	if (d->d_addr.sa.sa_family == AF_UNSPEC)
141564562Sgshapiro		d->d_addr.sa.sa_family = AF_INET;
141690792Sgshapiro#endif /* NETINET */
141764562Sgshapiro
141838032Speter	while (p != NULL)
141938032Speter	{
142038032Speter		register char *f;
142138032Speter		register char *v;
142238032Speter
142338032Speter		while (isascii(*p) && isspace(*p))
142438032Speter			p++;
142538032Speter		if (*p == '\0')
142638032Speter			break;
142738032Speter		f = p;
142838032Speter		p = strchr(p, ',');
142938032Speter		if (p != NULL)
143038032Speter			*p++ = '\0';
143138032Speter		v = strchr(f, '=');
143238032Speter		if (v == NULL)
143338032Speter			continue;
143438032Speter		while (isascii(*++v) && isspace(*v))
143538032Speter			continue;
143638032Speter		if (isascii(*f) && islower(*f))
143738032Speter			*f = toupper(*f);
143838032Speter
143938032Speter		switch (*f)
144038032Speter		{
144138032Speter		  case 'F':		/* address family */
144238032Speter			if (isascii(*v) && isdigit(*v))
144364562Sgshapiro				d->d_addr.sa.sa_family = atoi(v);
144490792Sgshapiro#if _FFR_DAEMON_NETUNIX
144590792Sgshapiro# ifdef NETUNIX
144690792Sgshapiro			else if (sm_strcasecmp(v, "unix") == 0 ||
144790792Sgshapiro				 sm_strcasecmp(v, "local") == 0)
144890792Sgshapiro				d->d_addr.sa.sa_family = AF_UNIX;
144990792Sgshapiro# endif /* NETUNIX */
145090792Sgshapiro#endif /* _FFR_DAEMON_NETUNIX */
145190792Sgshapiro#if NETINET
145290792Sgshapiro			else if (sm_strcasecmp(v, "inet") == 0)
145364562Sgshapiro				d->d_addr.sa.sa_family = AF_INET;
145490792Sgshapiro#endif /* NETINET */
145590792Sgshapiro#if NETINET6
145690792Sgshapiro			else if (sm_strcasecmp(v, "inet6") == 0)
145764562Sgshapiro				d->d_addr.sa.sa_family = AF_INET6;
145890792Sgshapiro#endif /* NETINET6 */
145990792Sgshapiro#if NETISO
146090792Sgshapiro			else if (sm_strcasecmp(v, "iso") == 0)
146164562Sgshapiro				d->d_addr.sa.sa_family = AF_ISO;
146290792Sgshapiro#endif /* NETISO */
146390792Sgshapiro#if NETNS
146490792Sgshapiro			else if (sm_strcasecmp(v, "ns") == 0)
146564562Sgshapiro				d->d_addr.sa.sa_family = AF_NS;
146690792Sgshapiro#endif /* NETNS */
146790792Sgshapiro#if NETX25
146890792Sgshapiro			else if (sm_strcasecmp(v, "x.25") == 0)
146964562Sgshapiro				d->d_addr.sa.sa_family = AF_CCITT;
147090792Sgshapiro#endif /* NETX25 */
147138032Speter			else
147264562Sgshapiro				syserr("554 5.3.5 Unknown address family %s in Family=option",
147364562Sgshapiro				       v);
147438032Speter			break;
147538032Speter
147638032Speter		  case 'A':		/* address */
147771345Sgshapiro			addr = v;
147838032Speter			break;
147938032Speter
148090792Sgshapiro#if MILTER
148190792Sgshapiro# if _FFR_MILTER_PERDAEMON
148290792Sgshapiro		  case 'I':
148390792Sgshapiro			d->d_inputfilterlist = v;
148490792Sgshapiro			break;
148590792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
148690792Sgshapiro#endif /* MILTER */
148790792Sgshapiro
148838032Speter		  case 'P':		/* port */
148971345Sgshapiro			port = v;
149038032Speter			break;
149138032Speter
149238032Speter		  case 'L':		/* listen queue size */
149364562Sgshapiro			d->d_listenqueue = atoi(v);
149438032Speter			break;
149538032Speter
149664562Sgshapiro		  case 'M':		/* modifiers (flags) */
149790792Sgshapiro			d->d_mflags = getmodifiers(v, d->d_flags);
149864562Sgshapiro			break;
149964562Sgshapiro
150038032Speter		  case 'S':		/* send buffer size */
150164562Sgshapiro			d->d_tcpsndbufsize = atoi(v);
150238032Speter			break;
150338032Speter
150438032Speter		  case 'R':		/* receive buffer size */
150564562Sgshapiro			d->d_tcprcvbufsize = atoi(v);
150638032Speter			break;
150738032Speter
150864562Sgshapiro		  case 'N':		/* name */
150964562Sgshapiro			d->d_name = v;
151064562Sgshapiro			break;
151164562Sgshapiro
151238032Speter		  default:
151364562Sgshapiro			syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
151464562Sgshapiro			       f);
151538032Speter		}
151638032Speter	}
151771345Sgshapiro
151871345Sgshapiro	/* Check addr and port after finding family */
151971345Sgshapiro	if (addr != NULL)
152071345Sgshapiro	{
152171345Sgshapiro		switch (d->d_addr.sa.sa_family)
152271345Sgshapiro		{
152390792Sgshapiro#if _FFR_DAEMON_NETUNIX
152490792Sgshapiro# if NETUNIX
152590792Sgshapiro		  case AF_UNIX:
152690792Sgshapiro			if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
152790792Sgshapiro			{
152890792Sgshapiro				errno = ENAMETOOLONG;
152990792Sgshapiro				syserr("setsockaddroptions: domain socket name too long: %s > %d",
153090792Sgshapiro				       addr, sizeof(d->d_addr.sunix.sun_path));
153190792Sgshapiro				break;
153290792Sgshapiro			}
153390792Sgshapiro
153490792Sgshapiro			/* file safety check done in opendaemonsocket() */
153590792Sgshapiro			(void) memset(&d->d_addr.sunix.sun_path, '\0',
153690792Sgshapiro				      sizeof(d->d_addr.sunix.sun_path));
153790792Sgshapiro			(void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
153890792Sgshapiro					  addr,
153990792Sgshapiro					  sizeof(d->d_addr.sunix.sun_path));
154090792Sgshapiro			break;
154190792Sgshapiro# endif /* NETUNIX */
154290792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
154390792Sgshapiro#if NETINET
154471345Sgshapiro		  case AF_INET:
154571345Sgshapiro			if (!isascii(*addr) || !isdigit(*addr) ||
154690792Sgshapiro			    ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
154790792Sgshapiro			     == INADDR_NONE))
154871345Sgshapiro			{
154971345Sgshapiro				register struct hostent *hp;
155071345Sgshapiro
155171345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET);
155271345Sgshapiro				if (hp == NULL)
155371345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
155471345Sgshapiro					       addr);
155571345Sgshapiro				else
155671345Sgshapiro				{
155771345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
155871345Sgshapiro					       hp->h_addrtype != AF_INET)
155971345Sgshapiro						hp->h_addr_list++;
156071345Sgshapiro					if (*(hp->h_addr_list) == NULL)
156171345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
156271345Sgshapiro						       addr);
156371345Sgshapiro					else
156471345Sgshapiro						memmove(&d->d_addr.sin.sin_addr,
156571345Sgshapiro							*(hp->h_addr_list),
156671345Sgshapiro							INADDRSZ);
156790792Sgshapiro# if NETINET6
156871345Sgshapiro					freehostent(hp);
156971345Sgshapiro					hp = NULL;
157090792Sgshapiro# endif /* NETINET6 */
157171345Sgshapiro				}
157271345Sgshapiro			}
157371345Sgshapiro			break;
157490792Sgshapiro#endif /* NETINET */
157571345Sgshapiro
157690792Sgshapiro#if NETINET6
157771345Sgshapiro		  case AF_INET6:
157890792Sgshapiro			if (anynet_pton(AF_INET6, addr,
157990792Sgshapiro					&d->d_addr.sin6.sin6_addr) != 1)
158071345Sgshapiro			{
158171345Sgshapiro				register struct hostent *hp;
158271345Sgshapiro
158371345Sgshapiro				hp = sm_gethostbyname(addr, AF_INET6);
158471345Sgshapiro				if (hp == NULL)
158571345Sgshapiro					syserr("554 5.3.0 host \"%s\" unknown",
158671345Sgshapiro					       addr);
158771345Sgshapiro				else
158871345Sgshapiro				{
158971345Sgshapiro					while (*(hp->h_addr_list) != NULL &&
159071345Sgshapiro					       hp->h_addrtype != AF_INET6)
159171345Sgshapiro						hp->h_addr_list++;
159271345Sgshapiro					if (*(hp->h_addr_list) == NULL)
159371345Sgshapiro						syserr("554 5.3.0 host \"%s\" unknown",
159471345Sgshapiro						       addr);
159571345Sgshapiro					else
159671345Sgshapiro						memmove(&d->d_addr.sin6.sin6_addr,
159771345Sgshapiro							*(hp->h_addr_list),
159871345Sgshapiro							IN6ADDRSZ);
159971345Sgshapiro					freehostent(hp);
160071345Sgshapiro					hp = NULL;
160171345Sgshapiro				}
160271345Sgshapiro			}
160371345Sgshapiro			break;
160490792Sgshapiro#endif /* NETINET6 */
160571345Sgshapiro
160671345Sgshapiro		  default:
160771345Sgshapiro			syserr("554 5.3.5 address= option unsupported for family %d",
160871345Sgshapiro			       d->d_addr.sa.sa_family);
160971345Sgshapiro			break;
161071345Sgshapiro		}
161171345Sgshapiro	}
161271345Sgshapiro
161371345Sgshapiro	if (port != NULL)
161471345Sgshapiro	{
161571345Sgshapiro		switch (d->d_addr.sa.sa_family)
161671345Sgshapiro		{
161790792Sgshapiro#if NETINET
161871345Sgshapiro		  case AF_INET:
161971345Sgshapiro			if (isascii(*port) && isdigit(*port))
162090792Sgshapiro				d->d_addr.sin.sin_port = htons((unsigned short)
162190792Sgshapiro						     atoi((const char *) port));
162271345Sgshapiro			else
162371345Sgshapiro			{
162490792Sgshapiro# ifdef NO_GETSERVBYNAME
162571345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
162671345Sgshapiro				       port);
162790792Sgshapiro# else /* NO_GETSERVBYNAME */
162871345Sgshapiro				register struct servent *sp;
162971345Sgshapiro
163071345Sgshapiro				sp = getservbyname(port, "tcp");
163171345Sgshapiro				if (sp == NULL)
163271345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
163371345Sgshapiro					       port);
163471345Sgshapiro				else
163571345Sgshapiro					d->d_addr.sin.sin_port = sp->s_port;
163690792Sgshapiro# endif /* NO_GETSERVBYNAME */
163771345Sgshapiro			}
163871345Sgshapiro			break;
163990792Sgshapiro#endif /* NETINET */
164071345Sgshapiro
164190792Sgshapiro#if NETINET6
164271345Sgshapiro		  case AF_INET6:
164371345Sgshapiro			if (isascii(*port) && isdigit(*port))
164490792Sgshapiro				d->d_addr.sin6.sin6_port = htons((unsigned short)
164590792Sgshapiro								  atoi(port));
164671345Sgshapiro			else
164771345Sgshapiro			{
164890792Sgshapiro# ifdef NO_GETSERVBYNAME
164971345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
165071345Sgshapiro				       port);
165190792Sgshapiro# else /* NO_GETSERVBYNAME */
165271345Sgshapiro				register struct servent *sp;
165371345Sgshapiro
165471345Sgshapiro				sp = getservbyname(port, "tcp");
165571345Sgshapiro				if (sp == NULL)
165671345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
165771345Sgshapiro					       port);
165871345Sgshapiro				else
165971345Sgshapiro					d->d_addr.sin6.sin6_port = sp->s_port;
166090792Sgshapiro# endif /* NO_GETSERVBYNAME */
166171345Sgshapiro			}
166271345Sgshapiro			break;
166390792Sgshapiro#endif /* NETINET6 */
166471345Sgshapiro
166590792Sgshapiro#if NETISO
166671345Sgshapiro		  case AF_ISO:
166771345Sgshapiro			/* assume two byte transport selector */
166871345Sgshapiro			if (isascii(*port) && isdigit(*port))
166990792Sgshapiro				portno = htons((unsigned short) atoi(port));
167071345Sgshapiro			else
167171345Sgshapiro			{
167290792Sgshapiro# ifdef NO_GETSERVBYNAME
167371345Sgshapiro				syserr("554 5.3.5 invalid port number: %s",
167471345Sgshapiro				       port);
167590792Sgshapiro# else /* NO_GETSERVBYNAME */
167671345Sgshapiro				register struct servent *sp;
167771345Sgshapiro
167871345Sgshapiro				sp = getservbyname(port, "tcp");
167971345Sgshapiro				if (sp == NULL)
168071345Sgshapiro					syserr("554 5.3.5 service \"%s\" unknown",
168171345Sgshapiro					       port);
168271345Sgshapiro				else
168371345Sgshapiro					portno = sp->s_port;
168490792Sgshapiro# endif /* NO_GETSERVBYNAME */
168571345Sgshapiro			}
168671345Sgshapiro			memmove(TSEL(&d->d_addr.siso),
168771345Sgshapiro				(char *) &portno, 2);
168871345Sgshapiro			break;
168990792Sgshapiro#endif /* NETISO */
169071345Sgshapiro
169171345Sgshapiro		  default:
169271345Sgshapiro			syserr("554 5.3.5 Port= option unsupported for family %d",
169371345Sgshapiro			       d->d_addr.sa.sa_family);
169471345Sgshapiro			break;
169571345Sgshapiro		}
169671345Sgshapiro	}
169738032Speter}
169890792Sgshapiro/*
169964562Sgshapiro**  SETDAEMONOPTIONS -- set options for running the MTA daemon
170038032Speter**
170138032Speter**	Parameters:
170264562Sgshapiro**		p -- the options line.
170364562Sgshapiro**
170464562Sgshapiro**	Returns:
170590792Sgshapiro**		true if successful, false otherwise.
170690792Sgshapiro**
170790792Sgshapiro**	Side Effects:
170890792Sgshapiro**		increments number of daemons.
170964562Sgshapiro*/
171064562Sgshapiro
171190792Sgshapiro#define DEF_LISTENQUEUE	10
171290792Sgshapiro
171364562Sgshapirobool
171464562Sgshapirosetdaemonoptions(p)
171564562Sgshapiro	register char *p;
171664562Sgshapiro{
171790792Sgshapiro	if (NDaemons >= MAXDAEMONS)
171890792Sgshapiro		return false;
171990792Sgshapiro	Daemons[NDaemons].d_socket = -1;
172090792Sgshapiro	Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
172190792Sgshapiro	clrbitmap(Daemons[NDaemons].d_flags);
172290792Sgshapiro	setsockaddroptions(p, &Daemons[NDaemons]);
172364562Sgshapiro
172490792Sgshapiro#if MILTER
172590792Sgshapiro# if _FFR_MILTER_PERDAEMON
172690792Sgshapiro	if (Daemons[NDaemons].d_inputfilterlist != NULL)
172790792Sgshapiro		Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
172890792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
172990792Sgshapiro#endif /* MILTER */
173090792Sgshapiro
173190792Sgshapiro	if (Daemons[NDaemons].d_name != NULL)
173290792Sgshapiro		Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
173364562Sgshapiro	else
173464562Sgshapiro	{
173564562Sgshapiro		char num[30];
173664562Sgshapiro
173790792Sgshapiro		(void) sm_snprintf(num, sizeof num, "Daemon%d", NDaemons);
173890792Sgshapiro		Daemons[NDaemons].d_name = newstr(num);
173964562Sgshapiro	}
174064562Sgshapiro
174164562Sgshapiro	if (tTd(37, 1))
174264562Sgshapiro	{
174390792Sgshapiro		sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
174490792Sgshapiro		if (bitnset(D_ETRNONLY, Daemons[NDaemons].d_flags))
174590792Sgshapiro			sm_dprintf("ETRNONLY ");
174690792Sgshapiro		if (bitnset(D_NOETRN, Daemons[NDaemons].d_flags))
174790792Sgshapiro			sm_dprintf("NOETRN ");
174890792Sgshapiro		sm_dprintf("\n");
174964562Sgshapiro	}
175090792Sgshapiro	++NDaemons;
175190792Sgshapiro	return true;
175264562Sgshapiro}
175390792Sgshapiro/*
175464562Sgshapiro**  INITDAEMON -- initialize daemon if not yet done.
175564562Sgshapiro**
175664562Sgshapiro**	Parameters:
175764562Sgshapiro**		none
175864562Sgshapiro**
175964562Sgshapiro**	Returns:
176064562Sgshapiro**		none
176164562Sgshapiro**
176264562Sgshapiro**	Side Effects:
176364562Sgshapiro**		initializes structure for one daemon.
176464562Sgshapiro*/
176590792Sgshapiro
176664562Sgshapirovoid
176764562Sgshapiroinitdaemon()
176864562Sgshapiro{
176990792Sgshapiro	if (NDaemons == 0)
177064562Sgshapiro	{
177190792Sgshapiro		Daemons[NDaemons].d_socket = -1;
177290792Sgshapiro		Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
177390792Sgshapiro		Daemons[NDaemons].d_name = "Daemon0";
177490792Sgshapiro		NDaemons = 1;
177564562Sgshapiro	}
177664562Sgshapiro}
177790792Sgshapiro/*
177864562Sgshapiro**  SETCLIENTOPTIONS -- set options for running the client
177964562Sgshapiro**
178064562Sgshapiro**	Parameters:
178164562Sgshapiro**		p -- the options line.
178264562Sgshapiro**
178364562Sgshapiro**	Returns:
178464562Sgshapiro**		none.
178564562Sgshapiro*/
178664562Sgshapiro
178790792Sgshapirostatic DAEMON_T	ClientSettings[AF_MAX + 1];
178864562Sgshapiro
178964562Sgshapirovoid
179064562Sgshapirosetclientoptions(p)
179164562Sgshapiro	register char *p;
179264562Sgshapiro{
179390792Sgshapiro	int family;
179490792Sgshapiro	DAEMON_T d;
179564562Sgshapiro
179664562Sgshapiro	memset(&d, '\0', sizeof d);
179764562Sgshapiro	setsockaddroptions(p, &d);
179864562Sgshapiro
179964562Sgshapiro	/* grab what we need */
180090792Sgshapiro	family = d.d_addr.sa.sa_family;
180190792Sgshapiro	STRUCTCOPY(d, ClientSettings[family]);
180290792Sgshapiro	setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
180390792Sgshapiro	if (d.d_name != NULL)
180490792Sgshapiro		ClientSettings[family].d_name = newstr(d.d_name);
180564562Sgshapiro	else
180690792Sgshapiro	{
180790792Sgshapiro		char num[30];
180890792Sgshapiro
180990792Sgshapiro		(void) sm_snprintf(num, sizeof num, "Client%d", family);
181090792Sgshapiro		ClientSettings[family].d_name = newstr(num);
181190792Sgshapiro	}
181264562Sgshapiro}
181390792Sgshapiro/*
181464562Sgshapiro**  ADDR_FAMILY -- determine address family from address
181564562Sgshapiro**
181664562Sgshapiro**	Parameters:
181764562Sgshapiro**		addr -- the string representation of the address
181864562Sgshapiro**
181964562Sgshapiro**	Returns:
182064562Sgshapiro**		AF_INET, AF_INET6 or AF_UNSPEC
182164562Sgshapiro**
182264562Sgshapiro**	Side Effects:
182364562Sgshapiro**		none.
182464562Sgshapiro*/
182564562Sgshapiro
182664562Sgshapirostatic int
182764562Sgshapiroaddr_family(addr)
182864562Sgshapiro	char *addr;
182964562Sgshapiro{
183090792Sgshapiro#if NETINET6
183164562Sgshapiro	SOCKADDR clt_addr;
183290792Sgshapiro#endif /* NETINET6 */
183364562Sgshapiro
183490792Sgshapiro#if NETINET
183564562Sgshapiro	if (inet_addr(addr) != INADDR_NONE)
183664562Sgshapiro	{
183764562Sgshapiro		if (tTd(16, 9))
183890792Sgshapiro			sm_dprintf("addr_family(%s): INET\n", addr);
183964562Sgshapiro		return AF_INET;
184064562Sgshapiro	}
184190792Sgshapiro#endif /* NETINET */
184290792Sgshapiro#if NETINET6
184390792Sgshapiro	if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
184464562Sgshapiro	{
184564562Sgshapiro		if (tTd(16, 9))
184690792Sgshapiro			sm_dprintf("addr_family(%s): INET6\n", addr);
184764562Sgshapiro		return AF_INET6;
184864562Sgshapiro	}
184990792Sgshapiro#endif /* NETINET6 */
185090792Sgshapiro#if _FFR_DAEMON_NETUNIX
185190792Sgshapiro# if NETUNIX
185290792Sgshapiro	if (*addr == '/')
185390792Sgshapiro	{
185490792Sgshapiro		if (tTd(16, 9))
185590792Sgshapiro			sm_dprintf("addr_family(%s): LOCAL\n", addr);
185690792Sgshapiro		return AF_UNIX;
185790792Sgshapiro	}
185890792Sgshapiro# endif /* NETUNIX */
185990792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
186064562Sgshapiro	if (tTd(16, 9))
186190792Sgshapiro		sm_dprintf("addr_family(%s): UNSPEC\n", addr);
186264562Sgshapiro	return AF_UNSPEC;
186364562Sgshapiro}
186490792Sgshapiro
186590792Sgshapiro/*
186690792Sgshapiro**  CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
186790792Sgshapiro**
186890792Sgshapiro**	Parameters:
186990792Sgshapiro**		flag -- the flag to test.
187090792Sgshapiro**
187190792Sgshapiro**	Returns:
187290792Sgshapiro**		true iff all configured clients have set the flag.
187390792Sgshapiro*/
187490792Sgshapiro
187590792Sgshapirobool
187690792Sgshapirochkclientmodifiers(flag)
187790792Sgshapiro	int flag;
187890792Sgshapiro{
187990792Sgshapiro	int i;
188090792Sgshapiro	bool flagisset;
188190792Sgshapiro
188290792Sgshapiro	flagisset = false;
188390792Sgshapiro	for (i = 0; i < AF_MAX; i++)
188490792Sgshapiro	{
188590792Sgshapiro		if (bitnset(D_ISSET, ClientSettings[i].d_flags))
188690792Sgshapiro		{
188790792Sgshapiro			if (!bitnset((char) flag, ClientSettings[i].d_flags))
188890792Sgshapiro				return false;
188990792Sgshapiro			flagisset = true;
189090792Sgshapiro		}
189190792Sgshapiro	}
189290792Sgshapiro	return flagisset;
189390792Sgshapiro}
189490792Sgshapiro
189590792Sgshapiro#if MILTER
189690792Sgshapiro# if _FFR_MILTER_PERDAEMON
189790792Sgshapiro/*
189890792Sgshapiro**  SETUP_DAEMON_FILTERS -- Parse per-socket filters
189990792Sgshapiro**
190090792Sgshapiro**	Parameters:
190190792Sgshapiro**		none
190290792Sgshapiro**
190390792Sgshapiro**	Returns:
190490792Sgshapiro**		none
190590792Sgshapiro*/
190690792Sgshapiro
190790792Sgshapirovoid
190890792Sgshapirosetup_daemon_milters()
190990792Sgshapiro{
191090792Sgshapiro	int idx;
191190792Sgshapiro
191290792Sgshapiro	if (OpMode == MD_SMTP)
191390792Sgshapiro	{
191490792Sgshapiro		/* no need to configure the daemons */
191590792Sgshapiro		return;
191690792Sgshapiro	}
191790792Sgshapiro
191890792Sgshapiro	for (idx = 0; idx < NDaemons; idx++)
191990792Sgshapiro	{
192090792Sgshapiro		if (Daemons[idx].d_inputfilterlist != NULL)
192190792Sgshapiro		{
192290792Sgshapiro			milter_config(Daemons[idx].d_inputfilterlist,
192390792Sgshapiro				      Daemons[idx].d_inputfilters,
192490792Sgshapiro				      MAXFILTERS);
192590792Sgshapiro		}
192690792Sgshapiro	}
192790792Sgshapiro}
192890792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
192990792Sgshapiro#endif /* MILTER */
193090792Sgshapiro/*
193164562Sgshapiro**  MAKECONNECTION -- make a connection to an SMTP socket on a machine.
193264562Sgshapiro**
193364562Sgshapiro**	Parameters:
193438032Speter**		host -- the name of the host.
193538032Speter**		port -- the port number to connect to.
193638032Speter**		mci -- a pointer to the mail connection information
193738032Speter**			structure to be filled in.
193838032Speter**		e -- the current envelope.
193990792Sgshapiro**		enough -- time at which to stop further connection attempts.
194090792Sgshapiro**			(0 means no limit)
194138032Speter**
194238032Speter**	Returns:
194338032Speter**		An exit code telling whether the connection could be
194438032Speter**			made and if not why not.
194538032Speter**
194638032Speter**	Side Effects:
194738032Speter**		none.
194838032Speter*/
194938032Speter
195038032Speterstatic jmp_buf	CtxConnectTimeout;
195138032Speter
195238032SpeterSOCKADDR	CurHostAddr;		/* address of current host */
195338032Speter
195438032Speterint
195590792Sgshapiromakeconnection(host, port, mci, e, enough)
195638032Speter	char *host;
195790792Sgshapiro	volatile unsigned int port;
195838032Speter	register MCI *mci;
195938032Speter	ENVELOPE *e;
196090792Sgshapiro	time_t enough;
196138032Speter{
196238032Speter	register volatile int addrno = 0;
196390792Sgshapiro	volatile int s;
196490792Sgshapiro	register struct hostent *volatile hp = (struct hostent *) NULL;
196538032Speter	SOCKADDR addr;
196664562Sgshapiro	SOCKADDR clt_addr;
196764562Sgshapiro	int save_errno = 0;
196864562Sgshapiro	volatile SOCKADDR_LEN_T addrlen;
196938032Speter	volatile bool firstconnect;
197090792Sgshapiro	SM_EVENT *volatile ev = NULL;
197190792Sgshapiro#if NETINET6
197290792Sgshapiro	volatile bool v6found = false;
197390792Sgshapiro#endif /* NETINET6 */
197464562Sgshapiro	volatile int family = InetMode;
197564562Sgshapiro	SOCKADDR_LEN_T len;
197664562Sgshapiro	volatile SOCKADDR_LEN_T socksize = 0;
197764562Sgshapiro	volatile bool clt_bind;
197864562Sgshapiro	BITMAP256 d_flags;
197964562Sgshapiro	char *p;
198064562Sgshapiro	extern ENVELOPE BlankEnvelope;
198138032Speter
198290792Sgshapiro	/* retranslate {daemon_flags} into bitmap */
198364562Sgshapiro	clrbitmap(d_flags);
198490792Sgshapiro	if ((p = macvalue(macid("{daemon_flags}"), e)) != NULL)
198564562Sgshapiro	{
198664562Sgshapiro		for (; *p != '\0'; p++)
198764562Sgshapiro		{
198864562Sgshapiro			if (!(isascii(*p) && isspace(*p)))
198971345Sgshapiro				setbitn(bitidx(*p), d_flags);
199064562Sgshapiro		}
199164562Sgshapiro	}
199264562Sgshapiro
199390792Sgshapiro#if NETINET6
199464562Sgshapiro v4retry:
199590792Sgshapiro#endif /* NETINET6 */
199690792Sgshapiro	clt_bind = false;
199764562Sgshapiro
199864562Sgshapiro	/* Set up the address for outgoing connection. */
199964562Sgshapiro	if (bitnset(D_BINDIF, d_flags) &&
200090792Sgshapiro	    (p = macvalue(macid("{if_addr}"), e)) != NULL &&
200173188Sgshapiro	    *p != '\0')
200264562Sgshapiro	{
200390792Sgshapiro#if NETINET6
200464562Sgshapiro		char p6[INET6_ADDRSTRLEN];
200590792Sgshapiro#endif /* NETINET6 */
200664562Sgshapiro
200764562Sgshapiro		memset(&clt_addr, '\0', sizeof clt_addr);
200864562Sgshapiro
200964562Sgshapiro		/* infer the address family from the address itself */
201064562Sgshapiro		clt_addr.sa.sa_family = addr_family(p);
201164562Sgshapiro		switch (clt_addr.sa.sa_family)
201264562Sgshapiro		{
201390792Sgshapiro#if NETINET
201464562Sgshapiro		  case AF_INET:
201573188Sgshapiro			clt_addr.sin.sin_addr.s_addr = inet_addr(p);
201673188Sgshapiro			if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
201773188Sgshapiro			    clt_addr.sin.sin_addr.s_addr != INADDR_LOOPBACK)
201864562Sgshapiro			{
201990792Sgshapiro				clt_bind = true;
202064562Sgshapiro				socksize = sizeof (struct sockaddr_in);
202164562Sgshapiro			}
202264562Sgshapiro			break;
202390792Sgshapiro#endif /* NETINET */
202464562Sgshapiro
202590792Sgshapiro#if NETINET6
202664562Sgshapiro		  case AF_INET6:
202764562Sgshapiro			if (inet_addr(p) != INADDR_NONE)
202890792Sgshapiro				(void) sm_snprintf(p6, sizeof p6,
202990792Sgshapiro						   "IPv6:::ffff:%s", p);
203064562Sgshapiro			else
203190792Sgshapiro				(void) sm_strlcpy(p6, p, sizeof p6);
203290792Sgshapiro			if (anynet_pton(AF_INET6, p6,
203390792Sgshapiro					&clt_addr.sin6.sin6_addr) == 1 &&
203473188Sgshapiro			    !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
203564562Sgshapiro			{
203690792Sgshapiro				clt_bind = true;
203764562Sgshapiro				socksize = sizeof (struct sockaddr_in6);
203864562Sgshapiro			}
203964562Sgshapiro			break;
204090792Sgshapiro#endif /* NETINET6 */
204164562Sgshapiro
204290792Sgshapiro#if 0
204364562Sgshapiro		  default:
204464562Sgshapiro			syserr("554 5.3.5 Address= option unsupported for family %d",
204564562Sgshapiro			       clt_addr.sa.sa_family);
204664562Sgshapiro			break;
204790792Sgshapiro#endif /* 0 */
204864562Sgshapiro		}
204964562Sgshapiro		if (clt_bind)
205064562Sgshapiro			family = clt_addr.sa.sa_family;
205164562Sgshapiro	}
205290792Sgshapiro
205390792Sgshapiro	/* D_BINDIF not set or not available, fallback to ClientPortOptions */
205490792Sgshapiro	if (!clt_bind)
205564562Sgshapiro	{
205690792Sgshapiro		STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
205764562Sgshapiro		switch (clt_addr.sa.sa_family)
205864562Sgshapiro		{
205990792Sgshapiro#if NETINET
206064562Sgshapiro		  case AF_INET:
206164562Sgshapiro			if (clt_addr.sin.sin_addr.s_addr == 0)
206264562Sgshapiro				clt_addr.sin.sin_addr.s_addr = INADDR_ANY;
206364562Sgshapiro			else
206490792Sgshapiro				clt_bind = true;
206564562Sgshapiro			if (clt_addr.sin.sin_port != 0)
206690792Sgshapiro				clt_bind = true;
206764562Sgshapiro			socksize = sizeof (struct sockaddr_in);
206864562Sgshapiro			break;
206990792Sgshapiro#endif /* NETINET */
207090792Sgshapiro#if NETINET6
207164562Sgshapiro		  case AF_INET6:
207264562Sgshapiro			if (IN6_IS_ADDR_UNSPECIFIED(&clt_addr.sin6.sin6_addr))
207364562Sgshapiro				clt_addr.sin6.sin6_addr = in6addr_any;
207464562Sgshapiro			else
207590792Sgshapiro				clt_bind = true;
207664562Sgshapiro			socksize = sizeof (struct sockaddr_in6);
207764562Sgshapiro			if (clt_addr.sin6.sin6_port != 0)
207890792Sgshapiro				clt_bind = true;
207964562Sgshapiro			break;
208090792Sgshapiro#endif /* NETINET6 */
208190792Sgshapiro#if NETISO
208264562Sgshapiro		  case AF_ISO:
208364562Sgshapiro			socksize = sizeof clt_addr.siso;
208490792Sgshapiro			clt_bind = true;
208564562Sgshapiro			break;
208690792Sgshapiro#endif /* NETISO */
208764562Sgshapiro		  default:
208864562Sgshapiro			break;
208964562Sgshapiro		}
209064562Sgshapiro	}
209164562Sgshapiro
209238032Speter	/*
209338032Speter	**  Set up the address for the mailer.
209438032Speter	**	Accept "[a.b.c.d]" syntax for host name.
209538032Speter	*/
209638032Speter
209773188Sgshapiro	SM_SET_H_ERRNO(0);
209838032Speter	errno = 0;
209964562Sgshapiro	memset(&CurHostAddr, '\0', sizeof CurHostAddr);
210064562Sgshapiro	memset(&addr, '\0', sizeof addr);
210138032Speter	SmtpPhase = mci->mci_phase = "initial connection";
210238032Speter	CurHostName = host;
210338032Speter
210438032Speter	if (host[0] == '[')
210538032Speter	{
210664562Sgshapiro		p = strchr(host, ']');
210738032Speter		if (p != NULL)
210838032Speter		{
210990792Sgshapiro#if NETINET
211064562Sgshapiro			unsigned long hid = INADDR_NONE;
211190792Sgshapiro#endif /* NETINET */
211290792Sgshapiro#if NETINET6
211364562Sgshapiro			struct sockaddr_in6 hid6;
211490792Sgshapiro#endif /* NETINET6 */
211564562Sgshapiro
211638032Speter			*p = '\0';
211790792Sgshapiro#if NETINET6
211864562Sgshapiro			memset(&hid6, '\0', sizeof hid6);
211990792Sgshapiro#endif /* NETINET6 */
212090792Sgshapiro#if NETINET
212164562Sgshapiro			if (family == AF_INET &&
212264562Sgshapiro			    (hid = inet_addr(&host[1])) != INADDR_NONE)
212338032Speter			{
212464562Sgshapiro				addr.sin.sin_family = AF_INET;
212564562Sgshapiro				addr.sin.sin_addr.s_addr = hid;
212664562Sgshapiro			}
212764562Sgshapiro			else
212890792Sgshapiro#endif /* NETINET */
212990792Sgshapiro#if NETINET6
213064562Sgshapiro			if (family == AF_INET6 &&
213190792Sgshapiro			    anynet_pton(AF_INET6, &host[1],
213290792Sgshapiro					&hid6.sin6_addr) == 1)
213364562Sgshapiro			{
213464562Sgshapiro				addr.sin6.sin6_family = AF_INET6;
213564562Sgshapiro				addr.sin6.sin6_addr = hid6.sin6_addr;
213664562Sgshapiro			}
213764562Sgshapiro			else
213890792Sgshapiro#endif /* NETINET6 */
213964562Sgshapiro			{
214038032Speter				/* try it as a host name (avoid MX lookup) */
214164562Sgshapiro				hp = sm_gethostbyname(&host[1], family);
214238032Speter				if (hp == NULL && p[-1] == '.')
214338032Speter				{
214490792Sgshapiro#if NAMED_BIND
214538032Speter					int oldopts = _res.options;
214638032Speter
214738032Speter					_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
214890792Sgshapiro#endif /* NAMED_BIND */
214938032Speter					p[-1] = '\0';
215064562Sgshapiro					hp = sm_gethostbyname(&host[1],
215164562Sgshapiro							      family);
215238032Speter					p[-1] = '.';
215390792Sgshapiro#if NAMED_BIND
215438032Speter					_res.options = oldopts;
215590792Sgshapiro#endif /* NAMED_BIND */
215638032Speter				}
215738032Speter				*p = ']';
215838032Speter				goto gothostent;
215938032Speter			}
216038032Speter			*p = ']';
216138032Speter		}
216238032Speter		if (p == NULL)
216338032Speter		{
216438032Speter			extern char MsgBuf[];
216538032Speter
216664562Sgshapiro			usrerrenh("5.1.2",
216764562Sgshapiro				  "553 Invalid numeric domain spec \"%s\"",
216864562Sgshapiro				  host);
216938032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
217064562Sgshapiro			errno = EINVAL;
217138032Speter			return EX_NOHOST;
217238032Speter		}
217338032Speter	}
217438032Speter	else
217538032Speter	{
217638032Speter		/* contortion to get around SGI cc complaints */
217738032Speter		{
217864562Sgshapiro			p = &host[strlen(host) - 1];
217964562Sgshapiro			hp = sm_gethostbyname(host, family);
218038032Speter			if (hp == NULL && *p == '.')
218138032Speter			{
218290792Sgshapiro#if NAMED_BIND
218338032Speter				int oldopts = _res.options;
218438032Speter
218538032Speter				_res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
218690792Sgshapiro#endif /* NAMED_BIND */
218738032Speter				*p = '\0';
218864562Sgshapiro				hp = sm_gethostbyname(host, family);
218938032Speter				*p = '.';
219090792Sgshapiro#if NAMED_BIND
219138032Speter				_res.options = oldopts;
219290792Sgshapiro#endif /* NAMED_BIND */
219338032Speter			}
219438032Speter		}
219538032Spetergothostent:
219638032Speter		if (hp == NULL)
219738032Speter		{
219890792Sgshapiro#if NAMED_BIND
219938032Speter			/* check for name server timeouts */
220090792Sgshapiro# if NETINET6
220190792Sgshapiro			if (WorkAroundBrokenAAAA && family == AF_INET6 &&
220290792Sgshapiro			    errno == ETIMEDOUT)
220338032Speter			{
220490792Sgshapiro				/*
220590792Sgshapiro				**  An attempt with family AF_INET may
220690792Sgshapiro				**  succeed By skipping the next section
220790792Sgshapiro				**  of code, we will try AF_INET before
220890792Sgshapiro				**  failing.
220990792Sgshapiro				*/
221090792Sgshapiro
221190792Sgshapiro				if (tTd(16, 10))
221290792Sgshapiro					sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
221338032Speter			}
221490792Sgshapiro			else
221590792Sgshapiro# endif /* NETINET6 */
221690792Sgshapiro			{
221790792Sgshapiro				if (errno == ETIMEDOUT ||
221890792Sgshapiro				    h_errno == TRY_AGAIN ||
221990792Sgshapiro				    (errno == ECONNREFUSED && UseNameServer))
222090792Sgshapiro				{
222190792Sgshapiro					save_errno = errno;
222290792Sgshapiro					mci_setstat(mci, EX_TEMPFAIL,
222390792Sgshapiro						    "4.4.3", NULL);
222490792Sgshapiro					errno = save_errno;
222590792Sgshapiro					return EX_TEMPFAIL;
222690792Sgshapiro				}
222790792Sgshapiro			}
222890792Sgshapiro#endif /* NAMED_BIND */
222990792Sgshapiro#if NETINET6
223064562Sgshapiro			/*
223164562Sgshapiro			**  Try v6 first, then fall back to v4.
223264562Sgshapiro			**  If we found a v6 address, but no v4
223364562Sgshapiro			**  addresses, then TEMPFAIL.
223464562Sgshapiro			*/
223564562Sgshapiro
223664562Sgshapiro			if (family == AF_INET6)
223764562Sgshapiro			{
223864562Sgshapiro				family = AF_INET;
223964562Sgshapiro				goto v4retry;
224064562Sgshapiro			}
224164562Sgshapiro			if (v6found)
224264562Sgshapiro				goto v6tempfail;
224390792Sgshapiro#endif /* NETINET6 */
224464562Sgshapiro			save_errno = errno;
224538032Speter			mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
224664562Sgshapiro			errno = save_errno;
224764562Sgshapiro			return EX_NOHOST;
224838032Speter		}
224938032Speter		addr.sa.sa_family = hp->h_addrtype;
225038032Speter		switch (hp->h_addrtype)
225138032Speter		{
225290792Sgshapiro#if NETINET
225338032Speter		  case AF_INET:
225464562Sgshapiro			memmove(&addr.sin.sin_addr,
225564562Sgshapiro				hp->h_addr,
225638032Speter				INADDRSZ);
225738032Speter			break;
225890792Sgshapiro#endif /* NETINET */
225938032Speter
226090792Sgshapiro#if NETINET6
226164562Sgshapiro		  case AF_INET6:
226264562Sgshapiro			memmove(&addr.sin6.sin6_addr,
226364562Sgshapiro				hp->h_addr,
226464562Sgshapiro				IN6ADDRSZ);
226564562Sgshapiro			break;
226690792Sgshapiro#endif /* NETINET6 */
226764562Sgshapiro
226838032Speter		  default:
226938032Speter			if (hp->h_length > sizeof addr.sa.sa_data)
227038032Speter			{
227138032Speter				syserr("makeconnection: long sa_data: family %d len %d",
227238032Speter					hp->h_addrtype, hp->h_length);
227338032Speter				mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
227464562Sgshapiro				errno = EINVAL;
227538032Speter				return EX_NOHOST;
227638032Speter			}
227790792Sgshapiro			memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
227838032Speter			break;
227938032Speter		}
228038032Speter		addrno = 1;
228138032Speter	}
228238032Speter
228338032Speter	/*
228438032Speter	**  Determine the port number.
228538032Speter	*/
228638032Speter
228738032Speter	if (port == 0)
228838032Speter	{
228990792Sgshapiro#ifdef NO_GETSERVBYNAME
229064562Sgshapiro		port = htons(25);
229190792Sgshapiro#else /* NO_GETSERVBYNAME */
229238032Speter		register struct servent *sp = getservbyname("smtp", "tcp");
229338032Speter
229438032Speter		if (sp == NULL)
229538032Speter		{
229638032Speter			if (LogLevel > 2)
229738032Speter				sm_syslog(LOG_ERR, NOQID,
229864562Sgshapiro					  "makeconnection: service \"smtp\" unknown");
229938032Speter			port = htons(25);
230038032Speter		}
230138032Speter		else
230238032Speter			port = sp->s_port;
230390792Sgshapiro#endif /* NO_GETSERVBYNAME */
230438032Speter	}
230538032Speter
230690792Sgshapiro#if NETINET6
230790792Sgshapiro	if (addr.sa.sa_family == AF_INET6 &&
230890792Sgshapiro	    IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
230990792Sgshapiro	    ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
231090792Sgshapiro	{
231190792Sgshapiro		/*
231290792Sgshapiro		**  Ignore mapped IPv4 address since
231390792Sgshapiro		**  there is a ClientPortOptions setting
231490792Sgshapiro		**  for IPv4.
231590792Sgshapiro		*/
231690792Sgshapiro
231790792Sgshapiro		goto nextaddr;
231890792Sgshapiro	}
231990792Sgshapiro#endif /* NETINET6 */
232090792Sgshapiro
232138032Speter	switch (addr.sa.sa_family)
232238032Speter	{
232390792Sgshapiro#if NETINET
232438032Speter	  case AF_INET:
232538032Speter		addr.sin.sin_port = port;
232638032Speter		addrlen = sizeof (struct sockaddr_in);
232738032Speter		break;
232890792Sgshapiro#endif /* NETINET */
232938032Speter
233090792Sgshapiro#if NETINET6
233164562Sgshapiro	  case AF_INET6:
233264562Sgshapiro		addr.sin6.sin6_port = port;
233364562Sgshapiro		addrlen = sizeof (struct sockaddr_in6);
233464562Sgshapiro		break;
233590792Sgshapiro#endif /* NETINET6 */
233664562Sgshapiro
233790792Sgshapiro#if NETISO
233838032Speter	  case AF_ISO:
233938032Speter		/* assume two byte transport selector */
234064562Sgshapiro		memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
234138032Speter		addrlen = sizeof (struct sockaddr_iso);
234238032Speter		break;
234390792Sgshapiro#endif /* NETISO */
234438032Speter
234538032Speter	  default:
234638032Speter		syserr("Can't connect to address family %d", addr.sa.sa_family);
234738032Speter		mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
234864562Sgshapiro		errno = EINVAL;
234990792Sgshapiro#if NETINET6
235071345Sgshapiro		if (hp != NULL)
235171345Sgshapiro			freehostent(hp);
235290792Sgshapiro#endif /* NETINET6 */
235364562Sgshapiro		return EX_NOHOST;
235438032Speter	}
235538032Speter
235638032Speter	/*
235738032Speter	**  Try to actually open the connection.
235838032Speter	*/
235938032Speter
236090792Sgshapiro#if XLA
236138032Speter	/* if too many connections, don't bother trying */
236238032Speter	if (!xla_noqueue_ok(host))
236371345Sgshapiro	{
236490792Sgshapiro# if NETINET6
236571345Sgshapiro		if (hp != NULL)
236671345Sgshapiro			freehostent(hp);
236790792Sgshapiro# endif /* NETINET6 */
236838032Speter		return EX_TEMPFAIL;
236971345Sgshapiro	}
237090792Sgshapiro#endif /* XLA */
237138032Speter
237290792Sgshapiro	firstconnect = true;
237338032Speter	for (;;)
237438032Speter	{
237538032Speter		if (tTd(16, 1))
237690792Sgshapiro			sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
237790792Sgshapiro				   host, anynet_ntoa(&addr), ntohs(port),
237890792Sgshapiro				   (int) addr.sa.sa_family);
237938032Speter
238038032Speter		/* save for logging */
238138032Speter		CurHostAddr = addr;
238238032Speter
238390792Sgshapiro#if HASRRESVPORT
238438032Speter		if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
238538032Speter		{
238638032Speter			int rport = IPPORT_RESERVED - 1;
238738032Speter
238838032Speter			s = rresvport(&rport);
238938032Speter		}
239038032Speter		else
239190792Sgshapiro#endif /* HASRRESVPORT */
239238032Speter		{
239390792Sgshapiro			s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
239438032Speter		}
239538032Speter		if (s < 0)
239638032Speter		{
239764562Sgshapiro			save_errno = errno;
239838032Speter			syserr("makeconnection: cannot create socket");
239990792Sgshapiro#if XLA
240038032Speter			xla_host_end(host);
240190792Sgshapiro#endif /* XLA */
240238032Speter			mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
240390792Sgshapiro#if NETINET6
240471345Sgshapiro			if (hp != NULL)
240571345Sgshapiro				freehostent(hp);
240690792Sgshapiro#endif /* NETINET6 */
240764562Sgshapiro			errno = save_errno;
240838032Speter			return EX_TEMPFAIL;
240938032Speter		}
241038032Speter
241190792Sgshapiro#ifdef SO_SNDBUF
241290792Sgshapiro		if (ClientSettings[family].d_tcpsndbufsize > 0)
241338032Speter		{
241438032Speter			if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
241590792Sgshapiro				       (char *) &ClientSettings[family].d_tcpsndbufsize,
241690792Sgshapiro				       sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
241738032Speter				syserr("makeconnection: setsockopt(SO_SNDBUF)");
241838032Speter		}
241990792Sgshapiro#endif /* SO_SNDBUF */
242090792Sgshapiro#ifdef SO_RCVBUF
242190792Sgshapiro		if (ClientSettings[family].d_tcprcvbufsize > 0)
242264562Sgshapiro		{
242364562Sgshapiro			if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
242490792Sgshapiro				       (char *) &ClientSettings[family].d_tcprcvbufsize,
242590792Sgshapiro				       sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
242664562Sgshapiro				syserr("makeconnection: setsockopt(SO_RCVBUF)");
242764562Sgshapiro		}
242890792Sgshapiro#endif /* SO_RCVBUF */
242938032Speter
243038032Speter		if (tTd(16, 1))
243190792Sgshapiro			sm_dprintf("makeconnection: fd=%d\n", s);
243238032Speter
243338032Speter		/* turn on network debugging? */
243438032Speter		if (tTd(16, 101))
243538032Speter		{
243638032Speter			int on = 1;
243764562Sgshapiro
243838032Speter			(void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
243938032Speter					  (char *)&on, sizeof on);
244038032Speter		}
244190792Sgshapiro		if (e->e_xfp != NULL)	/* for debugging */
244290792Sgshapiro			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
244390792Sgshapiro		errno = 0;		/* for debugging */
244438032Speter
244564562Sgshapiro		if (clt_bind)
244664562Sgshapiro		{
244764562Sgshapiro			int on = 1;
244864562Sgshapiro
244964562Sgshapiro			switch (clt_addr.sa.sa_family)
245064562Sgshapiro			{
245190792Sgshapiro#if NETINET
245264562Sgshapiro			  case AF_INET:
245364562Sgshapiro				if (clt_addr.sin.sin_port != 0)
245464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
245564562Sgshapiro							  SO_REUSEADDR,
245664562Sgshapiro							  (char *) &on,
245764562Sgshapiro							  sizeof on);
245864562Sgshapiro				break;
245990792Sgshapiro#endif /* NETINET */
246064562Sgshapiro
246190792Sgshapiro#if NETINET6
246264562Sgshapiro			  case AF_INET6:
246364562Sgshapiro				if (clt_addr.sin6.sin6_port != 0)
246464562Sgshapiro					(void) setsockopt(s, SOL_SOCKET,
246564562Sgshapiro							  SO_REUSEADDR,
246664562Sgshapiro							  (char *) &on,
246764562Sgshapiro							  sizeof on);
246864562Sgshapiro				break;
246990792Sgshapiro#endif /* NETINET6 */
247064562Sgshapiro			}
247164562Sgshapiro
247264562Sgshapiro			if (bind(s, &clt_addr.sa, socksize) < 0)
247364562Sgshapiro			{
247464562Sgshapiro				save_errno = errno;
247564562Sgshapiro				(void) close(s);
247664562Sgshapiro				errno = save_errno;
247764562Sgshapiro				syserr("makeconnection: cannot bind socket [%s]",
247864562Sgshapiro				       anynet_ntoa(&clt_addr));
247990792Sgshapiro#if NETINET6
248071345Sgshapiro				if (hp != NULL)
248171345Sgshapiro					freehostent(hp);
248290792Sgshapiro#endif /* NETINET6 */
248364562Sgshapiro				errno = save_errno;
248464562Sgshapiro				return EX_TEMPFAIL;
248564562Sgshapiro			}
248664562Sgshapiro		}
248764562Sgshapiro
248838032Speter		/*
248938032Speter		**  Linux seems to hang in connect for 90 minutes (!!!).
249038032Speter		**  Time out the connect to avoid this problem.
249138032Speter		*/
249238032Speter
249338032Speter		if (setjmp(CtxConnectTimeout) == 0)
249438032Speter		{
249538032Speter			int i;
249638032Speter
249738032Speter			if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
249890792Sgshapiro				ev = sm_setevent(TimeOuts.to_iconnect,
249990792Sgshapiro						 connecttimeout, 0);
250038032Speter			else if (TimeOuts.to_connect != 0)
250190792Sgshapiro				ev = sm_setevent(TimeOuts.to_connect,
250290792Sgshapiro						 connecttimeout, 0);
250338032Speter			else
250438032Speter				ev = NULL;
250538032Speter
250664562Sgshapiro			switch (ConnectOnlyTo.sa.sa_family)
250764562Sgshapiro			{
250890792Sgshapiro#if NETINET
250964562Sgshapiro			  case AF_INET:
251064562Sgshapiro				addr.sin.sin_addr.s_addr = ConnectOnlyTo.sin.sin_addr.s_addr;
251164562Sgshapiro				break;
251290792Sgshapiro#endif /* NETINET */
251364562Sgshapiro
251490792Sgshapiro#if NETINET6
251564562Sgshapiro			  case AF_INET6:
251664562Sgshapiro				memmove(&addr.sin6.sin6_addr,
251764562Sgshapiro					&ConnectOnlyTo.sin6.sin6_addr,
251864562Sgshapiro					IN6ADDRSZ);
251964562Sgshapiro				break;
252090792Sgshapiro#endif /* NETINET6 */
252164562Sgshapiro			}
252238032Speter			i = connect(s, (struct sockaddr *) &addr, addrlen);
252364562Sgshapiro			save_errno = errno;
252438032Speter			if (ev != NULL)
252590792Sgshapiro				sm_clrevent(ev);
252638032Speter			if (i >= 0)
252738032Speter				break;
252838032Speter		}
252938032Speter		else
253064562Sgshapiro			save_errno = errno;
253138032Speter
253294334Sgshapiro		/* couldn't connect.... figure out why */
253394334Sgshapiro		(void) close(s);
253494334Sgshapiro
253538032Speter		/* if running demand-dialed connection, try again */
253690792Sgshapiro		if (DialDelay > 0 && firstconnect &&
253790792Sgshapiro		    bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
253838032Speter		{
253938032Speter			if (tTd(16, 1))
254090792Sgshapiro				sm_dprintf("Connect failed (%s); trying again...\n",
254190792Sgshapiro					   sm_errstring(save_errno));
254290792Sgshapiro			firstconnect = false;
254364562Sgshapiro			(void) sleep(DialDelay);
254438032Speter			continue;
254538032Speter		}
254638032Speter
254790792Sgshapiro		if (LogLevel > 13)
254838032Speter			sm_syslog(LOG_INFO, e->e_id,
254938032Speter				  "makeconnection (%s [%s]) failed: %s",
255038032Speter				  host, anynet_ntoa(&addr),
255190792Sgshapiro				  sm_errstring(save_errno));
255238032Speter
255390792Sgshapiro#if NETINET6
255490792Sgshapironextaddr:
255590792Sgshapiro#endif /* NETINET6 */
255690792Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
255790792Sgshapiro		    (enough == 0 || curtime() < enough))
255838032Speter		{
255938032Speter			if (tTd(16, 1))
256090792Sgshapiro				sm_dprintf("Connect failed (%s); trying new address....\n",
256190792Sgshapiro					   sm_errstring(save_errno));
256238032Speter			switch (addr.sa.sa_family)
256338032Speter			{
256490792Sgshapiro#if NETINET
256538032Speter			  case AF_INET:
256664562Sgshapiro				memmove(&addr.sin.sin_addr,
256764562Sgshapiro					hp->h_addr_list[addrno++],
256864562Sgshapiro					INADDRSZ);
256938032Speter				break;
257090792Sgshapiro#endif /* NETINET */
257138032Speter
257290792Sgshapiro#if NETINET6
257364562Sgshapiro			  case AF_INET6:
257464562Sgshapiro				memmove(&addr.sin6.sin6_addr,
257564562Sgshapiro					hp->h_addr_list[addrno++],
257664562Sgshapiro					IN6ADDRSZ);
257764562Sgshapiro				break;
257890792Sgshapiro#endif /* NETINET6 */
257964562Sgshapiro
258038032Speter			  default:
258164562Sgshapiro				memmove(addr.sa.sa_data,
258264562Sgshapiro					hp->h_addr_list[addrno++],
258338032Speter					hp->h_length);
258438032Speter				break;
258538032Speter			}
258638032Speter			continue;
258738032Speter		}
258864562Sgshapiro		errno = save_errno;
258938032Speter
259090792Sgshapiro#if NETINET6
259164562Sgshapiro		if (family == AF_INET6)
259264562Sgshapiro		{
259364562Sgshapiro			if (tTd(16, 1))
259490792Sgshapiro				sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
259590792Sgshapiro					   sm_errstring(save_errno));
259690792Sgshapiro			v6found = true;
259764562Sgshapiro			family = AF_INET;
259871345Sgshapiro			if (hp != NULL)
259971345Sgshapiro			{
260071345Sgshapiro				freehostent(hp);
260171345Sgshapiro				hp = NULL;
260271345Sgshapiro			}
260364562Sgshapiro			goto v4retry;
260464562Sgshapiro		}
260564562Sgshapiro	v6tempfail:
260690792Sgshapiro#endif /* NETINET6 */
260738032Speter		/* couldn't open connection */
260890792Sgshapiro#if NETINET6
260964562Sgshapiro		/* Don't clobber an already saved errno from v4retry */
261064562Sgshapiro		if (errno > 0)
261190792Sgshapiro#endif /* NETINET6 */
261264562Sgshapiro			save_errno = errno;
261364562Sgshapiro		if (tTd(16, 1))
261490792Sgshapiro			sm_dprintf("Connect failed (%s)\n",
261590792Sgshapiro				   sm_errstring(save_errno));
261690792Sgshapiro#if XLA
261738032Speter		xla_host_end(host);
261890792Sgshapiro#endif /* XLA */
261938032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
262090792Sgshapiro#if NETINET6
262171345Sgshapiro		if (hp != NULL)
262271345Sgshapiro			freehostent(hp);
262390792Sgshapiro#endif /* NETINET6 */
262464562Sgshapiro		errno = save_errno;
262538032Speter		return EX_TEMPFAIL;
262638032Speter	}
262738032Speter
262890792Sgshapiro#if NETINET6
262971345Sgshapiro	if (hp != NULL)
263071345Sgshapiro	{
263171345Sgshapiro		freehostent(hp);
263271345Sgshapiro		hp = NULL;
263371345Sgshapiro	}
263490792Sgshapiro#endif /* NETINET6 */
263571345Sgshapiro
263638032Speter	/* connection ok, put it into canonical form */
263764562Sgshapiro	mci->mci_out = NULL;
263890792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
263990792Sgshapiro				       (void *) &s,
264090792Sgshapiro				       SM_IO_WRONLY, NULL)) == NULL ||
264138032Speter	    (s = dup(s)) < 0 ||
264290792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
264390792Sgshapiro				      (void *) &s,
264490792Sgshapiro				      SM_IO_RDONLY, NULL)) == NULL)
264538032Speter	{
264664562Sgshapiro		save_errno = errno;
264738032Speter		syserr("cannot open SMTP client channel, fd=%d", s);
264838032Speter		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
264964562Sgshapiro		if (mci->mci_out != NULL)
265090792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
265164562Sgshapiro		(void) close(s);
265264562Sgshapiro		errno = save_errno;
265338032Speter		return EX_TEMPFAIL;
265438032Speter	}
265590792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
265638032Speter
265790792Sgshapiro	/* set {client_flags} */
265890792Sgshapiro	if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
265990792Sgshapiro	{
266090792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
266190792Sgshapiro			  macid("{client_flags}"),
266290792Sgshapiro			  ClientSettings[addr.sa.sa_family].d_mflags);
266390792Sgshapiro	}
266490792Sgshapiro	else
266590792Sgshapiro		macdefine(&mci->mci_macro, A_PERM,
266690792Sgshapiro			  macid("{client_flags}"), "");
266790792Sgshapiro
266890792Sgshapiro	/* "add" {client_flags} to bitmap */
266990792Sgshapiro	if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
267090792Sgshapiro	{
267190792Sgshapiro		/* look for just this one flag */
267290792Sgshapiro		setbitn(D_IFNHELO, d_flags);
267390792Sgshapiro	}
267490792Sgshapiro
267564562Sgshapiro	/* find out name for Interface through which we connect */
267664562Sgshapiro	len = sizeof addr;
267764562Sgshapiro	if (getsockname(s, &addr.sa, &len) == 0)
267864562Sgshapiro	{
267964562Sgshapiro		char *name;
268090792Sgshapiro		char family[5];
268164562Sgshapiro
268290792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
268390792Sgshapiro			macid("{if_addr_out}"), anynet_ntoa(&addr));
268490792Sgshapiro		(void) sm_snprintf(family, sizeof(family), "%d",
268590792Sgshapiro			addr.sa.sa_family);
268690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
268790792Sgshapiro			macid("{if_family_out}"), family);
268864562Sgshapiro
268964562Sgshapiro		name = hostnamebyanyaddr(&addr);
269090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
269190792Sgshapiro			macid("{if_name_out}"), name);
269264562Sgshapiro		if (LogLevel > 11)
269364562Sgshapiro		{
269464562Sgshapiro			/* log connection information */
269564562Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
269664562Sgshapiro				  "SMTP outgoing connect on %.40s", name);
269764562Sgshapiro		}
269864562Sgshapiro		if (bitnset(D_IFNHELO, d_flags))
269964562Sgshapiro		{
270064562Sgshapiro			if (name[0] != '[' && strchr(name, '.') != NULL)
270164562Sgshapiro				mci->mci_heloname = newstr(name);
270264562Sgshapiro		}
270364562Sgshapiro	}
270464562Sgshapiro	else
270564562Sgshapiro	{
270690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
270790792Sgshapiro			macid("{if_name_out}"), NULL);
270890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
270990792Sgshapiro			macid("{if_addr_out}"), NULL);
271090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
271190792Sgshapiro			macid("{if_family_out}"), NULL);
271264562Sgshapiro	}
271338032Speter	mci_setstat(mci, EX_OK, NULL, NULL);
271464562Sgshapiro	return EX_OK;
271538032Speter}
271664562Sgshapiro
271764562Sgshapirostatic void
271864562Sgshapiroconnecttimeout()
271964562Sgshapiro{
272077349Sgshapiro	/*
272177349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
272277349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
272377349Sgshapiro	**	DOING.
272477349Sgshapiro	*/
272577349Sgshapiro
272664562Sgshapiro	errno = ETIMEDOUT;
272764562Sgshapiro	longjmp(CtxConnectTimeout, 1);
272864562Sgshapiro}
272990792Sgshapiro/*
273064562Sgshapiro**  MAKECONNECTION_DS -- make a connection to a domain socket.
273164562Sgshapiro**
273264562Sgshapiro**	Parameters:
273364562Sgshapiro**		mux_path -- the path of the socket to connect to.
273464562Sgshapiro**		mci -- a pointer to the mail connection information
273564562Sgshapiro**			structure to be filled in.
273664562Sgshapiro**
273764562Sgshapiro**	Returns:
273864562Sgshapiro**		An exit code telling whether the connection could be
273964562Sgshapiro**			made and if not why not.
274064562Sgshapiro**
274164562Sgshapiro**	Side Effects:
274264562Sgshapiro**		none.
274364562Sgshapiro*/
274464562Sgshapiro
274590792Sgshapiro#if NETUNIX
274690792Sgshapiroint
274790792Sgshapiromakeconnection_ds(mux_path, mci)
274864562Sgshapiro	char *mux_path;
274964562Sgshapiro	register MCI *mci;
275064562Sgshapiro{
275164562Sgshapiro	int sock;
275264562Sgshapiro	int rval, save_errno;
275364562Sgshapiro	long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK;
275464562Sgshapiro	struct sockaddr_un unix_addr;
275564562Sgshapiro
275664562Sgshapiro	/* if not safe, don't connect */
275764562Sgshapiro	rval = safefile(mux_path, RunAsUid, RunAsGid, RunAsUserName,
275864562Sgshapiro			sff, S_IRUSR|S_IWUSR, NULL);
275964562Sgshapiro
276064562Sgshapiro	if (rval != 0)
276164562Sgshapiro	{
276264562Sgshapiro		syserr("makeconnection_ds: unsafe domain socket");
276364562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.3.5", NULL);
276464562Sgshapiro		errno = rval;
276564562Sgshapiro		return EX_TEMPFAIL;
276664562Sgshapiro	}
276764562Sgshapiro
276864562Sgshapiro	/* prepare address structure */
276964562Sgshapiro	memset(&unix_addr, '\0', sizeof unix_addr);
277064562Sgshapiro	unix_addr.sun_family = AF_UNIX;
277164562Sgshapiro
277264562Sgshapiro	if (strlen(mux_path) >= sizeof unix_addr.sun_path)
277364562Sgshapiro	{
277464562Sgshapiro		syserr("makeconnection_ds: domain socket name too long");
277590792Sgshapiro
277690792Sgshapiro		/* XXX why TEMPFAIL but 5.x.y ? */
277764562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
277864562Sgshapiro		errno = ENAMETOOLONG;
277964562Sgshapiro		return EX_UNAVAILABLE;
278064562Sgshapiro	}
278190792Sgshapiro	(void) sm_strlcpy(unix_addr.sun_path, mux_path,
278290792Sgshapiro			  sizeof unix_addr.sun_path);
278364562Sgshapiro
278464562Sgshapiro	/* initialize domain socket */
278564562Sgshapiro	sock = socket(AF_UNIX, SOCK_STREAM, 0);
278664562Sgshapiro	if (sock == -1)
278764562Sgshapiro	{
278864562Sgshapiro		save_errno = errno;
278964562Sgshapiro		syserr("makeconnection_ds: could not create domain socket");
279064562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
279164562Sgshapiro		errno = save_errno;
279264562Sgshapiro		return EX_TEMPFAIL;
279364562Sgshapiro	}
279464562Sgshapiro
279564562Sgshapiro	/* connect to server */
279664562Sgshapiro	if (connect(sock, (struct sockaddr *) &unix_addr,
279764562Sgshapiro		    sizeof(unix_addr)) == -1)
279864562Sgshapiro	{
279964562Sgshapiro		save_errno = errno;
280064562Sgshapiro		syserr("Could not connect to socket %s", mux_path);
280164562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
280264562Sgshapiro		(void) close(sock);
280364562Sgshapiro		errno = save_errno;
280464562Sgshapiro		return EX_TEMPFAIL;
280564562Sgshapiro	}
280664562Sgshapiro
280764562Sgshapiro	/* connection ok, put it into canonical form */
280864562Sgshapiro	mci->mci_out = NULL;
280990792Sgshapiro	if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
281090792Sgshapiro				       (void *) &sock, SM_IO_WRONLY, NULL))
281190792Sgshapiro					== NULL
281290792Sgshapiro	    || (sock = dup(sock)) < 0 ||
281390792Sgshapiro	    (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
281490792Sgshapiro				      (void *) &sock, SM_IO_RDONLY, NULL))
281590792Sgshapiro					== NULL)
281664562Sgshapiro	{
281764562Sgshapiro		save_errno = errno;
281864562Sgshapiro		syserr("cannot open SMTP client channel, fd=%d", sock);
281964562Sgshapiro		mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
282064562Sgshapiro		if (mci->mci_out != NULL)
282190792Sgshapiro			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
282264562Sgshapiro		(void) close(sock);
282364562Sgshapiro		errno = save_errno;
282464562Sgshapiro		return EX_TEMPFAIL;
282564562Sgshapiro	}
282690792Sgshapiro	sm_io_automode(mci->mci_out, mci->mci_in);
282764562Sgshapiro
282864562Sgshapiro	mci_setstat(mci, EX_OK, NULL, NULL);
282964562Sgshapiro	errno = 0;
283064562Sgshapiro	return EX_OK;
283164562Sgshapiro}
283290792Sgshapiro#endif /* NETUNIX */
283390792Sgshapiro/*
283490792Sgshapiro**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
283577349Sgshapiro**
283677349Sgshapiro**	Parameters:
283790792Sgshapiro**		none.
283877349Sgshapiro**
283977349Sgshapiro**	Returns:
284077349Sgshapiro**		none.
284177349Sgshapiro**
284277349Sgshapiro**	Side Effects:
284390792Sgshapiro**		closes control socket, exits.
284477349Sgshapiro*/
284577349Sgshapiro
284690792Sgshapirovoid
284790792Sgshapiroshutdown_daemon()
284877349Sgshapiro{
284990792Sgshapiro	int i;
285090792Sgshapiro	char *reason;
285177349Sgshapiro
285290792Sgshapiro	sm_allsignals(true);
285390792Sgshapiro
285490792Sgshapiro	reason = ShutdownRequest;
285590792Sgshapiro	ShutdownRequest = NULL;
285690792Sgshapiro	PendingSignal = 0;
285790792Sgshapiro
285890792Sgshapiro	if (LogLevel > 79)
285990792Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)",
286090792Sgshapiro			  reason == NULL ? "implicit call" : reason);
286190792Sgshapiro
286290792Sgshapiro	FileName = NULL;
286390792Sgshapiro	closecontrolsocket(true);
286490792Sgshapiro#if XLA
286590792Sgshapiro	xla_all_end();
286690792Sgshapiro#endif /* XLA */
286790792Sgshapiro
286890792Sgshapiro	for (i = 0; i < NDaemons; i++)
286990792Sgshapiro	{
287090792Sgshapiro		if (Daemons[i].d_socket >= 0)
287190792Sgshapiro		{
287290792Sgshapiro			(void) close(Daemons[i].d_socket);
287390792Sgshapiro			Daemons[i].d_socket = -1;
287490792Sgshapiro
287590792Sgshapiro#if _FFR_DAEMON_NETUNIX
287690792Sgshapiro# if NETUNIX
287790792Sgshapiro			/* Remove named sockets */
287890792Sgshapiro			if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
287990792Sgshapiro			{
288090792Sgshapiro				int rval;
288190792Sgshapiro				long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
288290792Sgshapiro
288390792Sgshapiro				/* if not safe, don't use it */
288490792Sgshapiro				rval = safefile(Daemons[i].d_addr.sunix.sun_path,
288590792Sgshapiro						RunAsUid, RunAsGid,
288690792Sgshapiro						RunAsUserName, sff,
288790792Sgshapiro						S_IRUSR|S_IWUSR, NULL);
288890792Sgshapiro				if (rval == 0 &&
288990792Sgshapiro				    unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
289090792Sgshapiro				{
289190792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
289290792Sgshapiro						  "Could not remove daemon %s socket: %s: %s",
289390792Sgshapiro						  Daemons[i].d_name,
289490792Sgshapiro						  Daemons[i].d_addr.sunix.sun_path,
289590792Sgshapiro						  sm_errstring(errno));
289690792Sgshapiro				}
289790792Sgshapiro			}
289890792Sgshapiro# endif /* NETUNIX */
289990792Sgshapiro#endif	/* _FFR_DAEMON_NETUNIX */
290090792Sgshapiro		}
290190792Sgshapiro	}
290290792Sgshapiro
290390792Sgshapiro	finis(false, true, EX_OK);
290477349Sgshapiro}
290590792Sgshapiro/*
290677349Sgshapiro**  RESTART_DAEMON -- Performs a clean restart of the daemon
290777349Sgshapiro**
290877349Sgshapiro**	Parameters:
290977349Sgshapiro**		none.
291077349Sgshapiro**
291177349Sgshapiro**	Returns:
291277349Sgshapiro**		none.
291377349Sgshapiro**
291477349Sgshapiro**	Side Effects:
291577349Sgshapiro**		restarts the daemon or exits if restart fails.
291677349Sgshapiro*/
291777349Sgshapiro
291880785Sgshapiro/* Make a non-DFL/IGN signal a noop */
291980785Sgshapiro#define SM_NOOP_SIGNAL(sig, old)				\
292080785Sgshapirodo								\
292180785Sgshapiro{								\
292290792Sgshapiro	(old) = sm_signal((sig), sm_signal_noop);		\
292380785Sgshapiro	if ((old) == SIG_IGN || (old) == SIG_DFL)		\
292490792Sgshapiro		(void) sm_signal((sig), (old));			\
292580785Sgshapiro} while (0)
292680785Sgshapiro
292790792Sgshapirovoid
292877349Sgshapirorestart_daemon()
292977349Sgshapiro{
293090792Sgshapiro	bool drop;
293177349Sgshapiro	int i;
293277349Sgshapiro	int save_errno;
293377349Sgshapiro	char *reason;
293480785Sgshapiro	sigfunc_t ignore, oalrm, ousr1;
293577349Sgshapiro	extern int DtableSize;
293677349Sgshapiro
293780785Sgshapiro	/* clear the events to turn off SIGALRMs */
293890792Sgshapiro	sm_clear_events();
293990792Sgshapiro	sm_allsignals(true);
294077349Sgshapiro
294177349Sgshapiro	reason = RestartRequest;
294277349Sgshapiro	RestartRequest = NULL;
294377349Sgshapiro	PendingSignal = 0;
294477349Sgshapiro
294577349Sgshapiro	if (SaveArgv[0][0] != '/')
294677349Sgshapiro	{
294777349Sgshapiro		if (LogLevel > 3)
294877349Sgshapiro			sm_syslog(LOG_INFO, NOQID,
294977349Sgshapiro				  "could not restart: need full path");
295090792Sgshapiro		finis(false, true, EX_OSFILE);
295190792Sgshapiro		/* NOTREACHED */
295277349Sgshapiro	}
295377349Sgshapiro	if (LogLevel > 3)
295477349Sgshapiro		sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
295577349Sgshapiro			  SaveArgv[0],
295677349Sgshapiro			  reason == NULL ? "implicit call" : reason);
295777349Sgshapiro
295890792Sgshapiro	closecontrolsocket(true);
295998121Sgshapiro#if SM_CONF_SHM
296098121Sgshapiro	cleanup_shm(DaemonPid == getpid());
296198121Sgshapiro#endif /* SM_CONF_SHM */
296290792Sgshapiro
296390792Sgshapiro	/*
296490792Sgshapiro	**  Want to drop to the user who started the process in all cases
296590792Sgshapiro	**  *but* when running as "smmsp" for the clientmqueue queue run
296690792Sgshapiro	**  daemon.  In that case, UseMSP will be true, RunAsUid should not
296790792Sgshapiro	**  be root, and RealUid should be either 0 or RunAsUid.
296890792Sgshapiro	*/
296990792Sgshapiro
297090792Sgshapiro	drop = !(UseMSP && RunAsUid != 0 &&
297190792Sgshapiro		 (RealUid == 0 || RealUid == RunAsUid));
297290792Sgshapiro
297390792Sgshapiro	if (drop_privileges(drop) != EX_OK)
297477349Sgshapiro	{
297577349Sgshapiro		if (LogLevel > 0)
297677349Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
297790792Sgshapiro				  "could not drop privileges: %s",
297890792Sgshapiro				  sm_errstring(errno));
297990792Sgshapiro		finis(false, true, EX_OSERR);
298090792Sgshapiro		/* NOTREACHED */
298177349Sgshapiro	}
298277349Sgshapiro
298377349Sgshapiro	/* arrange for all the files to be closed */
298477349Sgshapiro	for (i = 3; i < DtableSize; i++)
298577349Sgshapiro	{
298677349Sgshapiro		register int j;
298777349Sgshapiro
298877349Sgshapiro		if ((j = fcntl(i, F_GETFD, 0)) != -1)
298977349Sgshapiro			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
299077349Sgshapiro	}
299177349Sgshapiro
299280785Sgshapiro	/*
299380785Sgshapiro	**  Need to allow signals before execve() to make them "harmless".
299480785Sgshapiro	**  However, the default action can be "terminate", so it isn't
299580785Sgshapiro	**  really harmless.  Setting signals to IGN will cause them to be
299680785Sgshapiro	**  ignored in the new process to, so that isn't a good alternative.
299780785Sgshapiro	*/
299880785Sgshapiro
299980785Sgshapiro	SM_NOOP_SIGNAL(SIGALRM, oalrm);
300080785Sgshapiro	SM_NOOP_SIGNAL(SIGCHLD, ignore);
300180785Sgshapiro	SM_NOOP_SIGNAL(SIGHUP, ignore);
300280785Sgshapiro	SM_NOOP_SIGNAL(SIGINT, ignore);
300380785Sgshapiro	SM_NOOP_SIGNAL(SIGPIPE, ignore);
300480785Sgshapiro	SM_NOOP_SIGNAL(SIGTERM, ignore);
300580785Sgshapiro#ifdef SIGUSR1
300680785Sgshapiro	SM_NOOP_SIGNAL(SIGUSR1, ousr1);
300780785Sgshapiro#endif /* SIGUSR1 */
300894334Sgshapiro
300994334Sgshapiro	/* Turn back on signals */
301090792Sgshapiro	sm_allsignals(false);
301177349Sgshapiro
301277349Sgshapiro	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
301377349Sgshapiro	save_errno = errno;
301477349Sgshapiro
301580785Sgshapiro	/* block signals again and restore needed signals */
301690792Sgshapiro	sm_allsignals(true);
301780785Sgshapiro
301880785Sgshapiro	/* For finis() events */
301990792Sgshapiro	(void) sm_signal(SIGALRM, oalrm);
302080785Sgshapiro
302180785Sgshapiro#ifdef SIGUSR1
302280785Sgshapiro	/* For debugging finis() */
302390792Sgshapiro	(void) sm_signal(SIGUSR1, ousr1);
302480785Sgshapiro#endif /* SIGUSR1 */
302577349Sgshapiro
302677349Sgshapiro	errno = save_errno;
302777349Sgshapiro	if (LogLevel > 0)
302890792Sgshapiro		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
302990792Sgshapiro			  SaveArgv[0], sm_errstring(errno));
303090792Sgshapiro	finis(false, true, EX_OSFILE);
303190792Sgshapiro	/* NOTREACHED */
303277349Sgshapiro}
303390792Sgshapiro/*
303438032Speter**  MYHOSTNAME -- return the name of this host.
303538032Speter**
303638032Speter**	Parameters:
303738032Speter**		hostbuf -- a place to return the name of this host.
303838032Speter**		size -- the size of hostbuf.
303938032Speter**
304038032Speter**	Returns:
304138032Speter**		A list of aliases for this host.
304238032Speter**
304338032Speter**	Side Effects:
304438032Speter**		Adds numeric codes to $=w.
304538032Speter*/
304638032Speter
304738032Speterstruct hostent *
304838032Spetermyhostname(hostbuf, size)
304938032Speter	char hostbuf[];
305038032Speter	int size;
305138032Speter{
305238032Speter	register struct hostent *hp;
305338032Speter
305473188Sgshapiro	if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
305590792Sgshapiro		(void) sm_strlcpy(hostbuf, "localhost", size);
305664562Sgshapiro	hp = sm_gethostbyname(hostbuf, InetMode);
305790792Sgshapiro#if NETINET && NETINET6
305880785Sgshapiro	if (hp == NULL && InetMode == AF_INET6)
305980785Sgshapiro	{
306080785Sgshapiro		/*
306180785Sgshapiro		**  It's possible that this IPv6 enabled machine doesn't
306280785Sgshapiro		**  actually have any IPv6 interfaces and, therefore, no
306380785Sgshapiro		**  IPv6 addresses.  Fall back to AF_INET.
306480785Sgshapiro		*/
306580785Sgshapiro
306680785Sgshapiro		hp = sm_gethostbyname(hostbuf, AF_INET);
306780785Sgshapiro	}
306890792Sgshapiro#endif /* NETINET && NETINET6 */
306938032Speter	if (hp == NULL)
307038032Speter		return NULL;
307138032Speter	if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
307264562Sgshapiro		(void) cleanstrcpy(hostbuf, hp->h_name, size);
307364562Sgshapiro
307490792Sgshapiro#if NETINFO
307564562Sgshapiro	if (strchr(hostbuf, '.') == NULL)
307638032Speter	{
307764562Sgshapiro		char *domainname;
307864562Sgshapiro
307964562Sgshapiro		domainname = ni_propval("/locations", NULL, "resolver",
308064562Sgshapiro					"domain", '\0');
308164562Sgshapiro		if (domainname != NULL &&
308264562Sgshapiro		    strlen(domainname) + strlen(hostbuf) + 1 < size)
308390792Sgshapiro			(void) sm_strlcat2(hostbuf, ".", domainname, size);
308438032Speter	}
308590792Sgshapiro#endif /* NETINFO */
308638032Speter
308738032Speter	/*
308838032Speter	**  If there is still no dot in the name, try looking for a
308938032Speter	**  dotted alias.
309038032Speter	*/
309138032Speter
309238032Speter	if (strchr(hostbuf, '.') == NULL)
309338032Speter	{
309438032Speter		char **ha;
309538032Speter
309664562Sgshapiro		for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
309738032Speter		{
309838032Speter			if (strchr(*ha, '.') != NULL)
309938032Speter			{
310064562Sgshapiro				(void) cleanstrcpy(hostbuf, *ha, size - 1);
310138032Speter				hostbuf[size - 1] = '\0';
310238032Speter				break;
310338032Speter			}
310438032Speter		}
310538032Speter	}
310638032Speter
310738032Speter	/*
310838032Speter	**  If _still_ no dot, wait for a while and try again -- it is
310938032Speter	**  possible that some service is starting up.  This can result
311038032Speter	**  in excessive delays if the system is badly configured, but
311138032Speter	**  there really isn't a way around that, particularly given that
311238032Speter	**  the config file hasn't been read at this point.
311338032Speter	**  All in all, a bit of a mess.
311438032Speter	*/
311538032Speter
311638032Speter	if (strchr(hostbuf, '.') == NULL &&
311790792Sgshapiro	    !getcanonname(hostbuf, size, true, NULL))
311838032Speter	{
311938032Speter		sm_syslog(LOG_CRIT, NOQID,
312064562Sgshapiro			  "My unqualified host name (%s) unknown; sleeping for retry",
312164562Sgshapiro			  hostbuf);
312238032Speter		message("My unqualified host name (%s) unknown; sleeping for retry",
312338032Speter			hostbuf);
312464562Sgshapiro		(void) sleep(60);
312590792Sgshapiro		if (!getcanonname(hostbuf, size, true, NULL))
312638032Speter		{
312738032Speter			sm_syslog(LOG_ALERT, NOQID,
312864562Sgshapiro				  "unable to qualify my own domain name (%s) -- using short name",
312964562Sgshapiro				  hostbuf);
313038032Speter			message("WARNING: unable to qualify my own domain name (%s) -- using short name",
313138032Speter				hostbuf);
313238032Speter		}
313338032Speter	}
313464562Sgshapiro	return hp;
313538032Speter}
313690792Sgshapiro/*
313738032Speter**  ADDRCMP -- compare two host addresses
313838032Speter**
313938032Speter**	Parameters:
314038032Speter**		hp -- hostent structure for the first address
314138032Speter**		ha -- actual first address
314238032Speter**		sa -- second address
314338032Speter**
314438032Speter**	Returns:
314538032Speter**		0 -- if ha and sa match
314638032Speter**		else -- they don't match
314738032Speter*/
314838032Speter
314964562Sgshapirostatic int
315038032Speteraddrcmp(hp, ha, sa)
315138032Speter	struct hostent *hp;
315238032Speter	char *ha;
315338032Speter	SOCKADDR *sa;
315438032Speter{
315590792Sgshapiro#if NETINET6
315690792Sgshapiro	unsigned char *a;
315790792Sgshapiro#endif /* NETINET6 */
315864562Sgshapiro
315938032Speter	switch (sa->sa.sa_family)
316038032Speter	{
316190792Sgshapiro#if NETINET
316238032Speter	  case AF_INET:
316338032Speter		if (hp->h_addrtype == AF_INET)
316464562Sgshapiro			return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
316538032Speter		break;
316690792Sgshapiro#endif /* NETINET */
316738032Speter
316890792Sgshapiro#if NETINET6
316964562Sgshapiro	  case AF_INET6:
317090792Sgshapiro		a = (unsigned char *) &sa->sin6.sin6_addr;
317164562Sgshapiro
317264562Sgshapiro		/* Straight binary comparison */
317364562Sgshapiro		if (hp->h_addrtype == AF_INET6)
317464562Sgshapiro			return memcmp(ha, a, IN6ADDRSZ);
317564562Sgshapiro
317664562Sgshapiro		/* If IPv4-mapped IPv6 address, compare the IPv4 section */
317764562Sgshapiro		if (hp->h_addrtype == AF_INET &&
317864562Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr))
317964562Sgshapiro			return memcmp(a + IN6ADDRSZ - INADDRSZ, ha, INADDRSZ);
318064562Sgshapiro		break;
318190792Sgshapiro#endif /* NETINET6 */
318238032Speter	}
318338032Speter	return -1;
318438032Speter}
318590792Sgshapiro/*
318664562Sgshapiro**  GETAUTHINFO -- get the real host name associated with a file descriptor
318738032Speter**
318838032Speter**	Uses RFC1413 protocol to try to get info from the other end.
318938032Speter**
319038032Speter**	Parameters:
319138032Speter**		fd -- the descriptor
319290792Sgshapiro**		may_be_forged -- an outage that is set to true if the
319338032Speter**			forward lookup of RealHostName does not match
319490792Sgshapiro**			RealHostAddr; set to false if they do match.
319538032Speter**
319638032Speter**	Returns:
319738032Speter**		The user@host information associated with this descriptor.
319838032Speter*/
319938032Speter
320038032Speterstatic jmp_buf	CtxAuthTimeout;
320138032Speter
320238032Speterstatic void
320338032Speterauthtimeout()
320438032Speter{
320577349Sgshapiro	/*
320677349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
320777349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
320877349Sgshapiro	**	DOING.
320977349Sgshapiro	*/
321077349Sgshapiro
321177349Sgshapiro	errno = ETIMEDOUT;
321238032Speter	longjmp(CtxAuthTimeout, 1);
321338032Speter}
321438032Speter
321538032Speterchar *
321638032Spetergetauthinfo(fd, may_be_forged)
321738032Speter	int fd;
321838032Speter	bool *may_be_forged;
321938032Speter{
322090792Sgshapiro	unsigned short SM_NONVOLATILE port = 0;
322138032Speter	SOCKADDR_LEN_T falen;
322238032Speter	register char *volatile p = NULL;
322338032Speter	SOCKADDR la;
322438032Speter	SOCKADDR_LEN_T lalen;
322590792Sgshapiro#ifndef NO_GETSERVBYNAME
322638032Speter	register struct servent *sp;
322790792Sgshapiro# if NETINET
322890792Sgshapiro	static unsigned short port4 = 0;
322990792Sgshapiro# endif /* NETINET */
323090792Sgshapiro# if NETINET6
323190792Sgshapiro	static unsigned short port6 = 0;
323290792Sgshapiro# endif /* NETINET6 */
323390792Sgshapiro#endif /* ! NO_GETSERVBYNAME */
323438032Speter	volatile int s;
323538032Speter	int i = 0;
323690792Sgshapiro	size_t len;
323790792Sgshapiro	SM_EVENT *ev;
323838032Speter	int nleft;
323938032Speter	struct hostent *hp;
324038032Speter	char *ostype = NULL;
324138032Speter	char **ha;
324238032Speter	char ibuf[MAXNAME + 1];
324338032Speter	static char hbuf[MAXNAME * 2 + 11];
324438032Speter
324590792Sgshapiro	*may_be_forged = false;
324638032Speter	falen = sizeof RealHostAddr;
324738032Speter	if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
324838032Speter	    falen <= 0 || RealHostAddr.sa.sa_family == 0)
324938032Speter	{
325064562Sgshapiro		if (i < 0)
325164562Sgshapiro		{
325264562Sgshapiro			/*
325364562Sgshapiro			**  ENOTSOCK is OK: bail on anything else, but reset
325464562Sgshapiro			**  errno in this case, so a mis-report doesn't
325564562Sgshapiro			**  happen later.
325664562Sgshapiro			*/
325790792Sgshapiro
325864562Sgshapiro			if (errno != ENOTSOCK)
325964562Sgshapiro				return NULL;
326064562Sgshapiro			errno = 0;
326164562Sgshapiro		}
326290792Sgshapiro		(void) sm_strlcpyn(hbuf, sizeof hbuf, 2, RealUserName,
326390792Sgshapiro				   "@localhost");
326438032Speter		if (tTd(9, 1))
326590792Sgshapiro			sm_dprintf("getauthinfo: %s\n", hbuf);
326638032Speter		return hbuf;
326738032Speter	}
326838032Speter
326938032Speter	if (RealHostName == NULL)
327038032Speter	{
327138032Speter		/* translate that to a host name */
327238032Speter		RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
327338032Speter		if (strlen(RealHostName) > MAXNAME)
327490792Sgshapiro			RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
327538032Speter	}
327638032Speter
327738032Speter	/* cross check RealHostName with forward DNS lookup */
327890792Sgshapiro	if (anynet_ntoa(&RealHostAddr)[0] != '[' &&
327990792Sgshapiro	    RealHostName[0] != '[')
328038032Speter	{
328180785Sgshapiro		int family;
328280785Sgshapiro
328380785Sgshapiro		family = RealHostAddr.sa.sa_family;
328490792Sgshapiro#if NETINET6 && NEEDSGETIPNODE
328580785Sgshapiro		/*
328680785Sgshapiro		**  If RealHostAddr is an IPv6 connection with an
328780785Sgshapiro		**  IPv4-mapped address, we need RealHostName's IPv4
328880785Sgshapiro		**  address(es) for addrcmp() to compare against
328980785Sgshapiro		**  RealHostAddr.
329080785Sgshapiro		**
329180785Sgshapiro		**  Actually, we only need to do this for systems
329280785Sgshapiro		**  which NEEDSGETIPNODE since the real getipnodebyname()
329380785Sgshapiro		**  already does V4MAPPED address via the AI_V4MAPPEDCFG
329480785Sgshapiro		**  flag.  A better fix to this problem is to add this
329580785Sgshapiro		**  functionality to our stub getipnodebyname().
329680785Sgshapiro		*/
329780785Sgshapiro
329880785Sgshapiro		if (family == AF_INET6 &&
329980785Sgshapiro		    IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
330080785Sgshapiro			family = AF_INET;
330190792Sgshapiro#endif /* NETINET6 && NEEDSGETIPNODE */
330280785Sgshapiro
330338032Speter		/* try to match the reverse against the forward lookup */
330480785Sgshapiro		hp = sm_gethostbyname(RealHostName, family);
330538032Speter		if (hp == NULL)
330690792Sgshapiro			*may_be_forged = true;
330738032Speter		else
330838032Speter		{
330938032Speter			for (ha = hp->h_addr_list; *ha != NULL; ha++)
331090792Sgshapiro			{
331138032Speter				if (addrcmp(hp, *ha, &RealHostAddr) == 0)
331238032Speter					break;
331390792Sgshapiro			}
331438032Speter			*may_be_forged = *ha == NULL;
331590792Sgshapiro#if NETINET6
331671345Sgshapiro			freehostent(hp);
331771345Sgshapiro			hp = NULL;
331890792Sgshapiro#endif /* NETINET6 */
331938032Speter		}
332038032Speter	}
332138032Speter
332238032Speter	if (TimeOuts.to_ident == 0)
332338032Speter		goto noident;
332438032Speter
332538032Speter	lalen = sizeof la;
332664562Sgshapiro	switch (RealHostAddr.sa.sa_family)
332738032Speter	{
332890792Sgshapiro#if NETINET
332964562Sgshapiro	  case AF_INET:
333064562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
333164562Sgshapiro		    lalen <= 0 ||
333264562Sgshapiro		    la.sa.sa_family != AF_INET)
333364562Sgshapiro		{
333464562Sgshapiro			/* no ident info */
333564562Sgshapiro			goto noident;
333664562Sgshapiro		}
333764562Sgshapiro		port = RealHostAddr.sin.sin_port;
333838032Speter
333964562Sgshapiro		/* create ident query */
334090792Sgshapiro		(void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
334164562Sgshapiro				ntohs(RealHostAddr.sin.sin_port),
334264562Sgshapiro				ntohs(la.sin.sin_port));
334338032Speter
334464562Sgshapiro		/* create local address */
334564562Sgshapiro		la.sin.sin_port = 0;
334638032Speter
334764562Sgshapiro		/* create foreign address */
334890792Sgshapiro# ifdef NO_GETSERVBYNAME
334938032Speter		RealHostAddr.sin.sin_port = htons(113);
335090792Sgshapiro# else /* NO_GETSERVBYNAME */
335190792Sgshapiro
335290792Sgshapiro		/*
335390792Sgshapiro		**  getservbyname() consumes about 5% of the time
335490792Sgshapiro		**  when receiving a small message (almost all of the time
335590792Sgshapiro		**  spent in this routine).
335690792Sgshapiro		**  Hence we store the port in a static variable
335790792Sgshapiro		**  to save this time.
335890792Sgshapiro		**  The portnumber shouldn't change very often...
335990792Sgshapiro		**  This code makes the assumption that the port number
336090792Sgshapiro		**  is not 0.
336190792Sgshapiro		*/
336290792Sgshapiro
336390792Sgshapiro		if (port4 == 0)
336490792Sgshapiro		{
336590792Sgshapiro			sp = getservbyname("auth", "tcp");
336690792Sgshapiro			if (sp != NULL)
336790792Sgshapiro				port4 = sp->s_port;
336890792Sgshapiro			else
336990792Sgshapiro				port4 = htons(113);
337090792Sgshapiro		}
337190792Sgshapiro		RealHostAddr.sin.sin_port = port4;
337264562Sgshapiro		break;
337390792Sgshapiro# endif /* NO_GETSERVBYNAME */
337490792Sgshapiro#endif /* NETINET */
337538032Speter
337690792Sgshapiro#if NETINET6
337764562Sgshapiro	  case AF_INET6:
337864562Sgshapiro		if (getsockname(fd, &la.sa, &lalen) < 0 ||
337964562Sgshapiro		    lalen <= 0 ||
338064562Sgshapiro		    la.sa.sa_family != AF_INET6)
338164562Sgshapiro		{
338264562Sgshapiro			/* no ident info */
338364562Sgshapiro			goto noident;
338464562Sgshapiro		}
338564562Sgshapiro		port = RealHostAddr.sin6.sin6_port;
338664562Sgshapiro
338764562Sgshapiro		/* create ident query */
338890792Sgshapiro		(void) sm_snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
338964562Sgshapiro				ntohs(RealHostAddr.sin6.sin6_port),
339064562Sgshapiro				ntohs(la.sin6.sin6_port));
339164562Sgshapiro
339264562Sgshapiro		/* create local address */
339364562Sgshapiro		la.sin6.sin6_port = 0;
339464562Sgshapiro
339564562Sgshapiro		/* create foreign address */
339690792Sgshapiro# ifdef NO_GETSERVBYNAME
339764562Sgshapiro		RealHostAddr.sin6.sin6_port = htons(113);
339890792Sgshapiro# else /* NO_GETSERVBYNAME */
339990792Sgshapiro		if (port6 == 0)
340090792Sgshapiro		{
340190792Sgshapiro			sp = getservbyname("auth", "tcp");
340290792Sgshapiro			if (sp != NULL)
340390792Sgshapiro				port6 = sp->s_port;
340490792Sgshapiro			else
340590792Sgshapiro				port6 = htons(113);
340690792Sgshapiro		}
340790792Sgshapiro		RealHostAddr.sin6.sin6_port = port6;
340864562Sgshapiro		break;
340990792Sgshapiro# endif /* NO_GETSERVBYNAME */
341090792Sgshapiro#endif /* NETINET6 */
341164562Sgshapiro	  default:
341264562Sgshapiro		/* no ident info */
341364562Sgshapiro		goto noident;
341464562Sgshapiro	}
341564562Sgshapiro
341638032Speter	s = -1;
341738032Speter	if (setjmp(CtxAuthTimeout) != 0)
341838032Speter	{
341938032Speter		if (s >= 0)
342038032Speter			(void) close(s);
342138032Speter		goto noident;
342238032Speter	}
342338032Speter
342438032Speter	/* put a timeout around the whole thing */
342590792Sgshapiro	ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
342638032Speter
342764562Sgshapiro
342838032Speter	/* connect to foreign IDENT server using same address as SMTP socket */
342964562Sgshapiro	s = socket(la.sa.sa_family, SOCK_STREAM, 0);
343038032Speter	if (s < 0)
343138032Speter	{
343290792Sgshapiro		sm_clrevent(ev);
343338032Speter		goto noident;
343438032Speter	}
343564562Sgshapiro	if (bind(s, &la.sa, lalen) < 0 ||
343664562Sgshapiro	    connect(s, &RealHostAddr.sa, lalen) < 0)
343738032Speter		goto closeident;
343838032Speter
343938032Speter	if (tTd(9, 10))
344090792Sgshapiro		sm_dprintf("getauthinfo: sent %s", ibuf);
344138032Speter
344238032Speter	/* send query */
344338032Speter	if (write(s, ibuf, strlen(ibuf)) < 0)
344438032Speter		goto closeident;
344538032Speter
344638032Speter	/* get result */
344738032Speter	p = &ibuf[0];
344838032Speter	nleft = sizeof ibuf - 1;
344938032Speter	while ((i = read(s, p, nleft)) > 0)
345038032Speter	{
345138032Speter		p += i;
345238032Speter		nleft -= i;
345338032Speter		*p = '\0';
345490792Sgshapiro		if (strchr(ibuf, '\n') != NULL || nleft <= 0)
345538032Speter			break;
345638032Speter	}
345738032Speter	(void) close(s);
345890792Sgshapiro	sm_clrevent(ev);
345938032Speter	if (i < 0 || p == &ibuf[0])
346038032Speter		goto noident;
346138032Speter
346238032Speter	if (*--p == '\n' && *--p == '\r')
346338032Speter		p--;
346438032Speter	*++p = '\0';
346538032Speter
346638032Speter	if (tTd(9, 3))
346790792Sgshapiro		sm_dprintf("getauthinfo:  got %s\n", ibuf);
346838032Speter
346938032Speter	/* parse result */
347038032Speter	p = strchr(ibuf, ':');
347138032Speter	if (p == NULL)
347238032Speter	{
347338032Speter		/* malformed response */
347438032Speter		goto noident;
347538032Speter	}
347638032Speter	while (isascii(*++p) && isspace(*p))
347738032Speter		continue;
347890792Sgshapiro	if (sm_strncasecmp(p, "userid", 6) != 0)
347938032Speter	{
348038032Speter		/* presumably an error string */
348138032Speter		goto noident;
348238032Speter	}
348338032Speter	p += 6;
348438032Speter	while (isascii(*p) && isspace(*p))
348538032Speter		p++;
348638032Speter	if (*p++ != ':')
348738032Speter	{
348838032Speter		/* either useridxx or malformed response */
348938032Speter		goto noident;
349038032Speter	}
349138032Speter
349238032Speter	/* p now points to the OSTYPE field */
349338032Speter	while (isascii(*p) && isspace(*p))
349438032Speter		p++;
349538032Speter	ostype = p;
349638032Speter	p = strchr(p, ':');
349738032Speter	if (p == NULL)
349838032Speter	{
349938032Speter		/* malformed response */
350038032Speter		goto noident;
350138032Speter	}
350238032Speter	else
350338032Speter	{
350438032Speter		char *charset;
350538032Speter
350638032Speter		*p = '\0';
350738032Speter		charset = strchr(ostype, ',');
350838032Speter		if (charset != NULL)
350938032Speter			*charset = '\0';
351038032Speter	}
351138032Speter
351238032Speter	/* 1413 says don't do this -- but it's broken otherwise */
351338032Speter	while (isascii(*++p) && isspace(*p))
351438032Speter		continue;
351538032Speter
351638032Speter	/* p now points to the authenticated name -- copy carefully */
351790792Sgshapiro	if (sm_strncasecmp(ostype, "other", 5) == 0 &&
351838032Speter	    (ostype[5] == ' ' || ostype[5] == '\0'))
351938032Speter	{
352090792Sgshapiro		(void) sm_strlcpy(hbuf, "IDENT:", sizeof hbuf);
352138032Speter		cleanstrcpy(&hbuf[6], p, MAXNAME);
352238032Speter	}
352338032Speter	else
352438032Speter		cleanstrcpy(hbuf, p, MAXNAME);
352590792Sgshapiro	len = strlen(hbuf);
352690792Sgshapiro	(void) sm_strlcpyn(&hbuf[len], sizeof hbuf - len, 2, "@",
352790792Sgshapiro			   RealHostName == NULL ? "localhost" : RealHostName);
352838032Speter	goto postident;
352938032Speter
353038032Spetercloseident:
353138032Speter	(void) close(s);
353290792Sgshapiro	sm_clrevent(ev);
353338032Speter
353438032Speternoident:
353564562Sgshapiro	/* put back the original incoming port */
353664562Sgshapiro	switch (RealHostAddr.sa.sa_family)
353764562Sgshapiro	{
353890792Sgshapiro#if NETINET
353964562Sgshapiro	  case AF_INET:
354064562Sgshapiro		if (port > 0)
354164562Sgshapiro			RealHostAddr.sin.sin_port = port;
354264562Sgshapiro		break;
354390792Sgshapiro#endif /* NETINET */
354464562Sgshapiro
354590792Sgshapiro#if NETINET6
354664562Sgshapiro	  case AF_INET6:
354764562Sgshapiro		if (port > 0)
354864562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
354964562Sgshapiro		break;
355090792Sgshapiro#endif /* NETINET6 */
355164562Sgshapiro	}
355264562Sgshapiro
355338032Speter	if (RealHostName == NULL)
355438032Speter	{
355538032Speter		if (tTd(9, 1))
355690792Sgshapiro			sm_dprintf("getauthinfo: NULL\n");
355738032Speter		return NULL;
355838032Speter	}
355990792Sgshapiro	(void) sm_strlcpy(hbuf, RealHostName, sizeof hbuf);
356038032Speter
356138032Speterpostident:
356290792Sgshapiro#if IP_SRCROUTE
356390792Sgshapiro# ifndef GET_IPOPT_DST
356490792Sgshapiro#  define GET_IPOPT_DST(dst)	(dst)
356590792Sgshapiro# endif /* ! GET_IPOPT_DST */
356638032Speter	/*
356738032Speter	**  Extract IP source routing information.
356838032Speter	**
356938032Speter	**	Format of output for a connection from site a through b
357038032Speter	**	through c to d:
357138032Speter	**		loose:      @site-c@site-b:site-a
357238032Speter	**		strict:	   !@site-c@site-b:site-a
357338032Speter	**
357438032Speter	**	o - pointer within ipopt_list structure.
357538032Speter	**	q - pointer within ls/ss rr route data
357638032Speter	**	p - pointer to hbuf
357738032Speter	*/
357838032Speter
357938032Speter	if (RealHostAddr.sa.sa_family == AF_INET)
358038032Speter	{
358138032Speter		SOCKOPT_LEN_T ipoptlen;
358238032Speter		int j;
358390792Sgshapiro		unsigned char *q;
358490792Sgshapiro		unsigned char *o;
358538032Speter		int l;
358664562Sgshapiro		struct IPOPTION ipopt;
358738032Speter
358838032Speter		ipoptlen = sizeof ipopt;
358938032Speter		if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
359038032Speter			       (char *) &ipopt, &ipoptlen) < 0)
359138032Speter			goto noipsr;
359238032Speter		if (ipoptlen == 0)
359338032Speter			goto noipsr;
359490792Sgshapiro		o = (unsigned char *) ipopt.IP_LIST;
359590792Sgshapiro		while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
359638032Speter		{
359738032Speter			switch (*o)
359838032Speter			{
359964562Sgshapiro			  case IPOPT_EOL:
360038032Speter				o = NULL;
360138032Speter				break;
360238032Speter
360338032Speter			  case IPOPT_NOP:
360438032Speter				o++;
360538032Speter				break;
360638032Speter
360738032Speter			  case IPOPT_SSRR:
360838032Speter			  case IPOPT_LSRR:
360938032Speter				/*
361038032Speter				**  Source routing.
361138032Speter				**	o[0] is the option type (loose/strict).
361238032Speter				**	o[1] is the length of this option,
361338032Speter				**		including option type and
361438032Speter				**		length.
361538032Speter				**	o[2] is the pointer into the route
361638032Speter				**		data.
361738032Speter				**	o[3] begins the route data.
361838032Speter				*/
361938032Speter
362038032Speter				p = &hbuf[strlen(hbuf)];
362138032Speter				l = sizeof hbuf - (hbuf - p) - 6;
362290792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(hbuf, p),
362390792Sgshapiro					" [%s@%.*s",
362490792Sgshapiro					*o == IPOPT_SSRR ? "!" : "",
362590792Sgshapiro					l > 240 ? 120 : l / 2,
362690792Sgshapiro					inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
362738032Speter				i = strlen(p);
362838032Speter				p += i;
362938032Speter				l -= strlen(p);
363038032Speter
363138032Speter				j = o[1] / sizeof(struct in_addr) - 1;
363238032Speter
363338032Speter				/* q skips length and router pointer to data */
363438032Speter				q = &o[3];
363538032Speter				for ( ; j >= 0; j--)
363638032Speter				{
363764562Sgshapiro					struct in_addr addr;
363864562Sgshapiro
363938032Speter					memcpy(&addr, q, sizeof(addr));
364090792Sgshapiro					(void) sm_snprintf(p,
364190792Sgshapiro						SPACELEFT(hbuf, p),
364290792Sgshapiro						"%c%.*s",
364390792Sgshapiro						j != 0 ? '@' : ':',
364490792Sgshapiro						l > 240 ? 120 :
364590792Sgshapiro							j == 0 ? l : l / 2,
364690792Sgshapiro						inet_ntoa(addr));
364738032Speter					i = strlen(p);
364838032Speter					p += i;
364938032Speter					l -= i + 1;
365064562Sgshapiro					q += sizeof(struct in_addr);
365138032Speter				}
365238032Speter				o += o[1];
365338032Speter				break;
365438032Speter
365538032Speter			  default:
365638032Speter				/* Skip over option */
365738032Speter				o += o[1];
365838032Speter				break;
365938032Speter			}
366038032Speter		}
366190792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
366238032Speter		goto postipsr;
366338032Speter	}
366438032Speter
366538032Speternoipsr:
366690792Sgshapiro#endif /* IP_SRCROUTE */
366738032Speter	if (RealHostName != NULL && RealHostName[0] != '[')
366838032Speter	{
366938032Speter		p = &hbuf[strlen(hbuf)];
367090792Sgshapiro		(void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
367190792Sgshapiro				   anynet_ntoa(&RealHostAddr));
367238032Speter	}
367338032Speter	if (*may_be_forged)
367438032Speter	{
367538032Speter		p = &hbuf[strlen(hbuf)];
367690792Sgshapiro		(void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
367790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
367890792Sgshapiro			  macid("{client_resolve}"), "FORGED");
367938032Speter	}
368038032Speter
368190792Sgshapiro#if IP_SRCROUTE
368238032Speterpostipsr:
368390792Sgshapiro#endif /* IP_SRCROUTE */
368464562Sgshapiro
368564562Sgshapiro	/* put back the original incoming port */
368664562Sgshapiro	switch (RealHostAddr.sa.sa_family)
368764562Sgshapiro	{
368890792Sgshapiro#if NETINET
368964562Sgshapiro	  case AF_INET:
369064562Sgshapiro		if (port > 0)
369164562Sgshapiro			RealHostAddr.sin.sin_port = port;
369264562Sgshapiro		break;
369390792Sgshapiro#endif /* NETINET */
369464562Sgshapiro
369590792Sgshapiro#if NETINET6
369664562Sgshapiro	  case AF_INET6:
369764562Sgshapiro		if (port > 0)
369864562Sgshapiro			RealHostAddr.sin6.sin6_port = port;
369964562Sgshapiro		break;
370090792Sgshapiro#endif /* NETINET6 */
370164562Sgshapiro	}
370264562Sgshapiro
370390792Sgshapiro	if (tTd(9, 1))
370490792Sgshapiro		sm_dprintf("getauthinfo: %s\n", hbuf);
370538032Speter	return hbuf;
370638032Speter}
370790792Sgshapiro/*
370838032Speter**  HOST_MAP_LOOKUP -- turn a hostname into canonical form
370938032Speter**
371038032Speter**	Parameters:
371138032Speter**		map -- a pointer to this map.
371238032Speter**		name -- the (presumably unqualified) hostname.
371338032Speter**		av -- unused -- for compatibility with other mapping
371438032Speter**			functions.
371538032Speter**		statp -- an exit status (out parameter) -- set to
371638032Speter**			EX_TEMPFAIL if the name server is unavailable.
371738032Speter**
371838032Speter**	Returns:
371938032Speter**		The mapping, if found.
372038032Speter**		NULL if no mapping found.
372138032Speter**
372238032Speter**	Side Effects:
372338032Speter**		Looks up the host specified in hbuf.  If it is not
372438032Speter**		the canonical name for that host, return the canonical
372538032Speter**		name (unless MF_MATCHONLY is set, which will cause the
372638032Speter**		status only to be returned).
372738032Speter*/
372838032Speter
372938032Speterchar *
373038032Speterhost_map_lookup(map, name, av, statp)
373138032Speter	MAP *map;
373238032Speter	char *name;
373338032Speter	char **av;
373438032Speter	int *statp;
373538032Speter{
373638032Speter	register struct hostent *hp;
373790792Sgshapiro#if NETINET
373838032Speter	struct in_addr in_addr;
373990792Sgshapiro#endif /* NETINET */
374090792Sgshapiro#if NETINET6
374164562Sgshapiro	struct in6_addr in6_addr;
374290792Sgshapiro#endif /* NETINET6 */
374364562Sgshapiro	char *cp, *ans = NULL;
374438032Speter	register STAB *s;
374590792Sgshapiro	time_t now;
374690792Sgshapiro#if NAMED_BIND
374790792Sgshapiro	time_t SM_NONVOLATILE retrans = 0;
374890792Sgshapiro	int SM_NONVOLATILE retry = 0;
374990792Sgshapiro#endif /* NAMED_BIND */
375038032Speter	char hbuf[MAXNAME + 1];
375138032Speter
375238032Speter	/*
375338032Speter	**  See if we have already looked up this name.  If so, just
375490792Sgshapiro	**  return it (unless expired).
375538032Speter	*/
375638032Speter
375790792Sgshapiro	now = curtime();
375838032Speter	s = stab(name, ST_NAMECANON, ST_ENTER);
375990792Sgshapiro	if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
376090792Sgshapiro	    s->s_namecanon.nc_exp >= now)
376138032Speter	{
376238032Speter		if (tTd(9, 1))
376390792Sgshapiro			sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
376490792Sgshapiro				    name,
376590792Sgshapiro				    s->s_namecanon.nc_cname == NULL
376638032Speter					? "NULL"
376738032Speter					: s->s_namecanon.nc_cname);
376838032Speter		errno = s->s_namecanon.nc_errno;
376973188Sgshapiro		SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
377038032Speter		*statp = s->s_namecanon.nc_stat;
377138032Speter		if (*statp == EX_TEMPFAIL)
377238032Speter		{
377338032Speter			CurEnv->e_status = "4.4.3";
377438032Speter			message("851 %s: Name server timeout",
377538032Speter				shortenstring(name, 33));
377638032Speter		}
377738032Speter		if (*statp != EX_OK)
377838032Speter			return NULL;
377938032Speter		if (s->s_namecanon.nc_cname == NULL)
378038032Speter		{
378138032Speter			syserr("host_map_lookup(%s): bogus NULL cache entry, errno = %d, h_errno = %d",
378264562Sgshapiro			       name,
378364562Sgshapiro			       s->s_namecanon.nc_errno,
378464562Sgshapiro			       s->s_namecanon.nc_herrno);
378538032Speter			return NULL;
378638032Speter		}
378738032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
378838032Speter			cp = map_rewrite(map, name, strlen(name), NULL);
378938032Speter		else
379038032Speter			cp = map_rewrite(map,
379138032Speter					 s->s_namecanon.nc_cname,
379238032Speter					 strlen(s->s_namecanon.nc_cname),
379338032Speter					 av);
379438032Speter		return cp;
379538032Speter	}
379638032Speter
379738032Speter	/*
379838032Speter	**  If we are running without a regular network connection (usually
379938032Speter	**  dial-on-demand) and we are just queueing, we want to avoid DNS
380038032Speter	**  lookups because those could try to connect to a server.
380138032Speter	*/
380238032Speter
380364562Sgshapiro	if (CurEnv->e_sendmode == SM_DEFER &&
380464562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
380538032Speter	{
380638032Speter		if (tTd(9, 1))
380790792Sgshapiro			sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
380838032Speter		*statp = EX_TEMPFAIL;
380938032Speter		return NULL;
381038032Speter	}
381138032Speter
381238032Speter	/*
381338032Speter	**  If first character is a bracket, then it is an address
381438032Speter	**  lookup.  Address is copied into a temporary buffer to
381538032Speter	**  strip the brackets and to preserve name if address is
381638032Speter	**  unknown.
381738032Speter	*/
381838032Speter
381964562Sgshapiro	if (tTd(9, 1))
382090792Sgshapiro		sm_dprintf("host_map_lookup(%s) => ", name);
382190792Sgshapiro#if NAMED_BIND
382290792Sgshapiro	if (map->map_timeout > 0)
382390792Sgshapiro	{
382490792Sgshapiro		retrans = _res.retrans;
382590792Sgshapiro		_res.retrans = map->map_timeout;
382690792Sgshapiro	}
382790792Sgshapiro	if (map->map_retry > 0)
382890792Sgshapiro	{
382990792Sgshapiro		retry = _res.retry;
383090792Sgshapiro		_res.retry = map->map_retry;
383190792Sgshapiro	}
383290792Sgshapiro#endif /* NAMED_BIND */
383390792Sgshapiro
383490792Sgshapiro	/* set default TTL */
383590792Sgshapiro	s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
383638032Speter	if (*name != '[')
383738032Speter	{
383890792Sgshapiro		int ttl;
383990792Sgshapiro
384090792Sgshapiro		(void) sm_strlcpy(hbuf, name, sizeof hbuf);
384190792Sgshapiro		if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX, &ttl))
384290792Sgshapiro		{
384364562Sgshapiro			ans = hbuf;
384490792Sgshapiro			if (ttl > 0)
384590792Sgshapiro				s->s_namecanon.nc_exp = now + SM_MIN(ttl,
384690792Sgshapiro								SM_DEFAULT_TTL);
384790792Sgshapiro		}
384864562Sgshapiro	}
384964562Sgshapiro	else
385064562Sgshapiro	{
385164562Sgshapiro		if ((cp = strchr(name, ']')) == NULL)
385271345Sgshapiro		{
385371345Sgshapiro			if (tTd(9, 1))
385490792Sgshapiro				sm_dprintf("FAILED\n");
385564562Sgshapiro			return NULL;
385671345Sgshapiro		}
385764562Sgshapiro		*cp = '\0';
385864562Sgshapiro
385964562Sgshapiro		hp = NULL;
386090792Sgshapiro#if NETINET
386164562Sgshapiro		if ((in_addr.s_addr = inet_addr(&name[1])) != INADDR_NONE)
386264562Sgshapiro			hp = sm_gethostbyaddr((char *)&in_addr,
386364562Sgshapiro					      INADDRSZ, AF_INET);
386490792Sgshapiro#endif /* NETINET */
386590792Sgshapiro#if NETINET6
386664562Sgshapiro		if (hp == NULL &&
386790792Sgshapiro		    anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
386864562Sgshapiro			hp = sm_gethostbyaddr((char *)&in6_addr,
386964562Sgshapiro					      IN6ADDRSZ, AF_INET6);
387090792Sgshapiro#endif /* NETINET6 */
387164562Sgshapiro		*cp = ']';
387264562Sgshapiro
387364562Sgshapiro		if (hp != NULL)
387438032Speter		{
387564562Sgshapiro			/* found a match -- copy out */
387690792Sgshapiro			ans = denlstring((char *) hp->h_name, true, true);
387790792Sgshapiro#if NETINET6
387890792Sgshapiro			if (ans == hp->h_name)
387990792Sgshapiro			{
388090792Sgshapiro				static char n[MAXNAME + 1];
388190792Sgshapiro
388290792Sgshapiro				/* hp->h_name is about to disappear */
388390792Sgshapiro				(void) sm_strlcpy(n, ans, sizeof n);
388490792Sgshapiro				ans = n;
388590792Sgshapiro			}
388671345Sgshapiro			freehostent(hp);
388771345Sgshapiro			hp = NULL;
388890792Sgshapiro#endif /* NETINET6 */
388938032Speter		}
389064562Sgshapiro	}
389190792Sgshapiro#if NAMED_BIND
389290792Sgshapiro	if (map->map_timeout > 0)
389390792Sgshapiro		_res.retrans = retrans;
389490792Sgshapiro	if (map->map_retry > 0)
389590792Sgshapiro		_res.retry = retry;
389690792Sgshapiro#endif /* NAMED_BIND */
389738032Speter
389864562Sgshapiro	s->s_namecanon.nc_flags |= NCF_VALID;	/* will be soon */
389938032Speter
390064562Sgshapiro	/* Found an answer */
390164562Sgshapiro	if (ans != NULL)
390264562Sgshapiro	{
390364562Sgshapiro		s->s_namecanon.nc_stat = *statp = EX_OK;
390490792Sgshapiro		if (s->s_namecanon.nc_cname != NULL)
390590792Sgshapiro			sm_free(s->s_namecanon.nc_cname);
390690792Sgshapiro		s->s_namecanon.nc_cname = sm_strdup_x(ans);
390764562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
390864562Sgshapiro			cp = map_rewrite(map, name, strlen(name), NULL);
390964562Sgshapiro		else
391064562Sgshapiro			cp = map_rewrite(map, ans, strlen(ans), av);
391171345Sgshapiro		if (tTd(9, 1))
391290792Sgshapiro			sm_dprintf("FOUND %s\n", ans);
391364562Sgshapiro		return cp;
391438032Speter	}
391538032Speter
391664562Sgshapiro
391764562Sgshapiro	/* No match found */
391838032Speter	s->s_namecanon.nc_errno = errno;
391990792Sgshapiro#if NAMED_BIND
392038032Speter	s->s_namecanon.nc_herrno = h_errno;
392164562Sgshapiro	if (tTd(9, 1))
392290792Sgshapiro		sm_dprintf("FAIL (%d)\n", h_errno);
392364562Sgshapiro	switch (h_errno)
392438032Speter	{
392564562Sgshapiro	  case TRY_AGAIN:
392664562Sgshapiro		if (UseNameServer)
392764562Sgshapiro		{
392864562Sgshapiro			CurEnv->e_status = "4.4.3";
392964562Sgshapiro			message("851 %s: Name server timeout",
393064562Sgshapiro				shortenstring(name, 33));
393164562Sgshapiro		}
393264562Sgshapiro		*statp = EX_TEMPFAIL;
393364562Sgshapiro		break;
393464562Sgshapiro
393564562Sgshapiro	  case HOST_NOT_FOUND:
393664562Sgshapiro	  case NO_DATA:
393764562Sgshapiro		*statp = EX_NOHOST;
393864562Sgshapiro		break;
393964562Sgshapiro
394064562Sgshapiro	  case NO_RECOVERY:
394164562Sgshapiro		*statp = EX_SOFTWARE;
394264562Sgshapiro		break;
394364562Sgshapiro
394464562Sgshapiro	  default:
394564562Sgshapiro		*statp = EX_UNAVAILABLE;
394664562Sgshapiro		break;
394738032Speter	}
394890792Sgshapiro#else /* NAMED_BIND */
394964562Sgshapiro	if (tTd(9, 1))
395090792Sgshapiro		sm_dprintf("FAIL\n");
395164562Sgshapiro	*statp = EX_NOHOST;
395290792Sgshapiro#endif /* NAMED_BIND */
395364562Sgshapiro	s->s_namecanon.nc_stat = *statp;
395464562Sgshapiro	return NULL;
395538032Speter}
395638032Speter/*
395790792Sgshapiro**  HOST_MAP_INIT -- initialize host class structures
395838032Speter**
395938032Speter**	Parameters:
396090792Sgshapiro**		map -- a pointer to this map.
396190792Sgshapiro**		args -- argument string.
396238032Speter**
396338032Speter**	Returns:
396490792Sgshapiro**		true.
396538032Speter*/
396638032Speter
396738032Speterbool
396838032Speterhost_map_init(map, args)
396938032Speter	MAP *map;
397038032Speter	char *args;
397138032Speter{
397238032Speter	register char *p = args;
397338032Speter
397438032Speter	for (;;)
397538032Speter	{
397638032Speter		while (isascii(*p) && isspace(*p))
397738032Speter			p++;
397838032Speter		if (*p != '-')
397938032Speter			break;
398038032Speter		switch (*++p)
398138032Speter		{
398238032Speter		  case 'a':
398338032Speter			map->map_app = ++p;
398438032Speter			break;
398538032Speter
398638032Speter		  case 'T':
398738032Speter			map->map_tapp = ++p;
398838032Speter			break;
398938032Speter
399038032Speter		  case 'm':
399138032Speter			map->map_mflags |= MF_MATCHONLY;
399238032Speter			break;
399338032Speter
399438032Speter		  case 't':
399538032Speter			map->map_mflags |= MF_NODEFER;
399638032Speter			break;
399764562Sgshapiro
399864562Sgshapiro		  case 'S':	/* only for consistency */
399964562Sgshapiro			map->map_spacesub = *++p;
400064562Sgshapiro			break;
400164562Sgshapiro
400264562Sgshapiro		  case 'D':
400364562Sgshapiro			map->map_mflags |= MF_DEFER;
400464562Sgshapiro			break;
400590792Sgshapiro
400690792Sgshapiro		  case 'd':
400790792Sgshapiro			{
400890792Sgshapiro				char *h;
400990792Sgshapiro
401090792Sgshapiro				while (isascii(*++p) && isspace(*p))
401190792Sgshapiro					continue;
401290792Sgshapiro				h = strchr(p, ' ');
401390792Sgshapiro				if (h != NULL)
401490792Sgshapiro					*h = '\0';
401590792Sgshapiro				map->map_timeout = convtime(p, 's');
401690792Sgshapiro				if (h != NULL)
401790792Sgshapiro					*h = ' ';
401890792Sgshapiro			}
401990792Sgshapiro			break;
402090792Sgshapiro
402190792Sgshapiro		  case 'r':
402290792Sgshapiro			while (isascii(*++p) && isspace(*p))
402390792Sgshapiro				continue;
402490792Sgshapiro			map->map_retry = atoi(p);
402590792Sgshapiro			break;
402638032Speter		}
402738032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
402838032Speter			p++;
402938032Speter		if (*p != '\0')
403038032Speter			*p++ = '\0';
403138032Speter	}
403238032Speter	if (map->map_app != NULL)
403338032Speter		map->map_app = newstr(map->map_app);
403438032Speter	if (map->map_tapp != NULL)
403538032Speter		map->map_tapp = newstr(map->map_tapp);
403690792Sgshapiro	return true;
403738032Speter}
403890792Sgshapiro
403964562Sgshapiro#if NETINET6
404064562Sgshapiro/*
404164562Sgshapiro**  ANYNET_NTOP -- convert an IPv6 network address to printable form.
404264562Sgshapiro**
404364562Sgshapiro**	Parameters:
404464562Sgshapiro**		s6a -- a pointer to an in6_addr structure.
404564562Sgshapiro**		dst -- buffer to store result in
404664562Sgshapiro**		dst_len -- size of dst buffer
404764562Sgshapiro**
404864562Sgshapiro**	Returns:
404964562Sgshapiro**		A printable version of that structure.
405064562Sgshapiro*/
405190792Sgshapiro
405264562Sgshapirochar *
405364562Sgshapiroanynet_ntop(s6a, dst, dst_len)
405464562Sgshapiro	struct in6_addr *s6a;
405564562Sgshapiro	char *dst;
405664562Sgshapiro	size_t dst_len;
405764562Sgshapiro{
405864562Sgshapiro	register char *ap;
405964562Sgshapiro
406064562Sgshapiro	if (IN6_IS_ADDR_V4MAPPED(s6a))
406164562Sgshapiro		ap = (char *) inet_ntop(AF_INET,
406264562Sgshapiro					&s6a->s6_addr[IN6ADDRSZ - INADDRSZ],
406364562Sgshapiro					dst, dst_len);
406464562Sgshapiro	else
406590792Sgshapiro	{
406690792Sgshapiro		char *d;
406790792Sgshapiro		size_t sz;
406890792Sgshapiro
406990792Sgshapiro		/* Save pointer to beginning of string */
407090792Sgshapiro		d = dst;
407190792Sgshapiro
407290792Sgshapiro		/* Add IPv6: protocol tag */
407390792Sgshapiro		sz = sm_strlcpy(dst, "IPv6:", dst_len);
407490792Sgshapiro		if (sz >= dst_len)
407590792Sgshapiro			return NULL;
407690792Sgshapiro		dst += sz;
407790792Sgshapiro		dst_len -= sz;
407864562Sgshapiro		ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
407990792Sgshapiro
408090792Sgshapiro		/* Restore pointer to beginning of string */
408190792Sgshapiro		if (ap != NULL)
408290792Sgshapiro			ap = d;
408390792Sgshapiro	}
408464562Sgshapiro	return ap;
408564562Sgshapiro}
408690792Sgshapiro
408790792Sgshapiro/*
408890792Sgshapiro**  ANYNET_PTON -- convert printed form to network address.
408990792Sgshapiro**
409090792Sgshapiro**	Wrapper for inet_pton() which handles IPv6: labels.
409190792Sgshapiro**
409290792Sgshapiro**	Parameters:
409390792Sgshapiro**		family -- address family
409490792Sgshapiro**		src -- string
409590792Sgshapiro**		dst -- destination address structure
409690792Sgshapiro**
409790792Sgshapiro**	Returns:
409890792Sgshapiro**		1 if the address was valid
409990792Sgshapiro**		0 if the address wasn't parseable
410090792Sgshapiro**		-1 if error
410190792Sgshapiro*/
410290792Sgshapiro
410390792Sgshapiroint
410490792Sgshapiroanynet_pton(family, src, dst)
410590792Sgshapiro	int family;
410690792Sgshapiro	const char *src;
410790792Sgshapiro	void *dst;
410890792Sgshapiro{
410990792Sgshapiro	if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
411090792Sgshapiro		src += 5;
411190792Sgshapiro	return inet_pton(family, src, dst);
411290792Sgshapiro}
411364562Sgshapiro#endif /* NETINET6 */
411490792Sgshapiro/*
411538032Speter**  ANYNET_NTOA -- convert a network address to printable form.
411638032Speter**
411738032Speter**	Parameters:
411838032Speter**		sap -- a pointer to a sockaddr structure.
411938032Speter**
412038032Speter**	Returns:
412138032Speter**		A printable version of that sockaddr.
412238032Speter*/
412338032Speter
412438032Speter#ifdef USE_SOCK_STREAM
412538032Speter
412664562Sgshapiro# if NETLINK
412764562Sgshapiro#  include <net/if_dl.h>
412864562Sgshapiro# endif /* NETLINK */
412938032Speter
413038032Speterchar *
413138032Speteranynet_ntoa(sap)
413238032Speter	register SOCKADDR *sap;
413338032Speter{
413438032Speter	register char *bp;
413538032Speter	register char *ap;
413638032Speter	int l;
413738032Speter	static char buf[100];
413838032Speter
413938032Speter	/* check for null/zero family */
414038032Speter	if (sap == NULL)
414138032Speter		return "NULLADDR";
414238032Speter	if (sap->sa.sa_family == 0)
414338032Speter		return "0";
414438032Speter
414538032Speter	switch (sap->sa.sa_family)
414638032Speter	{
414764562Sgshapiro# if NETUNIX
414838032Speter	  case AF_UNIX:
414964562Sgshapiro		if (sap->sunix.sun_path[0] != '\0')
415090792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "[UNIX: %.64s]",
415190792Sgshapiro					   sap->sunix.sun_path);
415264562Sgshapiro		else
415390792Sgshapiro			(void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof buf);
415438032Speter		return buf;
415564562Sgshapiro# endif /* NETUNIX */
415638032Speter
415764562Sgshapiro# if NETINET
415838032Speter	  case AF_INET:
415964562Sgshapiro		return (char *) inet_ntoa(sap->sin.sin_addr);
416064562Sgshapiro# endif /* NETINET */
416138032Speter
416264562Sgshapiro# if NETINET6
416364562Sgshapiro	  case AF_INET6:
416464562Sgshapiro		ap = anynet_ntop(&sap->sin6.sin6_addr, buf, sizeof buf);
416564562Sgshapiro		if (ap != NULL)
416664562Sgshapiro			return ap;
416764562Sgshapiro		break;
416864562Sgshapiro# endif /* NETINET6 */
416964562Sgshapiro
417064562Sgshapiro# if NETLINK
417138032Speter	  case AF_LINK:
417290792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "[LINK: %s]",
417390792Sgshapiro				   link_ntoa((struct sockaddr_dl *) &sap->sa));
417438032Speter		return buf;
417564562Sgshapiro# endif /* NETLINK */
417638032Speter	  default:
417738032Speter		/* this case is needed when nothing is #defined */
417838032Speter		/* in order to keep the switch syntactically correct */
417938032Speter		break;
418038032Speter	}
418138032Speter
418238032Speter	/* unknown family -- just dump bytes */
418390792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family);
418438032Speter	bp = &buf[strlen(buf)];
418538032Speter	ap = sap->sa.sa_data;
418638032Speter	for (l = sizeof sap->sa.sa_data; --l >= 0; )
418738032Speter	{
418890792Sgshapiro		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
418990792Sgshapiro				   *ap++ & 0377);
419038032Speter		bp += 3;
419138032Speter	}
419238032Speter	*--bp = '\0';
419338032Speter	return buf;
419438032Speter}
419590792Sgshapiro/*
419638032Speter**  HOSTNAMEBYANYADDR -- return name of host based on address
419738032Speter**
419838032Speter**	Parameters:
419938032Speter**		sap -- SOCKADDR pointer
420038032Speter**
420138032Speter**	Returns:
420238032Speter**		text representation of host name.
420338032Speter**
420438032Speter**	Side Effects:
420538032Speter**		none.
420638032Speter*/
420738032Speter
420838032Speterchar *
420938032Speterhostnamebyanyaddr(sap)
421038032Speter	register SOCKADDR *sap;
421138032Speter{
421238032Speter	register struct hostent *hp;
421364562Sgshapiro# if NAMED_BIND
421438032Speter	int saveretry;
421564562Sgshapiro# endif /* NAMED_BIND */
421664562Sgshapiro# if NETINET6
421764562Sgshapiro	struct in6_addr in6_addr;
421864562Sgshapiro# endif /* NETINET6 */
421938032Speter
422064562Sgshapiro# if NAMED_BIND
422138032Speter	/* shorten name server timeout to avoid higher level timeouts */
422238032Speter	saveretry = _res.retry;
422364562Sgshapiro	if (_res.retry * _res.retrans > 20)
422464562Sgshapiro		_res.retry = 20 / _res.retrans;
422564562Sgshapiro# endif /* NAMED_BIND */
422638032Speter
422738032Speter	switch (sap->sa.sa_family)
422838032Speter	{
422964562Sgshapiro# if NETINET
423038032Speter	  case AF_INET:
423138032Speter		hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
423290792Sgshapiro				      INADDRSZ, AF_INET);
423338032Speter		break;
423464562Sgshapiro# endif /* NETINET */
423538032Speter
423664562Sgshapiro# if NETINET6
423764562Sgshapiro	  case AF_INET6:
423864562Sgshapiro		hp = sm_gethostbyaddr((char *) &sap->sin6.sin6_addr,
423990792Sgshapiro				      IN6ADDRSZ, AF_INET6);
424064562Sgshapiro		break;
424164562Sgshapiro# endif /* NETINET6 */
424264562Sgshapiro
424364562Sgshapiro# if NETISO
424438032Speter	  case AF_ISO:
424538032Speter		hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
424690792Sgshapiro				      sizeof sap->siso.siso_addr, AF_ISO);
424738032Speter		break;
424864562Sgshapiro# endif /* NETISO */
424938032Speter
425064562Sgshapiro# if NETUNIX
425138032Speter	  case AF_UNIX:
425238032Speter		hp = NULL;
425338032Speter		break;
425464562Sgshapiro# endif /* NETUNIX */
425538032Speter
425638032Speter	  default:
425790792Sgshapiro		hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof sap->sa.sa_data,
425890792Sgshapiro				      sap->sa.sa_family);
425938032Speter		break;
426038032Speter	}
426138032Speter
426264562Sgshapiro# if NAMED_BIND
426338032Speter	_res.retry = saveretry;
426464562Sgshapiro# endif /* NAMED_BIND */
426538032Speter
426664562Sgshapiro# if NETINET || NETINET6
426764562Sgshapiro	if (hp != NULL && hp->h_name[0] != '['
426864562Sgshapiro#  if NETINET6
426964562Sgshapiro	    && inet_pton(AF_INET6, hp->h_name, &in6_addr) != 1
427064562Sgshapiro#  endif /* NETINET6 */
427164562Sgshapiro#  if NETINET
427264562Sgshapiro	    && inet_addr(hp->h_name) == INADDR_NONE
427364562Sgshapiro#  endif /* NETINET */
427464562Sgshapiro	    )
427571345Sgshapiro	{
427671345Sgshapiro		char *name;
427771345Sgshapiro
427890792Sgshapiro		name = denlstring((char *) hp->h_name, true, true);
427990792Sgshapiro#  if NETINET6
428071345Sgshapiro		if (name == hp->h_name)
428171345Sgshapiro		{
428271345Sgshapiro			static char n[MAXNAME + 1];
428371345Sgshapiro
428471345Sgshapiro			/* Copy the string, hp->h_name is about to disappear */
428590792Sgshapiro			(void) sm_strlcpy(n, name, sizeof n);
428671345Sgshapiro			name = n;
428771345Sgshapiro		}
428871345Sgshapiro		freehostent(hp);
428990792Sgshapiro#  endif /* NETINET6 */
429071345Sgshapiro		return name;
429171345Sgshapiro	}
429264562Sgshapiro# endif /* NETINET || NETINET6 */
429371345Sgshapiro
429490792Sgshapiro# if NETINET6
429571345Sgshapiro	if (hp != NULL)
429671345Sgshapiro	{
429771345Sgshapiro		freehostent(hp);
429871345Sgshapiro		hp = NULL;
429971345Sgshapiro	}
430090792Sgshapiro# endif /* NETINET6 */
430171345Sgshapiro
430264562Sgshapiro# if NETUNIX
430364562Sgshapiro	if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
430438032Speter		return "localhost";
430564562Sgshapiro# endif /* NETUNIX */
430638032Speter	{
430738032Speter		static char buf[203];
430838032Speter
430990792Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "[%.200s]",
431090792Sgshapiro				   anynet_ntoa(sap));
431138032Speter		return buf;
431238032Speter	}
431338032Speter}
431464562Sgshapiro#endif /* USE_SOCK_STREAM */
4315