xref: /titanic_54/usr/src/cmd/sendmail/src/deliver.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *	All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
6*7c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
7*7c478bd9Sstevel@tonic-gate  *
8*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
9*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
10*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
11*7c478bd9Sstevel@tonic-gate  *
12*7c478bd9Sstevel@tonic-gate  */
13*7c478bd9Sstevel@tonic-gate 
14*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
15*7c478bd9Sstevel@tonic-gate 
16*7c478bd9Sstevel@tonic-gate #include <sendmail.h>
17*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
18*7c478bd9Sstevel@tonic-gate 
19*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: deliver.c,v 8.986 2005/03/05 02:28:50 ca Exp $")
20*7c478bd9Sstevel@tonic-gate 
21*7c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT
22*7c478bd9Sstevel@tonic-gate # include <login_cap.h>
23*7c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */
24*7c478bd9Sstevel@tonic-gate 
25*7c478bd9Sstevel@tonic-gate #if NETINET || NETINET6
26*7c478bd9Sstevel@tonic-gate # include <arpa/inet.h>
27*7c478bd9Sstevel@tonic-gate #endif /* NETINET || NETINET6 */
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #if STARTTLS || SASL
30*7c478bd9Sstevel@tonic-gate # include "sfsasl.h"
31*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS || SASL */
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate static int	deliver __P((ENVELOPE *, ADDRESS *));
34*7c478bd9Sstevel@tonic-gate static void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
35*7c478bd9Sstevel@tonic-gate static void	mailfiletimeout __P((int));
36*7c478bd9Sstevel@tonic-gate static void	endwaittimeout __P((int));
37*7c478bd9Sstevel@tonic-gate static int	parse_hostsignature __P((char *, char **, MAILER *));
38*7c478bd9Sstevel@tonic-gate static void	sendenvelope __P((ENVELOPE *, int));
39*7c478bd9Sstevel@tonic-gate extern MCI	*mci_new __P((SM_RPOOL_T *));
40*7c478bd9Sstevel@tonic-gate static int	coloncmp __P((const char *, const char *));
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate #if STARTTLS
43*7c478bd9Sstevel@tonic-gate static int	starttls __P((MAILER *, MCI *, ENVELOPE *));
44*7c478bd9Sstevel@tonic-gate static int	endtlsclt __P((MCI *));
45*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
46*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL
47*7c478bd9Sstevel@tonic-gate static bool	iscltflgset __P((ENVELOPE *, int));
48*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate /*
51*7c478bd9Sstevel@tonic-gate **  SENDALL -- actually send all the messages.
52*7c478bd9Sstevel@tonic-gate **
53*7c478bd9Sstevel@tonic-gate **	Parameters:
54*7c478bd9Sstevel@tonic-gate **		e -- the envelope to send.
55*7c478bd9Sstevel@tonic-gate **		mode -- the delivery mode to use.  If SM_DEFAULT, use
56*7c478bd9Sstevel@tonic-gate **			the current e->e_sendmode.
57*7c478bd9Sstevel@tonic-gate **
58*7c478bd9Sstevel@tonic-gate **	Returns:
59*7c478bd9Sstevel@tonic-gate **		none.
60*7c478bd9Sstevel@tonic-gate **
61*7c478bd9Sstevel@tonic-gate **	Side Effects:
62*7c478bd9Sstevel@tonic-gate **		Scans the send lists and sends everything it finds.
63*7c478bd9Sstevel@tonic-gate **		Delivers any appropriate error messages.
64*7c478bd9Sstevel@tonic-gate **		If we are running in a non-interactive mode, takes the
65*7c478bd9Sstevel@tonic-gate **			appropriate action.
66*7c478bd9Sstevel@tonic-gate */
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate void
69*7c478bd9Sstevel@tonic-gate sendall(e, mode)
70*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
71*7c478bd9Sstevel@tonic-gate 	int mode;
72*7c478bd9Sstevel@tonic-gate {
73*7c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
74*7c478bd9Sstevel@tonic-gate 	char *owner;
75*7c478bd9Sstevel@tonic-gate 	int otherowners;
76*7c478bd9Sstevel@tonic-gate 	int save_errno;
77*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *ee;
78*7c478bd9Sstevel@tonic-gate 	ENVELOPE *splitenv = NULL;
79*7c478bd9Sstevel@tonic-gate 	int oldverbose = Verbose;
80*7c478bd9Sstevel@tonic-gate 	bool somedeliveries = false, expensive = false;
81*7c478bd9Sstevel@tonic-gate 	pid_t pid;
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	/*
84*7c478bd9Sstevel@tonic-gate 	**  If this message is to be discarded, don't bother sending
85*7c478bd9Sstevel@tonic-gate 	**  the message at all.
86*7c478bd9Sstevel@tonic-gate 	*/
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_DISCARD, e->e_flags))
89*7c478bd9Sstevel@tonic-gate 	{
90*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 1))
91*7c478bd9Sstevel@tonic-gate 			sm_dprintf("sendall: discarding id %s\n", e->e_id);
92*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE;
93*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 9)
94*7c478bd9Sstevel@tonic-gate 			logundelrcpts(e, "discarded", 9, true);
95*7c478bd9Sstevel@tonic-gate 		else if (LogLevel > 4)
96*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "discarded");
97*7c478bd9Sstevel@tonic-gate 		markstats(e, NULL, STATS_REJECT);
98*7c478bd9Sstevel@tonic-gate 		return;
99*7c478bd9Sstevel@tonic-gate 	}
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 	/*
102*7c478bd9Sstevel@tonic-gate 	**  If we have had global, fatal errors, don't bother sending
103*7c478bd9Sstevel@tonic-gate 	**  the message at all if we are in SMTP mode.  Local errors
104*7c478bd9Sstevel@tonic-gate 	**  (e.g., a single address failing) will still cause the other
105*7c478bd9Sstevel@tonic-gate 	**  addresses to be sent.
106*7c478bd9Sstevel@tonic-gate 	*/
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_FATALERRS, e->e_flags) &&
109*7c478bd9Sstevel@tonic-gate 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
110*7c478bd9Sstevel@tonic-gate 	{
111*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE;
112*7c478bd9Sstevel@tonic-gate 		return;
113*7c478bd9Sstevel@tonic-gate 	}
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	/* determine actual delivery mode */
116*7c478bd9Sstevel@tonic-gate 	if (mode == SM_DEFAULT)
117*7c478bd9Sstevel@tonic-gate 	{
118*7c478bd9Sstevel@tonic-gate 		mode = e->e_sendmode;
119*7c478bd9Sstevel@tonic-gate 		if (mode != SM_VERIFY && mode != SM_DEFER &&
120*7c478bd9Sstevel@tonic-gate 		    shouldqueue(e->e_msgpriority, e->e_ctime))
121*7c478bd9Sstevel@tonic-gate 			mode = SM_QUEUE;
122*7c478bd9Sstevel@tonic-gate 	}
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate 	if (tTd(13, 1))
125*7c478bd9Sstevel@tonic-gate 	{
126*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
127*7c478bd9Sstevel@tonic-gate 			mode, e->e_id);
128*7c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), &e->e_from, false);
129*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\te_flags = ");
130*7c478bd9Sstevel@tonic-gate 		printenvflags(e);
131*7c478bd9Sstevel@tonic-gate 		sm_dprintf("sendqueue:\n");
132*7c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), e->e_sendqueue, true);
133*7c478bd9Sstevel@tonic-gate 	}
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate 	/*
136*7c478bd9Sstevel@tonic-gate 	**  Do any preprocessing necessary for the mode we are running.
137*7c478bd9Sstevel@tonic-gate 	**	Check to make sure the hop count is reasonable.
138*7c478bd9Sstevel@tonic-gate 	**	Delete sends to the sender in mailing lists.
139*7c478bd9Sstevel@tonic-gate 	*/
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 	CurEnv = e;
142*7c478bd9Sstevel@tonic-gate 	if (tTd(62, 1))
143*7c478bd9Sstevel@tonic-gate 		checkfds(NULL);
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 	if (e->e_hopcount > MaxHopCount)
146*7c478bd9Sstevel@tonic-gate 	{
147*7c478bd9Sstevel@tonic-gate 		char *recip;
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 		if (e->e_sendqueue != NULL &&
150*7c478bd9Sstevel@tonic-gate 		    e->e_sendqueue->q_paddr != NULL)
151*7c478bd9Sstevel@tonic-gate 			recip = e->e_sendqueue->q_paddr;
152*7c478bd9Sstevel@tonic-gate 		else
153*7c478bd9Sstevel@tonic-gate 			recip = "(nobody)";
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate 		errno = 0;
156*7c478bd9Sstevel@tonic-gate 		queueup(e, WILL_BE_QUEUED(mode), false);
157*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
158*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_UNAVAILABLE;
159*7c478bd9Sstevel@tonic-gate 		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
160*7c478bd9Sstevel@tonic-gate 		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
161*7c478bd9Sstevel@tonic-gate 		       RealHostName == NULL ? "localhost" : RealHostName,
162*7c478bd9Sstevel@tonic-gate 		       recip);
163*7c478bd9Sstevel@tonic-gate 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
164*7c478bd9Sstevel@tonic-gate 		{
165*7c478bd9Sstevel@tonic-gate 			if (QS_IS_DEAD(q->q_state))
166*7c478bd9Sstevel@tonic-gate 				continue;
167*7c478bd9Sstevel@tonic-gate 			q->q_state = QS_BADADDR;
168*7c478bd9Sstevel@tonic-gate 			q->q_status = "5.4.6";
169*7c478bd9Sstevel@tonic-gate 			q->q_rstatus = "554 5.4.6 Too many hops";
170*7c478bd9Sstevel@tonic-gate 		}
171*7c478bd9Sstevel@tonic-gate 		return;
172*7c478bd9Sstevel@tonic-gate 	}
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate 	/*
175*7c478bd9Sstevel@tonic-gate 	**  Do sender deletion.
176*7c478bd9Sstevel@tonic-gate 	**
177*7c478bd9Sstevel@tonic-gate 	**	If the sender should be queued up, skip this.
178*7c478bd9Sstevel@tonic-gate 	**	This can happen if the name server is hosed when you
179*7c478bd9Sstevel@tonic-gate 	**	are trying to send mail.  The result is that the sender
180*7c478bd9Sstevel@tonic-gate 	**	is instantiated in the queue as a recipient.
181*7c478bd9Sstevel@tonic-gate 	*/
182*7c478bd9Sstevel@tonic-gate 
183*7c478bd9Sstevel@tonic-gate 	if (!bitset(EF_METOO, e->e_flags) &&
184*7c478bd9Sstevel@tonic-gate 	    !QS_IS_QUEUEUP(e->e_from.q_state))
185*7c478bd9Sstevel@tonic-gate 	{
186*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 5))
187*7c478bd9Sstevel@tonic-gate 		{
188*7c478bd9Sstevel@tonic-gate 			sm_dprintf("sendall: QS_SENDER ");
189*7c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), &e->e_from, false);
190*7c478bd9Sstevel@tonic-gate 		}
191*7c478bd9Sstevel@tonic-gate 		e->e_from.q_state = QS_SENDER;
192*7c478bd9Sstevel@tonic-gate 		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
193*7c478bd9Sstevel@tonic-gate 	}
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 	/*
196*7c478bd9Sstevel@tonic-gate 	**  Handle alias owners.
197*7c478bd9Sstevel@tonic-gate 	**
198*7c478bd9Sstevel@tonic-gate 	**	We scan up the q_alias chain looking for owners.
199*7c478bd9Sstevel@tonic-gate 	**	We discard owners that are the same as the return path.
200*7c478bd9Sstevel@tonic-gate 	*/
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
203*7c478bd9Sstevel@tonic-gate 	{
204*7c478bd9Sstevel@tonic-gate 		register struct address *a;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate 		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
207*7c478bd9Sstevel@tonic-gate 			continue;
208*7c478bd9Sstevel@tonic-gate 		if (a != NULL)
209*7c478bd9Sstevel@tonic-gate 			q->q_owner = a->q_owner;
210*7c478bd9Sstevel@tonic-gate 
211*7c478bd9Sstevel@tonic-gate 		if (q->q_owner != NULL &&
212*7c478bd9Sstevel@tonic-gate 		    !QS_IS_DEAD(q->q_state) &&
213*7c478bd9Sstevel@tonic-gate 		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
214*7c478bd9Sstevel@tonic-gate 			q->q_owner = NULL;
215*7c478bd9Sstevel@tonic-gate 	}
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	if (tTd(13, 25))
218*7c478bd9Sstevel@tonic-gate 	{
219*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\nAfter first owner pass, sendq =\n");
220*7c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), e->e_sendqueue, true);
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	owner = "";
224*7c478bd9Sstevel@tonic-gate 	otherowners = 1;
225*7c478bd9Sstevel@tonic-gate 	while (owner != NULL && otherowners > 0)
226*7c478bd9Sstevel@tonic-gate 	{
227*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 28))
228*7c478bd9Sstevel@tonic-gate 			sm_dprintf("owner = \"%s\", otherowners = %d\n",
229*7c478bd9Sstevel@tonic-gate 				   owner, otherowners);
230*7c478bd9Sstevel@tonic-gate 		owner = NULL;
231*7c478bd9Sstevel@tonic-gate 		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
234*7c478bd9Sstevel@tonic-gate 		{
235*7c478bd9Sstevel@tonic-gate 			if (tTd(13, 30))
236*7c478bd9Sstevel@tonic-gate 			{
237*7c478bd9Sstevel@tonic-gate 				sm_dprintf("Checking ");
238*7c478bd9Sstevel@tonic-gate 				printaddr(sm_debug_file(), q, false);
239*7c478bd9Sstevel@tonic-gate 			}
240*7c478bd9Sstevel@tonic-gate 			if (QS_IS_DEAD(q->q_state))
241*7c478bd9Sstevel@tonic-gate 			{
242*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
243*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... QS_IS_DEAD\n");
244*7c478bd9Sstevel@tonic-gate 				continue;
245*7c478bd9Sstevel@tonic-gate 			}
246*7c478bd9Sstevel@tonic-gate 			if (tTd(13, 29) && !tTd(13, 30))
247*7c478bd9Sstevel@tonic-gate 			{
248*7c478bd9Sstevel@tonic-gate 				sm_dprintf("Checking ");
249*7c478bd9Sstevel@tonic-gate 				printaddr(sm_debug_file(), q, false);
250*7c478bd9Sstevel@tonic-gate 			}
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 			if (q->q_owner != NULL)
253*7c478bd9Sstevel@tonic-gate 			{
254*7c478bd9Sstevel@tonic-gate 				if (owner == NULL)
255*7c478bd9Sstevel@tonic-gate 				{
256*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 40))
257*7c478bd9Sstevel@tonic-gate 						sm_dprintf("    ... First owner = \"%s\"\n",
258*7c478bd9Sstevel@tonic-gate 							   q->q_owner);
259*7c478bd9Sstevel@tonic-gate 					owner = q->q_owner;
260*7c478bd9Sstevel@tonic-gate 				}
261*7c478bd9Sstevel@tonic-gate 				else if (owner != q->q_owner)
262*7c478bd9Sstevel@tonic-gate 				{
263*7c478bd9Sstevel@tonic-gate 					if (strcmp(owner, q->q_owner) == 0)
264*7c478bd9Sstevel@tonic-gate 					{
265*7c478bd9Sstevel@tonic-gate 						if (tTd(13, 40))
266*7c478bd9Sstevel@tonic-gate 							sm_dprintf("    ... Same owner = \"%s\"\n",
267*7c478bd9Sstevel@tonic-gate 								   owner);
268*7c478bd9Sstevel@tonic-gate 
269*7c478bd9Sstevel@tonic-gate 						/* make future comparisons cheap */
270*7c478bd9Sstevel@tonic-gate 						q->q_owner = owner;
271*7c478bd9Sstevel@tonic-gate 					}
272*7c478bd9Sstevel@tonic-gate 					else
273*7c478bd9Sstevel@tonic-gate 					{
274*7c478bd9Sstevel@tonic-gate 						if (tTd(13, 40))
275*7c478bd9Sstevel@tonic-gate 							sm_dprintf("    ... Another owner \"%s\"\n",
276*7c478bd9Sstevel@tonic-gate 								   q->q_owner);
277*7c478bd9Sstevel@tonic-gate 						otherowners++;
278*7c478bd9Sstevel@tonic-gate 					}
279*7c478bd9Sstevel@tonic-gate 					owner = q->q_owner;
280*7c478bd9Sstevel@tonic-gate 				}
281*7c478bd9Sstevel@tonic-gate 				else if (tTd(13, 40))
282*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... Same owner = \"%s\"\n",
283*7c478bd9Sstevel@tonic-gate 						   owner);
284*7c478bd9Sstevel@tonic-gate 			}
285*7c478bd9Sstevel@tonic-gate 			else
286*7c478bd9Sstevel@tonic-gate 			{
287*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 40))
288*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... Null owner\n");
289*7c478bd9Sstevel@tonic-gate 				otherowners++;
290*7c478bd9Sstevel@tonic-gate 			}
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate 			if (QS_IS_BADADDR(q->q_state))
293*7c478bd9Sstevel@tonic-gate 			{
294*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
295*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... QS_IS_BADADDR\n");
296*7c478bd9Sstevel@tonic-gate 				continue;
297*7c478bd9Sstevel@tonic-gate 			}
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 			if (QS_IS_QUEUEUP(q->q_state))
300*7c478bd9Sstevel@tonic-gate 			{
301*7c478bd9Sstevel@tonic-gate 				MAILER *m = q->q_mailer;
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 				/*
304*7c478bd9Sstevel@tonic-gate 				**  If we have temporary address failures
305*7c478bd9Sstevel@tonic-gate 				**  (e.g., dns failure) and a fallback MX is
306*7c478bd9Sstevel@tonic-gate 				**  set, send directly to the fallback MX host.
307*7c478bd9Sstevel@tonic-gate 				*/
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 				if (FallbackMX != NULL &&
310*7c478bd9Sstevel@tonic-gate 				    !wordinclass(FallbackMX, 'w') &&
311*7c478bd9Sstevel@tonic-gate 				    mode != SM_VERIFY &&
312*7c478bd9Sstevel@tonic-gate 				    !bitnset(M_NOMX, m->m_flags) &&
313*7c478bd9Sstevel@tonic-gate 				    strcmp(m->m_mailer, "[IPC]") == 0 &&
314*7c478bd9Sstevel@tonic-gate 				    m->m_argv[0] != NULL &&
315*7c478bd9Sstevel@tonic-gate 				    strcmp(m->m_argv[0], "TCP") == 0)
316*7c478bd9Sstevel@tonic-gate 				{
317*7c478bd9Sstevel@tonic-gate 					int len;
318*7c478bd9Sstevel@tonic-gate 					char *p;
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 30))
321*7c478bd9Sstevel@tonic-gate 						sm_dprintf("    ... FallbackMX\n");
322*7c478bd9Sstevel@tonic-gate 
323*7c478bd9Sstevel@tonic-gate 					len = strlen(FallbackMX) + 1;
324*7c478bd9Sstevel@tonic-gate 					p = sm_rpool_malloc_x(e->e_rpool, len);
325*7c478bd9Sstevel@tonic-gate 					(void) sm_strlcpy(p, FallbackMX, len);
326*7c478bd9Sstevel@tonic-gate 					q->q_state = QS_OK;
327*7c478bd9Sstevel@tonic-gate 					q->q_host = p;
328*7c478bd9Sstevel@tonic-gate 				}
329*7c478bd9Sstevel@tonic-gate 				else
330*7c478bd9Sstevel@tonic-gate 				{
331*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 30))
332*7c478bd9Sstevel@tonic-gate 						sm_dprintf("    ... QS_IS_QUEUEUP\n");
333*7c478bd9Sstevel@tonic-gate 					continue;
334*7c478bd9Sstevel@tonic-gate 				}
335*7c478bd9Sstevel@tonic-gate 			}
336*7c478bd9Sstevel@tonic-gate 
337*7c478bd9Sstevel@tonic-gate 			/*
338*7c478bd9Sstevel@tonic-gate 			**  If this mailer is expensive, and if we don't
339*7c478bd9Sstevel@tonic-gate 			**  want to make connections now, just mark these
340*7c478bd9Sstevel@tonic-gate 			**  addresses and return.  This is useful if we
341*7c478bd9Sstevel@tonic-gate 			**  want to batch connections to reduce load.  This
342*7c478bd9Sstevel@tonic-gate 			**  will cause the messages to be queued up, and a
343*7c478bd9Sstevel@tonic-gate 			**  daemon will come along to send the messages later.
344*7c478bd9Sstevel@tonic-gate 			*/
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate 			if (NoConnect && !Verbose &&
347*7c478bd9Sstevel@tonic-gate 			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
348*7c478bd9Sstevel@tonic-gate 			{
349*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
350*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... expensive\n");
351*7c478bd9Sstevel@tonic-gate 				q->q_state = QS_QUEUEUP;
352*7c478bd9Sstevel@tonic-gate 				expensive = true;
353*7c478bd9Sstevel@tonic-gate 			}
354*7c478bd9Sstevel@tonic-gate 			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
355*7c478bd9Sstevel@tonic-gate 				 QueueLimitId == NULL &&
356*7c478bd9Sstevel@tonic-gate 				 QueueLimitSender == NULL &&
357*7c478bd9Sstevel@tonic-gate 				 QueueLimitRecipient == NULL)
358*7c478bd9Sstevel@tonic-gate 			{
359*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
360*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... hold\n");
361*7c478bd9Sstevel@tonic-gate 				q->q_state = QS_QUEUEUP;
362*7c478bd9Sstevel@tonic-gate 				expensive = true;
363*7c478bd9Sstevel@tonic-gate 			}
364*7c478bd9Sstevel@tonic-gate 			else if (QueueMode != QM_QUARANTINE &&
365*7c478bd9Sstevel@tonic-gate 				 e->e_quarmsg != NULL)
366*7c478bd9Sstevel@tonic-gate 			{
367*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
368*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... quarantine: %s\n",
369*7c478bd9Sstevel@tonic-gate 						   e->e_quarmsg);
370*7c478bd9Sstevel@tonic-gate 				q->q_state = QS_QUEUEUP;
371*7c478bd9Sstevel@tonic-gate 				expensive = true;
372*7c478bd9Sstevel@tonic-gate 			}
373*7c478bd9Sstevel@tonic-gate 			else
374*7c478bd9Sstevel@tonic-gate 			{
375*7c478bd9Sstevel@tonic-gate 				if (tTd(13, 30))
376*7c478bd9Sstevel@tonic-gate 					sm_dprintf("    ... deliverable\n");
377*7c478bd9Sstevel@tonic-gate 				somedeliveries = true;
378*7c478bd9Sstevel@tonic-gate 			}
379*7c478bd9Sstevel@tonic-gate 		}
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 		if (owner != NULL && otherowners > 0)
382*7c478bd9Sstevel@tonic-gate 		{
383*7c478bd9Sstevel@tonic-gate 			/*
384*7c478bd9Sstevel@tonic-gate 			**  Split this envelope into two.
385*7c478bd9Sstevel@tonic-gate 			*/
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
388*7c478bd9Sstevel@tonic-gate 							    sizeof *ee);
389*7c478bd9Sstevel@tonic-gate 			STRUCTCOPY(*e, *ee);
390*7c478bd9Sstevel@tonic-gate 			ee->e_message = NULL;
391*7c478bd9Sstevel@tonic-gate 			ee->e_id = NULL;
392*7c478bd9Sstevel@tonic-gate 			assign_queueid(ee);
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 			if (tTd(13, 1))
395*7c478bd9Sstevel@tonic-gate 				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
396*7c478bd9Sstevel@tonic-gate 					   e->e_id, ee->e_id, owner,
397*7c478bd9Sstevel@tonic-gate 					   otherowners);
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 			ee->e_header = copyheader(e->e_header, ee->e_rpool);
400*7c478bd9Sstevel@tonic-gate 			ee->e_sendqueue = copyqueue(e->e_sendqueue,
401*7c478bd9Sstevel@tonic-gate 						    ee->e_rpool);
402*7c478bd9Sstevel@tonic-gate 			ee->e_errorqueue = copyqueue(e->e_errorqueue,
403*7c478bd9Sstevel@tonic-gate 						     ee->e_rpool);
404*7c478bd9Sstevel@tonic-gate 			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
405*7c478bd9Sstevel@tonic-gate 			ee->e_flags |= EF_NORECEIPT;
406*7c478bd9Sstevel@tonic-gate 			setsender(owner, ee, NULL, '\0', true);
407*7c478bd9Sstevel@tonic-gate 			if (tTd(13, 5))
408*7c478bd9Sstevel@tonic-gate 			{
409*7c478bd9Sstevel@tonic-gate 				sm_dprintf("sendall(split): QS_SENDER ");
410*7c478bd9Sstevel@tonic-gate 				printaddr(sm_debug_file(), &ee->e_from, false);
411*7c478bd9Sstevel@tonic-gate 			}
412*7c478bd9Sstevel@tonic-gate 			ee->e_from.q_state = QS_SENDER;
413*7c478bd9Sstevel@tonic-gate 			ee->e_dfp = NULL;
414*7c478bd9Sstevel@tonic-gate 			ee->e_lockfp = NULL;
415*7c478bd9Sstevel@tonic-gate 			ee->e_xfp = NULL;
416*7c478bd9Sstevel@tonic-gate 			ee->e_qgrp = e->e_qgrp;
417*7c478bd9Sstevel@tonic-gate 			ee->e_qdir = e->e_qdir;
418*7c478bd9Sstevel@tonic-gate 			ee->e_errormode = EM_MAIL;
419*7c478bd9Sstevel@tonic-gate 			ee->e_sibling = splitenv;
420*7c478bd9Sstevel@tonic-gate 			ee->e_statmsg = NULL;
421*7c478bd9Sstevel@tonic-gate 			if (e->e_quarmsg != NULL)
422*7c478bd9Sstevel@tonic-gate 				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
423*7c478bd9Sstevel@tonic-gate 								  e->e_quarmsg);
424*7c478bd9Sstevel@tonic-gate 			splitenv = ee;
425*7c478bd9Sstevel@tonic-gate 
426*7c478bd9Sstevel@tonic-gate 			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
427*7c478bd9Sstevel@tonic-gate 			{
428*7c478bd9Sstevel@tonic-gate 				if (q->q_owner == owner)
429*7c478bd9Sstevel@tonic-gate 				{
430*7c478bd9Sstevel@tonic-gate 					q->q_state = QS_CLONED;
431*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 6))
432*7c478bd9Sstevel@tonic-gate 						sm_dprintf("\t... stripping %s from original envelope\n",
433*7c478bd9Sstevel@tonic-gate 							   q->q_paddr);
434*7c478bd9Sstevel@tonic-gate 				}
435*7c478bd9Sstevel@tonic-gate 			}
436*7c478bd9Sstevel@tonic-gate 			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
437*7c478bd9Sstevel@tonic-gate 			{
438*7c478bd9Sstevel@tonic-gate 				if (q->q_owner != owner)
439*7c478bd9Sstevel@tonic-gate 				{
440*7c478bd9Sstevel@tonic-gate 					q->q_state = QS_CLONED;
441*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 6))
442*7c478bd9Sstevel@tonic-gate 						sm_dprintf("\t... dropping %s from cloned envelope\n",
443*7c478bd9Sstevel@tonic-gate 							   q->q_paddr);
444*7c478bd9Sstevel@tonic-gate 				}
445*7c478bd9Sstevel@tonic-gate 				else
446*7c478bd9Sstevel@tonic-gate 				{
447*7c478bd9Sstevel@tonic-gate 					/* clear DSN parameters */
448*7c478bd9Sstevel@tonic-gate 					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
449*7c478bd9Sstevel@tonic-gate 					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
450*7c478bd9Sstevel@tonic-gate 					if (tTd(13, 6))
451*7c478bd9Sstevel@tonic-gate 						sm_dprintf("\t... moving %s to cloned envelope\n",
452*7c478bd9Sstevel@tonic-gate 							   q->q_paddr);
453*7c478bd9Sstevel@tonic-gate 				}
454*7c478bd9Sstevel@tonic-gate 			}
455*7c478bd9Sstevel@tonic-gate 
456*7c478bd9Sstevel@tonic-gate 			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
457*7c478bd9Sstevel@tonic-gate 				dup_queue_file(e, ee, DATAFL_LETTER);
458*7c478bd9Sstevel@tonic-gate 
459*7c478bd9Sstevel@tonic-gate 			/*
460*7c478bd9Sstevel@tonic-gate 			**  Give the split envelope access to the parent
461*7c478bd9Sstevel@tonic-gate 			**  transcript file for errors obtained while
462*7c478bd9Sstevel@tonic-gate 			**  processing the recipients (done before the
463*7c478bd9Sstevel@tonic-gate 			**  envelope splitting).
464*7c478bd9Sstevel@tonic-gate 			*/
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 			if (e->e_xfp != NULL)
467*7c478bd9Sstevel@tonic-gate 				ee->e_xfp = sm_io_dup(e->e_xfp);
468*7c478bd9Sstevel@tonic-gate 
469*7c478bd9Sstevel@tonic-gate 			/* failed to dup e->e_xfp, start a new transcript */
470*7c478bd9Sstevel@tonic-gate 			if (ee->e_xfp == NULL)
471*7c478bd9Sstevel@tonic-gate 				openxscript(ee);
472*7c478bd9Sstevel@tonic-gate 
473*7c478bd9Sstevel@tonic-gate 			if (mode != SM_VERIFY && LogLevel > 4)
474*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
475*7c478bd9Sstevel@tonic-gate 					  "%s: clone: owner=%s",
476*7c478bd9Sstevel@tonic-gate 					  ee->e_id, owner);
477*7c478bd9Sstevel@tonic-gate 		}
478*7c478bd9Sstevel@tonic-gate 	}
479*7c478bd9Sstevel@tonic-gate 
480*7c478bd9Sstevel@tonic-gate 	if (owner != NULL)
481*7c478bd9Sstevel@tonic-gate 	{
482*7c478bd9Sstevel@tonic-gate 		setsender(owner, e, NULL, '\0', true);
483*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 5))
484*7c478bd9Sstevel@tonic-gate 		{
485*7c478bd9Sstevel@tonic-gate 			sm_dprintf("sendall(owner): QS_SENDER ");
486*7c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), &e->e_from, false);
487*7c478bd9Sstevel@tonic-gate 		}
488*7c478bd9Sstevel@tonic-gate 		e->e_from.q_state = QS_SENDER;
489*7c478bd9Sstevel@tonic-gate 		e->e_errormode = EM_MAIL;
490*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_NORECEIPT;
491*7c478bd9Sstevel@tonic-gate 		e->e_flags &= ~EF_FATALERRS;
492*7c478bd9Sstevel@tonic-gate 	}
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 	/* if nothing to be delivered, just queue up everything */
495*7c478bd9Sstevel@tonic-gate 	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
496*7c478bd9Sstevel@tonic-gate 	    mode != SM_VERIFY)
497*7c478bd9Sstevel@tonic-gate 	{
498*7c478bd9Sstevel@tonic-gate 		time_t now;
499*7c478bd9Sstevel@tonic-gate 
500*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 29))
501*7c478bd9Sstevel@tonic-gate 			sm_dprintf("No deliveries: auto-queuing\n");
502*7c478bd9Sstevel@tonic-gate 		mode = SM_QUEUE;
503*7c478bd9Sstevel@tonic-gate 		now = curtime();
504*7c478bd9Sstevel@tonic-gate 
505*7c478bd9Sstevel@tonic-gate 		/* treat this as a delivery in terms of counting tries */
506*7c478bd9Sstevel@tonic-gate 		e->e_dtime = now;
507*7c478bd9Sstevel@tonic-gate 		if (!expensive)
508*7c478bd9Sstevel@tonic-gate 			e->e_ntries++;
509*7c478bd9Sstevel@tonic-gate 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
510*7c478bd9Sstevel@tonic-gate 		{
511*7c478bd9Sstevel@tonic-gate 			ee->e_dtime = now;
512*7c478bd9Sstevel@tonic-gate 			if (!expensive)
513*7c478bd9Sstevel@tonic-gate 				ee->e_ntries++;
514*7c478bd9Sstevel@tonic-gate 		}
515*7c478bd9Sstevel@tonic-gate 	}
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate 	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
518*7c478bd9Sstevel@tonic-gate 	     (mode != SM_VERIFY &&
519*7c478bd9Sstevel@tonic-gate 	      (SuperSafe == SAFE_REALLY ||
520*7c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_REALLY_POSTMILTER))) &&
521*7c478bd9Sstevel@tonic-gate 	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
522*7c478bd9Sstevel@tonic-gate 	{
523*7c478bd9Sstevel@tonic-gate 		bool msync;
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 		/*
526*7c478bd9Sstevel@tonic-gate 		**  Be sure everything is instantiated in the queue.
527*7c478bd9Sstevel@tonic-gate 		**  Split envelopes first in case the machine crashes.
528*7c478bd9Sstevel@tonic-gate 		**  If the original were done first, we may lose
529*7c478bd9Sstevel@tonic-gate 		**  recipients.
530*7c478bd9Sstevel@tonic-gate 		*/
531*7c478bd9Sstevel@tonic-gate 
532*7c478bd9Sstevel@tonic-gate #if !HASFLOCK
533*7c478bd9Sstevel@tonic-gate 		msync = false;
534*7c478bd9Sstevel@tonic-gate #else /* !HASFLOCK */
535*7c478bd9Sstevel@tonic-gate 		msync = mode == SM_FORK;
536*7c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */
537*7c478bd9Sstevel@tonic-gate 
538*7c478bd9Sstevel@tonic-gate 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
539*7c478bd9Sstevel@tonic-gate 			queueup(ee, WILL_BE_QUEUED(mode), msync);
540*7c478bd9Sstevel@tonic-gate 		queueup(e, WILL_BE_QUEUED(mode), msync);
541*7c478bd9Sstevel@tonic-gate 	}
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 	if (tTd(62, 10))
544*7c478bd9Sstevel@tonic-gate 		checkfds("after envelope splitting");
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 	/*
547*7c478bd9Sstevel@tonic-gate 	**  If we belong in background, fork now.
548*7c478bd9Sstevel@tonic-gate 	*/
549*7c478bd9Sstevel@tonic-gate 
550*7c478bd9Sstevel@tonic-gate 	if (tTd(13, 20))
551*7c478bd9Sstevel@tonic-gate 	{
552*7c478bd9Sstevel@tonic-gate 		sm_dprintf("sendall: final mode = %c\n", mode);
553*7c478bd9Sstevel@tonic-gate 		if (tTd(13, 21))
554*7c478bd9Sstevel@tonic-gate 		{
555*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
556*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
557*7c478bd9Sstevel@tonic-gate 				   e->e_id, e->e_from.q_paddr);
558*7c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), e->e_sendqueue, true);
559*7c478bd9Sstevel@tonic-gate 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
560*7c478bd9Sstevel@tonic-gate 			{
561*7c478bd9Sstevel@tonic-gate 				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
562*7c478bd9Sstevel@tonic-gate 					   ee->e_id, ee->e_from.q_paddr);
563*7c478bd9Sstevel@tonic-gate 				printaddr(sm_debug_file(), ee->e_sendqueue, true);
564*7c478bd9Sstevel@tonic-gate 			}
565*7c478bd9Sstevel@tonic-gate 			sm_dprintf("==========================================================\n\n");
566*7c478bd9Sstevel@tonic-gate 		}
567*7c478bd9Sstevel@tonic-gate 	}
568*7c478bd9Sstevel@tonic-gate 	switch (mode)
569*7c478bd9Sstevel@tonic-gate 	{
570*7c478bd9Sstevel@tonic-gate 	  case SM_VERIFY:
571*7c478bd9Sstevel@tonic-gate 		Verbose = 2;
572*7c478bd9Sstevel@tonic-gate 		break;
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate 	  case SM_QUEUE:
575*7c478bd9Sstevel@tonic-gate 	  case SM_DEFER:
576*7c478bd9Sstevel@tonic-gate #if HASFLOCK
577*7c478bd9Sstevel@tonic-gate   queueonly:
578*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */
579*7c478bd9Sstevel@tonic-gate 		if (e->e_nrcpts > 0)
580*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_INQUEUE;
581*7c478bd9Sstevel@tonic-gate 		dropenvelope(e, splitenv != NULL, true);
582*7c478bd9Sstevel@tonic-gate 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
583*7c478bd9Sstevel@tonic-gate 		{
584*7c478bd9Sstevel@tonic-gate 			if (ee->e_nrcpts > 0)
585*7c478bd9Sstevel@tonic-gate 				ee->e_flags |= EF_INQUEUE;
586*7c478bd9Sstevel@tonic-gate 			dropenvelope(ee, false, true);
587*7c478bd9Sstevel@tonic-gate 		}
588*7c478bd9Sstevel@tonic-gate 		return;
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate 	  case SM_FORK:
591*7c478bd9Sstevel@tonic-gate 		if (e->e_xfp != NULL)
592*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
593*7c478bd9Sstevel@tonic-gate 
594*7c478bd9Sstevel@tonic-gate #if !HASFLOCK
595*7c478bd9Sstevel@tonic-gate 		/*
596*7c478bd9Sstevel@tonic-gate 		**  Since fcntl locking has the interesting semantic that
597*7c478bd9Sstevel@tonic-gate 		**  the lock is owned by a process, not by an open file
598*7c478bd9Sstevel@tonic-gate 		**  descriptor, we have to flush this to the queue, and
599*7c478bd9Sstevel@tonic-gate 		**  then restart from scratch in the child.
600*7c478bd9Sstevel@tonic-gate 		*/
601*7c478bd9Sstevel@tonic-gate 
602*7c478bd9Sstevel@tonic-gate 		{
603*7c478bd9Sstevel@tonic-gate 			/* save id for future use */
604*7c478bd9Sstevel@tonic-gate 			char *qid = e->e_id;
605*7c478bd9Sstevel@tonic-gate 
606*7c478bd9Sstevel@tonic-gate 			/* now drop the envelope in the parent */
607*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_INQUEUE;
608*7c478bd9Sstevel@tonic-gate 			dropenvelope(e, splitenv != NULL, false);
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 			/* arrange to reacquire lock after fork */
611*7c478bd9Sstevel@tonic-gate 			e->e_id = qid;
612*7c478bd9Sstevel@tonic-gate 		}
613*7c478bd9Sstevel@tonic-gate 
614*7c478bd9Sstevel@tonic-gate 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
615*7c478bd9Sstevel@tonic-gate 		{
616*7c478bd9Sstevel@tonic-gate 			/* save id for future use */
617*7c478bd9Sstevel@tonic-gate 			char *qid = ee->e_id;
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate 			/* drop envelope in parent */
620*7c478bd9Sstevel@tonic-gate 			ee->e_flags |= EF_INQUEUE;
621*7c478bd9Sstevel@tonic-gate 			dropenvelope(ee, false, false);
622*7c478bd9Sstevel@tonic-gate 
623*7c478bd9Sstevel@tonic-gate 			/* and save qid for reacquisition */
624*7c478bd9Sstevel@tonic-gate 			ee->e_id = qid;
625*7c478bd9Sstevel@tonic-gate 		}
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate 		/*
630*7c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the parent
631*7c478bd9Sstevel@tonic-gate 		**  does not wait, the parent may close the maps thereby
632*7c478bd9Sstevel@tonic-gate 		**  removing any shared memory used by the map.  Therefore,
633*7c478bd9Sstevel@tonic-gate 		**  close the maps now so the child will dynamically open
634*7c478bd9Sstevel@tonic-gate 		**  them if necessary.
635*7c478bd9Sstevel@tonic-gate 		*/
636*7c478bd9Sstevel@tonic-gate 
637*7c478bd9Sstevel@tonic-gate 		closemaps(false);
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 		pid = fork();
640*7c478bd9Sstevel@tonic-gate 		if (pid < 0)
641*7c478bd9Sstevel@tonic-gate 		{
642*7c478bd9Sstevel@tonic-gate 			syserr("deliver: fork 1");
643*7c478bd9Sstevel@tonic-gate #if HASFLOCK
644*7c478bd9Sstevel@tonic-gate 			goto queueonly;
645*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */
646*7c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
647*7c478bd9Sstevel@tonic-gate 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
648*7c478bd9Sstevel@tonic-gate 				ee->e_id = NULL;
649*7c478bd9Sstevel@tonic-gate 			return;
650*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */
651*7c478bd9Sstevel@tonic-gate 		}
652*7c478bd9Sstevel@tonic-gate 		else if (pid > 0)
653*7c478bd9Sstevel@tonic-gate 		{
654*7c478bd9Sstevel@tonic-gate #if HASFLOCK
655*7c478bd9Sstevel@tonic-gate 			/* be sure we leave the temp files to our child */
656*7c478bd9Sstevel@tonic-gate 			/* close any random open files in the envelope */
657*7c478bd9Sstevel@tonic-gate 			closexscript(e);
658*7c478bd9Sstevel@tonic-gate 			if (e->e_dfp != NULL)
659*7c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
660*7c478bd9Sstevel@tonic-gate 			e->e_dfp = NULL;
661*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_HAS_DF;
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 			/* can't call unlockqueue to avoid unlink of xfp */
664*7c478bd9Sstevel@tonic-gate 			if (e->e_lockfp != NULL)
665*7c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
666*7c478bd9Sstevel@tonic-gate 			else
667*7c478bd9Sstevel@tonic-gate 				syserr("%s: sendall: null lockfp", e->e_id);
668*7c478bd9Sstevel@tonic-gate 			e->e_lockfp = NULL;
669*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */
670*7c478bd9Sstevel@tonic-gate 
671*7c478bd9Sstevel@tonic-gate 			/* make sure the parent doesn't own the envelope */
672*7c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK
675*7c478bd9Sstevel@tonic-gate 			/* catch intermediate zombie */
676*7c478bd9Sstevel@tonic-gate 			(void) waitfor(pid);
677*7c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */
678*7c478bd9Sstevel@tonic-gate 			return;
679*7c478bd9Sstevel@tonic-gate 		}
680*7c478bd9Sstevel@tonic-gate 
681*7c478bd9Sstevel@tonic-gate 		/* Reset global flags */
682*7c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
683*7c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
684*7c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
685*7c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
686*7c478bd9Sstevel@tonic-gate 
687*7c478bd9Sstevel@tonic-gate 		/*
688*7c478bd9Sstevel@tonic-gate 		**  Initialize exception stack and default exception
689*7c478bd9Sstevel@tonic-gate 		**  handler for child process.
690*7c478bd9Sstevel@tonic-gate 		*/
691*7c478bd9Sstevel@tonic-gate 
692*7c478bd9Sstevel@tonic-gate 		sm_exc_newthread(fatal_error);
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate 		/*
695*7c478bd9Sstevel@tonic-gate 		**  Since we have accepted responsbility for the message,
696*7c478bd9Sstevel@tonic-gate 		**  change the SIGTERM handler.  intsig() (the old handler)
697*7c478bd9Sstevel@tonic-gate 		**  would remove the envelope if this was a command line
698*7c478bd9Sstevel@tonic-gate 		**  message submission.
699*7c478bd9Sstevel@tonic-gate 		*/
700*7c478bd9Sstevel@tonic-gate 
701*7c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGTERM, SIG_DFL);
702*7c478bd9Sstevel@tonic-gate 
703*7c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK
704*7c478bd9Sstevel@tonic-gate 		/* double fork to avoid zombies */
705*7c478bd9Sstevel@tonic-gate 		pid = fork();
706*7c478bd9Sstevel@tonic-gate 		if (pid > 0)
707*7c478bd9Sstevel@tonic-gate 			exit(EX_OK);
708*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
709*7c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 		CurrentPid = getpid();
712*7c478bd9Sstevel@tonic-gate 
713*7c478bd9Sstevel@tonic-gate 		/* be sure we are immune from the terminal */
714*7c478bd9Sstevel@tonic-gate 		disconnect(2, e);
715*7c478bd9Sstevel@tonic-gate 		clearstats();
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 		/* prevent parent from waiting if there was an error */
718*7c478bd9Sstevel@tonic-gate 		if (pid < 0)
719*7c478bd9Sstevel@tonic-gate 		{
720*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
721*7c478bd9Sstevel@tonic-gate 			syserr("deliver: fork 2");
722*7c478bd9Sstevel@tonic-gate #if HASFLOCK
723*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_INQUEUE;
724*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */
725*7c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
726*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */
727*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
728*7c478bd9Sstevel@tonic-gate 		}
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 		/* be sure to give error messages in child */
731*7c478bd9Sstevel@tonic-gate 		QuickAbort = false;
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 		/*
734*7c478bd9Sstevel@tonic-gate 		**  Close any cached connections.
735*7c478bd9Sstevel@tonic-gate 		**
736*7c478bd9Sstevel@tonic-gate 		**	We don't send the QUIT protocol because the parent
737*7c478bd9Sstevel@tonic-gate 		**	still knows about the connection.
738*7c478bd9Sstevel@tonic-gate 		**
739*7c478bd9Sstevel@tonic-gate 		**	This should only happen when delivering an error
740*7c478bd9Sstevel@tonic-gate 		**	message.
741*7c478bd9Sstevel@tonic-gate 		*/
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate 		mci_flush(false, NULL);
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate #if HASFLOCK
746*7c478bd9Sstevel@tonic-gate 		break;
747*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */
748*7c478bd9Sstevel@tonic-gate 
749*7c478bd9Sstevel@tonic-gate 		/*
750*7c478bd9Sstevel@tonic-gate 		**  Now reacquire and run the various queue files.
751*7c478bd9Sstevel@tonic-gate 		*/
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
754*7c478bd9Sstevel@tonic-gate 		{
755*7c478bd9Sstevel@tonic-gate 			ENVELOPE *sibling = ee->e_sibling;
756*7c478bd9Sstevel@tonic-gate 
757*7c478bd9Sstevel@tonic-gate 			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
758*7c478bd9Sstevel@tonic-gate 				      false, false, ee);
759*7c478bd9Sstevel@tonic-gate 			ee->e_sibling = sibling;
760*7c478bd9Sstevel@tonic-gate 		}
761*7c478bd9Sstevel@tonic-gate 		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
762*7c478bd9Sstevel@tonic-gate 			      false, false, e);
763*7c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
764*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */
765*7c478bd9Sstevel@tonic-gate 	}
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate 	sendenvelope(e, mode);
768*7c478bd9Sstevel@tonic-gate 	dropenvelope(e, true, true);
769*7c478bd9Sstevel@tonic-gate 	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
770*7c478bd9Sstevel@tonic-gate 	{
771*7c478bd9Sstevel@tonic-gate 		CurEnv = ee;
772*7c478bd9Sstevel@tonic-gate 		if (mode != SM_VERIFY)
773*7c478bd9Sstevel@tonic-gate 			openxscript(ee);
774*7c478bd9Sstevel@tonic-gate 		sendenvelope(ee, mode);
775*7c478bd9Sstevel@tonic-gate 		dropenvelope(ee, true, true);
776*7c478bd9Sstevel@tonic-gate 	}
777*7c478bd9Sstevel@tonic-gate 	CurEnv = e;
778*7c478bd9Sstevel@tonic-gate 
779*7c478bd9Sstevel@tonic-gate 	Verbose = oldverbose;
780*7c478bd9Sstevel@tonic-gate 	if (mode == SM_FORK)
781*7c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
782*7c478bd9Sstevel@tonic-gate }
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate static void
785*7c478bd9Sstevel@tonic-gate sendenvelope(e, mode)
786*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
787*7c478bd9Sstevel@tonic-gate 	int mode;
788*7c478bd9Sstevel@tonic-gate {
789*7c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
790*7c478bd9Sstevel@tonic-gate 	bool didany;
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 	if (tTd(13, 10))
793*7c478bd9Sstevel@tonic-gate 		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
794*7c478bd9Sstevel@tonic-gate 			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
795*7c478bd9Sstevel@tonic-gate 			   e->e_flags);
796*7c478bd9Sstevel@tonic-gate 	if (LogLevel > 80)
797*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id,
798*7c478bd9Sstevel@tonic-gate 			  "sendenvelope, flags=0x%lx",
799*7c478bd9Sstevel@tonic-gate 			  e->e_flags);
800*7c478bd9Sstevel@tonic-gate 
801*7c478bd9Sstevel@tonic-gate 	/*
802*7c478bd9Sstevel@tonic-gate 	**  If we have had global, fatal errors, don't bother sending
803*7c478bd9Sstevel@tonic-gate 	**  the message at all if we are in SMTP mode.  Local errors
804*7c478bd9Sstevel@tonic-gate 	**  (e.g., a single address failing) will still cause the other
805*7c478bd9Sstevel@tonic-gate 	**  addresses to be sent.
806*7c478bd9Sstevel@tonic-gate 	*/
807*7c478bd9Sstevel@tonic-gate 
808*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_FATALERRS, e->e_flags) &&
809*7c478bd9Sstevel@tonic-gate 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
810*7c478bd9Sstevel@tonic-gate 	{
811*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE;
812*7c478bd9Sstevel@tonic-gate 		return;
813*7c478bd9Sstevel@tonic-gate 	}
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate 	/*
816*7c478bd9Sstevel@tonic-gate 	**  Don't attempt deliveries if we want to bounce now
817*7c478bd9Sstevel@tonic-gate 	**  or if deliver-by time is exceeded.
818*7c478bd9Sstevel@tonic-gate 	*/
819*7c478bd9Sstevel@tonic-gate 
820*7c478bd9Sstevel@tonic-gate 	if (!bitset(EF_RESPONSE, e->e_flags) &&
821*7c478bd9Sstevel@tonic-gate 	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
822*7c478bd9Sstevel@tonic-gate 	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
823*7c478bd9Sstevel@tonic-gate 	      curtime() > e->e_ctime + e->e_deliver_by)))
824*7c478bd9Sstevel@tonic-gate 		return;
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 	/*
827*7c478bd9Sstevel@tonic-gate 	**  Run through the list and send everything.
828*7c478bd9Sstevel@tonic-gate 	**
829*7c478bd9Sstevel@tonic-gate 	**	Set EF_GLOBALERRS so that error messages during delivery
830*7c478bd9Sstevel@tonic-gate 	**	result in returned mail.
831*7c478bd9Sstevel@tonic-gate 	*/
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate 	e->e_nsent = 0;
834*7c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_GLOBALERRS;
835*7c478bd9Sstevel@tonic-gate 
836*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
837*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
838*7c478bd9Sstevel@tonic-gate 	didany = false;
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate 	if (!bitset(EF_SPLIT, e->e_flags))
841*7c478bd9Sstevel@tonic-gate 	{
842*7c478bd9Sstevel@tonic-gate 		ENVELOPE *oldsib;
843*7c478bd9Sstevel@tonic-gate 		ENVELOPE *ee;
844*7c478bd9Sstevel@tonic-gate 
845*7c478bd9Sstevel@tonic-gate 		/*
846*7c478bd9Sstevel@tonic-gate 		**  Save old sibling and set it to NULL to avoid
847*7c478bd9Sstevel@tonic-gate 		**  queueing up the same envelopes again.
848*7c478bd9Sstevel@tonic-gate 		**  This requires that envelopes in that list have
849*7c478bd9Sstevel@tonic-gate 		**  been take care of before (or at some other place).
850*7c478bd9Sstevel@tonic-gate 		*/
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate 		oldsib = e->e_sibling;
853*7c478bd9Sstevel@tonic-gate 		e->e_sibling = NULL;
854*7c478bd9Sstevel@tonic-gate 		if (!split_by_recipient(e) &&
855*7c478bd9Sstevel@tonic-gate 		    bitset(EF_FATALERRS, e->e_flags))
856*7c478bd9Sstevel@tonic-gate 		{
857*7c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
858*7c478bd9Sstevel@tonic-gate 				e->e_flags |= EF_CLRQUEUE;
859*7c478bd9Sstevel@tonic-gate 			return;
860*7c478bd9Sstevel@tonic-gate 		}
861*7c478bd9Sstevel@tonic-gate 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
862*7c478bd9Sstevel@tonic-gate 			queueup(ee, false, true);
863*7c478bd9Sstevel@tonic-gate 
864*7c478bd9Sstevel@tonic-gate 		/* clean up */
865*7c478bd9Sstevel@tonic-gate 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
866*7c478bd9Sstevel@tonic-gate 		{
867*7c478bd9Sstevel@tonic-gate 			/* now unlock the job */
868*7c478bd9Sstevel@tonic-gate 			closexscript(ee);
869*7c478bd9Sstevel@tonic-gate 			unlockqueue(ee);
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate 			/* this envelope is marked unused */
872*7c478bd9Sstevel@tonic-gate 			if (ee->e_dfp != NULL)
873*7c478bd9Sstevel@tonic-gate 			{
874*7c478bd9Sstevel@tonic-gate 				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
875*7c478bd9Sstevel@tonic-gate 				ee->e_dfp = NULL;
876*7c478bd9Sstevel@tonic-gate 			}
877*7c478bd9Sstevel@tonic-gate 			ee->e_id = NULL;
878*7c478bd9Sstevel@tonic-gate 			ee->e_flags &= ~EF_HAS_DF;
879*7c478bd9Sstevel@tonic-gate 		}
880*7c478bd9Sstevel@tonic-gate 		e->e_sibling = oldsib;
881*7c478bd9Sstevel@tonic-gate 	}
882*7c478bd9Sstevel@tonic-gate 
883*7c478bd9Sstevel@tonic-gate 	/* now run through the queue */
884*7c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
885*7c478bd9Sstevel@tonic-gate 	{
886*7c478bd9Sstevel@tonic-gate #if XDEBUG
887*7c478bd9Sstevel@tonic-gate 		char wbuf[MAXNAME + 20];
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
890*7c478bd9Sstevel@tonic-gate 				   MAXNAME, q->q_paddr);
891*7c478bd9Sstevel@tonic-gate 		checkfd012(wbuf);
892*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
893*7c478bd9Sstevel@tonic-gate 		if (mode == SM_VERIFY)
894*7c478bd9Sstevel@tonic-gate 		{
895*7c478bd9Sstevel@tonic-gate 			e->e_to = q->q_paddr;
896*7c478bd9Sstevel@tonic-gate 			if (QS_IS_SENDABLE(q->q_state))
897*7c478bd9Sstevel@tonic-gate 			{
898*7c478bd9Sstevel@tonic-gate 				if (q->q_host != NULL && q->q_host[0] != '\0')
899*7c478bd9Sstevel@tonic-gate 					message("deliverable: mailer %s, host %s, user %s",
900*7c478bd9Sstevel@tonic-gate 						q->q_mailer->m_name,
901*7c478bd9Sstevel@tonic-gate 						q->q_host,
902*7c478bd9Sstevel@tonic-gate 						q->q_user);
903*7c478bd9Sstevel@tonic-gate 				else
904*7c478bd9Sstevel@tonic-gate 					message("deliverable: mailer %s, user %s",
905*7c478bd9Sstevel@tonic-gate 						q->q_mailer->m_name,
906*7c478bd9Sstevel@tonic-gate 						q->q_user);
907*7c478bd9Sstevel@tonic-gate 			}
908*7c478bd9Sstevel@tonic-gate 		}
909*7c478bd9Sstevel@tonic-gate 		else if (QS_IS_OK(q->q_state))
910*7c478bd9Sstevel@tonic-gate 		{
911*7c478bd9Sstevel@tonic-gate 			/*
912*7c478bd9Sstevel@tonic-gate 			**  Checkpoint the send list every few addresses
913*7c478bd9Sstevel@tonic-gate 			*/
914*7c478bd9Sstevel@tonic-gate 
915*7c478bd9Sstevel@tonic-gate 			if (CheckpointInterval > 0 &&
916*7c478bd9Sstevel@tonic-gate 			    e->e_nsent >= CheckpointInterval)
917*7c478bd9Sstevel@tonic-gate 			{
918*7c478bd9Sstevel@tonic-gate 				queueup(e, false, false);
919*7c478bd9Sstevel@tonic-gate 				e->e_nsent = 0;
920*7c478bd9Sstevel@tonic-gate 			}
921*7c478bd9Sstevel@tonic-gate 			(void) deliver(e, q);
922*7c478bd9Sstevel@tonic-gate 			didany = true;
923*7c478bd9Sstevel@tonic-gate 		}
924*7c478bd9Sstevel@tonic-gate 	}
925*7c478bd9Sstevel@tonic-gate 	if (didany)
926*7c478bd9Sstevel@tonic-gate 	{
927*7c478bd9Sstevel@tonic-gate 		e->e_dtime = curtime();
928*7c478bd9Sstevel@tonic-gate 		e->e_ntries++;
929*7c478bd9Sstevel@tonic-gate 	}
930*7c478bd9Sstevel@tonic-gate 
931*7c478bd9Sstevel@tonic-gate #if XDEBUG
932*7c478bd9Sstevel@tonic-gate 	checkfd012("end of sendenvelope");
933*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
934*7c478bd9Sstevel@tonic-gate }
935*7c478bd9Sstevel@tonic-gate 
936*7c478bd9Sstevel@tonic-gate #if REQUIRES_DIR_FSYNC
937*7c478bd9Sstevel@tonic-gate /*
938*7c478bd9Sstevel@tonic-gate **  SYNC_DIR -- fsync a directory based on a filename
939*7c478bd9Sstevel@tonic-gate **
940*7c478bd9Sstevel@tonic-gate **	Parameters:
941*7c478bd9Sstevel@tonic-gate **		filename -- path of file
942*7c478bd9Sstevel@tonic-gate **		panic -- panic?
943*7c478bd9Sstevel@tonic-gate **
944*7c478bd9Sstevel@tonic-gate **	Returns:
945*7c478bd9Sstevel@tonic-gate **		none
946*7c478bd9Sstevel@tonic-gate */
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate void
949*7c478bd9Sstevel@tonic-gate sync_dir(filename, panic)
950*7c478bd9Sstevel@tonic-gate 	char *filename;
951*7c478bd9Sstevel@tonic-gate 	bool panic;
952*7c478bd9Sstevel@tonic-gate {
953*7c478bd9Sstevel@tonic-gate 	int dirfd;
954*7c478bd9Sstevel@tonic-gate 	char *dirp;
955*7c478bd9Sstevel@tonic-gate 	char dir[MAXPATHLEN];
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate 	if (!RequiresDirfsync)
958*7c478bd9Sstevel@tonic-gate 		return;
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	/* filesystems which require the directory be synced */
961*7c478bd9Sstevel@tonic-gate 	dirp = strrchr(filename, '/');
962*7c478bd9Sstevel@tonic-gate 	if (dirp != NULL)
963*7c478bd9Sstevel@tonic-gate 	{
964*7c478bd9Sstevel@tonic-gate 		if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir)
965*7c478bd9Sstevel@tonic-gate 			return;
966*7c478bd9Sstevel@tonic-gate 		dir[dirp - filename] = '\0';
967*7c478bd9Sstevel@tonic-gate 		dirp = dir;
968*7c478bd9Sstevel@tonic-gate 	}
969*7c478bd9Sstevel@tonic-gate 	else
970*7c478bd9Sstevel@tonic-gate 		dirp = ".";
971*7c478bd9Sstevel@tonic-gate 	dirfd = open(dirp, O_RDONLY, 0700);
972*7c478bd9Sstevel@tonic-gate 	if (tTd(40,32))
973*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
974*7c478bd9Sstevel@tonic-gate 			  dirp, dirfd);
975*7c478bd9Sstevel@tonic-gate 	if (dirfd >= 0)
976*7c478bd9Sstevel@tonic-gate 	{
977*7c478bd9Sstevel@tonic-gate 		if (fsync(dirfd) < 0)
978*7c478bd9Sstevel@tonic-gate 		{
979*7c478bd9Sstevel@tonic-gate 			if (panic)
980*7c478bd9Sstevel@tonic-gate 				syserr("!sync_dir: cannot fsync directory %s",
981*7c478bd9Sstevel@tonic-gate 				       dirp);
982*7c478bd9Sstevel@tonic-gate 			else if (LogLevel > 1)
983*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
984*7c478bd9Sstevel@tonic-gate 					  "sync_dir: cannot fsync directory %s: %s",
985*7c478bd9Sstevel@tonic-gate 					  dirp, sm_errstring(errno));
986*7c478bd9Sstevel@tonic-gate 		}
987*7c478bd9Sstevel@tonic-gate 		(void) close(dirfd);
988*7c478bd9Sstevel@tonic-gate 	}
989*7c478bd9Sstevel@tonic-gate }
990*7c478bd9Sstevel@tonic-gate #endif /* REQUIRES_DIR_FSYNC */
991*7c478bd9Sstevel@tonic-gate /*
992*7c478bd9Sstevel@tonic-gate **  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
993*7c478bd9Sstevel@tonic-gate **
994*7c478bd9Sstevel@tonic-gate **	Parameters:
995*7c478bd9Sstevel@tonic-gate **		e -- the existing envelope
996*7c478bd9Sstevel@tonic-gate **		ee -- the new envelope
997*7c478bd9Sstevel@tonic-gate **		type -- the queue file type (e.g., DATAFL_LETTER)
998*7c478bd9Sstevel@tonic-gate **
999*7c478bd9Sstevel@tonic-gate **	Returns:
1000*7c478bd9Sstevel@tonic-gate **		none
1001*7c478bd9Sstevel@tonic-gate */
1002*7c478bd9Sstevel@tonic-gate 
1003*7c478bd9Sstevel@tonic-gate static void
1004*7c478bd9Sstevel@tonic-gate dup_queue_file(e, ee, type)
1005*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e, *ee;
1006*7c478bd9Sstevel@tonic-gate 	int type;
1007*7c478bd9Sstevel@tonic-gate {
1008*7c478bd9Sstevel@tonic-gate 	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
1009*7c478bd9Sstevel@tonic-gate 
1010*7c478bd9Sstevel@tonic-gate 	ee->e_dfp = NULL;
1011*7c478bd9Sstevel@tonic-gate 	ee->e_xfp = NULL;
1012*7c478bd9Sstevel@tonic-gate 
1013*7c478bd9Sstevel@tonic-gate 	/*
1014*7c478bd9Sstevel@tonic-gate 	**  Make sure both are in the same directory.
1015*7c478bd9Sstevel@tonic-gate 	*/
1016*7c478bd9Sstevel@tonic-gate 
1017*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf);
1018*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf);
1019*7c478bd9Sstevel@tonic-gate 
1020*7c478bd9Sstevel@tonic-gate 	/* Force the df to disk if it's not there yet */
1021*7c478bd9Sstevel@tonic-gate 	if (type == DATAFL_LETTER && e->e_dfp != NULL &&
1022*7c478bd9Sstevel@tonic-gate 	    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
1023*7c478bd9Sstevel@tonic-gate 	    errno != EINVAL)
1024*7c478bd9Sstevel@tonic-gate 	{
1025*7c478bd9Sstevel@tonic-gate 		syserr("!dup_queue_file: can't commit %s", f1buf);
1026*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
1027*7c478bd9Sstevel@tonic-gate 	}
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 	if (link(f1buf, f2buf) < 0)
1030*7c478bd9Sstevel@tonic-gate 	{
1031*7c478bd9Sstevel@tonic-gate 		int save_errno = errno;
1032*7c478bd9Sstevel@tonic-gate 
1033*7c478bd9Sstevel@tonic-gate 		syserr("sendall: link(%s, %s)", f1buf, f2buf);
1034*7c478bd9Sstevel@tonic-gate 		if (save_errno == EEXIST)
1035*7c478bd9Sstevel@tonic-gate 		{
1036*7c478bd9Sstevel@tonic-gate 			if (unlink(f2buf) < 0)
1037*7c478bd9Sstevel@tonic-gate 			{
1038*7c478bd9Sstevel@tonic-gate 				syserr("!sendall: unlink(%s): permanent",
1039*7c478bd9Sstevel@tonic-gate 				       f2buf);
1040*7c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
1041*7c478bd9Sstevel@tonic-gate 			}
1042*7c478bd9Sstevel@tonic-gate 			if (link(f1buf, f2buf) < 0)
1043*7c478bd9Sstevel@tonic-gate 			{
1044*7c478bd9Sstevel@tonic-gate 				syserr("!sendall: link(%s, %s): permanent",
1045*7c478bd9Sstevel@tonic-gate 				       f1buf, f2buf);
1046*7c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
1047*7c478bd9Sstevel@tonic-gate 			}
1048*7c478bd9Sstevel@tonic-gate 		}
1049*7c478bd9Sstevel@tonic-gate 	}
1050*7c478bd9Sstevel@tonic-gate 	SYNC_DIR(f2buf, true);
1051*7c478bd9Sstevel@tonic-gate }
1052*7c478bd9Sstevel@tonic-gate /*
1053*7c478bd9Sstevel@tonic-gate **  DOFORK -- do a fork, retrying a couple of times on failure.
1054*7c478bd9Sstevel@tonic-gate **
1055*7c478bd9Sstevel@tonic-gate **	This MUST be a macro, since after a vfork we are running
1056*7c478bd9Sstevel@tonic-gate **	two processes on the same stack!!!
1057*7c478bd9Sstevel@tonic-gate **
1058*7c478bd9Sstevel@tonic-gate **	Parameters:
1059*7c478bd9Sstevel@tonic-gate **		none.
1060*7c478bd9Sstevel@tonic-gate **
1061*7c478bd9Sstevel@tonic-gate **	Returns:
1062*7c478bd9Sstevel@tonic-gate **		From a macro???  You've got to be kidding!
1063*7c478bd9Sstevel@tonic-gate **
1064*7c478bd9Sstevel@tonic-gate **	Side Effects:
1065*7c478bd9Sstevel@tonic-gate **		Modifies the ==> LOCAL <== variable 'pid', leaving:
1066*7c478bd9Sstevel@tonic-gate **			pid of child in parent, zero in child.
1067*7c478bd9Sstevel@tonic-gate **			-1 on unrecoverable error.
1068*7c478bd9Sstevel@tonic-gate **
1069*7c478bd9Sstevel@tonic-gate **	Notes:
1070*7c478bd9Sstevel@tonic-gate **		I'm awfully sorry this looks so awful.  That's
1071*7c478bd9Sstevel@tonic-gate **		vfork for you.....
1072*7c478bd9Sstevel@tonic-gate */
1073*7c478bd9Sstevel@tonic-gate 
1074*7c478bd9Sstevel@tonic-gate #define NFORKTRIES	5
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate #ifndef FORK
1077*7c478bd9Sstevel@tonic-gate # define FORK	fork
1078*7c478bd9Sstevel@tonic-gate #endif /* ! FORK */
1079*7c478bd9Sstevel@tonic-gate 
1080*7c478bd9Sstevel@tonic-gate #define DOFORK(fORKfN) \
1081*7c478bd9Sstevel@tonic-gate {\
1082*7c478bd9Sstevel@tonic-gate 	register int i;\
1083*7c478bd9Sstevel@tonic-gate \
1084*7c478bd9Sstevel@tonic-gate 	for (i = NFORKTRIES; --i >= 0; )\
1085*7c478bd9Sstevel@tonic-gate 	{\
1086*7c478bd9Sstevel@tonic-gate 		pid = fORKfN();\
1087*7c478bd9Sstevel@tonic-gate 		if (pid >= 0)\
1088*7c478bd9Sstevel@tonic-gate 			break;\
1089*7c478bd9Sstevel@tonic-gate 		if (i > 0)\
1090*7c478bd9Sstevel@tonic-gate 			(void) sleep((unsigned) NFORKTRIES - i);\
1091*7c478bd9Sstevel@tonic-gate 	}\
1092*7c478bd9Sstevel@tonic-gate }
1093*7c478bd9Sstevel@tonic-gate /*
1094*7c478bd9Sstevel@tonic-gate **  DOFORK -- simple fork interface to DOFORK.
1095*7c478bd9Sstevel@tonic-gate **
1096*7c478bd9Sstevel@tonic-gate **	Parameters:
1097*7c478bd9Sstevel@tonic-gate **		none.
1098*7c478bd9Sstevel@tonic-gate **
1099*7c478bd9Sstevel@tonic-gate **	Returns:
1100*7c478bd9Sstevel@tonic-gate **		pid of child in parent.
1101*7c478bd9Sstevel@tonic-gate **		zero in child.
1102*7c478bd9Sstevel@tonic-gate **		-1 on error.
1103*7c478bd9Sstevel@tonic-gate **
1104*7c478bd9Sstevel@tonic-gate **	Side Effects:
1105*7c478bd9Sstevel@tonic-gate **		returns twice, once in parent and once in child.
1106*7c478bd9Sstevel@tonic-gate */
1107*7c478bd9Sstevel@tonic-gate 
1108*7c478bd9Sstevel@tonic-gate pid_t
1109*7c478bd9Sstevel@tonic-gate dofork()
1110*7c478bd9Sstevel@tonic-gate {
1111*7c478bd9Sstevel@tonic-gate 	register pid_t pid = -1;
1112*7c478bd9Sstevel@tonic-gate 
1113*7c478bd9Sstevel@tonic-gate 	DOFORK(fork);
1114*7c478bd9Sstevel@tonic-gate 	return pid;
1115*7c478bd9Sstevel@tonic-gate }
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate /*
1118*7c478bd9Sstevel@tonic-gate **  COLONCMP -- compare host-signatures up to first ':' or EOS
1119*7c478bd9Sstevel@tonic-gate **
1120*7c478bd9Sstevel@tonic-gate **	This takes two strings which happen to be host-signatures and
1121*7c478bd9Sstevel@tonic-gate **	compares them. If the lowest preference portions of the MX-RR's
1122*7c478bd9Sstevel@tonic-gate **	match (up to ':' or EOS, whichever is first), then we have
1123*7c478bd9Sstevel@tonic-gate **	match. This is used for coattail-piggybacking messages during
1124*7c478bd9Sstevel@tonic-gate **	message delivery.
1125*7c478bd9Sstevel@tonic-gate **	If the signatures are the same up to the first ':' the remainder of
1126*7c478bd9Sstevel@tonic-gate **	the signatures are then compared with a normal strcmp(). This saves
1127*7c478bd9Sstevel@tonic-gate **	re-examining the first part of the signatures.
1128*7c478bd9Sstevel@tonic-gate **
1129*7c478bd9Sstevel@tonic-gate **	Parameters:
1130*7c478bd9Sstevel@tonic-gate **		a - first host-signature
1131*7c478bd9Sstevel@tonic-gate **		b - second host-signature
1132*7c478bd9Sstevel@tonic-gate **
1133*7c478bd9Sstevel@tonic-gate **	Returns:
1134*7c478bd9Sstevel@tonic-gate **		HS_MATCH_NO -- no "match".
1135*7c478bd9Sstevel@tonic-gate **		HS_MATCH_FIRST -- "match" for the first MX preference
1136*7c478bd9Sstevel@tonic-gate **			(up to the first colon (':')).
1137*7c478bd9Sstevel@tonic-gate **		HS_MATCH_FULL -- match for the entire MX record.
1138*7c478bd9Sstevel@tonic-gate **
1139*7c478bd9Sstevel@tonic-gate **	Side Effects:
1140*7c478bd9Sstevel@tonic-gate **		none.
1141*7c478bd9Sstevel@tonic-gate */
1142*7c478bd9Sstevel@tonic-gate 
1143*7c478bd9Sstevel@tonic-gate #define HS_MATCH_NO	0
1144*7c478bd9Sstevel@tonic-gate #define HS_MATCH_FIRST	1
1145*7c478bd9Sstevel@tonic-gate #define HS_MATCH_FULL	2
1146*7c478bd9Sstevel@tonic-gate 
1147*7c478bd9Sstevel@tonic-gate static int
1148*7c478bd9Sstevel@tonic-gate coloncmp(a, b)
1149*7c478bd9Sstevel@tonic-gate 	register const char *a;
1150*7c478bd9Sstevel@tonic-gate 	register const char *b;
1151*7c478bd9Sstevel@tonic-gate {
1152*7c478bd9Sstevel@tonic-gate 	int ret = HS_MATCH_NO;
1153*7c478bd9Sstevel@tonic-gate 	int braclev = 0;
1154*7c478bd9Sstevel@tonic-gate 
1155*7c478bd9Sstevel@tonic-gate 	while (*a == *b++)
1156*7c478bd9Sstevel@tonic-gate 	{
1157*7c478bd9Sstevel@tonic-gate 		/* Need to account for IPv6 bracketed addresses */
1158*7c478bd9Sstevel@tonic-gate 		if (*a == '[')
1159*7c478bd9Sstevel@tonic-gate 			braclev++;
1160*7c478bd9Sstevel@tonic-gate 		else if (*a == ']' && braclev > 0)
1161*7c478bd9Sstevel@tonic-gate 			braclev--;
1162*7c478bd9Sstevel@tonic-gate 		else if (*a == ':' && braclev <= 0)
1163*7c478bd9Sstevel@tonic-gate 		{
1164*7c478bd9Sstevel@tonic-gate 			ret = HS_MATCH_FIRST;
1165*7c478bd9Sstevel@tonic-gate 			a++;
1166*7c478bd9Sstevel@tonic-gate 			break;
1167*7c478bd9Sstevel@tonic-gate 		}
1168*7c478bd9Sstevel@tonic-gate 		else if (*a == '\0')
1169*7c478bd9Sstevel@tonic-gate 			return HS_MATCH_FULL; /* a full match */
1170*7c478bd9Sstevel@tonic-gate 		a++;
1171*7c478bd9Sstevel@tonic-gate 	}
1172*7c478bd9Sstevel@tonic-gate 	if (ret == HS_MATCH_NO &&
1173*7c478bd9Sstevel@tonic-gate 	    braclev <= 0 &&
1174*7c478bd9Sstevel@tonic-gate 	    ((*a == '\0' && *(b - 1) == ':') ||
1175*7c478bd9Sstevel@tonic-gate 	     (*a == ':' && *(b - 1) == '\0')))
1176*7c478bd9Sstevel@tonic-gate 		return HS_MATCH_FIRST;
1177*7c478bd9Sstevel@tonic-gate 	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
1178*7c478bd9Sstevel@tonic-gate 		return HS_MATCH_FULL;
1179*7c478bd9Sstevel@tonic-gate 
1180*7c478bd9Sstevel@tonic-gate 	return ret;
1181*7c478bd9Sstevel@tonic-gate }
1182*7c478bd9Sstevel@tonic-gate 
1183*7c478bd9Sstevel@tonic-gate /*
1184*7c478bd9Sstevel@tonic-gate **  SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
1185*7c478bd9Sstevel@tonic-gate **
1186*7c478bd9Sstevel@tonic-gate **	Parameters:
1187*7c478bd9Sstevel@tonic-gate **		e -- envelope
1188*7c478bd9Sstevel@tonic-gate **		tried_fallbacksmarthost -- has been tried already? (in/out)
1189*7c478bd9Sstevel@tonic-gate **		hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
1190*7c478bd9Sstevel@tonic-gate **		hbsz -- size of hostbuf
1191*7c478bd9Sstevel@tonic-gate **		status -- current delivery status
1192*7c478bd9Sstevel@tonic-gate **
1193*7c478bd9Sstevel@tonic-gate **	Returns:
1194*7c478bd9Sstevel@tonic-gate **		true iff FallbackSmartHost should be tried.
1195*7c478bd9Sstevel@tonic-gate */
1196*7c478bd9Sstevel@tonic-gate 
1197*7c478bd9Sstevel@tonic-gate static bool
1198*7c478bd9Sstevel@tonic-gate should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
1199*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
1200*7c478bd9Sstevel@tonic-gate 	bool *tried_fallbacksmarthost;
1201*7c478bd9Sstevel@tonic-gate 	char *hostbuf;
1202*7c478bd9Sstevel@tonic-gate 	size_t hbsz;
1203*7c478bd9Sstevel@tonic-gate 	int status;
1204*7c478bd9Sstevel@tonic-gate {
1205*7c478bd9Sstevel@tonic-gate 	/*
1206*7c478bd9Sstevel@tonic-gate 	**  If the host was not found and a FallbackSmartHost is defined
1207*7c478bd9Sstevel@tonic-gate 	**  (and we have not yet tried it), then make one last try with
1208*7c478bd9Sstevel@tonic-gate 	**  it as the host.
1209*7c478bd9Sstevel@tonic-gate 	*/
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 	if (status == EX_NOHOST && FallbackSmartHost != NULL &&
1212*7c478bd9Sstevel@tonic-gate 	    !*tried_fallbacksmarthost)
1213*7c478bd9Sstevel@tonic-gate 	{
1214*7c478bd9Sstevel@tonic-gate 		*tried_fallbacksmarthost = true;
1215*7c478bd9Sstevel@tonic-gate 		expand(FallbackSmartHost, hostbuf, hbsz, e);
1216*7c478bd9Sstevel@tonic-gate 		if (!wordinclass(hostbuf, 'w'))
1217*7c478bd9Sstevel@tonic-gate 		{
1218*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 1))
1219*7c478bd9Sstevel@tonic-gate 				sm_dprintf("one last try with FallbackSmartHost %s\n",
1220*7c478bd9Sstevel@tonic-gate 					   hostbuf);
1221*7c478bd9Sstevel@tonic-gate 			return true;
1222*7c478bd9Sstevel@tonic-gate 		}
1223*7c478bd9Sstevel@tonic-gate 	}
1224*7c478bd9Sstevel@tonic-gate 	return false;
1225*7c478bd9Sstevel@tonic-gate }
1226*7c478bd9Sstevel@tonic-gate /*
1227*7c478bd9Sstevel@tonic-gate **  DELIVER -- Deliver a message to a list of addresses.
1228*7c478bd9Sstevel@tonic-gate **
1229*7c478bd9Sstevel@tonic-gate **	This routine delivers to everyone on the same host as the
1230*7c478bd9Sstevel@tonic-gate **	user on the head of the list.  It is clever about mailers
1231*7c478bd9Sstevel@tonic-gate **	that don't handle multiple users.  It is NOT guaranteed
1232*7c478bd9Sstevel@tonic-gate **	that it will deliver to all these addresses however -- so
1233*7c478bd9Sstevel@tonic-gate **	deliver should be called once for each address on the
1234*7c478bd9Sstevel@tonic-gate **	list.
1235*7c478bd9Sstevel@tonic-gate **	Deliver tries to be as opportunistic as possible about piggybacking
1236*7c478bd9Sstevel@tonic-gate **	messages. Some definitions to make understanding easier follow below.
1237*7c478bd9Sstevel@tonic-gate **	Piggybacking occurs when an existing connection to a mail host can
1238*7c478bd9Sstevel@tonic-gate **	be used to send the same message to more than one recipient at the
1239*7c478bd9Sstevel@tonic-gate **	same time. So "no piggybacking" means one message for one recipient
1240*7c478bd9Sstevel@tonic-gate **	per connection. "Intentional piggybacking" happens when the
1241*7c478bd9Sstevel@tonic-gate **	recipients' host address (not the mail host address) is used to
1242*7c478bd9Sstevel@tonic-gate **	attempt piggybacking. Recipients with the same host address
1243*7c478bd9Sstevel@tonic-gate **	have the same mail host. "Coincidental piggybacking" relies on
1244*7c478bd9Sstevel@tonic-gate **	piggybacking based on all the mail host addresses in the MX-RR. This
1245*7c478bd9Sstevel@tonic-gate **	is "coincidental" in the fact it could not be predicted until the
1246*7c478bd9Sstevel@tonic-gate **	MX Resource Records for the hosts were obtained and examined. For
1247*7c478bd9Sstevel@tonic-gate **	example (preference order and equivalence is important, not values):
1248*7c478bd9Sstevel@tonic-gate **		domain1 IN MX 10 mxhost-A
1249*7c478bd9Sstevel@tonic-gate **			IN MX 20 mxhost-B
1250*7c478bd9Sstevel@tonic-gate **		domain2 IN MX  4 mxhost-A
1251*7c478bd9Sstevel@tonic-gate **			IN MX  8 mxhost-B
1252*7c478bd9Sstevel@tonic-gate **	Domain1 and domain2 can piggyback the same message to mxhost-A or
1253*7c478bd9Sstevel@tonic-gate **	mxhost-B (if mxhost-A cannot be reached).
1254*7c478bd9Sstevel@tonic-gate **	"Coattail piggybacking" relaxes the strictness of "coincidental
1255*7c478bd9Sstevel@tonic-gate **	piggybacking" in the hope that most significant (lowest value)
1256*7c478bd9Sstevel@tonic-gate **	MX preference host(s) can create more piggybacking. For example
1257*7c478bd9Sstevel@tonic-gate **	(again, preference order and equivalence is important, not values):
1258*7c478bd9Sstevel@tonic-gate **		domain3 IN MX 100 mxhost-C
1259*7c478bd9Sstevel@tonic-gate **			IN MX 100 mxhost-D
1260*7c478bd9Sstevel@tonic-gate **			IN MX 200 mxhost-E
1261*7c478bd9Sstevel@tonic-gate **		domain4 IN MX  50 mxhost-C
1262*7c478bd9Sstevel@tonic-gate **			IN MX  50 mxhost-D
1263*7c478bd9Sstevel@tonic-gate **			IN MX  80 mxhost-F
1264*7c478bd9Sstevel@tonic-gate **	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
1265*7c478bd9Sstevel@tonic-gate **	is available. Same with mxhost-D because in both RR's the preference
1266*7c478bd9Sstevel@tonic-gate **	value is the same as mxhost-C, respectively.
1267*7c478bd9Sstevel@tonic-gate **	So deliver attempts coattail piggybacking when possible. If the
1268*7c478bd9Sstevel@tonic-gate **	first MX preference level hosts cannot be used then the piggybacking
1269*7c478bd9Sstevel@tonic-gate **	reverts to coincidental piggybacking. Using the above example you
1270*7c478bd9Sstevel@tonic-gate **	cannot deliver to mxhost-F for domain3 regardless of preference value.
1271*7c478bd9Sstevel@tonic-gate **	("Coattail" from "riding on the coattails of your predecessor" meaning
1272*7c478bd9Sstevel@tonic-gate **	gaining benefit from a predecessor effort with no or little addition
1273*7c478bd9Sstevel@tonic-gate **	effort. The predecessor here being the preceding MX RR).
1274*7c478bd9Sstevel@tonic-gate **
1275*7c478bd9Sstevel@tonic-gate **	Parameters:
1276*7c478bd9Sstevel@tonic-gate **		e -- the envelope to deliver.
1277*7c478bd9Sstevel@tonic-gate **		firstto -- head of the address list to deliver to.
1278*7c478bd9Sstevel@tonic-gate **
1279*7c478bd9Sstevel@tonic-gate **	Returns:
1280*7c478bd9Sstevel@tonic-gate **		zero -- successfully delivered.
1281*7c478bd9Sstevel@tonic-gate **		else -- some failure, see ExitStat for more info.
1282*7c478bd9Sstevel@tonic-gate **
1283*7c478bd9Sstevel@tonic-gate **	Side Effects:
1284*7c478bd9Sstevel@tonic-gate **		The standard input is passed off to someone.
1285*7c478bd9Sstevel@tonic-gate */
1286*7c478bd9Sstevel@tonic-gate 
1287*7c478bd9Sstevel@tonic-gate static int
1288*7c478bd9Sstevel@tonic-gate deliver(e, firstto)
1289*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
1290*7c478bd9Sstevel@tonic-gate 	ADDRESS *firstto;
1291*7c478bd9Sstevel@tonic-gate {
1292*7c478bd9Sstevel@tonic-gate 	char *host;			/* host being sent to */
1293*7c478bd9Sstevel@tonic-gate 	char *user;			/* user being sent to */
1294*7c478bd9Sstevel@tonic-gate 	char **pvp;
1295*7c478bd9Sstevel@tonic-gate 	register char **mvp;
1296*7c478bd9Sstevel@tonic-gate 	register char *p;
1297*7c478bd9Sstevel@tonic-gate 	register MAILER *m;		/* mailer for this recipient */
1298*7c478bd9Sstevel@tonic-gate 	ADDRESS *volatile ctladdr;
1299*7c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT
1300*7c478bd9Sstevel@tonic-gate 	ADDRESS *volatile contextaddr = NULL;
1301*7c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */
1302*7c478bd9Sstevel@tonic-gate 	register MCI *volatile mci;
1303*7c478bd9Sstevel@tonic-gate 	register ADDRESS *SM_NONVOLATILE to = firstto;
1304*7c478bd9Sstevel@tonic-gate 	volatile bool clever = false;	/* running user smtp to this mailer */
1305*7c478bd9Sstevel@tonic-gate 	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
1306*7c478bd9Sstevel@tonic-gate 	int rcode;			/* response code */
1307*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int lmtp_rcode = EX_OK;
1308*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
1309*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
1310*7c478bd9Sstevel@tonic-gate 	char *firstsig;			/* signature of firstto */
1311*7c478bd9Sstevel@tonic-gate 	volatile pid_t pid = -1;
1312*7c478bd9Sstevel@tonic-gate 	char *volatile curhost;
1313*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE unsigned short port = 0;
1314*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE time_t enough = 0;
1315*7c478bd9Sstevel@tonic-gate #if NETUNIX
1316*7c478bd9Sstevel@tonic-gate 	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
1317*7c478bd9Sstevel@tonic-gate #endif /* NETUNIX */
1318*7c478bd9Sstevel@tonic-gate 	time_t xstart;
1319*7c478bd9Sstevel@tonic-gate 	bool suidwarn;
1320*7c478bd9Sstevel@tonic-gate 	bool anyok;			/* at least one address was OK */
1321*7c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
1322*7c478bd9Sstevel@tonic-gate 	bool ovr;
1323*7c478bd9Sstevel@tonic-gate 	bool quarantine;
1324*7c478bd9Sstevel@tonic-gate 	int strsize;
1325*7c478bd9Sstevel@tonic-gate 	int rcptcount;
1326*7c478bd9Sstevel@tonic-gate 	int ret;
1327*7c478bd9Sstevel@tonic-gate 	static int tobufsize = 0;
1328*7c478bd9Sstevel@tonic-gate 	static char *tobuf = NULL;
1329*7c478bd9Sstevel@tonic-gate 	char *rpath;	/* translated return path */
1330*7c478bd9Sstevel@tonic-gate 	int mpvect[2];
1331*7c478bd9Sstevel@tonic-gate 	int rpvect[2];
1332*7c478bd9Sstevel@tonic-gate 	char *mxhosts[MAXMXHOSTS + 1];
1333*7c478bd9Sstevel@tonic-gate 	char *pv[MAXPV + 1];
1334*7c478bd9Sstevel@tonic-gate 	char buf[MAXNAME + 1];
1335*7c478bd9Sstevel@tonic-gate 	char cbuf[MAXPATHLEN];
1336*7c478bd9Sstevel@tonic-gate 
1337*7c478bd9Sstevel@tonic-gate 	errno = 0;
1338*7c478bd9Sstevel@tonic-gate 	if (!QS_IS_OK(to->q_state))
1339*7c478bd9Sstevel@tonic-gate 		return 0;
1340*7c478bd9Sstevel@tonic-gate 
1341*7c478bd9Sstevel@tonic-gate 	suidwarn = geteuid() == 0;
1342*7c478bd9Sstevel@tonic-gate 
1343*7c478bd9Sstevel@tonic-gate 	m = to->q_mailer;
1344*7c478bd9Sstevel@tonic-gate 	host = to->q_host;
1345*7c478bd9Sstevel@tonic-gate 	CurEnv = e;			/* just in case */
1346*7c478bd9Sstevel@tonic-gate 	e->e_statmsg = NULL;
1347*7c478bd9Sstevel@tonic-gate 	SmtpError[0] = '\0';
1348*7c478bd9Sstevel@tonic-gate 	xstart = curtime();
1349*7c478bd9Sstevel@tonic-gate 
1350*7c478bd9Sstevel@tonic-gate 	if (tTd(10, 1))
1351*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
1352*7c478bd9Sstevel@tonic-gate 			e->e_id, m->m_name, host, to->q_user);
1353*7c478bd9Sstevel@tonic-gate 	if (tTd(10, 100))
1354*7c478bd9Sstevel@tonic-gate 		printopenfds(false);
1355*7c478bd9Sstevel@tonic-gate 
1356*7c478bd9Sstevel@tonic-gate 	/*
1357*7c478bd9Sstevel@tonic-gate 	**  Clear {client_*} macros if this is a bounce message to
1358*7c478bd9Sstevel@tonic-gate 	**  prevent rejection by check_compat ruleset.
1359*7c478bd9Sstevel@tonic-gate 	*/
1360*7c478bd9Sstevel@tonic-gate 
1361*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_RESPONSE, e->e_flags))
1362*7c478bd9Sstevel@tonic-gate 	{
1363*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
1364*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
1365*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
1366*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
1367*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
1368*7c478bd9Sstevel@tonic-gate 	}
1369*7c478bd9Sstevel@tonic-gate 
1370*7c478bd9Sstevel@tonic-gate 	SM_TRY
1371*7c478bd9Sstevel@tonic-gate 	{
1372*7c478bd9Sstevel@tonic-gate 	ADDRESS *skip_back = NULL;
1373*7c478bd9Sstevel@tonic-gate 
1374*7c478bd9Sstevel@tonic-gate 	/*
1375*7c478bd9Sstevel@tonic-gate 	**  Do initial argv setup.
1376*7c478bd9Sstevel@tonic-gate 	**	Insert the mailer name.  Notice that $x expansion is
1377*7c478bd9Sstevel@tonic-gate 	**	NOT done on the mailer name.  Then, if the mailer has
1378*7c478bd9Sstevel@tonic-gate 	**	a picky -f flag, we insert it as appropriate.  This
1379*7c478bd9Sstevel@tonic-gate 	**	code does not check for 'pv' overflow; this places a
1380*7c478bd9Sstevel@tonic-gate 	**	manifest lower limit of 4 for MAXPV.
1381*7c478bd9Sstevel@tonic-gate 	**		The from address rewrite is expected to make
1382*7c478bd9Sstevel@tonic-gate 	**		the address relative to the other end.
1383*7c478bd9Sstevel@tonic-gate 	*/
1384*7c478bd9Sstevel@tonic-gate 
1385*7c478bd9Sstevel@tonic-gate 	/* rewrite from address, using rewriting rules */
1386*7c478bd9Sstevel@tonic-gate 	rcode = EX_OK;
1387*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
1388*7c478bd9Sstevel@tonic-gate 		p = e->e_sender;
1389*7c478bd9Sstevel@tonic-gate 	else
1390*7c478bd9Sstevel@tonic-gate 		p = e->e_from.q_paddr;
1391*7c478bd9Sstevel@tonic-gate 	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
1392*7c478bd9Sstevel@tonic-gate 	if (strlen(rpath) > MAXSHORTSTR)
1393*7c478bd9Sstevel@tonic-gate 	{
1394*7c478bd9Sstevel@tonic-gate 		rpath = shortenstring(rpath, MAXSHORTSTR);
1395*7c478bd9Sstevel@tonic-gate 
1396*7c478bd9Sstevel@tonic-gate 		/* avoid bogus errno */
1397*7c478bd9Sstevel@tonic-gate 		errno = 0;
1398*7c478bd9Sstevel@tonic-gate 		syserr("remotename: huge return path %s", rpath);
1399*7c478bd9Sstevel@tonic-gate 	}
1400*7c478bd9Sstevel@tonic-gate 	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
1401*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'g', rpath);
1402*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'h', host);
1403*7c478bd9Sstevel@tonic-gate 	Errors = 0;
1404*7c478bd9Sstevel@tonic-gate 	pvp = pv;
1405*7c478bd9Sstevel@tonic-gate 	*pvp++ = m->m_argv[0];
1406*7c478bd9Sstevel@tonic-gate 
1407*7c478bd9Sstevel@tonic-gate 	/* ignore long term host status information if mailer flag W is set */
1408*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_NOHOSTSTAT, m->m_flags))
1409*7c478bd9Sstevel@tonic-gate 		IgnoreHostStatus = true;
1410*7c478bd9Sstevel@tonic-gate 
1411*7c478bd9Sstevel@tonic-gate 	/* insert -f or -r flag as appropriate */
1412*7c478bd9Sstevel@tonic-gate 	if (FromFlag &&
1413*7c478bd9Sstevel@tonic-gate 	    (bitnset(M_FOPT, m->m_flags) ||
1414*7c478bd9Sstevel@tonic-gate 	     bitnset(M_ROPT, m->m_flags)))
1415*7c478bd9Sstevel@tonic-gate 	{
1416*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_FOPT, m->m_flags))
1417*7c478bd9Sstevel@tonic-gate 			*pvp++ = "-f";
1418*7c478bd9Sstevel@tonic-gate 		else
1419*7c478bd9Sstevel@tonic-gate 			*pvp++ = "-r";
1420*7c478bd9Sstevel@tonic-gate 		*pvp++ = rpath;
1421*7c478bd9Sstevel@tonic-gate 	}
1422*7c478bd9Sstevel@tonic-gate 
1423*7c478bd9Sstevel@tonic-gate 	/*
1424*7c478bd9Sstevel@tonic-gate 	**  Append the other fixed parts of the argv.  These run
1425*7c478bd9Sstevel@tonic-gate 	**  up to the first entry containing "$u".  There can only
1426*7c478bd9Sstevel@tonic-gate 	**  be one of these, and there are only a few more slots
1427*7c478bd9Sstevel@tonic-gate 	**  in the pv after it.
1428*7c478bd9Sstevel@tonic-gate 	*/
1429*7c478bd9Sstevel@tonic-gate 
1430*7c478bd9Sstevel@tonic-gate 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1431*7c478bd9Sstevel@tonic-gate 	{
1432*7c478bd9Sstevel@tonic-gate 		/* can't use strchr here because of sign extension problems */
1433*7c478bd9Sstevel@tonic-gate 		while (*p != '\0')
1434*7c478bd9Sstevel@tonic-gate 		{
1435*7c478bd9Sstevel@tonic-gate 			if ((*p++ & 0377) == MACROEXPAND)
1436*7c478bd9Sstevel@tonic-gate 			{
1437*7c478bd9Sstevel@tonic-gate 				if (*p == 'u')
1438*7c478bd9Sstevel@tonic-gate 					break;
1439*7c478bd9Sstevel@tonic-gate 			}
1440*7c478bd9Sstevel@tonic-gate 		}
1441*7c478bd9Sstevel@tonic-gate 
1442*7c478bd9Sstevel@tonic-gate 		if (*p != '\0')
1443*7c478bd9Sstevel@tonic-gate 			break;
1444*7c478bd9Sstevel@tonic-gate 
1445*7c478bd9Sstevel@tonic-gate 		/* this entry is safe -- go ahead and process it */
1446*7c478bd9Sstevel@tonic-gate 		expand(*mvp, buf, sizeof buf, e);
1447*7c478bd9Sstevel@tonic-gate 		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1448*7c478bd9Sstevel@tonic-gate 		if (pvp >= &pv[MAXPV - 3])
1449*7c478bd9Sstevel@tonic-gate 		{
1450*7c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 Too many parameters to %s before $u",
1451*7c478bd9Sstevel@tonic-gate 			       pv[0]);
1452*7c478bd9Sstevel@tonic-gate 			rcode = -1;
1453*7c478bd9Sstevel@tonic-gate 			goto cleanup;
1454*7c478bd9Sstevel@tonic-gate 		}
1455*7c478bd9Sstevel@tonic-gate 	}
1456*7c478bd9Sstevel@tonic-gate 
1457*7c478bd9Sstevel@tonic-gate 	/*
1458*7c478bd9Sstevel@tonic-gate 	**  If we have no substitution for the user name in the argument
1459*7c478bd9Sstevel@tonic-gate 	**  list, we know that we must supply the names otherwise -- and
1460*7c478bd9Sstevel@tonic-gate 	**  SMTP is the answer!!
1461*7c478bd9Sstevel@tonic-gate 	*/
1462*7c478bd9Sstevel@tonic-gate 
1463*7c478bd9Sstevel@tonic-gate 	if (*mvp == NULL)
1464*7c478bd9Sstevel@tonic-gate 	{
1465*7c478bd9Sstevel@tonic-gate 		/* running LMTP or SMTP */
1466*7c478bd9Sstevel@tonic-gate 		clever = true;
1467*7c478bd9Sstevel@tonic-gate 		*pvp = NULL;
1468*7c478bd9Sstevel@tonic-gate 	}
1469*7c478bd9Sstevel@tonic-gate 	else if (bitnset(M_LMTP, m->m_flags))
1470*7c478bd9Sstevel@tonic-gate 	{
1471*7c478bd9Sstevel@tonic-gate 		/* not running LMTP */
1472*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NULL,
1473*7c478bd9Sstevel@tonic-gate 			  "Warning: mailer %s: LMTP flag (F=z) turned off",
1474*7c478bd9Sstevel@tonic-gate 			  m->m_name);
1475*7c478bd9Sstevel@tonic-gate 		clrbitn(M_LMTP, m->m_flags);
1476*7c478bd9Sstevel@tonic-gate 	}
1477*7c478bd9Sstevel@tonic-gate 
1478*7c478bd9Sstevel@tonic-gate 	/*
1479*7c478bd9Sstevel@tonic-gate 	**  At this point *mvp points to the argument with $u.  We
1480*7c478bd9Sstevel@tonic-gate 	**  run through our address list and append all the addresses
1481*7c478bd9Sstevel@tonic-gate 	**  we can.  If we run out of space, do not fret!  We can
1482*7c478bd9Sstevel@tonic-gate 	**  always send another copy later.
1483*7c478bd9Sstevel@tonic-gate 	*/
1484*7c478bd9Sstevel@tonic-gate 
1485*7c478bd9Sstevel@tonic-gate 	e->e_to = NULL;
1486*7c478bd9Sstevel@tonic-gate 	strsize = 2;
1487*7c478bd9Sstevel@tonic-gate 	rcptcount = 0;
1488*7c478bd9Sstevel@tonic-gate 	ctladdr = NULL;
1489*7c478bd9Sstevel@tonic-gate 	if (firstto->q_signature == NULL)
1490*7c478bd9Sstevel@tonic-gate 		firstto->q_signature = hostsignature(firstto->q_mailer,
1491*7c478bd9Sstevel@tonic-gate 						     firstto->q_host);
1492*7c478bd9Sstevel@tonic-gate 	firstsig = firstto->q_signature;
1493*7c478bd9Sstevel@tonic-gate 
1494*7c478bd9Sstevel@tonic-gate 	for (; to != NULL; to = to->q_next)
1495*7c478bd9Sstevel@tonic-gate 	{
1496*7c478bd9Sstevel@tonic-gate 		/* avoid sending multiple recipients to dumb mailers */
1497*7c478bd9Sstevel@tonic-gate 		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
1498*7c478bd9Sstevel@tonic-gate 			break;
1499*7c478bd9Sstevel@tonic-gate 
1500*7c478bd9Sstevel@tonic-gate 		/* if already sent or not for this host, don't send */
1501*7c478bd9Sstevel@tonic-gate 		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
1502*7c478bd9Sstevel@tonic-gate 			continue;
1503*7c478bd9Sstevel@tonic-gate 
1504*7c478bd9Sstevel@tonic-gate 		/*
1505*7c478bd9Sstevel@tonic-gate 		**  Must be same mailer to keep grouping rcpts.
1506*7c478bd9Sstevel@tonic-gate 		**  If mailers don't match: continue; sendqueue is not
1507*7c478bd9Sstevel@tonic-gate 		**  sorted by mailers, so don't break;
1508*7c478bd9Sstevel@tonic-gate 		*/
1509*7c478bd9Sstevel@tonic-gate 
1510*7c478bd9Sstevel@tonic-gate 		if (to->q_mailer != firstto->q_mailer)
1511*7c478bd9Sstevel@tonic-gate 			continue;
1512*7c478bd9Sstevel@tonic-gate 
1513*7c478bd9Sstevel@tonic-gate 		if (to->q_signature == NULL) /* for safety */
1514*7c478bd9Sstevel@tonic-gate 			to->q_signature = hostsignature(to->q_mailer,
1515*7c478bd9Sstevel@tonic-gate 							to->q_host);
1516*7c478bd9Sstevel@tonic-gate 
1517*7c478bd9Sstevel@tonic-gate 		/*
1518*7c478bd9Sstevel@tonic-gate 		**  This is for coincidental and tailcoat piggybacking messages
1519*7c478bd9Sstevel@tonic-gate 		**  to the same mail host. While the signatures are identical
1520*7c478bd9Sstevel@tonic-gate 		**  (that's the MX-RR's are identical) we can do coincidental
1521*7c478bd9Sstevel@tonic-gate 		**  piggybacking. We try hard for coattail piggybacking
1522*7c478bd9Sstevel@tonic-gate 		**  with the same mail host when the next recipient has the
1523*7c478bd9Sstevel@tonic-gate 		**  same host at lowest preference. It may be that this
1524*7c478bd9Sstevel@tonic-gate 		**  won't work out, so 'skip_back' is maintained if a backup
1525*7c478bd9Sstevel@tonic-gate 		**  to coincidental piggybacking or full signature must happen.
1526*7c478bd9Sstevel@tonic-gate 		*/
1527*7c478bd9Sstevel@tonic-gate 
1528*7c478bd9Sstevel@tonic-gate 		ret = firstto == to ? HS_MATCH_FULL :
1529*7c478bd9Sstevel@tonic-gate 				      coloncmp(to->q_signature, firstsig);
1530*7c478bd9Sstevel@tonic-gate 		if (ret == HS_MATCH_FULL)
1531*7c478bd9Sstevel@tonic-gate 			skip_back = to;
1532*7c478bd9Sstevel@tonic-gate 		else if (ret == HS_MATCH_NO)
1533*7c478bd9Sstevel@tonic-gate 			break;
1534*7c478bd9Sstevel@tonic-gate 
1535*7c478bd9Sstevel@tonic-gate 		if (!clever)
1536*7c478bd9Sstevel@tonic-gate 		{
1537*7c478bd9Sstevel@tonic-gate 			/* avoid overflowing tobuf */
1538*7c478bd9Sstevel@tonic-gate 			strsize += strlen(to->q_paddr) + 1;
1539*7c478bd9Sstevel@tonic-gate 			if (strsize > TOBUFSIZE)
1540*7c478bd9Sstevel@tonic-gate 				break;
1541*7c478bd9Sstevel@tonic-gate 		}
1542*7c478bd9Sstevel@tonic-gate 
1543*7c478bd9Sstevel@tonic-gate 		if (++rcptcount > to->q_mailer->m_maxrcpt)
1544*7c478bd9Sstevel@tonic-gate 			break;
1545*7c478bd9Sstevel@tonic-gate 
1546*7c478bd9Sstevel@tonic-gate 		if (tTd(10, 1))
1547*7c478bd9Sstevel@tonic-gate 		{
1548*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\nsend to ");
1549*7c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), to, false);
1550*7c478bd9Sstevel@tonic-gate 		}
1551*7c478bd9Sstevel@tonic-gate 
1552*7c478bd9Sstevel@tonic-gate 		/* compute effective uid/gid when sending */
1553*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
1554*7c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT
1555*7c478bd9Sstevel@tonic-gate 			contextaddr = ctladdr = getctladdr(to);
1556*7c478bd9Sstevel@tonic-gate # else /* HASSETUSERCONTEXT */
1557*7c478bd9Sstevel@tonic-gate 			ctladdr = getctladdr(to);
1558*7c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */
1559*7c478bd9Sstevel@tonic-gate 
1560*7c478bd9Sstevel@tonic-gate 		if (tTd(10, 2))
1561*7c478bd9Sstevel@tonic-gate 		{
1562*7c478bd9Sstevel@tonic-gate 			sm_dprintf("ctladdr=");
1563*7c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), ctladdr, false);
1564*7c478bd9Sstevel@tonic-gate 		}
1565*7c478bd9Sstevel@tonic-gate 
1566*7c478bd9Sstevel@tonic-gate 		user = to->q_user;
1567*7c478bd9Sstevel@tonic-gate 		e->e_to = to->q_paddr;
1568*7c478bd9Sstevel@tonic-gate 
1569*7c478bd9Sstevel@tonic-gate 		/*
1570*7c478bd9Sstevel@tonic-gate 		**  Check to see that these people are allowed to
1571*7c478bd9Sstevel@tonic-gate 		**  talk to each other.
1572*7c478bd9Sstevel@tonic-gate 		**  Check also for overflow of e_msgsize.
1573*7c478bd9Sstevel@tonic-gate 		*/
1574*7c478bd9Sstevel@tonic-gate 
1575*7c478bd9Sstevel@tonic-gate 		if (m->m_maxsize != 0 &&
1576*7c478bd9Sstevel@tonic-gate 		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
1577*7c478bd9Sstevel@tonic-gate 		{
1578*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_NO_BODY_RETN;
1579*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
1580*7c478bd9Sstevel@tonic-gate 				to->q_status = "5.2.3";
1581*7c478bd9Sstevel@tonic-gate 			else
1582*7c478bd9Sstevel@tonic-gate 				to->q_status = "5.3.4";
1583*7c478bd9Sstevel@tonic-gate 
1584*7c478bd9Sstevel@tonic-gate 			/* set to->q_rstatus = NULL; or to the following? */
1585*7c478bd9Sstevel@tonic-gate 			usrerrenh(to->q_status,
1586*7c478bd9Sstevel@tonic-gate 				  "552 Message is too large; %ld bytes max",
1587*7c478bd9Sstevel@tonic-gate 				  m->m_maxsize);
1588*7c478bd9Sstevel@tonic-gate 			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
1589*7c478bd9Sstevel@tonic-gate 			giveresponse(EX_UNAVAILABLE, to->q_status, m,
1590*7c478bd9Sstevel@tonic-gate 				     NULL, ctladdr, xstart, e, to);
1591*7c478bd9Sstevel@tonic-gate 			continue;
1592*7c478bd9Sstevel@tonic-gate 		}
1593*7c478bd9Sstevel@tonic-gate 		SM_SET_H_ERRNO(0);
1594*7c478bd9Sstevel@tonic-gate 		ovr = true;
1595*7c478bd9Sstevel@tonic-gate 
1596*7c478bd9Sstevel@tonic-gate 		/* do config file checking of compatibility */
1597*7c478bd9Sstevel@tonic-gate 		quarantine = (e->e_quarmsg != NULL);
1598*7c478bd9Sstevel@tonic-gate 		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1599*7c478bd9Sstevel@tonic-gate 				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1600*7c478bd9Sstevel@tonic-gate 				e->e_id);
1601*7c478bd9Sstevel@tonic-gate 		if (rcode == EX_OK)
1602*7c478bd9Sstevel@tonic-gate 		{
1603*7c478bd9Sstevel@tonic-gate 			/* do in-code checking if not discarding */
1604*7c478bd9Sstevel@tonic-gate 			if (!bitset(EF_DISCARD, e->e_flags))
1605*7c478bd9Sstevel@tonic-gate 			{
1606*7c478bd9Sstevel@tonic-gate 				rcode = checkcompat(to, e);
1607*7c478bd9Sstevel@tonic-gate 				ovr = false;
1608*7c478bd9Sstevel@tonic-gate 			}
1609*7c478bd9Sstevel@tonic-gate 		}
1610*7c478bd9Sstevel@tonic-gate 		if (rcode != EX_OK)
1611*7c478bd9Sstevel@tonic-gate 		{
1612*7c478bd9Sstevel@tonic-gate 			markfailure(e, to, NULL, rcode, ovr);
1613*7c478bd9Sstevel@tonic-gate 			giveresponse(rcode, to->q_status, m,
1614*7c478bd9Sstevel@tonic-gate 				     NULL, ctladdr, xstart, e, to);
1615*7c478bd9Sstevel@tonic-gate 			continue;
1616*7c478bd9Sstevel@tonic-gate 		}
1617*7c478bd9Sstevel@tonic-gate 		if (!quarantine && e->e_quarmsg != NULL)
1618*7c478bd9Sstevel@tonic-gate 		{
1619*7c478bd9Sstevel@tonic-gate 			/*
1620*7c478bd9Sstevel@tonic-gate 			**  check_compat or checkcompat() has tried
1621*7c478bd9Sstevel@tonic-gate 			**  to quarantine but that isn't supported.
1622*7c478bd9Sstevel@tonic-gate 			**  Revert the attempt.
1623*7c478bd9Sstevel@tonic-gate 			*/
1624*7c478bd9Sstevel@tonic-gate 
1625*7c478bd9Sstevel@tonic-gate 			e->e_quarmsg = NULL;
1626*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
1627*7c478bd9Sstevel@tonic-gate 				  macid("{quarantine}"), "");
1628*7c478bd9Sstevel@tonic-gate 		}
1629*7c478bd9Sstevel@tonic-gate 		if (bitset(EF_DISCARD, e->e_flags))
1630*7c478bd9Sstevel@tonic-gate 		{
1631*7c478bd9Sstevel@tonic-gate 			if (tTd(10, 5))
1632*7c478bd9Sstevel@tonic-gate 			{
1633*7c478bd9Sstevel@tonic-gate 				sm_dprintf("deliver: discarding recipient ");
1634*7c478bd9Sstevel@tonic-gate 				printaddr(sm_debug_file(), to, false);
1635*7c478bd9Sstevel@tonic-gate 			}
1636*7c478bd9Sstevel@tonic-gate 
1637*7c478bd9Sstevel@tonic-gate 			/* pretend the message was sent */
1638*7c478bd9Sstevel@tonic-gate 			/* XXX should we log something here? */
1639*7c478bd9Sstevel@tonic-gate 			to->q_state = QS_DISCARDED;
1640*7c478bd9Sstevel@tonic-gate 
1641*7c478bd9Sstevel@tonic-gate 			/*
1642*7c478bd9Sstevel@tonic-gate 			**  Remove discard bit to prevent discard of
1643*7c478bd9Sstevel@tonic-gate 			**  future recipients.  This is safe because the
1644*7c478bd9Sstevel@tonic-gate 			**  true "global discard" has been handled before
1645*7c478bd9Sstevel@tonic-gate 			**  we get here.
1646*7c478bd9Sstevel@tonic-gate 			*/
1647*7c478bd9Sstevel@tonic-gate 
1648*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_DISCARD;
1649*7c478bd9Sstevel@tonic-gate 			continue;
1650*7c478bd9Sstevel@tonic-gate 		}
1651*7c478bd9Sstevel@tonic-gate 
1652*7c478bd9Sstevel@tonic-gate 		/*
1653*7c478bd9Sstevel@tonic-gate 		**  Strip quote bits from names if the mailer is dumb
1654*7c478bd9Sstevel@tonic-gate 		**	about them.
1655*7c478bd9Sstevel@tonic-gate 		*/
1656*7c478bd9Sstevel@tonic-gate 
1657*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_STRIPQ, m->m_flags))
1658*7c478bd9Sstevel@tonic-gate 		{
1659*7c478bd9Sstevel@tonic-gate 			stripquotes(user);
1660*7c478bd9Sstevel@tonic-gate 			stripquotes(host);
1661*7c478bd9Sstevel@tonic-gate 		}
1662*7c478bd9Sstevel@tonic-gate 
1663*7c478bd9Sstevel@tonic-gate 		/*
1664*7c478bd9Sstevel@tonic-gate 		**  Strip all leading backslashes if requested and the
1665*7c478bd9Sstevel@tonic-gate 		**  next character is alphanumerical (the latter can
1666*7c478bd9Sstevel@tonic-gate 		**  probably relaxed a bit, see RFC2821).
1667*7c478bd9Sstevel@tonic-gate 		*/
1668*7c478bd9Sstevel@tonic-gate 
1669*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
1670*7c478bd9Sstevel@tonic-gate 			stripbackslash(user);
1671*7c478bd9Sstevel@tonic-gate 
1672*7c478bd9Sstevel@tonic-gate 		/* hack attack -- delivermail compatibility */
1673*7c478bd9Sstevel@tonic-gate 		if (m == ProgMailer && *user == '|')
1674*7c478bd9Sstevel@tonic-gate 			user++;
1675*7c478bd9Sstevel@tonic-gate 
1676*7c478bd9Sstevel@tonic-gate 		/*
1677*7c478bd9Sstevel@tonic-gate 		**  If an error message has already been given, don't
1678*7c478bd9Sstevel@tonic-gate 		**	bother to send to this address.
1679*7c478bd9Sstevel@tonic-gate 		**
1680*7c478bd9Sstevel@tonic-gate 		**	>>>>>>>>>> This clause assumes that the local mailer
1681*7c478bd9Sstevel@tonic-gate 		**	>> NOTE >> cannot do any further aliasing; that
1682*7c478bd9Sstevel@tonic-gate 		**	>>>>>>>>>> function is subsumed by sendmail.
1683*7c478bd9Sstevel@tonic-gate 		*/
1684*7c478bd9Sstevel@tonic-gate 
1685*7c478bd9Sstevel@tonic-gate 		if (!QS_IS_OK(to->q_state))
1686*7c478bd9Sstevel@tonic-gate 			continue;
1687*7c478bd9Sstevel@tonic-gate 
1688*7c478bd9Sstevel@tonic-gate 		/*
1689*7c478bd9Sstevel@tonic-gate 		**  See if this user name is "special".
1690*7c478bd9Sstevel@tonic-gate 		**	If the user name has a slash in it, assume that this
1691*7c478bd9Sstevel@tonic-gate 		**	is a file -- send it off without further ado.  Note
1692*7c478bd9Sstevel@tonic-gate 		**	that this type of addresses is not processed along
1693*7c478bd9Sstevel@tonic-gate 		**	with the others, so we fudge on the To person.
1694*7c478bd9Sstevel@tonic-gate 		*/
1695*7c478bd9Sstevel@tonic-gate 
1696*7c478bd9Sstevel@tonic-gate 		if (strcmp(m->m_mailer, "[FILE]") == 0)
1697*7c478bd9Sstevel@tonic-gate 		{
1698*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, 'u', user);
1699*7c478bd9Sstevel@tonic-gate 			p = to->q_home;
1700*7c478bd9Sstevel@tonic-gate 			if (p == NULL && ctladdr != NULL)
1701*7c478bd9Sstevel@tonic-gate 				p = ctladdr->q_home;
1702*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, 'z', p);
1703*7c478bd9Sstevel@tonic-gate 			expand(m->m_argv[1], buf, sizeof buf, e);
1704*7c478bd9Sstevel@tonic-gate 			if (strlen(buf) > 0)
1705*7c478bd9Sstevel@tonic-gate 				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
1706*7c478bd9Sstevel@tonic-gate 			else
1707*7c478bd9Sstevel@tonic-gate 			{
1708*7c478bd9Sstevel@tonic-gate 				syserr("empty filename specification for mailer %s",
1709*7c478bd9Sstevel@tonic-gate 				       m->m_name);
1710*7c478bd9Sstevel@tonic-gate 				rcode = EX_CONFIG;
1711*7c478bd9Sstevel@tonic-gate 			}
1712*7c478bd9Sstevel@tonic-gate 			giveresponse(rcode, to->q_status, m, NULL,
1713*7c478bd9Sstevel@tonic-gate 				     ctladdr, xstart, e, to);
1714*7c478bd9Sstevel@tonic-gate 			markfailure(e, to, NULL, rcode, true);
1715*7c478bd9Sstevel@tonic-gate 			e->e_nsent++;
1716*7c478bd9Sstevel@tonic-gate 			if (rcode == EX_OK)
1717*7c478bd9Sstevel@tonic-gate 			{
1718*7c478bd9Sstevel@tonic-gate 				to->q_state = QS_SENT;
1719*7c478bd9Sstevel@tonic-gate 				if (bitnset(M_LOCALMAILER, m->m_flags) &&
1720*7c478bd9Sstevel@tonic-gate 				    bitset(QPINGONSUCCESS, to->q_flags))
1721*7c478bd9Sstevel@tonic-gate 				{
1722*7c478bd9Sstevel@tonic-gate 					to->q_flags |= QDELIVERED;
1723*7c478bd9Sstevel@tonic-gate 					to->q_status = "2.1.5";
1724*7c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(e->e_xfp,
1725*7c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
1726*7c478bd9Sstevel@tonic-gate 							     "%s... Successfully delivered\n",
1727*7c478bd9Sstevel@tonic-gate 							     to->q_paddr);
1728*7c478bd9Sstevel@tonic-gate 				}
1729*7c478bd9Sstevel@tonic-gate 			}
1730*7c478bd9Sstevel@tonic-gate 			to->q_statdate = curtime();
1731*7c478bd9Sstevel@tonic-gate 			markstats(e, to, STATS_NORMAL);
1732*7c478bd9Sstevel@tonic-gate 			continue;
1733*7c478bd9Sstevel@tonic-gate 		}
1734*7c478bd9Sstevel@tonic-gate 
1735*7c478bd9Sstevel@tonic-gate 		/*
1736*7c478bd9Sstevel@tonic-gate 		**  Address is verified -- add this user to mailer
1737*7c478bd9Sstevel@tonic-gate 		**  argv, and add it to the print list of recipients.
1738*7c478bd9Sstevel@tonic-gate 		*/
1739*7c478bd9Sstevel@tonic-gate 
1740*7c478bd9Sstevel@tonic-gate 		/* link together the chain of recipients */
1741*7c478bd9Sstevel@tonic-gate 		to->q_tchain = tochain;
1742*7c478bd9Sstevel@tonic-gate 		tochain = to;
1743*7c478bd9Sstevel@tonic-gate 		e->e_to = "[CHAIN]";
1744*7c478bd9Sstevel@tonic-gate 
1745*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
1746*7c478bd9Sstevel@tonic-gate 		p = to->q_home;
1747*7c478bd9Sstevel@tonic-gate 		if (p == NULL && ctladdr != NULL)
1748*7c478bd9Sstevel@tonic-gate 			p = ctladdr->q_home;
1749*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
1750*7c478bd9Sstevel@tonic-gate 
1751*7c478bd9Sstevel@tonic-gate 		/* set the ${dsn_notify} macro if applicable */
1752*7c478bd9Sstevel@tonic-gate 		if (bitset(QHASNOTIFY, to->q_flags))
1753*7c478bd9Sstevel@tonic-gate 		{
1754*7c478bd9Sstevel@tonic-gate 			char notify[MAXLINE];
1755*7c478bd9Sstevel@tonic-gate 
1756*7c478bd9Sstevel@tonic-gate 			notify[0] = '\0';
1757*7c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONSUCCESS, to->q_flags))
1758*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcat(notify, "SUCCESS,",
1759*7c478bd9Sstevel@tonic-gate 						  sizeof notify);
1760*7c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONFAILURE, to->q_flags))
1761*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcat(notify, "FAILURE,",
1762*7c478bd9Sstevel@tonic-gate 						  sizeof notify);
1763*7c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONDELAY, to->q_flags))
1764*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcat(notify, "DELAY,",
1765*7c478bd9Sstevel@tonic-gate 						  sizeof notify);
1766*7c478bd9Sstevel@tonic-gate 
1767*7c478bd9Sstevel@tonic-gate 			/* Set to NEVER or drop trailing comma */
1768*7c478bd9Sstevel@tonic-gate 			if (notify[0] == '\0')
1769*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcat(notify, "NEVER",
1770*7c478bd9Sstevel@tonic-gate 						  sizeof notify);
1771*7c478bd9Sstevel@tonic-gate 			else
1772*7c478bd9Sstevel@tonic-gate 				notify[strlen(notify) - 1] = '\0';
1773*7c478bd9Sstevel@tonic-gate 
1774*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP,
1775*7c478bd9Sstevel@tonic-gate 				macid("{dsn_notify}"), notify);
1776*7c478bd9Sstevel@tonic-gate 		}
1777*7c478bd9Sstevel@tonic-gate 		else
1778*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
1779*7c478bd9Sstevel@tonic-gate 				macid("{dsn_notify}"), NULL);
1780*7c478bd9Sstevel@tonic-gate 
1781*7c478bd9Sstevel@tonic-gate 		/*
1782*7c478bd9Sstevel@tonic-gate 		**  Expand out this user into argument list.
1783*7c478bd9Sstevel@tonic-gate 		*/
1784*7c478bd9Sstevel@tonic-gate 
1785*7c478bd9Sstevel@tonic-gate 		if (!clever)
1786*7c478bd9Sstevel@tonic-gate 		{
1787*7c478bd9Sstevel@tonic-gate 			expand(*mvp, buf, sizeof buf, e);
1788*7c478bd9Sstevel@tonic-gate 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1789*7c478bd9Sstevel@tonic-gate 			if (pvp >= &pv[MAXPV - 2])
1790*7c478bd9Sstevel@tonic-gate 			{
1791*7c478bd9Sstevel@tonic-gate 				/* allow some space for trailing parms */
1792*7c478bd9Sstevel@tonic-gate 				break;
1793*7c478bd9Sstevel@tonic-gate 			}
1794*7c478bd9Sstevel@tonic-gate 		}
1795*7c478bd9Sstevel@tonic-gate 	}
1796*7c478bd9Sstevel@tonic-gate 
1797*7c478bd9Sstevel@tonic-gate 	/* see if any addresses still exist */
1798*7c478bd9Sstevel@tonic-gate 	if (tochain == NULL)
1799*7c478bd9Sstevel@tonic-gate 	{
1800*7c478bd9Sstevel@tonic-gate 		rcode = 0;
1801*7c478bd9Sstevel@tonic-gate 		goto cleanup;
1802*7c478bd9Sstevel@tonic-gate 	}
1803*7c478bd9Sstevel@tonic-gate 
1804*7c478bd9Sstevel@tonic-gate 	/* print out messages as full list */
1805*7c478bd9Sstevel@tonic-gate 	strsize = 1;
1806*7c478bd9Sstevel@tonic-gate 	for (to = tochain; to != NULL; to = to->q_tchain)
1807*7c478bd9Sstevel@tonic-gate 		strsize += strlen(to->q_paddr) + 1;
1808*7c478bd9Sstevel@tonic-gate 	if (strsize < TOBUFSIZE)
1809*7c478bd9Sstevel@tonic-gate 		strsize = TOBUFSIZE;
1810*7c478bd9Sstevel@tonic-gate 	if (strsize > tobufsize)
1811*7c478bd9Sstevel@tonic-gate 	{
1812*7c478bd9Sstevel@tonic-gate 		SM_FREE_CLR(tobuf);
1813*7c478bd9Sstevel@tonic-gate 		tobuf = sm_pmalloc_x(strsize);
1814*7c478bd9Sstevel@tonic-gate 		tobufsize = strsize;
1815*7c478bd9Sstevel@tonic-gate 	}
1816*7c478bd9Sstevel@tonic-gate 	p = tobuf;
1817*7c478bd9Sstevel@tonic-gate 	*p = '\0';
1818*7c478bd9Sstevel@tonic-gate 	for (to = tochain; to != NULL; to = to->q_tchain)
1819*7c478bd9Sstevel@tonic-gate 	{
1820*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
1821*7c478bd9Sstevel@tonic-gate 				   ",", to->q_paddr);
1822*7c478bd9Sstevel@tonic-gate 		p += strlen(p);
1823*7c478bd9Sstevel@tonic-gate 	}
1824*7c478bd9Sstevel@tonic-gate 	e->e_to = tobuf + 1;
1825*7c478bd9Sstevel@tonic-gate 
1826*7c478bd9Sstevel@tonic-gate 	/*
1827*7c478bd9Sstevel@tonic-gate 	**  Fill out any parameters after the $u parameter.
1828*7c478bd9Sstevel@tonic-gate 	*/
1829*7c478bd9Sstevel@tonic-gate 
1830*7c478bd9Sstevel@tonic-gate 	if (!clever)
1831*7c478bd9Sstevel@tonic-gate 	{
1832*7c478bd9Sstevel@tonic-gate 		while (*++mvp != NULL)
1833*7c478bd9Sstevel@tonic-gate 		{
1834*7c478bd9Sstevel@tonic-gate 			expand(*mvp, buf, sizeof buf, e);
1835*7c478bd9Sstevel@tonic-gate 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1836*7c478bd9Sstevel@tonic-gate 			if (pvp >= &pv[MAXPV])
1837*7c478bd9Sstevel@tonic-gate 				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
1838*7c478bd9Sstevel@tonic-gate 				       pv[0]);
1839*7c478bd9Sstevel@tonic-gate 		}
1840*7c478bd9Sstevel@tonic-gate 	}
1841*7c478bd9Sstevel@tonic-gate 	*pvp++ = NULL;
1842*7c478bd9Sstevel@tonic-gate 
1843*7c478bd9Sstevel@tonic-gate 	/*
1844*7c478bd9Sstevel@tonic-gate 	**  Call the mailer.
1845*7c478bd9Sstevel@tonic-gate 	**	The argument vector gets built, pipes
1846*7c478bd9Sstevel@tonic-gate 	**	are created as necessary, and we fork & exec as
1847*7c478bd9Sstevel@tonic-gate 	**	appropriate.
1848*7c478bd9Sstevel@tonic-gate 	**	If we are running SMTP, we just need to clean up.
1849*7c478bd9Sstevel@tonic-gate 	*/
1850*7c478bd9Sstevel@tonic-gate 
1851*7c478bd9Sstevel@tonic-gate 	/* XXX this seems a bit wierd */
1852*7c478bd9Sstevel@tonic-gate 	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
1853*7c478bd9Sstevel@tonic-gate 	    bitset(QGOODUID, e->e_from.q_flags))
1854*7c478bd9Sstevel@tonic-gate 		ctladdr = &e->e_from;
1855*7c478bd9Sstevel@tonic-gate 
1856*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
1857*7c478bd9Sstevel@tonic-gate 	if (ConfigLevel < 2)
1858*7c478bd9Sstevel@tonic-gate 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
1859*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
1860*7c478bd9Sstevel@tonic-gate 
1861*7c478bd9Sstevel@tonic-gate 	if (tTd(11, 1))
1862*7c478bd9Sstevel@tonic-gate 	{
1863*7c478bd9Sstevel@tonic-gate 		sm_dprintf("openmailer:");
1864*7c478bd9Sstevel@tonic-gate 		printav(sm_debug_file(), pv);
1865*7c478bd9Sstevel@tonic-gate 	}
1866*7c478bd9Sstevel@tonic-gate 	errno = 0;
1867*7c478bd9Sstevel@tonic-gate 	SM_SET_H_ERRNO(0);
1868*7c478bd9Sstevel@tonic-gate 	CurHostName = NULL;
1869*7c478bd9Sstevel@tonic-gate 
1870*7c478bd9Sstevel@tonic-gate 	/*
1871*7c478bd9Sstevel@tonic-gate 	**  Deal with the special case of mail handled through an IPC
1872*7c478bd9Sstevel@tonic-gate 	**  connection.
1873*7c478bd9Sstevel@tonic-gate 	**	In this case we don't actually fork.  We must be
1874*7c478bd9Sstevel@tonic-gate 	**	running SMTP for this to work.  We will return a
1875*7c478bd9Sstevel@tonic-gate 	**	zero pid to indicate that we are running IPC.
1876*7c478bd9Sstevel@tonic-gate 	**  We also handle a debug version that just talks to stdin/out.
1877*7c478bd9Sstevel@tonic-gate 	*/
1878*7c478bd9Sstevel@tonic-gate 
1879*7c478bd9Sstevel@tonic-gate 	curhost = NULL;
1880*7c478bd9Sstevel@tonic-gate 	SmtpPhase = NULL;
1881*7c478bd9Sstevel@tonic-gate 	mci = NULL;
1882*7c478bd9Sstevel@tonic-gate 
1883*7c478bd9Sstevel@tonic-gate #if XDEBUG
1884*7c478bd9Sstevel@tonic-gate 	{
1885*7c478bd9Sstevel@tonic-gate 		char wbuf[MAXLINE];
1886*7c478bd9Sstevel@tonic-gate 
1887*7c478bd9Sstevel@tonic-gate 		/* make absolutely certain 0, 1, and 2 are in use */
1888*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
1889*7c478bd9Sstevel@tonic-gate 				   shortenstring(e->e_to, MAXSHORTSTR),
1890*7c478bd9Sstevel@tonic-gate 				   m->m_name);
1891*7c478bd9Sstevel@tonic-gate 		checkfd012(wbuf);
1892*7c478bd9Sstevel@tonic-gate 	}
1893*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
1894*7c478bd9Sstevel@tonic-gate 
1895*7c478bd9Sstevel@tonic-gate 	/* check for 8-bit available */
1896*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags) &&
1897*7c478bd9Sstevel@tonic-gate 	    bitnset(M_7BITS, m->m_flags) &&
1898*7c478bd9Sstevel@tonic-gate 	    (bitset(EF_DONT_MIME, e->e_flags) ||
1899*7c478bd9Sstevel@tonic-gate 	     !(bitset(MM_MIME8BIT, MimeMode) ||
1900*7c478bd9Sstevel@tonic-gate 	       (bitset(EF_IS_MIME, e->e_flags) &&
1901*7c478bd9Sstevel@tonic-gate 		bitset(MM_CVTMIME, MimeMode)))))
1902*7c478bd9Sstevel@tonic-gate 	{
1903*7c478bd9Sstevel@tonic-gate 		e->e_status = "5.6.3";
1904*7c478bd9Sstevel@tonic-gate 		usrerrenh(e->e_status,
1905*7c478bd9Sstevel@tonic-gate 			  "554 Cannot send 8-bit data to 7-bit destination");
1906*7c478bd9Sstevel@tonic-gate 		rcode = EX_DATAERR;
1907*7c478bd9Sstevel@tonic-gate 		goto give_up;
1908*7c478bd9Sstevel@tonic-gate 	}
1909*7c478bd9Sstevel@tonic-gate 
1910*7c478bd9Sstevel@tonic-gate 	if (tTd(62, 8))
1911*7c478bd9Sstevel@tonic-gate 		checkfds("before delivery");
1912*7c478bd9Sstevel@tonic-gate 
1913*7c478bd9Sstevel@tonic-gate 	/* check for Local Person Communication -- not for mortals!!! */
1914*7c478bd9Sstevel@tonic-gate 	if (strcmp(m->m_mailer, "[LPC]") == 0)
1915*7c478bd9Sstevel@tonic-gate 	{
1916*7c478bd9Sstevel@tonic-gate 		if (clever)
1917*7c478bd9Sstevel@tonic-gate 		{
1918*7c478bd9Sstevel@tonic-gate 			/* flush any expired connections */
1919*7c478bd9Sstevel@tonic-gate 			(void) mci_scan(NULL);
1920*7c478bd9Sstevel@tonic-gate 
1921*7c478bd9Sstevel@tonic-gate 			/* try to get a cached connection or just a slot */
1922*7c478bd9Sstevel@tonic-gate 			mci = mci_get(m->m_name, m);
1923*7c478bd9Sstevel@tonic-gate 			if (mci->mci_host == NULL)
1924*7c478bd9Sstevel@tonic-gate 				mci->mci_host = m->m_name;
1925*7c478bd9Sstevel@tonic-gate 			CurHostName = mci->mci_host;
1926*7c478bd9Sstevel@tonic-gate 			if (mci->mci_state != MCIS_CLOSED)
1927*7c478bd9Sstevel@tonic-gate 			{
1928*7c478bd9Sstevel@tonic-gate 				message("Using cached SMTP/LPC connection for %s...",
1929*7c478bd9Sstevel@tonic-gate 					m->m_name);
1930*7c478bd9Sstevel@tonic-gate 				mci->mci_deliveries++;
1931*7c478bd9Sstevel@tonic-gate 				goto do_transfer;
1932*7c478bd9Sstevel@tonic-gate 			}
1933*7c478bd9Sstevel@tonic-gate 		}
1934*7c478bd9Sstevel@tonic-gate 		else
1935*7c478bd9Sstevel@tonic-gate 		{
1936*7c478bd9Sstevel@tonic-gate 			mci = mci_new(e->e_rpool);
1937*7c478bd9Sstevel@tonic-gate 		}
1938*7c478bd9Sstevel@tonic-gate 		mci->mci_in = smioin;
1939*7c478bd9Sstevel@tonic-gate 		mci->mci_out = smioout;
1940*7c478bd9Sstevel@tonic-gate 		mci->mci_mailer = m;
1941*7c478bd9Sstevel@tonic-gate 		mci->mci_host = m->m_name;
1942*7c478bd9Sstevel@tonic-gate 		if (clever)
1943*7c478bd9Sstevel@tonic-gate 		{
1944*7c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_OPENING;
1945*7c478bd9Sstevel@tonic-gate 			mci_cache(mci);
1946*7c478bd9Sstevel@tonic-gate 		}
1947*7c478bd9Sstevel@tonic-gate 		else
1948*7c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_OPEN;
1949*7c478bd9Sstevel@tonic-gate 	}
1950*7c478bd9Sstevel@tonic-gate 	else if (strcmp(m->m_mailer, "[IPC]") == 0)
1951*7c478bd9Sstevel@tonic-gate 	{
1952*7c478bd9Sstevel@tonic-gate 		register int i;
1953*7c478bd9Sstevel@tonic-gate 
1954*7c478bd9Sstevel@tonic-gate 		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
1955*7c478bd9Sstevel@tonic-gate 		{
1956*7c478bd9Sstevel@tonic-gate 			syserr("null destination for %s mailer", m->m_mailer);
1957*7c478bd9Sstevel@tonic-gate 			rcode = EX_CONFIG;
1958*7c478bd9Sstevel@tonic-gate 			goto give_up;
1959*7c478bd9Sstevel@tonic-gate 		}
1960*7c478bd9Sstevel@tonic-gate 
1961*7c478bd9Sstevel@tonic-gate # if NETUNIX
1962*7c478bd9Sstevel@tonic-gate 		if (strcmp(pv[0], "FILE") == 0)
1963*7c478bd9Sstevel@tonic-gate 		{
1964*7c478bd9Sstevel@tonic-gate 			curhost = CurHostName = "localhost";
1965*7c478bd9Sstevel@tonic-gate 			mux_path = pv[1];
1966*7c478bd9Sstevel@tonic-gate 		}
1967*7c478bd9Sstevel@tonic-gate 		else
1968*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
1969*7c478bd9Sstevel@tonic-gate 		{
1970*7c478bd9Sstevel@tonic-gate 			CurHostName = pv[1];
1971*7c478bd9Sstevel@tonic-gate 			curhost = hostsignature(m, pv[1]);
1972*7c478bd9Sstevel@tonic-gate 		}
1973*7c478bd9Sstevel@tonic-gate 
1974*7c478bd9Sstevel@tonic-gate 		if (curhost == NULL || curhost[0] == '\0')
1975*7c478bd9Sstevel@tonic-gate 		{
1976*7c478bd9Sstevel@tonic-gate 			syserr("null host signature for %s", pv[1]);
1977*7c478bd9Sstevel@tonic-gate 			rcode = EX_CONFIG;
1978*7c478bd9Sstevel@tonic-gate 			goto give_up;
1979*7c478bd9Sstevel@tonic-gate 		}
1980*7c478bd9Sstevel@tonic-gate 
1981*7c478bd9Sstevel@tonic-gate 		if (!clever)
1982*7c478bd9Sstevel@tonic-gate 		{
1983*7c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 non-clever IPC");
1984*7c478bd9Sstevel@tonic-gate 			rcode = EX_CONFIG;
1985*7c478bd9Sstevel@tonic-gate 			goto give_up;
1986*7c478bd9Sstevel@tonic-gate 		}
1987*7c478bd9Sstevel@tonic-gate 		if (pv[2] != NULL
1988*7c478bd9Sstevel@tonic-gate # if NETUNIX
1989*7c478bd9Sstevel@tonic-gate 		    && mux_path == NULL
1990*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
1991*7c478bd9Sstevel@tonic-gate 		    )
1992*7c478bd9Sstevel@tonic-gate 		{
1993*7c478bd9Sstevel@tonic-gate 			port = htons((unsigned short) atoi(pv[2]));
1994*7c478bd9Sstevel@tonic-gate 			if (port == 0)
1995*7c478bd9Sstevel@tonic-gate 			{
1996*7c478bd9Sstevel@tonic-gate # ifdef NO_GETSERVBYNAME
1997*7c478bd9Sstevel@tonic-gate 				syserr("Invalid port number: %s", pv[2]);
1998*7c478bd9Sstevel@tonic-gate # else /* NO_GETSERVBYNAME */
1999*7c478bd9Sstevel@tonic-gate 				struct servent *sp = getservbyname(pv[2], "tcp");
2000*7c478bd9Sstevel@tonic-gate 
2001*7c478bd9Sstevel@tonic-gate 				if (sp == NULL)
2002*7c478bd9Sstevel@tonic-gate 					syserr("Service %s unknown", pv[2]);
2003*7c478bd9Sstevel@tonic-gate 				else
2004*7c478bd9Sstevel@tonic-gate 					port = sp->s_port;
2005*7c478bd9Sstevel@tonic-gate # endif /* NO_GETSERVBYNAME */
2006*7c478bd9Sstevel@tonic-gate 			}
2007*7c478bd9Sstevel@tonic-gate 		}
2008*7c478bd9Sstevel@tonic-gate 
2009*7c478bd9Sstevel@tonic-gate 		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
2010*7c478bd9Sstevel@tonic-gate 		if (TimeOuts.to_aconnect > 0)
2011*7c478bd9Sstevel@tonic-gate 			enough = curtime() + TimeOuts.to_aconnect;
2012*7c478bd9Sstevel@tonic-gate tryhost:
2013*7c478bd9Sstevel@tonic-gate 		while (hostnum < nummxhosts)
2014*7c478bd9Sstevel@tonic-gate 		{
2015*7c478bd9Sstevel@tonic-gate 			char sep = ':';
2016*7c478bd9Sstevel@tonic-gate 			char *endp;
2017*7c478bd9Sstevel@tonic-gate 			static char hostbuf[MAXNAME + 1];
2018*7c478bd9Sstevel@tonic-gate 			bool tried_fallbacksmarthost = false;
2019*7c478bd9Sstevel@tonic-gate 
2020*7c478bd9Sstevel@tonic-gate # if NETINET6
2021*7c478bd9Sstevel@tonic-gate 			if (*mxhosts[hostnum] == '[')
2022*7c478bd9Sstevel@tonic-gate 			{
2023*7c478bd9Sstevel@tonic-gate 				endp = strchr(mxhosts[hostnum] + 1, ']');
2024*7c478bd9Sstevel@tonic-gate 				if (endp != NULL)
2025*7c478bd9Sstevel@tonic-gate 					endp = strpbrk(endp + 1, ":,");
2026*7c478bd9Sstevel@tonic-gate 			}
2027*7c478bd9Sstevel@tonic-gate 			else
2028*7c478bd9Sstevel@tonic-gate 				endp = strpbrk(mxhosts[hostnum], ":,");
2029*7c478bd9Sstevel@tonic-gate # else /* NETINET6 */
2030*7c478bd9Sstevel@tonic-gate 			endp = strpbrk(mxhosts[hostnum], ":,");
2031*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */
2032*7c478bd9Sstevel@tonic-gate 			if (endp != NULL)
2033*7c478bd9Sstevel@tonic-gate 			{
2034*7c478bd9Sstevel@tonic-gate 				sep = *endp;
2035*7c478bd9Sstevel@tonic-gate 				*endp = '\0';
2036*7c478bd9Sstevel@tonic-gate 			}
2037*7c478bd9Sstevel@tonic-gate 
2038*7c478bd9Sstevel@tonic-gate 			if (hostnum == 1 && skip_back != NULL)
2039*7c478bd9Sstevel@tonic-gate 			{
2040*7c478bd9Sstevel@tonic-gate 				/*
2041*7c478bd9Sstevel@tonic-gate 				**  Coattail piggybacking is no longer an
2042*7c478bd9Sstevel@tonic-gate 				**  option with the mail host next to be tried
2043*7c478bd9Sstevel@tonic-gate 				**  no longer the lowest MX preference
2044*7c478bd9Sstevel@tonic-gate 				**  (hostnum == 1 meaning we're on the second
2045*7c478bd9Sstevel@tonic-gate 				**  preference). We do not try to coattail
2046*7c478bd9Sstevel@tonic-gate 				**  piggyback more than the first MX preference.
2047*7c478bd9Sstevel@tonic-gate 				**  Revert 'tochain' to last location for
2048*7c478bd9Sstevel@tonic-gate 				**  coincidental piggybacking. This works this
2049*7c478bd9Sstevel@tonic-gate 				**  easily because the q_tchain kept getting
2050*7c478bd9Sstevel@tonic-gate 				**  added to the top of the linked list.
2051*7c478bd9Sstevel@tonic-gate 				*/
2052*7c478bd9Sstevel@tonic-gate 
2053*7c478bd9Sstevel@tonic-gate 				tochain = skip_back;
2054*7c478bd9Sstevel@tonic-gate 			}
2055*7c478bd9Sstevel@tonic-gate 
2056*7c478bd9Sstevel@tonic-gate 			if (*mxhosts[hostnum] == '\0')
2057*7c478bd9Sstevel@tonic-gate 			{
2058*7c478bd9Sstevel@tonic-gate 				syserr("deliver: null host name in signature");
2059*7c478bd9Sstevel@tonic-gate 				hostnum++;
2060*7c478bd9Sstevel@tonic-gate 				if (endp != NULL)
2061*7c478bd9Sstevel@tonic-gate 					*endp = sep;
2062*7c478bd9Sstevel@tonic-gate 				continue;
2063*7c478bd9Sstevel@tonic-gate 			}
2064*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
2065*7c478bd9Sstevel@tonic-gate 					  sizeof hostbuf);
2066*7c478bd9Sstevel@tonic-gate 			hostnum++;
2067*7c478bd9Sstevel@tonic-gate 			if (endp != NULL)
2068*7c478bd9Sstevel@tonic-gate 				*endp = sep;
2069*7c478bd9Sstevel@tonic-gate 
2070*7c478bd9Sstevel@tonic-gate   one_last_try:
2071*7c478bd9Sstevel@tonic-gate 			/* see if we already know that this host is fried */
2072*7c478bd9Sstevel@tonic-gate 			CurHostName = hostbuf;
2073*7c478bd9Sstevel@tonic-gate 			mci = mci_get(hostbuf, m);
2074*7c478bd9Sstevel@tonic-gate 			if (mci->mci_state != MCIS_CLOSED)
2075*7c478bd9Sstevel@tonic-gate 			{
2076*7c478bd9Sstevel@tonic-gate 				char *type;
2077*7c478bd9Sstevel@tonic-gate 
2078*7c478bd9Sstevel@tonic-gate 				if (tTd(11, 1))
2079*7c478bd9Sstevel@tonic-gate 				{
2080*7c478bd9Sstevel@tonic-gate 					sm_dprintf("openmailer: ");
2081*7c478bd9Sstevel@tonic-gate 					mci_dump(sm_debug_file(), mci, false);
2082*7c478bd9Sstevel@tonic-gate 				}
2083*7c478bd9Sstevel@tonic-gate 				CurHostName = mci->mci_host;
2084*7c478bd9Sstevel@tonic-gate 				if (bitnset(M_LMTP, m->m_flags))
2085*7c478bd9Sstevel@tonic-gate 					type = "L";
2086*7c478bd9Sstevel@tonic-gate 				else if (bitset(MCIF_ESMTP, mci->mci_flags))
2087*7c478bd9Sstevel@tonic-gate 					type = "ES";
2088*7c478bd9Sstevel@tonic-gate 				else
2089*7c478bd9Sstevel@tonic-gate 					type = "S";
2090*7c478bd9Sstevel@tonic-gate 				message("Using cached %sMTP connection to %s via %s...",
2091*7c478bd9Sstevel@tonic-gate 					type, hostbuf, m->m_name);
2092*7c478bd9Sstevel@tonic-gate 				mci->mci_deliveries++;
2093*7c478bd9Sstevel@tonic-gate 				break;
2094*7c478bd9Sstevel@tonic-gate 			}
2095*7c478bd9Sstevel@tonic-gate 			mci->mci_mailer = m;
2096*7c478bd9Sstevel@tonic-gate 			if (mci->mci_exitstat != EX_OK)
2097*7c478bd9Sstevel@tonic-gate 			{
2098*7c478bd9Sstevel@tonic-gate 				if (mci->mci_exitstat == EX_TEMPFAIL)
2099*7c478bd9Sstevel@tonic-gate 					goodmxfound = true;
2100*7c478bd9Sstevel@tonic-gate 
2101*7c478bd9Sstevel@tonic-gate 				/* Try FallbackSmartHost? */
2102*7c478bd9Sstevel@tonic-gate 				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2103*7c478bd9Sstevel@tonic-gate 						    hostbuf, sizeof hostbuf,
2104*7c478bd9Sstevel@tonic-gate 						    mci->mci_exitstat))
2105*7c478bd9Sstevel@tonic-gate 					goto one_last_try;
2106*7c478bd9Sstevel@tonic-gate 
2107*7c478bd9Sstevel@tonic-gate 				continue;
2108*7c478bd9Sstevel@tonic-gate 			}
2109*7c478bd9Sstevel@tonic-gate 
2110*7c478bd9Sstevel@tonic-gate 			if (mci_lock_host(mci) != EX_OK)
2111*7c478bd9Sstevel@tonic-gate 			{
2112*7c478bd9Sstevel@tonic-gate 				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
2113*7c478bd9Sstevel@tonic-gate 				goodmxfound = true;
2114*7c478bd9Sstevel@tonic-gate 				continue;
2115*7c478bd9Sstevel@tonic-gate 			}
2116*7c478bd9Sstevel@tonic-gate 
2117*7c478bd9Sstevel@tonic-gate 			/* try the connection */
2118*7c478bd9Sstevel@tonic-gate 			sm_setproctitle(true, e, "%s %s: %s",
2119*7c478bd9Sstevel@tonic-gate 					qid_printname(e),
2120*7c478bd9Sstevel@tonic-gate 					hostbuf, "user open");
2121*7c478bd9Sstevel@tonic-gate # if NETUNIX
2122*7c478bd9Sstevel@tonic-gate 			if (mux_path != NULL)
2123*7c478bd9Sstevel@tonic-gate 			{
2124*7c478bd9Sstevel@tonic-gate 				message("Connecting to %s via %s...",
2125*7c478bd9Sstevel@tonic-gate 					mux_path, m->m_name);
2126*7c478bd9Sstevel@tonic-gate 				i = makeconnection_ds((char *) mux_path, mci);
2127*7c478bd9Sstevel@tonic-gate 			}
2128*7c478bd9Sstevel@tonic-gate 			else
2129*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */
2130*7c478bd9Sstevel@tonic-gate 			{
2131*7c478bd9Sstevel@tonic-gate 				if (port == 0)
2132*7c478bd9Sstevel@tonic-gate 					message("Connecting to %s via %s...",
2133*7c478bd9Sstevel@tonic-gate 						hostbuf, m->m_name);
2134*7c478bd9Sstevel@tonic-gate 				else
2135*7c478bd9Sstevel@tonic-gate 					message("Connecting to %s port %d via %s...",
2136*7c478bd9Sstevel@tonic-gate 						hostbuf, ntohs(port),
2137*7c478bd9Sstevel@tonic-gate 						m->m_name);
2138*7c478bd9Sstevel@tonic-gate 				i = makeconnection(hostbuf, port, mci, e,
2139*7c478bd9Sstevel@tonic-gate 						   enough);
2140*7c478bd9Sstevel@tonic-gate 			}
2141*7c478bd9Sstevel@tonic-gate 			mci->mci_errno = errno;
2142*7c478bd9Sstevel@tonic-gate 			mci->mci_lastuse = curtime();
2143*7c478bd9Sstevel@tonic-gate 			mci->mci_deliveries = 0;
2144*7c478bd9Sstevel@tonic-gate 			mci->mci_exitstat = i;
2145*7c478bd9Sstevel@tonic-gate # if NAMED_BIND
2146*7c478bd9Sstevel@tonic-gate 			mci->mci_herrno = h_errno;
2147*7c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */
2148*7c478bd9Sstevel@tonic-gate 
2149*7c478bd9Sstevel@tonic-gate 			/*
2150*7c478bd9Sstevel@tonic-gate 			**  Have we tried long enough to get a connection?
2151*7c478bd9Sstevel@tonic-gate 			**	If yes, skip to the fallback MX hosts
2152*7c478bd9Sstevel@tonic-gate 			**	(if existent).
2153*7c478bd9Sstevel@tonic-gate 			*/
2154*7c478bd9Sstevel@tonic-gate 
2155*7c478bd9Sstevel@tonic-gate 			if (enough > 0 && mci->mci_lastuse >= enough)
2156*7c478bd9Sstevel@tonic-gate 			{
2157*7c478bd9Sstevel@tonic-gate 				int h;
2158*7c478bd9Sstevel@tonic-gate # if NAMED_BIND
2159*7c478bd9Sstevel@tonic-gate 				extern int NumFallbackMXHosts;
2160*7c478bd9Sstevel@tonic-gate # else /* NAMED_BIND */
2161*7c478bd9Sstevel@tonic-gate 				const int NumFallbackMXHosts = 0;
2162*7c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */
2163*7c478bd9Sstevel@tonic-gate 
2164*7c478bd9Sstevel@tonic-gate 				if (hostnum < nummxhosts && LogLevel > 9)
2165*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2166*7c478bd9Sstevel@tonic-gate 						  "Timeout.to_aconnect occurred before exhausting all addresses");
2167*7c478bd9Sstevel@tonic-gate 
2168*7c478bd9Sstevel@tonic-gate 				/* turn off timeout if fallback available */
2169*7c478bd9Sstevel@tonic-gate 				if (NumFallbackMXHosts > 0)
2170*7c478bd9Sstevel@tonic-gate 					enough = 0;
2171*7c478bd9Sstevel@tonic-gate 
2172*7c478bd9Sstevel@tonic-gate 				/* skip to a fallback MX host */
2173*7c478bd9Sstevel@tonic-gate 				h = nummxhosts - NumFallbackMXHosts;
2174*7c478bd9Sstevel@tonic-gate 				if (hostnum < h)
2175*7c478bd9Sstevel@tonic-gate 					hostnum = h;
2176*7c478bd9Sstevel@tonic-gate 			}
2177*7c478bd9Sstevel@tonic-gate 			if (i == EX_OK)
2178*7c478bd9Sstevel@tonic-gate 			{
2179*7c478bd9Sstevel@tonic-gate 				goodmxfound = true;
2180*7c478bd9Sstevel@tonic-gate 				markstats(e, firstto, STATS_CONNECT);
2181*7c478bd9Sstevel@tonic-gate 				mci->mci_state = MCIS_OPENING;
2182*7c478bd9Sstevel@tonic-gate 				mci_cache(mci);
2183*7c478bd9Sstevel@tonic-gate 				if (TrafficLogFile != NULL)
2184*7c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(TrafficLogFile,
2185*7c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
2186*7c478bd9Sstevel@tonic-gate 							     "%05d === CONNECT %s\n",
2187*7c478bd9Sstevel@tonic-gate 							     (int) CurrentPid,
2188*7c478bd9Sstevel@tonic-gate 							     hostbuf);
2189*7c478bd9Sstevel@tonic-gate 				break;
2190*7c478bd9Sstevel@tonic-gate 			}
2191*7c478bd9Sstevel@tonic-gate 			else
2192*7c478bd9Sstevel@tonic-gate 			{
2193*7c478bd9Sstevel@tonic-gate 				/* Try FallbackSmartHost? */
2194*7c478bd9Sstevel@tonic-gate 				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2195*7c478bd9Sstevel@tonic-gate 						    hostbuf, sizeof hostbuf, i))
2196*7c478bd9Sstevel@tonic-gate 					goto one_last_try;
2197*7c478bd9Sstevel@tonic-gate 
2198*7c478bd9Sstevel@tonic-gate 				if (tTd(11, 1))
2199*7c478bd9Sstevel@tonic-gate 					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
2200*7c478bd9Sstevel@tonic-gate 						   i, errno);
2201*7c478bd9Sstevel@tonic-gate 				if (i == EX_TEMPFAIL)
2202*7c478bd9Sstevel@tonic-gate 					goodmxfound = true;
2203*7c478bd9Sstevel@tonic-gate 				mci_unlock_host(mci);
2204*7c478bd9Sstevel@tonic-gate 			}
2205*7c478bd9Sstevel@tonic-gate 
2206*7c478bd9Sstevel@tonic-gate 			/* enter status of this host */
2207*7c478bd9Sstevel@tonic-gate 			setstat(i);
2208*7c478bd9Sstevel@tonic-gate 
2209*7c478bd9Sstevel@tonic-gate 			/* should print some message here for -v mode */
2210*7c478bd9Sstevel@tonic-gate 		}
2211*7c478bd9Sstevel@tonic-gate 		if (mci == NULL)
2212*7c478bd9Sstevel@tonic-gate 		{
2213*7c478bd9Sstevel@tonic-gate 			syserr("deliver: no host name");
2214*7c478bd9Sstevel@tonic-gate 			rcode = EX_SOFTWARE;
2215*7c478bd9Sstevel@tonic-gate 			goto give_up;
2216*7c478bd9Sstevel@tonic-gate 		}
2217*7c478bd9Sstevel@tonic-gate 		mci->mci_pid = 0;
2218*7c478bd9Sstevel@tonic-gate 	}
2219*7c478bd9Sstevel@tonic-gate 	else
2220*7c478bd9Sstevel@tonic-gate 	{
2221*7c478bd9Sstevel@tonic-gate 		/* flush any expired connections */
2222*7c478bd9Sstevel@tonic-gate 		(void) mci_scan(NULL);
2223*7c478bd9Sstevel@tonic-gate 		mci = NULL;
2224*7c478bd9Sstevel@tonic-gate 
2225*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_LMTP, m->m_flags))
2226*7c478bd9Sstevel@tonic-gate 		{
2227*7c478bd9Sstevel@tonic-gate 			/* try to get a cached connection */
2228*7c478bd9Sstevel@tonic-gate 			mci = mci_get(m->m_name, m);
2229*7c478bd9Sstevel@tonic-gate 			if (mci->mci_host == NULL)
2230*7c478bd9Sstevel@tonic-gate 				mci->mci_host = m->m_name;
2231*7c478bd9Sstevel@tonic-gate 			CurHostName = mci->mci_host;
2232*7c478bd9Sstevel@tonic-gate 			if (mci->mci_state != MCIS_CLOSED)
2233*7c478bd9Sstevel@tonic-gate 			{
2234*7c478bd9Sstevel@tonic-gate 				message("Using cached LMTP connection for %s...",
2235*7c478bd9Sstevel@tonic-gate 					m->m_name);
2236*7c478bd9Sstevel@tonic-gate 				mci->mci_deliveries++;
2237*7c478bd9Sstevel@tonic-gate 				goto do_transfer;
2238*7c478bd9Sstevel@tonic-gate 			}
2239*7c478bd9Sstevel@tonic-gate 		}
2240*7c478bd9Sstevel@tonic-gate 
2241*7c478bd9Sstevel@tonic-gate 		/* announce the connection to verbose listeners */
2242*7c478bd9Sstevel@tonic-gate 		if (host == NULL || host[0] == '\0')
2243*7c478bd9Sstevel@tonic-gate 			message("Connecting to %s...", m->m_name);
2244*7c478bd9Sstevel@tonic-gate 		else
2245*7c478bd9Sstevel@tonic-gate 			message("Connecting to %s via %s...", host, m->m_name);
2246*7c478bd9Sstevel@tonic-gate 		if (TrafficLogFile != NULL)
2247*7c478bd9Sstevel@tonic-gate 		{
2248*7c478bd9Sstevel@tonic-gate 			char **av;
2249*7c478bd9Sstevel@tonic-gate 
2250*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2251*7c478bd9Sstevel@tonic-gate 					     "%05d === EXEC", (int) CurrentPid);
2252*7c478bd9Sstevel@tonic-gate 			for (av = pv; *av != NULL; av++)
2253*7c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(TrafficLogFile,
2254*7c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT, " %s",
2255*7c478bd9Sstevel@tonic-gate 						     *av);
2256*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2257*7c478bd9Sstevel@tonic-gate 					     "\n");
2258*7c478bd9Sstevel@tonic-gate 		}
2259*7c478bd9Sstevel@tonic-gate 
2260*7c478bd9Sstevel@tonic-gate #if XDEBUG
2261*7c478bd9Sstevel@tonic-gate 		checkfd012("before creating mail pipe");
2262*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
2263*7c478bd9Sstevel@tonic-gate 
2264*7c478bd9Sstevel@tonic-gate 		/* create a pipe to shove the mail through */
2265*7c478bd9Sstevel@tonic-gate 		if (pipe(mpvect) < 0)
2266*7c478bd9Sstevel@tonic-gate 		{
2267*7c478bd9Sstevel@tonic-gate 			syserr("%s... openmailer(%s): pipe (to mailer)",
2268*7c478bd9Sstevel@tonic-gate 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2269*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 1))
2270*7c478bd9Sstevel@tonic-gate 				sm_dprintf("openmailer: NULL\n");
2271*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2272*7c478bd9Sstevel@tonic-gate 			goto give_up;
2273*7c478bd9Sstevel@tonic-gate 		}
2274*7c478bd9Sstevel@tonic-gate 
2275*7c478bd9Sstevel@tonic-gate #if XDEBUG
2276*7c478bd9Sstevel@tonic-gate 		/* make sure we didn't get one of the standard I/O files */
2277*7c478bd9Sstevel@tonic-gate 		if (mpvect[0] < 3 || mpvect[1] < 3)
2278*7c478bd9Sstevel@tonic-gate 		{
2279*7c478bd9Sstevel@tonic-gate 			syserr("%s... openmailer(%s): bogus mpvect %d %d",
2280*7c478bd9Sstevel@tonic-gate 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
2281*7c478bd9Sstevel@tonic-gate 			       mpvect[0], mpvect[1]);
2282*7c478bd9Sstevel@tonic-gate 			printopenfds(true);
2283*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 1))
2284*7c478bd9Sstevel@tonic-gate 				sm_dprintf("openmailer: NULL\n");
2285*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2286*7c478bd9Sstevel@tonic-gate 			goto give_up;
2287*7c478bd9Sstevel@tonic-gate 		}
2288*7c478bd9Sstevel@tonic-gate 
2289*7c478bd9Sstevel@tonic-gate 		/* make sure system call isn't dead meat */
2290*7c478bd9Sstevel@tonic-gate 		checkfdopen(mpvect[0], "mpvect[0]");
2291*7c478bd9Sstevel@tonic-gate 		checkfdopen(mpvect[1], "mpvect[1]");
2292*7c478bd9Sstevel@tonic-gate 		if (mpvect[0] == mpvect[1] ||
2293*7c478bd9Sstevel@tonic-gate 		    (e->e_lockfp != NULL &&
2294*7c478bd9Sstevel@tonic-gate 		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
2295*7c478bd9Sstevel@tonic-gate 						 NULL) ||
2296*7c478bd9Sstevel@tonic-gate 		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
2297*7c478bd9Sstevel@tonic-gate 						 NULL))))
2298*7c478bd9Sstevel@tonic-gate 		{
2299*7c478bd9Sstevel@tonic-gate 			if (e->e_lockfp == NULL)
2300*7c478bd9Sstevel@tonic-gate 				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
2301*7c478bd9Sstevel@tonic-gate 				       shortenstring(e->e_to, MAXSHORTSTR),
2302*7c478bd9Sstevel@tonic-gate 				       m->m_name, mpvect[0], mpvect[1]);
2303*7c478bd9Sstevel@tonic-gate 			else
2304*7c478bd9Sstevel@tonic-gate 				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
2305*7c478bd9Sstevel@tonic-gate 				       shortenstring(e->e_to, MAXSHORTSTR),
2306*7c478bd9Sstevel@tonic-gate 				       m->m_name, mpvect[0], mpvect[1],
2307*7c478bd9Sstevel@tonic-gate 				       sm_io_getinfo(e->e_lockfp,
2308*7c478bd9Sstevel@tonic-gate 						     SM_IO_WHAT_FD, NULL));
2309*7c478bd9Sstevel@tonic-gate 		}
2310*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
2311*7c478bd9Sstevel@tonic-gate 
2312*7c478bd9Sstevel@tonic-gate 		/* create a return pipe */
2313*7c478bd9Sstevel@tonic-gate 		if (pipe(rpvect) < 0)
2314*7c478bd9Sstevel@tonic-gate 		{
2315*7c478bd9Sstevel@tonic-gate 			syserr("%s... openmailer(%s): pipe (from mailer)",
2316*7c478bd9Sstevel@tonic-gate 			       shortenstring(e->e_to, MAXSHORTSTR),
2317*7c478bd9Sstevel@tonic-gate 			       m->m_name);
2318*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[0]);
2319*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[1]);
2320*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 1))
2321*7c478bd9Sstevel@tonic-gate 				sm_dprintf("openmailer: NULL\n");
2322*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2323*7c478bd9Sstevel@tonic-gate 			goto give_up;
2324*7c478bd9Sstevel@tonic-gate 		}
2325*7c478bd9Sstevel@tonic-gate #if XDEBUG
2326*7c478bd9Sstevel@tonic-gate 		checkfdopen(rpvect[0], "rpvect[0]");
2327*7c478bd9Sstevel@tonic-gate 		checkfdopen(rpvect[1], "rpvect[1]");
2328*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
2329*7c478bd9Sstevel@tonic-gate 
2330*7c478bd9Sstevel@tonic-gate 		/*
2331*7c478bd9Sstevel@tonic-gate 		**  Actually fork the mailer process.
2332*7c478bd9Sstevel@tonic-gate 		**	DOFORK is clever about retrying.
2333*7c478bd9Sstevel@tonic-gate 		**
2334*7c478bd9Sstevel@tonic-gate 		**	Dispose of SIGCHLD signal catchers that may be laying
2335*7c478bd9Sstevel@tonic-gate 		**	around so that endmailer will get it.
2336*7c478bd9Sstevel@tonic-gate 		*/
2337*7c478bd9Sstevel@tonic-gate 
2338*7c478bd9Sstevel@tonic-gate 		if (e->e_xfp != NULL)	/* for debugging */
2339*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
2340*7c478bd9Sstevel@tonic-gate 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
2341*7c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, SIG_DFL);
2342*7c478bd9Sstevel@tonic-gate 
2343*7c478bd9Sstevel@tonic-gate 
2344*7c478bd9Sstevel@tonic-gate 		DOFORK(FORK);
2345*7c478bd9Sstevel@tonic-gate 		/* pid is set by DOFORK */
2346*7c478bd9Sstevel@tonic-gate 
2347*7c478bd9Sstevel@tonic-gate 		if (pid < 0)
2348*7c478bd9Sstevel@tonic-gate 		{
2349*7c478bd9Sstevel@tonic-gate 			/* failure */
2350*7c478bd9Sstevel@tonic-gate 			syserr("%s... openmailer(%s): cannot fork",
2351*7c478bd9Sstevel@tonic-gate 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2352*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[0]);
2353*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[1]);
2354*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[0]);
2355*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[1]);
2356*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 1))
2357*7c478bd9Sstevel@tonic-gate 				sm_dprintf("openmailer: NULL\n");
2358*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2359*7c478bd9Sstevel@tonic-gate 			goto give_up;
2360*7c478bd9Sstevel@tonic-gate 		}
2361*7c478bd9Sstevel@tonic-gate 		else if (pid == 0)
2362*7c478bd9Sstevel@tonic-gate 		{
2363*7c478bd9Sstevel@tonic-gate 			int save_errno;
2364*7c478bd9Sstevel@tonic-gate 			int sff;
2365*7c478bd9Sstevel@tonic-gate 			int new_euid = NO_UID;
2366*7c478bd9Sstevel@tonic-gate 			int new_ruid = NO_UID;
2367*7c478bd9Sstevel@tonic-gate 			int new_gid = NO_GID;
2368*7c478bd9Sstevel@tonic-gate 			char *user = NULL;
2369*7c478bd9Sstevel@tonic-gate 			struct stat stb;
2370*7c478bd9Sstevel@tonic-gate 			extern int DtableSize;
2371*7c478bd9Sstevel@tonic-gate 
2372*7c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
2373*7c478bd9Sstevel@tonic-gate 
2374*7c478bd9Sstevel@tonic-gate 			/* clear the events to turn off SIGALRMs */
2375*7c478bd9Sstevel@tonic-gate 			sm_clear_events();
2376*7c478bd9Sstevel@tonic-gate 
2377*7c478bd9Sstevel@tonic-gate 			/* Reset global flags */
2378*7c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
2379*7c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
2380*7c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
2381*7c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
2382*7c478bd9Sstevel@tonic-gate 
2383*7c478bd9Sstevel@tonic-gate 			if (e->e_lockfp != NULL)
2384*7c478bd9Sstevel@tonic-gate 				(void) close(sm_io_getinfo(e->e_lockfp,
2385*7c478bd9Sstevel@tonic-gate 							   SM_IO_WHAT_FD,
2386*7c478bd9Sstevel@tonic-gate 							   NULL));
2387*7c478bd9Sstevel@tonic-gate 
2388*7c478bd9Sstevel@tonic-gate 			/* child -- set up input & exec mailer */
2389*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGALRM, sm_signal_noop);
2390*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGCHLD, SIG_DFL);
2391*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGHUP, SIG_IGN);
2392*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGINT, SIG_IGN);
2393*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGTERM, SIG_DFL);
2394*7c478bd9Sstevel@tonic-gate # ifdef SIGUSR1
2395*7c478bd9Sstevel@tonic-gate 			(void) sm_signal(SIGUSR1, sm_signal_noop);
2396*7c478bd9Sstevel@tonic-gate # endif /* SIGUSR1 */
2397*7c478bd9Sstevel@tonic-gate 
2398*7c478bd9Sstevel@tonic-gate 			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
2399*7c478bd9Sstevel@tonic-gate 				stb.st_mode = 0;
2400*7c478bd9Sstevel@tonic-gate 
2401*7c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT
2402*7c478bd9Sstevel@tonic-gate 			/*
2403*7c478bd9Sstevel@tonic-gate 			**  Set user resources.
2404*7c478bd9Sstevel@tonic-gate 			*/
2405*7c478bd9Sstevel@tonic-gate 
2406*7c478bd9Sstevel@tonic-gate 			if (contextaddr != NULL)
2407*7c478bd9Sstevel@tonic-gate 			{
2408*7c478bd9Sstevel@tonic-gate 				int sucflags;
2409*7c478bd9Sstevel@tonic-gate 				struct passwd *pwd;
2410*7c478bd9Sstevel@tonic-gate 
2411*7c478bd9Sstevel@tonic-gate 				if (contextaddr->q_ruser != NULL)
2412*7c478bd9Sstevel@tonic-gate 					pwd = sm_getpwnam(contextaddr->q_ruser);
2413*7c478bd9Sstevel@tonic-gate 				else
2414*7c478bd9Sstevel@tonic-gate 					pwd = sm_getpwnam(contextaddr->q_user);
2415*7c478bd9Sstevel@tonic-gate 				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2416*7c478bd9Sstevel@tonic-gate #ifdef LOGIN_SETMAC
2417*7c478bd9Sstevel@tonic-gate 				sucflags |= LOGIN_SETMAC;
2418*7c478bd9Sstevel@tonic-gate #endif /* LOGIN_SETMAC */
2419*7c478bd9Sstevel@tonic-gate 				if (pwd != NULL &&
2420*7c478bd9Sstevel@tonic-gate 				    setusercontext(NULL, pwd, pwd->pw_uid,
2421*7c478bd9Sstevel@tonic-gate 						   sucflags) == -1 &&
2422*7c478bd9Sstevel@tonic-gate 				    suidwarn)
2423*7c478bd9Sstevel@tonic-gate 				{
2424*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: setusercontext() failed");
2425*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2426*7c478bd9Sstevel@tonic-gate 				}
2427*7c478bd9Sstevel@tonic-gate 			}
2428*7c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */
2429*7c478bd9Sstevel@tonic-gate 
2430*7c478bd9Sstevel@tonic-gate #if HASNICE
2431*7c478bd9Sstevel@tonic-gate 			/* tweak niceness */
2432*7c478bd9Sstevel@tonic-gate 			if (m->m_nice != 0)
2433*7c478bd9Sstevel@tonic-gate 				(void) nice(m->m_nice);
2434*7c478bd9Sstevel@tonic-gate #endif /* HASNICE */
2435*7c478bd9Sstevel@tonic-gate 
2436*7c478bd9Sstevel@tonic-gate 			/* reset group id */
2437*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2438*7c478bd9Sstevel@tonic-gate 			{
2439*7c478bd9Sstevel@tonic-gate 				if (m->m_gid == NO_GID)
2440*7c478bd9Sstevel@tonic-gate 					new_gid = RunAsGid;
2441*7c478bd9Sstevel@tonic-gate 				else
2442*7c478bd9Sstevel@tonic-gate 					new_gid = m->m_gid;
2443*7c478bd9Sstevel@tonic-gate 			}
2444*7c478bd9Sstevel@tonic-gate 			else if (bitset(S_ISGID, stb.st_mode))
2445*7c478bd9Sstevel@tonic-gate 				new_gid = stb.st_gid;
2446*7c478bd9Sstevel@tonic-gate 			else if (ctladdr != NULL && ctladdr->q_gid != 0)
2447*7c478bd9Sstevel@tonic-gate 			{
2448*7c478bd9Sstevel@tonic-gate 				if (!DontInitGroups)
2449*7c478bd9Sstevel@tonic-gate 				{
2450*7c478bd9Sstevel@tonic-gate 					user = ctladdr->q_ruser;
2451*7c478bd9Sstevel@tonic-gate 					if (user == NULL)
2452*7c478bd9Sstevel@tonic-gate 						user = ctladdr->q_user;
2453*7c478bd9Sstevel@tonic-gate 
2454*7c478bd9Sstevel@tonic-gate 					if (initgroups(user,
2455*7c478bd9Sstevel@tonic-gate 						       ctladdr->q_gid) == -1
2456*7c478bd9Sstevel@tonic-gate 					    && suidwarn)
2457*7c478bd9Sstevel@tonic-gate 					{
2458*7c478bd9Sstevel@tonic-gate 						syserr("openmailer: initgroups(%s, %d) failed",
2459*7c478bd9Sstevel@tonic-gate 							user, ctladdr->q_gid);
2460*7c478bd9Sstevel@tonic-gate 						exit(EX_TEMPFAIL);
2461*7c478bd9Sstevel@tonic-gate 					}
2462*7c478bd9Sstevel@tonic-gate 				}
2463*7c478bd9Sstevel@tonic-gate 				else
2464*7c478bd9Sstevel@tonic-gate 				{
2465*7c478bd9Sstevel@tonic-gate 					GIDSET_T gidset[1];
2466*7c478bd9Sstevel@tonic-gate 
2467*7c478bd9Sstevel@tonic-gate 					gidset[0] = ctladdr->q_gid;
2468*7c478bd9Sstevel@tonic-gate 					if (setgroups(1, gidset) == -1
2469*7c478bd9Sstevel@tonic-gate 					    && suidwarn)
2470*7c478bd9Sstevel@tonic-gate 					{
2471*7c478bd9Sstevel@tonic-gate 						syserr("openmailer: setgroups() failed");
2472*7c478bd9Sstevel@tonic-gate 						exit(EX_TEMPFAIL);
2473*7c478bd9Sstevel@tonic-gate 					}
2474*7c478bd9Sstevel@tonic-gate 				}
2475*7c478bd9Sstevel@tonic-gate 				new_gid = ctladdr->q_gid;
2476*7c478bd9Sstevel@tonic-gate 			}
2477*7c478bd9Sstevel@tonic-gate 			else
2478*7c478bd9Sstevel@tonic-gate 			{
2479*7c478bd9Sstevel@tonic-gate 				if (!DontInitGroups)
2480*7c478bd9Sstevel@tonic-gate 				{
2481*7c478bd9Sstevel@tonic-gate 					user = DefUser;
2482*7c478bd9Sstevel@tonic-gate 					if (initgroups(DefUser, DefGid) == -1 &&
2483*7c478bd9Sstevel@tonic-gate 					    suidwarn)
2484*7c478bd9Sstevel@tonic-gate 					{
2485*7c478bd9Sstevel@tonic-gate 						syserr("openmailer: initgroups(%s, %d) failed",
2486*7c478bd9Sstevel@tonic-gate 						       DefUser, DefGid);
2487*7c478bd9Sstevel@tonic-gate 						exit(EX_TEMPFAIL);
2488*7c478bd9Sstevel@tonic-gate 					}
2489*7c478bd9Sstevel@tonic-gate 				}
2490*7c478bd9Sstevel@tonic-gate 				else
2491*7c478bd9Sstevel@tonic-gate 				{
2492*7c478bd9Sstevel@tonic-gate 					GIDSET_T gidset[1];
2493*7c478bd9Sstevel@tonic-gate 
2494*7c478bd9Sstevel@tonic-gate 					gidset[0] = DefGid;
2495*7c478bd9Sstevel@tonic-gate 					if (setgroups(1, gidset) == -1
2496*7c478bd9Sstevel@tonic-gate 					    && suidwarn)
2497*7c478bd9Sstevel@tonic-gate 					{
2498*7c478bd9Sstevel@tonic-gate 						syserr("openmailer: setgroups() failed");
2499*7c478bd9Sstevel@tonic-gate 						exit(EX_TEMPFAIL);
2500*7c478bd9Sstevel@tonic-gate 					}
2501*7c478bd9Sstevel@tonic-gate 				}
2502*7c478bd9Sstevel@tonic-gate 				if (m->m_gid == NO_GID)
2503*7c478bd9Sstevel@tonic-gate 					new_gid = DefGid;
2504*7c478bd9Sstevel@tonic-gate 				else
2505*7c478bd9Sstevel@tonic-gate 					new_gid = m->m_gid;
2506*7c478bd9Sstevel@tonic-gate 			}
2507*7c478bd9Sstevel@tonic-gate 			if (new_gid != NO_GID)
2508*7c478bd9Sstevel@tonic-gate 			{
2509*7c478bd9Sstevel@tonic-gate 				if (RunAsUid != 0 &&
2510*7c478bd9Sstevel@tonic-gate 				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
2511*7c478bd9Sstevel@tonic-gate 				    new_gid != getgid() &&
2512*7c478bd9Sstevel@tonic-gate 				    new_gid != getegid())
2513*7c478bd9Sstevel@tonic-gate 				{
2514*7c478bd9Sstevel@tonic-gate 					/* Only root can change the gid */
2515*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
2516*7c478bd9Sstevel@tonic-gate 					       (int) RunAsUid, (int) new_gid,
2517*7c478bd9Sstevel@tonic-gate 					       (int) getgid(), (int) getegid());
2518*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2519*7c478bd9Sstevel@tonic-gate 				}
2520*7c478bd9Sstevel@tonic-gate 
2521*7c478bd9Sstevel@tonic-gate 				if (setgid(new_gid) < 0 && suidwarn)
2522*7c478bd9Sstevel@tonic-gate 				{
2523*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: setgid(%ld) failed",
2524*7c478bd9Sstevel@tonic-gate 					       (long) new_gid);
2525*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2526*7c478bd9Sstevel@tonic-gate 				}
2527*7c478bd9Sstevel@tonic-gate 			}
2528*7c478bd9Sstevel@tonic-gate 
2529*7c478bd9Sstevel@tonic-gate 			/* change root to some "safe" directory */
2530*7c478bd9Sstevel@tonic-gate 			if (m->m_rootdir != NULL)
2531*7c478bd9Sstevel@tonic-gate 			{
2532*7c478bd9Sstevel@tonic-gate 				expand(m->m_rootdir, cbuf, sizeof cbuf, e);
2533*7c478bd9Sstevel@tonic-gate 				if (tTd(11, 20))
2534*7c478bd9Sstevel@tonic-gate 					sm_dprintf("openmailer: chroot %s\n",
2535*7c478bd9Sstevel@tonic-gate 						   cbuf);
2536*7c478bd9Sstevel@tonic-gate 				if (chroot(cbuf) < 0)
2537*7c478bd9Sstevel@tonic-gate 				{
2538*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: Cannot chroot(%s)",
2539*7c478bd9Sstevel@tonic-gate 					       cbuf);
2540*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2541*7c478bd9Sstevel@tonic-gate 				}
2542*7c478bd9Sstevel@tonic-gate 				if (chdir("/") < 0)
2543*7c478bd9Sstevel@tonic-gate 				{
2544*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: cannot chdir(/)");
2545*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2546*7c478bd9Sstevel@tonic-gate 				}
2547*7c478bd9Sstevel@tonic-gate 			}
2548*7c478bd9Sstevel@tonic-gate 
2549*7c478bd9Sstevel@tonic-gate 			/* reset user id */
2550*7c478bd9Sstevel@tonic-gate 			endpwent();
2551*7c478bd9Sstevel@tonic-gate 			sm_mbdb_terminate();
2552*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2553*7c478bd9Sstevel@tonic-gate 			{
2554*7c478bd9Sstevel@tonic-gate 				if (m->m_uid == NO_UID)
2555*7c478bd9Sstevel@tonic-gate 					new_euid = RunAsUid;
2556*7c478bd9Sstevel@tonic-gate 				else
2557*7c478bd9Sstevel@tonic-gate 					new_euid = m->m_uid;
2558*7c478bd9Sstevel@tonic-gate 
2559*7c478bd9Sstevel@tonic-gate 				/*
2560*7c478bd9Sstevel@tonic-gate 				**  Undo the effects of the uid change in main
2561*7c478bd9Sstevel@tonic-gate 				**  for signal handling.  The real uid may
2562*7c478bd9Sstevel@tonic-gate 				**  be used by mailer in adding a "From "
2563*7c478bd9Sstevel@tonic-gate 				**  line.
2564*7c478bd9Sstevel@tonic-gate 				*/
2565*7c478bd9Sstevel@tonic-gate 
2566*7c478bd9Sstevel@tonic-gate 				if (RealUid != 0 && RealUid != getuid())
2567*7c478bd9Sstevel@tonic-gate 				{
2568*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID
2569*7c478bd9Sstevel@tonic-gate #  if HASSETREUID
2570*7c478bd9Sstevel@tonic-gate 					if (setreuid(RealUid, geteuid()) < 0)
2571*7c478bd9Sstevel@tonic-gate 					{
2572*7c478bd9Sstevel@tonic-gate 						syserr("openmailer: setreuid(%d, %d) failed",
2573*7c478bd9Sstevel@tonic-gate 						       (int) RealUid, (int) geteuid());
2574*7c478bd9Sstevel@tonic-gate 						exit(EX_OSERR);
2575*7c478bd9Sstevel@tonic-gate 					}
2576*7c478bd9Sstevel@tonic-gate #  endif /* HASSETREUID */
2577*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */
2578*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID
2579*7c478bd9Sstevel@tonic-gate 					new_ruid = RealUid;
2580*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */
2581*7c478bd9Sstevel@tonic-gate 				}
2582*7c478bd9Sstevel@tonic-gate 			}
2583*7c478bd9Sstevel@tonic-gate 			else if (bitset(S_ISUID, stb.st_mode))
2584*7c478bd9Sstevel@tonic-gate 				new_ruid = stb.st_uid;
2585*7c478bd9Sstevel@tonic-gate 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
2586*7c478bd9Sstevel@tonic-gate 				new_ruid = ctladdr->q_uid;
2587*7c478bd9Sstevel@tonic-gate 			else if (m->m_uid != NO_UID)
2588*7c478bd9Sstevel@tonic-gate 				new_ruid = m->m_uid;
2589*7c478bd9Sstevel@tonic-gate 			else
2590*7c478bd9Sstevel@tonic-gate 				new_ruid = DefUid;
2591*7c478bd9Sstevel@tonic-gate 
2592*7c478bd9Sstevel@tonic-gate # if _FFR_USE_SETLOGIN
2593*7c478bd9Sstevel@tonic-gate 			/* run disconnected from terminal and set login name */
2594*7c478bd9Sstevel@tonic-gate 			if (setsid() >= 0 &&
2595*7c478bd9Sstevel@tonic-gate 			    ctladdr != NULL && ctladdr->q_uid != 0 &&
2596*7c478bd9Sstevel@tonic-gate 			    new_euid == ctladdr->q_uid)
2597*7c478bd9Sstevel@tonic-gate 			{
2598*7c478bd9Sstevel@tonic-gate 				struct passwd *pwd;
2599*7c478bd9Sstevel@tonic-gate 
2600*7c478bd9Sstevel@tonic-gate 				pwd = sm_getpwuid(ctladdr->q_uid);
2601*7c478bd9Sstevel@tonic-gate 				if (pwd != NULL && suidwarn)
2602*7c478bd9Sstevel@tonic-gate 					(void) setlogin(pwd->pw_name);
2603*7c478bd9Sstevel@tonic-gate 				endpwent();
2604*7c478bd9Sstevel@tonic-gate 			}
2605*7c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SETLOGIN */
2606*7c478bd9Sstevel@tonic-gate 
2607*7c478bd9Sstevel@tonic-gate 			if (new_euid != NO_UID)
2608*7c478bd9Sstevel@tonic-gate 			{
2609*7c478bd9Sstevel@tonic-gate 				if (RunAsUid != 0 && new_euid != RunAsUid)
2610*7c478bd9Sstevel@tonic-gate 				{
2611*7c478bd9Sstevel@tonic-gate 					/* Only root can change the uid */
2612*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
2613*7c478bd9Sstevel@tonic-gate 					       (int) new_euid, (int) RunAsUid);
2614*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2615*7c478bd9Sstevel@tonic-gate 				}
2616*7c478bd9Sstevel@tonic-gate 
2617*7c478bd9Sstevel@tonic-gate 				vendor_set_uid(new_euid);
2618*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID
2619*7c478bd9Sstevel@tonic-gate 				if (seteuid(new_euid) < 0 && suidwarn)
2620*7c478bd9Sstevel@tonic-gate 				{
2621*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: seteuid(%ld) failed",
2622*7c478bd9Sstevel@tonic-gate 					       (long) new_euid);
2623*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2624*7c478bd9Sstevel@tonic-gate 				}
2625*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */
2626*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID
2627*7c478bd9Sstevel@tonic-gate 				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
2628*7c478bd9Sstevel@tonic-gate 				{
2629*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: setreuid(%ld, %ld) failed",
2630*7c478bd9Sstevel@tonic-gate 					       (long) new_ruid, (long) new_euid);
2631*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2632*7c478bd9Sstevel@tonic-gate 				}
2633*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */
2634*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETUID
2635*7c478bd9Sstevel@tonic-gate 				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
2636*7c478bd9Sstevel@tonic-gate 				{
2637*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: setuid(%ld) failed",
2638*7c478bd9Sstevel@tonic-gate 					       (long) new_euid);
2639*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2640*7c478bd9Sstevel@tonic-gate 				}
2641*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETUID */
2642*7c478bd9Sstevel@tonic-gate 			}
2643*7c478bd9Sstevel@tonic-gate 			else if (new_ruid != NO_UID)
2644*7c478bd9Sstevel@tonic-gate 			{
2645*7c478bd9Sstevel@tonic-gate 				vendor_set_uid(new_ruid);
2646*7c478bd9Sstevel@tonic-gate 				if (setuid(new_ruid) < 0 && suidwarn)
2647*7c478bd9Sstevel@tonic-gate 				{
2648*7c478bd9Sstevel@tonic-gate 					syserr("openmailer: setuid(%ld) failed",
2649*7c478bd9Sstevel@tonic-gate 					       (long) new_ruid);
2650*7c478bd9Sstevel@tonic-gate 					exit(EX_TEMPFAIL);
2651*7c478bd9Sstevel@tonic-gate 				}
2652*7c478bd9Sstevel@tonic-gate 			}
2653*7c478bd9Sstevel@tonic-gate 
2654*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 2))
2655*7c478bd9Sstevel@tonic-gate 				sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
2656*7c478bd9Sstevel@tonic-gate 					   (int) getuid(), (int) geteuid(),
2657*7c478bd9Sstevel@tonic-gate 					   (int) getgid(), (int) getegid());
2658*7c478bd9Sstevel@tonic-gate 
2659*7c478bd9Sstevel@tonic-gate 			/* move into some "safe" directory */
2660*7c478bd9Sstevel@tonic-gate 			if (m->m_execdir != NULL)
2661*7c478bd9Sstevel@tonic-gate 			{
2662*7c478bd9Sstevel@tonic-gate 				char *q;
2663*7c478bd9Sstevel@tonic-gate 
2664*7c478bd9Sstevel@tonic-gate 				for (p = m->m_execdir; p != NULL; p = q)
2665*7c478bd9Sstevel@tonic-gate 				{
2666*7c478bd9Sstevel@tonic-gate 					q = strchr(p, ':');
2667*7c478bd9Sstevel@tonic-gate 					if (q != NULL)
2668*7c478bd9Sstevel@tonic-gate 						*q = '\0';
2669*7c478bd9Sstevel@tonic-gate 					expand(p, cbuf, sizeof cbuf, e);
2670*7c478bd9Sstevel@tonic-gate 					if (q != NULL)
2671*7c478bd9Sstevel@tonic-gate 						*q++ = ':';
2672*7c478bd9Sstevel@tonic-gate 					if (tTd(11, 20))
2673*7c478bd9Sstevel@tonic-gate 						sm_dprintf("openmailer: trydir %s\n",
2674*7c478bd9Sstevel@tonic-gate 							   cbuf);
2675*7c478bd9Sstevel@tonic-gate 					if (cbuf[0] != '\0' &&
2676*7c478bd9Sstevel@tonic-gate 					    chdir(cbuf) >= 0)
2677*7c478bd9Sstevel@tonic-gate 						break;
2678*7c478bd9Sstevel@tonic-gate 				}
2679*7c478bd9Sstevel@tonic-gate 			}
2680*7c478bd9Sstevel@tonic-gate 
2681*7c478bd9Sstevel@tonic-gate 			/* Check safety of program to be run */
2682*7c478bd9Sstevel@tonic-gate 			sff = SFF_ROOTOK|SFF_EXECOK;
2683*7c478bd9Sstevel@tonic-gate 			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
2684*7c478bd9Sstevel@tonic-gate 				     DontBlameSendmail))
2685*7c478bd9Sstevel@tonic-gate 				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2686*7c478bd9Sstevel@tonic-gate 			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
2687*7c478bd9Sstevel@tonic-gate 				    DontBlameSendmail))
2688*7c478bd9Sstevel@tonic-gate 				sff |= SFF_NOPATHCHECK;
2689*7c478bd9Sstevel@tonic-gate 			else
2690*7c478bd9Sstevel@tonic-gate 				sff |= SFF_SAFEDIRPATH;
2691*7c478bd9Sstevel@tonic-gate 			ret = safefile(m->m_mailer, getuid(), getgid(),
2692*7c478bd9Sstevel@tonic-gate 				       user, sff, 0, NULL);
2693*7c478bd9Sstevel@tonic-gate 			if (ret != 0)
2694*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
2695*7c478bd9Sstevel@tonic-gate 					  "Warning: program %s unsafe: %s",
2696*7c478bd9Sstevel@tonic-gate 					  m->m_mailer, sm_errstring(ret));
2697*7c478bd9Sstevel@tonic-gate 
2698*7c478bd9Sstevel@tonic-gate 			/* arrange to filter std & diag output of command */
2699*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[0]);
2700*7c478bd9Sstevel@tonic-gate 			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
2701*7c478bd9Sstevel@tonic-gate 			{
2702*7c478bd9Sstevel@tonic-gate 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
2703*7c478bd9Sstevel@tonic-gate 				       shortenstring(e->e_to, MAXSHORTSTR),
2704*7c478bd9Sstevel@tonic-gate 				       m->m_name, rpvect[1]);
2705*7c478bd9Sstevel@tonic-gate 				_exit(EX_OSERR);
2706*7c478bd9Sstevel@tonic-gate 			}
2707*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[1]);
2708*7c478bd9Sstevel@tonic-gate 
2709*7c478bd9Sstevel@tonic-gate 			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
2710*7c478bd9Sstevel@tonic-gate 			{
2711*7c478bd9Sstevel@tonic-gate 				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
2712*7c478bd9Sstevel@tonic-gate 				       shortenstring(e->e_to, MAXSHORTSTR),
2713*7c478bd9Sstevel@tonic-gate 				       m->m_name);
2714*7c478bd9Sstevel@tonic-gate 				_exit(EX_OSERR);
2715*7c478bd9Sstevel@tonic-gate 			}
2716*7c478bd9Sstevel@tonic-gate 
2717*7c478bd9Sstevel@tonic-gate 			/* arrange to get standard input */
2718*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[1]);
2719*7c478bd9Sstevel@tonic-gate 			if (dup2(mpvect[0], STDIN_FILENO) < 0)
2720*7c478bd9Sstevel@tonic-gate 			{
2721*7c478bd9Sstevel@tonic-gate 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
2722*7c478bd9Sstevel@tonic-gate 				       shortenstring(e->e_to, MAXSHORTSTR),
2723*7c478bd9Sstevel@tonic-gate 				       m->m_name, mpvect[0]);
2724*7c478bd9Sstevel@tonic-gate 				_exit(EX_OSERR);
2725*7c478bd9Sstevel@tonic-gate 			}
2726*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[0]);
2727*7c478bd9Sstevel@tonic-gate 
2728*7c478bd9Sstevel@tonic-gate 			/* arrange for all the files to be closed */
2729*7c478bd9Sstevel@tonic-gate 			sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2730*7c478bd9Sstevel@tonic-gate 
2731*7c478bd9Sstevel@tonic-gate # if !_FFR_USE_SETLOGIN
2732*7c478bd9Sstevel@tonic-gate 			/* run disconnected from terminal */
2733*7c478bd9Sstevel@tonic-gate 			(void) setsid();
2734*7c478bd9Sstevel@tonic-gate # endif /* !_FFR_USE_SETLOGIN */
2735*7c478bd9Sstevel@tonic-gate 
2736*7c478bd9Sstevel@tonic-gate 			/* try to execute the mailer */
2737*7c478bd9Sstevel@tonic-gate 			(void) execve(m->m_mailer, (ARGV_T) pv,
2738*7c478bd9Sstevel@tonic-gate 				      (ARGV_T) UserEnviron);
2739*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
2740*7c478bd9Sstevel@tonic-gate 			syserr("Cannot exec %s", m->m_mailer);
2741*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_LOCALMAILER, m->m_flags) ||
2742*7c478bd9Sstevel@tonic-gate 			    transienterror(save_errno))
2743*7c478bd9Sstevel@tonic-gate 				_exit(EX_OSERR);
2744*7c478bd9Sstevel@tonic-gate 			_exit(EX_UNAVAILABLE);
2745*7c478bd9Sstevel@tonic-gate 		}
2746*7c478bd9Sstevel@tonic-gate 
2747*7c478bd9Sstevel@tonic-gate 		/*
2748*7c478bd9Sstevel@tonic-gate 		**  Set up return value.
2749*7c478bd9Sstevel@tonic-gate 		*/
2750*7c478bd9Sstevel@tonic-gate 
2751*7c478bd9Sstevel@tonic-gate 		if (mci == NULL)
2752*7c478bd9Sstevel@tonic-gate 		{
2753*7c478bd9Sstevel@tonic-gate 			if (clever)
2754*7c478bd9Sstevel@tonic-gate 			{
2755*7c478bd9Sstevel@tonic-gate 				/*
2756*7c478bd9Sstevel@tonic-gate 				**  Allocate from general heap, not
2757*7c478bd9Sstevel@tonic-gate 				**  envelope rpool, because this mci
2758*7c478bd9Sstevel@tonic-gate 				**  is going to be cached.
2759*7c478bd9Sstevel@tonic-gate 				*/
2760*7c478bd9Sstevel@tonic-gate 
2761*7c478bd9Sstevel@tonic-gate 				mci = mci_new(NULL);
2762*7c478bd9Sstevel@tonic-gate 			}
2763*7c478bd9Sstevel@tonic-gate 			else
2764*7c478bd9Sstevel@tonic-gate 			{
2765*7c478bd9Sstevel@tonic-gate 				/*
2766*7c478bd9Sstevel@tonic-gate 				**  Prevent a storage leak by allocating
2767*7c478bd9Sstevel@tonic-gate 				**  this from the envelope rpool.
2768*7c478bd9Sstevel@tonic-gate 				*/
2769*7c478bd9Sstevel@tonic-gate 
2770*7c478bd9Sstevel@tonic-gate 				mci = mci_new(e->e_rpool);
2771*7c478bd9Sstevel@tonic-gate 			}
2772*7c478bd9Sstevel@tonic-gate 		}
2773*7c478bd9Sstevel@tonic-gate 		mci->mci_mailer = m;
2774*7c478bd9Sstevel@tonic-gate 		if (clever)
2775*7c478bd9Sstevel@tonic-gate 		{
2776*7c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_OPENING;
2777*7c478bd9Sstevel@tonic-gate 			mci_cache(mci);
2778*7c478bd9Sstevel@tonic-gate 		}
2779*7c478bd9Sstevel@tonic-gate 		else
2780*7c478bd9Sstevel@tonic-gate 		{
2781*7c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_OPEN;
2782*7c478bd9Sstevel@tonic-gate 		}
2783*7c478bd9Sstevel@tonic-gate 		mci->mci_pid = pid;
2784*7c478bd9Sstevel@tonic-gate 		(void) close(mpvect[0]);
2785*7c478bd9Sstevel@tonic-gate 		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2786*7c478bd9Sstevel@tonic-gate 					  (void *) &(mpvect[1]), SM_IO_WRONLY_B,
2787*7c478bd9Sstevel@tonic-gate 					  NULL);
2788*7c478bd9Sstevel@tonic-gate 		if (mci->mci_out == NULL)
2789*7c478bd9Sstevel@tonic-gate 		{
2790*7c478bd9Sstevel@tonic-gate 			syserr("deliver: cannot create mailer output channel, fd=%d",
2791*7c478bd9Sstevel@tonic-gate 			       mpvect[1]);
2792*7c478bd9Sstevel@tonic-gate 			(void) close(mpvect[1]);
2793*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[0]);
2794*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[1]);
2795*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2796*7c478bd9Sstevel@tonic-gate 			goto give_up;
2797*7c478bd9Sstevel@tonic-gate 		}
2798*7c478bd9Sstevel@tonic-gate 
2799*7c478bd9Sstevel@tonic-gate 		(void) close(rpvect[1]);
2800*7c478bd9Sstevel@tonic-gate 		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2801*7c478bd9Sstevel@tonic-gate 					 (void *) &(rpvect[0]), SM_IO_RDONLY_B,
2802*7c478bd9Sstevel@tonic-gate 					 NULL);
2803*7c478bd9Sstevel@tonic-gate 		if (mci->mci_in == NULL)
2804*7c478bd9Sstevel@tonic-gate 		{
2805*7c478bd9Sstevel@tonic-gate 			syserr("deliver: cannot create mailer input channel, fd=%d",
2806*7c478bd9Sstevel@tonic-gate 			       mpvect[1]);
2807*7c478bd9Sstevel@tonic-gate 			(void) close(rpvect[0]);
2808*7c478bd9Sstevel@tonic-gate 			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
2809*7c478bd9Sstevel@tonic-gate 			mci->mci_out = NULL;
2810*7c478bd9Sstevel@tonic-gate 			rcode = EX_OSERR;
2811*7c478bd9Sstevel@tonic-gate 			goto give_up;
2812*7c478bd9Sstevel@tonic-gate 		}
2813*7c478bd9Sstevel@tonic-gate 	}
2814*7c478bd9Sstevel@tonic-gate 
2815*7c478bd9Sstevel@tonic-gate 	/*
2816*7c478bd9Sstevel@tonic-gate 	**  If we are in SMTP opening state, send initial protocol.
2817*7c478bd9Sstevel@tonic-gate 	*/
2818*7c478bd9Sstevel@tonic-gate 
2819*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_7BITS, m->m_flags) &&
2820*7c478bd9Sstevel@tonic-gate 	    (!clever || mci->mci_state == MCIS_OPENING))
2821*7c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_7BIT;
2822*7c478bd9Sstevel@tonic-gate 	if (clever && mci->mci_state != MCIS_CLOSED)
2823*7c478bd9Sstevel@tonic-gate 	{
2824*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL
2825*7c478bd9Sstevel@tonic-gate 		int dotpos;
2826*7c478bd9Sstevel@tonic-gate 		char *srvname;
2827*7c478bd9Sstevel@tonic-gate 		extern SOCKADDR CurHostAddr;
2828*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */
2829*7c478bd9Sstevel@tonic-gate 
2830*7c478bd9Sstevel@tonic-gate # if SASL
2831*7c478bd9Sstevel@tonic-gate #  define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
2832*7c478bd9Sstevel@tonic-gate # endif /* SASL */
2833*7c478bd9Sstevel@tonic-gate # if STARTTLS
2834*7c478bd9Sstevel@tonic-gate #  define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
2835*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */
2836*7c478bd9Sstevel@tonic-gate # define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
2837*7c478bd9Sstevel@tonic-gate # define SET_HELO(f)		f |= MCIF_ONLY_EHLO
2838*7c478bd9Sstevel@tonic-gate # define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
2839*7c478bd9Sstevel@tonic-gate 
2840*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL
2841*7c478bd9Sstevel@tonic-gate 		/* don't use CurHostName, it is changed in many places */
2842*7c478bd9Sstevel@tonic-gate 		if (mci->mci_host != NULL)
2843*7c478bd9Sstevel@tonic-gate 		{
2844*7c478bd9Sstevel@tonic-gate 			srvname = mci->mci_host;
2845*7c478bd9Sstevel@tonic-gate 			dotpos = strlen(srvname) - 1;
2846*7c478bd9Sstevel@tonic-gate 			if (dotpos >= 0)
2847*7c478bd9Sstevel@tonic-gate 			{
2848*7c478bd9Sstevel@tonic-gate 				if (srvname[dotpos] == '.')
2849*7c478bd9Sstevel@tonic-gate 					srvname[dotpos] = '\0';
2850*7c478bd9Sstevel@tonic-gate 				else
2851*7c478bd9Sstevel@tonic-gate 					dotpos = -1;
2852*7c478bd9Sstevel@tonic-gate 			}
2853*7c478bd9Sstevel@tonic-gate 		}
2854*7c478bd9Sstevel@tonic-gate 		else if (mci->mci_mailer != NULL)
2855*7c478bd9Sstevel@tonic-gate 		{
2856*7c478bd9Sstevel@tonic-gate 			srvname = mci->mci_mailer->m_name;
2857*7c478bd9Sstevel@tonic-gate 			dotpos = -1;
2858*7c478bd9Sstevel@tonic-gate 		}
2859*7c478bd9Sstevel@tonic-gate 		else
2860*7c478bd9Sstevel@tonic-gate 		{
2861*7c478bd9Sstevel@tonic-gate 			srvname = "local";
2862*7c478bd9Sstevel@tonic-gate 			dotpos = -1;
2863*7c478bd9Sstevel@tonic-gate 		}
2864*7c478bd9Sstevel@tonic-gate 
2865*7c478bd9Sstevel@tonic-gate 		/* don't set {server_name} to NULL or "": see getauth() */
2866*7c478bd9Sstevel@tonic-gate 		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
2867*7c478bd9Sstevel@tonic-gate 			  srvname);
2868*7c478bd9Sstevel@tonic-gate 
2869*7c478bd9Sstevel@tonic-gate 		/* CurHostAddr is set by makeconnection() and mci_get() */
2870*7c478bd9Sstevel@tonic-gate 		if (CurHostAddr.sa.sa_family != 0)
2871*7c478bd9Sstevel@tonic-gate 		{
2872*7c478bd9Sstevel@tonic-gate 			macdefine(&mci->mci_macro, A_TEMP,
2873*7c478bd9Sstevel@tonic-gate 				  macid("{server_addr}"),
2874*7c478bd9Sstevel@tonic-gate 				  anynet_ntoa(&CurHostAddr));
2875*7c478bd9Sstevel@tonic-gate 		}
2876*7c478bd9Sstevel@tonic-gate 		else if (mci->mci_mailer != NULL)
2877*7c478bd9Sstevel@tonic-gate 		{
2878*7c478bd9Sstevel@tonic-gate 			/* mailer name is unique, use it as address */
2879*7c478bd9Sstevel@tonic-gate 			macdefine(&mci->mci_macro, A_PERM,
2880*7c478bd9Sstevel@tonic-gate 				  macid("{server_addr}"),
2881*7c478bd9Sstevel@tonic-gate 				  mci->mci_mailer->m_name);
2882*7c478bd9Sstevel@tonic-gate 		}
2883*7c478bd9Sstevel@tonic-gate 		else
2884*7c478bd9Sstevel@tonic-gate 		{
2885*7c478bd9Sstevel@tonic-gate 			/* don't set it to NULL or "": see getauth() */
2886*7c478bd9Sstevel@tonic-gate 			macdefine(&mci->mci_macro, A_PERM,
2887*7c478bd9Sstevel@tonic-gate 				  macid("{server_addr}"), "0");
2888*7c478bd9Sstevel@tonic-gate 		}
2889*7c478bd9Sstevel@tonic-gate 
2890*7c478bd9Sstevel@tonic-gate 		/* undo change of srvname (mci->mci_host) */
2891*7c478bd9Sstevel@tonic-gate 		if (dotpos >= 0)
2892*7c478bd9Sstevel@tonic-gate 			srvname[dotpos] = '.';
2893*7c478bd9Sstevel@tonic-gate 
2894*7c478bd9Sstevel@tonic-gate reconnect:	/* after switching to an encrypted connection */
2895*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */
2896*7c478bd9Sstevel@tonic-gate 
2897*7c478bd9Sstevel@tonic-gate 		/* set the current connection information */
2898*7c478bd9Sstevel@tonic-gate 		e->e_mci = mci;
2899*7c478bd9Sstevel@tonic-gate # if SASL
2900*7c478bd9Sstevel@tonic-gate 		mci->mci_saslcap = NULL;
2901*7c478bd9Sstevel@tonic-gate # endif /* SASL */
2902*7c478bd9Sstevel@tonic-gate 		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
2903*7c478bd9Sstevel@tonic-gate 		CLR_HELO(mci->mci_flags);
2904*7c478bd9Sstevel@tonic-gate 
2905*7c478bd9Sstevel@tonic-gate 		if (IS_DLVR_RETURN(e))
2906*7c478bd9Sstevel@tonic-gate 		{
2907*7c478bd9Sstevel@tonic-gate 			/*
2908*7c478bd9Sstevel@tonic-gate 			**  Check whether other side can deliver e-mail
2909*7c478bd9Sstevel@tonic-gate 			**  fast enough
2910*7c478bd9Sstevel@tonic-gate 			*/
2911*7c478bd9Sstevel@tonic-gate 
2912*7c478bd9Sstevel@tonic-gate 			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
2913*7c478bd9Sstevel@tonic-gate 			{
2914*7c478bd9Sstevel@tonic-gate 				e->e_status = "5.4.7";
2915*7c478bd9Sstevel@tonic-gate 				usrerrenh(e->e_status,
2916*7c478bd9Sstevel@tonic-gate 					  "554 Server does not support Deliver By");
2917*7c478bd9Sstevel@tonic-gate 				rcode = EX_UNAVAILABLE;
2918*7c478bd9Sstevel@tonic-gate 				goto give_up;
2919*7c478bd9Sstevel@tonic-gate 			}
2920*7c478bd9Sstevel@tonic-gate 			if (e->e_deliver_by > 0 &&
2921*7c478bd9Sstevel@tonic-gate 			    e->e_deliver_by - (curtime() - e->e_ctime) <
2922*7c478bd9Sstevel@tonic-gate 			    mci->mci_min_by)
2923*7c478bd9Sstevel@tonic-gate 			{
2924*7c478bd9Sstevel@tonic-gate 				e->e_status = "5.4.7";
2925*7c478bd9Sstevel@tonic-gate 				usrerrenh(e->e_status,
2926*7c478bd9Sstevel@tonic-gate 					  "554 Message can't be delivered in time; %ld < %ld",
2927*7c478bd9Sstevel@tonic-gate 					  e->e_deliver_by - (curtime() - e->e_ctime),
2928*7c478bd9Sstevel@tonic-gate 					  mci->mci_min_by);
2929*7c478bd9Sstevel@tonic-gate 				rcode = EX_UNAVAILABLE;
2930*7c478bd9Sstevel@tonic-gate 				goto give_up;
2931*7c478bd9Sstevel@tonic-gate 			}
2932*7c478bd9Sstevel@tonic-gate 		}
2933*7c478bd9Sstevel@tonic-gate 
2934*7c478bd9Sstevel@tonic-gate # if STARTTLS
2935*7c478bd9Sstevel@tonic-gate 		/* first TLS then AUTH to provide a security layer */
2936*7c478bd9Sstevel@tonic-gate 		if (mci->mci_state != MCIS_CLOSED &&
2937*7c478bd9Sstevel@tonic-gate 		    !DONE_STARTTLS(mci->mci_flags))
2938*7c478bd9Sstevel@tonic-gate 		{
2939*7c478bd9Sstevel@tonic-gate 			int olderrors;
2940*7c478bd9Sstevel@tonic-gate 			bool usetls;
2941*7c478bd9Sstevel@tonic-gate 			bool saveQuickAbort = QuickAbort;
2942*7c478bd9Sstevel@tonic-gate 			bool saveSuprErrs = SuprErrs;
2943*7c478bd9Sstevel@tonic-gate 			char *host = NULL;
2944*7c478bd9Sstevel@tonic-gate 
2945*7c478bd9Sstevel@tonic-gate 			rcode = EX_OK;
2946*7c478bd9Sstevel@tonic-gate 			usetls = bitset(MCIF_TLS, mci->mci_flags);
2947*7c478bd9Sstevel@tonic-gate 			if (usetls)
2948*7c478bd9Sstevel@tonic-gate 				usetls = !iscltflgset(e, D_NOTLS);
2949*7c478bd9Sstevel@tonic-gate 
2950*7c478bd9Sstevel@tonic-gate 			if (usetls)
2951*7c478bd9Sstevel@tonic-gate 			{
2952*7c478bd9Sstevel@tonic-gate 				host = macvalue(macid("{server_name}"), e);
2953*7c478bd9Sstevel@tonic-gate 				olderrors = Errors;
2954*7c478bd9Sstevel@tonic-gate 				QuickAbort = false;
2955*7c478bd9Sstevel@tonic-gate 				SuprErrs = true;
2956*7c478bd9Sstevel@tonic-gate 				if (rscheck("try_tls", host, NULL, e,
2957*7c478bd9Sstevel@tonic-gate 					    RSF_RMCOMM, 7, host, NOQID) != EX_OK
2958*7c478bd9Sstevel@tonic-gate 				    || Errors > olderrors)
2959*7c478bd9Sstevel@tonic-gate 					usetls = false;
2960*7c478bd9Sstevel@tonic-gate 				SuprErrs = saveSuprErrs;
2961*7c478bd9Sstevel@tonic-gate 				QuickAbort = saveQuickAbort;
2962*7c478bd9Sstevel@tonic-gate 			}
2963*7c478bd9Sstevel@tonic-gate 
2964*7c478bd9Sstevel@tonic-gate 			if (usetls)
2965*7c478bd9Sstevel@tonic-gate 			{
2966*7c478bd9Sstevel@tonic-gate 				if ((rcode = starttls(m, mci, e)) == EX_OK)
2967*7c478bd9Sstevel@tonic-gate 				{
2968*7c478bd9Sstevel@tonic-gate 					/* start again without STARTTLS */
2969*7c478bd9Sstevel@tonic-gate 					mci->mci_flags |= MCIF_TLSACT;
2970*7c478bd9Sstevel@tonic-gate 				}
2971*7c478bd9Sstevel@tonic-gate 				else
2972*7c478bd9Sstevel@tonic-gate 				{
2973*7c478bd9Sstevel@tonic-gate 					char *s;
2974*7c478bd9Sstevel@tonic-gate 
2975*7c478bd9Sstevel@tonic-gate 					/*
2976*7c478bd9Sstevel@tonic-gate 					**  TLS negotation failed, what to do?
2977*7c478bd9Sstevel@tonic-gate 					**  fall back to unencrypted connection
2978*7c478bd9Sstevel@tonic-gate 					**  or abort? How to decide?
2979*7c478bd9Sstevel@tonic-gate 					**  set a macro and call a ruleset.
2980*7c478bd9Sstevel@tonic-gate 					*/
2981*7c478bd9Sstevel@tonic-gate 
2982*7c478bd9Sstevel@tonic-gate 					mci->mci_flags &= ~MCIF_TLS;
2983*7c478bd9Sstevel@tonic-gate 					switch (rcode)
2984*7c478bd9Sstevel@tonic-gate 					{
2985*7c478bd9Sstevel@tonic-gate 					  case EX_TEMPFAIL:
2986*7c478bd9Sstevel@tonic-gate 						s = "TEMP";
2987*7c478bd9Sstevel@tonic-gate 						break;
2988*7c478bd9Sstevel@tonic-gate 					  case EX_USAGE:
2989*7c478bd9Sstevel@tonic-gate 						s = "USAGE";
2990*7c478bd9Sstevel@tonic-gate 						break;
2991*7c478bd9Sstevel@tonic-gate 					  case EX_PROTOCOL:
2992*7c478bd9Sstevel@tonic-gate 						s = "PROTOCOL";
2993*7c478bd9Sstevel@tonic-gate 						break;
2994*7c478bd9Sstevel@tonic-gate 					  case EX_SOFTWARE:
2995*7c478bd9Sstevel@tonic-gate 						s = "SOFTWARE";
2996*7c478bd9Sstevel@tonic-gate 						break;
2997*7c478bd9Sstevel@tonic-gate 
2998*7c478bd9Sstevel@tonic-gate 					  /* everything else is a failure */
2999*7c478bd9Sstevel@tonic-gate 					  default:
3000*7c478bd9Sstevel@tonic-gate 						s = "FAILURE";
3001*7c478bd9Sstevel@tonic-gate 						rcode = EX_TEMPFAIL;
3002*7c478bd9Sstevel@tonic-gate 					}
3003*7c478bd9Sstevel@tonic-gate 					macdefine(&e->e_macro, A_PERM,
3004*7c478bd9Sstevel@tonic-gate 						  macid("{verify}"), s);
3005*7c478bd9Sstevel@tonic-gate 				}
3006*7c478bd9Sstevel@tonic-gate 			}
3007*7c478bd9Sstevel@tonic-gate 			else
3008*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
3009*7c478bd9Sstevel@tonic-gate 					  macid("{verify}"), "NONE");
3010*7c478bd9Sstevel@tonic-gate 			olderrors = Errors;
3011*7c478bd9Sstevel@tonic-gate 			QuickAbort = false;
3012*7c478bd9Sstevel@tonic-gate 			SuprErrs = true;
3013*7c478bd9Sstevel@tonic-gate 
3014*7c478bd9Sstevel@tonic-gate 			/*
3015*7c478bd9Sstevel@tonic-gate 			**  rcode == EX_SOFTWARE is special:
3016*7c478bd9Sstevel@tonic-gate 			**  the TLS negotation failed
3017*7c478bd9Sstevel@tonic-gate 			**  we have to drop the connection no matter what
3018*7c478bd9Sstevel@tonic-gate 			**  However, we call tls_server to give it the chance
3019*7c478bd9Sstevel@tonic-gate 			**  to log the problem and return an appropriate
3020*7c478bd9Sstevel@tonic-gate 			**  error code.
3021*7c478bd9Sstevel@tonic-gate 			*/
3022*7c478bd9Sstevel@tonic-gate 
3023*7c478bd9Sstevel@tonic-gate 			if (rscheck("tls_server",
3024*7c478bd9Sstevel@tonic-gate 				    macvalue(macid("{verify}"), e),
3025*7c478bd9Sstevel@tonic-gate 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
3026*7c478bd9Sstevel@tonic-gate 				    host, NOQID) != EX_OK ||
3027*7c478bd9Sstevel@tonic-gate 			    Errors > olderrors ||
3028*7c478bd9Sstevel@tonic-gate 			    rcode == EX_SOFTWARE)
3029*7c478bd9Sstevel@tonic-gate 			{
3030*7c478bd9Sstevel@tonic-gate 				char enhsc[ENHSCLEN];
3031*7c478bd9Sstevel@tonic-gate 				extern char MsgBuf[];
3032*7c478bd9Sstevel@tonic-gate 
3033*7c478bd9Sstevel@tonic-gate 				if (ISSMTPCODE(MsgBuf) &&
3034*7c478bd9Sstevel@tonic-gate 				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
3035*7c478bd9Sstevel@tonic-gate 				{
3036*7c478bd9Sstevel@tonic-gate 					p = sm_rpool_strdup_x(e->e_rpool,
3037*7c478bd9Sstevel@tonic-gate 							      MsgBuf);
3038*7c478bd9Sstevel@tonic-gate 				}
3039*7c478bd9Sstevel@tonic-gate 				else
3040*7c478bd9Sstevel@tonic-gate 				{
3041*7c478bd9Sstevel@tonic-gate 					p = "403 4.7.0 server not authenticated.";
3042*7c478bd9Sstevel@tonic-gate 					(void) sm_strlcpy(enhsc, "4.7.0",
3043*7c478bd9Sstevel@tonic-gate 							  sizeof enhsc);
3044*7c478bd9Sstevel@tonic-gate 				}
3045*7c478bd9Sstevel@tonic-gate 				SuprErrs = saveSuprErrs;
3046*7c478bd9Sstevel@tonic-gate 				QuickAbort = saveQuickAbort;
3047*7c478bd9Sstevel@tonic-gate 
3048*7c478bd9Sstevel@tonic-gate 				if (rcode == EX_SOFTWARE)
3049*7c478bd9Sstevel@tonic-gate 				{
3050*7c478bd9Sstevel@tonic-gate 					/* drop the connection */
3051*7c478bd9Sstevel@tonic-gate 					mci->mci_state = MCIS_QUITING;
3052*7c478bd9Sstevel@tonic-gate 					if (mci->mci_in != NULL)
3053*7c478bd9Sstevel@tonic-gate 					{
3054*7c478bd9Sstevel@tonic-gate 						(void) sm_io_close(mci->mci_in,
3055*7c478bd9Sstevel@tonic-gate 								   SM_TIME_DEFAULT);
3056*7c478bd9Sstevel@tonic-gate 						mci->mci_in = NULL;
3057*7c478bd9Sstevel@tonic-gate 					}
3058*7c478bd9Sstevel@tonic-gate 					mci->mci_flags &= ~MCIF_TLSACT;
3059*7c478bd9Sstevel@tonic-gate 					(void) endmailer(mci, e, pv);
3060*7c478bd9Sstevel@tonic-gate 				}
3061*7c478bd9Sstevel@tonic-gate 				else
3062*7c478bd9Sstevel@tonic-gate 				{
3063*7c478bd9Sstevel@tonic-gate 					/* abort transfer */
3064*7c478bd9Sstevel@tonic-gate 					smtpquit(m, mci, e);
3065*7c478bd9Sstevel@tonic-gate 				}
3066*7c478bd9Sstevel@tonic-gate 
3067*7c478bd9Sstevel@tonic-gate 				/* avoid bogus error msg */
3068*7c478bd9Sstevel@tonic-gate 				mci->mci_errno = 0;
3069*7c478bd9Sstevel@tonic-gate 
3070*7c478bd9Sstevel@tonic-gate 				/* temp or permanent failure? */
3071*7c478bd9Sstevel@tonic-gate 				rcode = (*p == '4') ? EX_TEMPFAIL
3072*7c478bd9Sstevel@tonic-gate 						    : EX_UNAVAILABLE;
3073*7c478bd9Sstevel@tonic-gate 				mci_setstat(mci, rcode, enhsc, p);
3074*7c478bd9Sstevel@tonic-gate 
3075*7c478bd9Sstevel@tonic-gate 				/*
3076*7c478bd9Sstevel@tonic-gate 				**  hack to get the error message into
3077*7c478bd9Sstevel@tonic-gate 				**  the envelope (done in giveresponse())
3078*7c478bd9Sstevel@tonic-gate 				*/
3079*7c478bd9Sstevel@tonic-gate 
3080*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcpy(SmtpError, p,
3081*7c478bd9Sstevel@tonic-gate 						  sizeof SmtpError);
3082*7c478bd9Sstevel@tonic-gate 			}
3083*7c478bd9Sstevel@tonic-gate 			QuickAbort = saveQuickAbort;
3084*7c478bd9Sstevel@tonic-gate 			SuprErrs = saveSuprErrs;
3085*7c478bd9Sstevel@tonic-gate 			if (DONE_STARTTLS(mci->mci_flags) &&
3086*7c478bd9Sstevel@tonic-gate 			    mci->mci_state != MCIS_CLOSED)
3087*7c478bd9Sstevel@tonic-gate 			{
3088*7c478bd9Sstevel@tonic-gate 				SET_HELO(mci->mci_flags);
3089*7c478bd9Sstevel@tonic-gate 				mci->mci_flags &= ~MCIF_EXTENS;
3090*7c478bd9Sstevel@tonic-gate 				goto reconnect;
3091*7c478bd9Sstevel@tonic-gate 			}
3092*7c478bd9Sstevel@tonic-gate 		}
3093*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */
3094*7c478bd9Sstevel@tonic-gate # if SASL
3095*7c478bd9Sstevel@tonic-gate 		/* if other server supports authentication let's authenticate */
3096*7c478bd9Sstevel@tonic-gate 		if (mci->mci_state != MCIS_CLOSED &&
3097*7c478bd9Sstevel@tonic-gate 		    mci->mci_saslcap != NULL &&
3098*7c478bd9Sstevel@tonic-gate 		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
3099*7c478bd9Sstevel@tonic-gate 		{
3100*7c478bd9Sstevel@tonic-gate 			/* Should we require some minimum authentication? */
3101*7c478bd9Sstevel@tonic-gate 			if ((ret = smtpauth(m, mci, e)) == EX_OK)
3102*7c478bd9Sstevel@tonic-gate 			{
3103*7c478bd9Sstevel@tonic-gate 				int result;
3104*7c478bd9Sstevel@tonic-gate 				sasl_ssf_t *ssf = NULL;
3105*7c478bd9Sstevel@tonic-gate 
3106*7c478bd9Sstevel@tonic-gate 				/* Get security strength (features) */
3107*7c478bd9Sstevel@tonic-gate 				result = sasl_getprop(mci->mci_conn, SASL_SSF,
3108*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
3109*7c478bd9Sstevel@tonic-gate 						      (const void **) &ssf);
3110*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
3111*7c478bd9Sstevel@tonic-gate 						      (void **) &ssf);
3112*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
3113*7c478bd9Sstevel@tonic-gate 
3114*7c478bd9Sstevel@tonic-gate 				/* XXX authid? */
3115*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
3116*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
3117*7c478bd9Sstevel@tonic-gate 						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
3118*7c478bd9Sstevel@tonic-gate 						  mci->mci_host,
3119*7c478bd9Sstevel@tonic-gate 						  macvalue(macid("{auth_type}"), e),
3120*7c478bd9Sstevel@tonic-gate 						  result == SASL_OK ? *ssf : 0);
3121*7c478bd9Sstevel@tonic-gate 
3122*7c478bd9Sstevel@tonic-gate 				/*
3123*7c478bd9Sstevel@tonic-gate 				**  Only switch to encrypted connection
3124*7c478bd9Sstevel@tonic-gate 				**  if a security layer has been negotiated
3125*7c478bd9Sstevel@tonic-gate 				*/
3126*7c478bd9Sstevel@tonic-gate 
3127*7c478bd9Sstevel@tonic-gate 				if (result == SASL_OK && *ssf > 0)
3128*7c478bd9Sstevel@tonic-gate 				{
3129*7c478bd9Sstevel@tonic-gate 					/*
3130*7c478bd9Sstevel@tonic-gate 					**  Convert I/O layer to use SASL.
3131*7c478bd9Sstevel@tonic-gate 					**  If the call fails, the connection
3132*7c478bd9Sstevel@tonic-gate 					**  is aborted.
3133*7c478bd9Sstevel@tonic-gate 					*/
3134*7c478bd9Sstevel@tonic-gate 
3135*7c478bd9Sstevel@tonic-gate 					if (sfdcsasl(&mci->mci_in,
3136*7c478bd9Sstevel@tonic-gate 						     &mci->mci_out,
3137*7c478bd9Sstevel@tonic-gate 						     mci->mci_conn) == 0)
3138*7c478bd9Sstevel@tonic-gate 					{
3139*7c478bd9Sstevel@tonic-gate 						mci->mci_flags &= ~MCIF_EXTENS;
3140*7c478bd9Sstevel@tonic-gate 						mci->mci_flags |= MCIF_AUTHACT|
3141*7c478bd9Sstevel@tonic-gate 								  MCIF_ONLY_EHLO;
3142*7c478bd9Sstevel@tonic-gate 						goto reconnect;
3143*7c478bd9Sstevel@tonic-gate 					}
3144*7c478bd9Sstevel@tonic-gate 					syserr("AUTH TLS switch failed in client");
3145*7c478bd9Sstevel@tonic-gate 				}
3146*7c478bd9Sstevel@tonic-gate 				/* else? XXX */
3147*7c478bd9Sstevel@tonic-gate 				mci->mci_flags |= MCIF_AUTHACT;
3148*7c478bd9Sstevel@tonic-gate 
3149*7c478bd9Sstevel@tonic-gate 			}
3150*7c478bd9Sstevel@tonic-gate 			else if (ret == EX_TEMPFAIL)
3151*7c478bd9Sstevel@tonic-gate 			{
3152*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
3153*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, NOQID,
3154*7c478bd9Sstevel@tonic-gate 						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
3155*7c478bd9Sstevel@tonic-gate 						  mci->mci_host);
3156*7c478bd9Sstevel@tonic-gate 				smtpquit(m, mci, e);
3157*7c478bd9Sstevel@tonic-gate 
3158*7c478bd9Sstevel@tonic-gate 				/* avoid bogus error msg */
3159*7c478bd9Sstevel@tonic-gate 				mci->mci_errno = 0;
3160*7c478bd9Sstevel@tonic-gate 				rcode = EX_TEMPFAIL;
3161*7c478bd9Sstevel@tonic-gate 				mci_setstat(mci, rcode, "4.3.0", p);
3162*7c478bd9Sstevel@tonic-gate 
3163*7c478bd9Sstevel@tonic-gate 				/*
3164*7c478bd9Sstevel@tonic-gate 				**  hack to get the error message into
3165*7c478bd9Sstevel@tonic-gate 				**  the envelope (done in giveresponse())
3166*7c478bd9Sstevel@tonic-gate 				*/
3167*7c478bd9Sstevel@tonic-gate 
3168*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcpy(SmtpError,
3169*7c478bd9Sstevel@tonic-gate 						  "Temporary AUTH failure",
3170*7c478bd9Sstevel@tonic-gate 						  sizeof SmtpError);
3171*7c478bd9Sstevel@tonic-gate 			}
3172*7c478bd9Sstevel@tonic-gate 		}
3173*7c478bd9Sstevel@tonic-gate # endif /* SASL */
3174*7c478bd9Sstevel@tonic-gate 	}
3175*7c478bd9Sstevel@tonic-gate 
3176*7c478bd9Sstevel@tonic-gate 
3177*7c478bd9Sstevel@tonic-gate do_transfer:
3178*7c478bd9Sstevel@tonic-gate 	/* clear out per-message flags from connection structure */
3179*7c478bd9Sstevel@tonic-gate 	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
3180*7c478bd9Sstevel@tonic-gate 
3181*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags) &&
3182*7c478bd9Sstevel@tonic-gate 	    !bitset(EF_DONT_MIME, e->e_flags) &&
3183*7c478bd9Sstevel@tonic-gate 	    bitnset(M_7BITS, m->m_flags))
3184*7c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_CVT8TO7;
3185*7c478bd9Sstevel@tonic-gate 
3186*7c478bd9Sstevel@tonic-gate #if MIME7TO8
3187*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_MAKE8BIT, m->m_flags) &&
3188*7c478bd9Sstevel@tonic-gate 	    !bitset(MCIF_7BIT, mci->mci_flags) &&
3189*7c478bd9Sstevel@tonic-gate 	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
3190*7c478bd9Sstevel@tonic-gate 	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
3191*7c478bd9Sstevel@tonic-gate 	      sm_strcasecmp(p, "base64") == 0) &&
3192*7c478bd9Sstevel@tonic-gate 	    (p = hvalue("Content-Type", e->e_header)) != NULL)
3193*7c478bd9Sstevel@tonic-gate 	{
3194*7c478bd9Sstevel@tonic-gate 		/* may want to convert 7 -> 8 */
3195*7c478bd9Sstevel@tonic-gate 		/* XXX should really parse it here -- and use a class XXX */
3196*7c478bd9Sstevel@tonic-gate 		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
3197*7c478bd9Sstevel@tonic-gate 		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
3198*7c478bd9Sstevel@tonic-gate 			mci->mci_flags |= MCIF_CVT7TO8;
3199*7c478bd9Sstevel@tonic-gate 	}
3200*7c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */
3201*7c478bd9Sstevel@tonic-gate 
3202*7c478bd9Sstevel@tonic-gate 	if (tTd(11, 1))
3203*7c478bd9Sstevel@tonic-gate 	{
3204*7c478bd9Sstevel@tonic-gate 		sm_dprintf("openmailer: ");
3205*7c478bd9Sstevel@tonic-gate 		mci_dump(sm_debug_file(), mci, false);
3206*7c478bd9Sstevel@tonic-gate 	}
3207*7c478bd9Sstevel@tonic-gate 
3208*7c478bd9Sstevel@tonic-gate #if _FFR_CLIENT_SIZE
3209*7c478bd9Sstevel@tonic-gate 	/*
3210*7c478bd9Sstevel@tonic-gate 	**  See if we know the maximum size and
3211*7c478bd9Sstevel@tonic-gate 	**  abort if the message is too big.
3212*7c478bd9Sstevel@tonic-gate 	**
3213*7c478bd9Sstevel@tonic-gate 	**  NOTE: _FFR_CLIENT_SIZE is untested.
3214*7c478bd9Sstevel@tonic-gate 	*/
3215*7c478bd9Sstevel@tonic-gate 
3216*7c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_SIZE, mci->mci_flags) &&
3217*7c478bd9Sstevel@tonic-gate 	    mci->mci_maxsize > 0 &&
3218*7c478bd9Sstevel@tonic-gate 	    e->e_msgsize > mci->mci_maxsize)
3219*7c478bd9Sstevel@tonic-gate 	{
3220*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_NO_BODY_RETN;
3221*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_LOCALMAILER, m->m_flags))
3222*7c478bd9Sstevel@tonic-gate 			e->e_status = "5.2.3";
3223*7c478bd9Sstevel@tonic-gate 		else
3224*7c478bd9Sstevel@tonic-gate 			e->e_status = "5.3.4";
3225*7c478bd9Sstevel@tonic-gate 
3226*7c478bd9Sstevel@tonic-gate 		usrerrenh(e->e_status,
3227*7c478bd9Sstevel@tonic-gate 			  "552 Message is too large; %ld bytes max",
3228*7c478bd9Sstevel@tonic-gate 			  mci->mci_maxsize);
3229*7c478bd9Sstevel@tonic-gate 		rcode = EX_DATAERR;
3230*7c478bd9Sstevel@tonic-gate 
3231*7c478bd9Sstevel@tonic-gate 		/* Need an e_message for error */
3232*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(SmtpError, sizeof SmtpError,
3233*7c478bd9Sstevel@tonic-gate 				   "Message is too large; %ld bytes max",
3234*7c478bd9Sstevel@tonic-gate 				   mci->mci_maxsize);
3235*7c478bd9Sstevel@tonic-gate 		goto give_up;
3236*7c478bd9Sstevel@tonic-gate 	}
3237*7c478bd9Sstevel@tonic-gate #endif /* _FFR_CLIENT_SIZE */
3238*7c478bd9Sstevel@tonic-gate 
3239*7c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_OPEN)
3240*7c478bd9Sstevel@tonic-gate 	{
3241*7c478bd9Sstevel@tonic-gate 		/* couldn't open the mailer */
3242*7c478bd9Sstevel@tonic-gate 		rcode = mci->mci_exitstat;
3243*7c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
3244*7c478bd9Sstevel@tonic-gate 		SM_SET_H_ERRNO(mci->mci_herrno);
3245*7c478bd9Sstevel@tonic-gate 		if (rcode == EX_OK)
3246*7c478bd9Sstevel@tonic-gate 		{
3247*7c478bd9Sstevel@tonic-gate 			/* shouldn't happen */
3248*7c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
3249*7c478bd9Sstevel@tonic-gate 			       (unsigned long) mci, rcode, errno,
3250*7c478bd9Sstevel@tonic-gate 			       mci->mci_state, firstsig);
3251*7c478bd9Sstevel@tonic-gate 			mci_dump_all(smioout, true);
3252*7c478bd9Sstevel@tonic-gate 			rcode = EX_SOFTWARE;
3253*7c478bd9Sstevel@tonic-gate 		}
3254*7c478bd9Sstevel@tonic-gate 		else if (nummxhosts > hostnum)
3255*7c478bd9Sstevel@tonic-gate 		{
3256*7c478bd9Sstevel@tonic-gate 			/* try next MX site */
3257*7c478bd9Sstevel@tonic-gate 			goto tryhost;
3258*7c478bd9Sstevel@tonic-gate 		}
3259*7c478bd9Sstevel@tonic-gate 	}
3260*7c478bd9Sstevel@tonic-gate 	else if (!clever)
3261*7c478bd9Sstevel@tonic-gate 	{
3262*7c478bd9Sstevel@tonic-gate 		/*
3263*7c478bd9Sstevel@tonic-gate 		**  Format and send message.
3264*7c478bd9Sstevel@tonic-gate 		*/
3265*7c478bd9Sstevel@tonic-gate 
3266*7c478bd9Sstevel@tonic-gate 		putfromline(mci, e);
3267*7c478bd9Sstevel@tonic-gate 		(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
3268*7c478bd9Sstevel@tonic-gate 		(*e->e_putbody)(mci, e, NULL);
3269*7c478bd9Sstevel@tonic-gate 
3270*7c478bd9Sstevel@tonic-gate 		/* get the exit status */
3271*7c478bd9Sstevel@tonic-gate 		rcode = endmailer(mci, e, pv);
3272*7c478bd9Sstevel@tonic-gate 		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
3273*7c478bd9Sstevel@tonic-gate 		{
3274*7c478bd9Sstevel@tonic-gate 			/*
3275*7c478bd9Sstevel@tonic-gate 			**  Need an e_message for mailq display.
3276*7c478bd9Sstevel@tonic-gate 			**  We set SmtpError as
3277*7c478bd9Sstevel@tonic-gate 			*/
3278*7c478bd9Sstevel@tonic-gate 
3279*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(SmtpError, sizeof SmtpError,
3280*7c478bd9Sstevel@tonic-gate 					   "%s mailer (%s) exited with EX_TEMPFAIL",
3281*7c478bd9Sstevel@tonic-gate 					   m->m_name, m->m_mailer);
3282*7c478bd9Sstevel@tonic-gate 		}
3283*7c478bd9Sstevel@tonic-gate 	}
3284*7c478bd9Sstevel@tonic-gate 	else
3285*7c478bd9Sstevel@tonic-gate 	{
3286*7c478bd9Sstevel@tonic-gate 		/*
3287*7c478bd9Sstevel@tonic-gate 		**  Send the MAIL FROM: protocol
3288*7c478bd9Sstevel@tonic-gate 		*/
3289*7c478bd9Sstevel@tonic-gate 
3290*7c478bd9Sstevel@tonic-gate 		/* XXX this isn't pipelined... */
3291*7c478bd9Sstevel@tonic-gate 		rcode = smtpmailfrom(m, mci, e);
3292*7c478bd9Sstevel@tonic-gate 		if (rcode == EX_OK)
3293*7c478bd9Sstevel@tonic-gate 		{
3294*7c478bd9Sstevel@tonic-gate 			register int i;
3295*7c478bd9Sstevel@tonic-gate # if PIPELINING
3296*7c478bd9Sstevel@tonic-gate 			ADDRESS *volatile pchain;
3297*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
3298*7c478bd9Sstevel@tonic-gate 
3299*7c478bd9Sstevel@tonic-gate 			/* send the recipient list */
3300*7c478bd9Sstevel@tonic-gate 			tobuf[0] = '\0';
3301*7c478bd9Sstevel@tonic-gate 			mci->mci_retryrcpt = false;
3302*7c478bd9Sstevel@tonic-gate 			mci->mci_tolist = tobuf;
3303*7c478bd9Sstevel@tonic-gate # if PIPELINING
3304*7c478bd9Sstevel@tonic-gate 			pchain = NULL;
3305*7c478bd9Sstevel@tonic-gate 			mci->mci_nextaddr = NULL;
3306*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
3307*7c478bd9Sstevel@tonic-gate 
3308*7c478bd9Sstevel@tonic-gate 			for (to = tochain; to != NULL; to = to->q_tchain)
3309*7c478bd9Sstevel@tonic-gate 			{
3310*7c478bd9Sstevel@tonic-gate 				if (!QS_IS_UNMARKED(to->q_state))
3311*7c478bd9Sstevel@tonic-gate 					continue;
3312*7c478bd9Sstevel@tonic-gate 
3313*7c478bd9Sstevel@tonic-gate 				/* mark recipient state as "ok so far" */
3314*7c478bd9Sstevel@tonic-gate 				to->q_state = QS_OK;
3315*7c478bd9Sstevel@tonic-gate 				e->e_to = to->q_paddr;
3316*7c478bd9Sstevel@tonic-gate # if STARTTLS
3317*7c478bd9Sstevel@tonic-gate 				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3318*7c478bd9Sstevel@tonic-gate 					    RSF_RMCOMM|RSF_COUNT, 3,
3319*7c478bd9Sstevel@tonic-gate 					    mci->mci_host, e->e_id);
3320*7c478bd9Sstevel@tonic-gate 				if (i != EX_OK)
3321*7c478bd9Sstevel@tonic-gate 				{
3322*7c478bd9Sstevel@tonic-gate 					markfailure(e, to, mci, i, false);
3323*7c478bd9Sstevel@tonic-gate 					giveresponse(i, to->q_status,  m, mci,
3324*7c478bd9Sstevel@tonic-gate 						     ctladdr, xstart, e, to);
3325*7c478bd9Sstevel@tonic-gate 					if (i == EX_TEMPFAIL)
3326*7c478bd9Sstevel@tonic-gate 					{
3327*7c478bd9Sstevel@tonic-gate 						mci->mci_retryrcpt = true;
3328*7c478bd9Sstevel@tonic-gate 						to->q_state = QS_RETRY;
3329*7c478bd9Sstevel@tonic-gate 					}
3330*7c478bd9Sstevel@tonic-gate 					continue;
3331*7c478bd9Sstevel@tonic-gate 				}
3332*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */
3333*7c478bd9Sstevel@tonic-gate 
3334*7c478bd9Sstevel@tonic-gate 				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
3335*7c478bd9Sstevel@tonic-gate # if PIPELINING
3336*7c478bd9Sstevel@tonic-gate 				if (i == EX_OK &&
3337*7c478bd9Sstevel@tonic-gate 				    bitset(MCIF_PIPELINED, mci->mci_flags))
3338*7c478bd9Sstevel@tonic-gate 				{
3339*7c478bd9Sstevel@tonic-gate 					/*
3340*7c478bd9Sstevel@tonic-gate 					**  Add new element to list of
3341*7c478bd9Sstevel@tonic-gate 					**  recipients for pipelining.
3342*7c478bd9Sstevel@tonic-gate 					*/
3343*7c478bd9Sstevel@tonic-gate 
3344*7c478bd9Sstevel@tonic-gate 					to->q_pchain = NULL;
3345*7c478bd9Sstevel@tonic-gate 					if (mci->mci_nextaddr == NULL)
3346*7c478bd9Sstevel@tonic-gate 						mci->mci_nextaddr = to;
3347*7c478bd9Sstevel@tonic-gate 					if (pchain == NULL)
3348*7c478bd9Sstevel@tonic-gate 						pchain = to;
3349*7c478bd9Sstevel@tonic-gate 					else
3350*7c478bd9Sstevel@tonic-gate 					{
3351*7c478bd9Sstevel@tonic-gate 						pchain->q_pchain = to;
3352*7c478bd9Sstevel@tonic-gate 						pchain = pchain->q_pchain;
3353*7c478bd9Sstevel@tonic-gate 					}
3354*7c478bd9Sstevel@tonic-gate 				}
3355*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
3356*7c478bd9Sstevel@tonic-gate 				if (i != EX_OK)
3357*7c478bd9Sstevel@tonic-gate 				{
3358*7c478bd9Sstevel@tonic-gate 					markfailure(e, to, mci, i, false);
3359*7c478bd9Sstevel@tonic-gate 					giveresponse(i, to->q_status, m, mci,
3360*7c478bd9Sstevel@tonic-gate 						     ctladdr, xstart, e, to);
3361*7c478bd9Sstevel@tonic-gate 					if (i == EX_TEMPFAIL)
3362*7c478bd9Sstevel@tonic-gate 						to->q_state = QS_RETRY;
3363*7c478bd9Sstevel@tonic-gate 				}
3364*7c478bd9Sstevel@tonic-gate 			}
3365*7c478bd9Sstevel@tonic-gate 
3366*7c478bd9Sstevel@tonic-gate 			/* No recipients in list and no missing responses? */
3367*7c478bd9Sstevel@tonic-gate 			if (tobuf[0] == '\0'
3368*7c478bd9Sstevel@tonic-gate # if PIPELINING
3369*7c478bd9Sstevel@tonic-gate 			    && mci->mci_nextaddr == NULL
3370*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
3371*7c478bd9Sstevel@tonic-gate 			   )
3372*7c478bd9Sstevel@tonic-gate 			{
3373*7c478bd9Sstevel@tonic-gate 				rcode = EX_OK;
3374*7c478bd9Sstevel@tonic-gate 				e->e_to = NULL;
3375*7c478bd9Sstevel@tonic-gate 				if (bitset(MCIF_CACHED, mci->mci_flags))
3376*7c478bd9Sstevel@tonic-gate 					smtprset(m, mci, e);
3377*7c478bd9Sstevel@tonic-gate 			}
3378*7c478bd9Sstevel@tonic-gate 			else
3379*7c478bd9Sstevel@tonic-gate 			{
3380*7c478bd9Sstevel@tonic-gate 				e->e_to = tobuf + 1;
3381*7c478bd9Sstevel@tonic-gate 				rcode = smtpdata(m, mci, e, ctladdr, xstart);
3382*7c478bd9Sstevel@tonic-gate 			}
3383*7c478bd9Sstevel@tonic-gate 		}
3384*7c478bd9Sstevel@tonic-gate 		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
3385*7c478bd9Sstevel@tonic-gate 		{
3386*7c478bd9Sstevel@tonic-gate 			/* try next MX site */
3387*7c478bd9Sstevel@tonic-gate 			goto tryhost;
3388*7c478bd9Sstevel@tonic-gate 		}
3389*7c478bd9Sstevel@tonic-gate 	}
3390*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
3391*7c478bd9Sstevel@tonic-gate 	if (ConfigLevel < 2)
3392*7c478bd9Sstevel@tonic-gate 		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
3393*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
3394*7c478bd9Sstevel@tonic-gate 
3395*7c478bd9Sstevel@tonic-gate 	if (tTd(62, 1))
3396*7c478bd9Sstevel@tonic-gate 		checkfds("after delivery");
3397*7c478bd9Sstevel@tonic-gate 
3398*7c478bd9Sstevel@tonic-gate 	/*
3399*7c478bd9Sstevel@tonic-gate 	**  Do final status disposal.
3400*7c478bd9Sstevel@tonic-gate 	**	We check for something in tobuf for the SMTP case.
3401*7c478bd9Sstevel@tonic-gate 	**	If we got a temporary failure, arrange to queue the
3402*7c478bd9Sstevel@tonic-gate 	**		addressees.
3403*7c478bd9Sstevel@tonic-gate 	*/
3404*7c478bd9Sstevel@tonic-gate 
3405*7c478bd9Sstevel@tonic-gate   give_up:
3406*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_LMTP, m->m_flags))
3407*7c478bd9Sstevel@tonic-gate 	{
3408*7c478bd9Sstevel@tonic-gate 		lmtp_rcode = rcode;
3409*7c478bd9Sstevel@tonic-gate 		tobuf[0] = '\0';
3410*7c478bd9Sstevel@tonic-gate 		anyok = false;
3411*7c478bd9Sstevel@tonic-gate 		strsize = 0;
3412*7c478bd9Sstevel@tonic-gate 	}
3413*7c478bd9Sstevel@tonic-gate 	else
3414*7c478bd9Sstevel@tonic-gate 		anyok = rcode == EX_OK;
3415*7c478bd9Sstevel@tonic-gate 
3416*7c478bd9Sstevel@tonic-gate 	for (to = tochain; to != NULL; to = to->q_tchain)
3417*7c478bd9Sstevel@tonic-gate 	{
3418*7c478bd9Sstevel@tonic-gate 		/* see if address already marked */
3419*7c478bd9Sstevel@tonic-gate 		if (!QS_IS_OK(to->q_state))
3420*7c478bd9Sstevel@tonic-gate 			continue;
3421*7c478bd9Sstevel@tonic-gate 
3422*7c478bd9Sstevel@tonic-gate 		/* if running LMTP, get the status for each address */
3423*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_LMTP, m->m_flags))
3424*7c478bd9Sstevel@tonic-gate 		{
3425*7c478bd9Sstevel@tonic-gate 			if (lmtp_rcode == EX_OK)
3426*7c478bd9Sstevel@tonic-gate 				rcode = smtpgetstat(m, mci, e);
3427*7c478bd9Sstevel@tonic-gate 			if (rcode == EX_OK)
3428*7c478bd9Sstevel@tonic-gate 			{
3429*7c478bd9Sstevel@tonic-gate 				strsize += sm_strlcat2(tobuf + strsize, ",",
3430*7c478bd9Sstevel@tonic-gate 						to->q_paddr,
3431*7c478bd9Sstevel@tonic-gate 						tobufsize - strsize);
3432*7c478bd9Sstevel@tonic-gate 				SM_ASSERT(strsize < tobufsize);
3433*7c478bd9Sstevel@tonic-gate 				anyok = true;
3434*7c478bd9Sstevel@tonic-gate 			}
3435*7c478bd9Sstevel@tonic-gate 			else
3436*7c478bd9Sstevel@tonic-gate 			{
3437*7c478bd9Sstevel@tonic-gate 				e->e_to = to->q_paddr;
3438*7c478bd9Sstevel@tonic-gate 				markfailure(e, to, mci, rcode, true);
3439*7c478bd9Sstevel@tonic-gate 				giveresponse(rcode, to->q_status, m, mci,
3440*7c478bd9Sstevel@tonic-gate 					     ctladdr, xstart, e, to);
3441*7c478bd9Sstevel@tonic-gate 				e->e_to = tobuf + 1;
3442*7c478bd9Sstevel@tonic-gate 				continue;
3443*7c478bd9Sstevel@tonic-gate 			}
3444*7c478bd9Sstevel@tonic-gate 		}
3445*7c478bd9Sstevel@tonic-gate 		else
3446*7c478bd9Sstevel@tonic-gate 		{
3447*7c478bd9Sstevel@tonic-gate 			/* mark bad addresses */
3448*7c478bd9Sstevel@tonic-gate 			if (rcode != EX_OK)
3449*7c478bd9Sstevel@tonic-gate 			{
3450*7c478bd9Sstevel@tonic-gate 				if (goodmxfound && rcode == EX_NOHOST)
3451*7c478bd9Sstevel@tonic-gate 					rcode = EX_TEMPFAIL;
3452*7c478bd9Sstevel@tonic-gate 				markfailure(e, to, mci, rcode, true);
3453*7c478bd9Sstevel@tonic-gate 				continue;
3454*7c478bd9Sstevel@tonic-gate 			}
3455*7c478bd9Sstevel@tonic-gate 		}
3456*7c478bd9Sstevel@tonic-gate 
3457*7c478bd9Sstevel@tonic-gate 		/* successful delivery */
3458*7c478bd9Sstevel@tonic-gate 		to->q_state = QS_SENT;
3459*7c478bd9Sstevel@tonic-gate 		to->q_statdate = curtime();
3460*7c478bd9Sstevel@tonic-gate 		e->e_nsent++;
3461*7c478bd9Sstevel@tonic-gate 
3462*7c478bd9Sstevel@tonic-gate 		/*
3463*7c478bd9Sstevel@tonic-gate 		**  Checkpoint the send list every few addresses
3464*7c478bd9Sstevel@tonic-gate 		*/
3465*7c478bd9Sstevel@tonic-gate 
3466*7c478bd9Sstevel@tonic-gate 		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
3467*7c478bd9Sstevel@tonic-gate 		{
3468*7c478bd9Sstevel@tonic-gate 			queueup(e, false, false);
3469*7c478bd9Sstevel@tonic-gate 			e->e_nsent = 0;
3470*7c478bd9Sstevel@tonic-gate 		}
3471*7c478bd9Sstevel@tonic-gate 
3472*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_LOCALMAILER, m->m_flags) &&
3473*7c478bd9Sstevel@tonic-gate 		    bitset(QPINGONSUCCESS, to->q_flags))
3474*7c478bd9Sstevel@tonic-gate 		{
3475*7c478bd9Sstevel@tonic-gate 			to->q_flags |= QDELIVERED;
3476*7c478bd9Sstevel@tonic-gate 			to->q_status = "2.1.5";
3477*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3478*7c478bd9Sstevel@tonic-gate 					     "%s... Successfully delivered\n",
3479*7c478bd9Sstevel@tonic-gate 					     to->q_paddr);
3480*7c478bd9Sstevel@tonic-gate 		}
3481*7c478bd9Sstevel@tonic-gate 		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
3482*7c478bd9Sstevel@tonic-gate 			 bitset(QPRIMARY, to->q_flags) &&
3483*7c478bd9Sstevel@tonic-gate 			 !bitset(MCIF_DSN, mci->mci_flags))
3484*7c478bd9Sstevel@tonic-gate 		{
3485*7c478bd9Sstevel@tonic-gate 			to->q_flags |= QRELAYED;
3486*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3487*7c478bd9Sstevel@tonic-gate 					     "%s... relayed; expect no further notifications\n",
3488*7c478bd9Sstevel@tonic-gate 					     to->q_paddr);
3489*7c478bd9Sstevel@tonic-gate 		}
3490*7c478bd9Sstevel@tonic-gate 		else if (IS_DLVR_NOTIFY(e) &&
3491*7c478bd9Sstevel@tonic-gate 			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
3492*7c478bd9Sstevel@tonic-gate 			 bitset(QPRIMARY, to->q_flags) &&
3493*7c478bd9Sstevel@tonic-gate 			 (!bitset(QHASNOTIFY, to->q_flags) ||
3494*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONSUCCESS, to->q_flags) ||
3495*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONFAILURE, to->q_flags) ||
3496*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONDELAY, to->q_flags)))
3497*7c478bd9Sstevel@tonic-gate 		{
3498*7c478bd9Sstevel@tonic-gate 			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
3499*7c478bd9Sstevel@tonic-gate 			to->q_flags |= QBYNRELAY;
3500*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3501*7c478bd9Sstevel@tonic-gate 					     "%s... Deliver-by notify: relayed\n",
3502*7c478bd9Sstevel@tonic-gate 					     to->q_paddr);
3503*7c478bd9Sstevel@tonic-gate 		}
3504*7c478bd9Sstevel@tonic-gate 		else if (IS_DLVR_TRACE(e) &&
3505*7c478bd9Sstevel@tonic-gate 			 (!bitset(QHASNOTIFY, to->q_flags) ||
3506*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONSUCCESS, to->q_flags) ||
3507*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONFAILURE, to->q_flags) ||
3508*7c478bd9Sstevel@tonic-gate 			  bitset(QPINGONDELAY, to->q_flags)) &&
3509*7c478bd9Sstevel@tonic-gate 			 bitset(QPRIMARY, to->q_flags))
3510*7c478bd9Sstevel@tonic-gate 		{
3511*7c478bd9Sstevel@tonic-gate 			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
3512*7c478bd9Sstevel@tonic-gate 			to->q_flags |= QBYTRACE;
3513*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3514*7c478bd9Sstevel@tonic-gate 					     "%s... Deliver-By trace: relayed\n",
3515*7c478bd9Sstevel@tonic-gate 					     to->q_paddr);
3516*7c478bd9Sstevel@tonic-gate 		}
3517*7c478bd9Sstevel@tonic-gate 	}
3518*7c478bd9Sstevel@tonic-gate 
3519*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_LMTP, m->m_flags))
3520*7c478bd9Sstevel@tonic-gate 	{
3521*7c478bd9Sstevel@tonic-gate 		/*
3522*7c478bd9Sstevel@tonic-gate 		**  Global information applies to the last recipient only;
3523*7c478bd9Sstevel@tonic-gate 		**  clear it out to avoid bogus errors.
3524*7c478bd9Sstevel@tonic-gate 		*/
3525*7c478bd9Sstevel@tonic-gate 
3526*7c478bd9Sstevel@tonic-gate 		rcode = EX_OK;
3527*7c478bd9Sstevel@tonic-gate 		e->e_statmsg = NULL;
3528*7c478bd9Sstevel@tonic-gate 
3529*7c478bd9Sstevel@tonic-gate 		/* reset the mci state for the next transaction */
3530*7c478bd9Sstevel@tonic-gate 		if (mci != NULL &&
3531*7c478bd9Sstevel@tonic-gate 		    (mci->mci_state == MCIS_MAIL ||
3532*7c478bd9Sstevel@tonic-gate 		     mci->mci_state == MCIS_RCPT ||
3533*7c478bd9Sstevel@tonic-gate 		     mci->mci_state == MCIS_DATA))
3534*7c478bd9Sstevel@tonic-gate 		{
3535*7c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_OPEN;
3536*7c478bd9Sstevel@tonic-gate 			SmtpPhase = mci->mci_phase = "idle";
3537*7c478bd9Sstevel@tonic-gate 			sm_setproctitle(true, e, "%s: %s", CurHostName,
3538*7c478bd9Sstevel@tonic-gate 					mci->mci_phase);
3539*7c478bd9Sstevel@tonic-gate 		}
3540*7c478bd9Sstevel@tonic-gate 	}
3541*7c478bd9Sstevel@tonic-gate 
3542*7c478bd9Sstevel@tonic-gate 	if (tobuf[0] != '\0')
3543*7c478bd9Sstevel@tonic-gate 	{
3544*7c478bd9Sstevel@tonic-gate 		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
3545*7c478bd9Sstevel@tonic-gate #if 0
3546*7c478bd9Sstevel@tonic-gate 		/*
3547*7c478bd9Sstevel@tonic-gate 		**  This code is disabled for now because I am not
3548*7c478bd9Sstevel@tonic-gate 		**  sure that copying status from the first recipient
3549*7c478bd9Sstevel@tonic-gate 		**  to all non-status'ed recipients is a good idea.
3550*7c478bd9Sstevel@tonic-gate 		*/
3551*7c478bd9Sstevel@tonic-gate 
3552*7c478bd9Sstevel@tonic-gate 		if (tochain->q_message != NULL &&
3553*7c478bd9Sstevel@tonic-gate 		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
3554*7c478bd9Sstevel@tonic-gate 		{
3555*7c478bd9Sstevel@tonic-gate 			for (to = tochain->q_tchain; to != NULL;
3556*7c478bd9Sstevel@tonic-gate 			     to = to->q_tchain)
3557*7c478bd9Sstevel@tonic-gate 			{
3558*7c478bd9Sstevel@tonic-gate 				/* see if address already marked */
3559*7c478bd9Sstevel@tonic-gate 				if (QS_IS_QUEUEUP(to->q_state) &&
3560*7c478bd9Sstevel@tonic-gate 				    to->q_message == NULL)
3561*7c478bd9Sstevel@tonic-gate 					to->q_message = sm_rpool_strdup_x(e->e_rpool,
3562*7c478bd9Sstevel@tonic-gate 							tochain->q_message);
3563*7c478bd9Sstevel@tonic-gate 			}
3564*7c478bd9Sstevel@tonic-gate 		}
3565*7c478bd9Sstevel@tonic-gate #endif /* 0 */
3566*7c478bd9Sstevel@tonic-gate 	}
3567*7c478bd9Sstevel@tonic-gate 	if (anyok)
3568*7c478bd9Sstevel@tonic-gate 		markstats(e, tochain, STATS_NORMAL);
3569*7c478bd9Sstevel@tonic-gate 	mci_store_persistent(mci);
3570*7c478bd9Sstevel@tonic-gate 
3571*7c478bd9Sstevel@tonic-gate 	/* Some recipients were tempfailed, try them on the next host */
3572*7c478bd9Sstevel@tonic-gate 	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
3573*7c478bd9Sstevel@tonic-gate 	{
3574*7c478bd9Sstevel@tonic-gate 		/* try next MX site */
3575*7c478bd9Sstevel@tonic-gate 		goto tryhost;
3576*7c478bd9Sstevel@tonic-gate 	}
3577*7c478bd9Sstevel@tonic-gate 
3578*7c478bd9Sstevel@tonic-gate 	/* now close the connection */
3579*7c478bd9Sstevel@tonic-gate 	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
3580*7c478bd9Sstevel@tonic-gate 	    !bitset(MCIF_CACHED, mci->mci_flags))
3581*7c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
3582*7c478bd9Sstevel@tonic-gate 
3583*7c478bd9Sstevel@tonic-gate cleanup: ;
3584*7c478bd9Sstevel@tonic-gate 	}
3585*7c478bd9Sstevel@tonic-gate 	SM_FINALLY
3586*7c478bd9Sstevel@tonic-gate 	{
3587*7c478bd9Sstevel@tonic-gate 		/*
3588*7c478bd9Sstevel@tonic-gate 		**  Restore state and return.
3589*7c478bd9Sstevel@tonic-gate 		*/
3590*7c478bd9Sstevel@tonic-gate #if XDEBUG
3591*7c478bd9Sstevel@tonic-gate 		char wbuf[MAXLINE];
3592*7c478bd9Sstevel@tonic-gate 
3593*7c478bd9Sstevel@tonic-gate 		/* make absolutely certain 0, 1, and 2 are in use */
3594*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(wbuf, sizeof wbuf,
3595*7c478bd9Sstevel@tonic-gate 				   "%s... end of deliver(%s)",
3596*7c478bd9Sstevel@tonic-gate 				   e->e_to == NULL ? "NO-TO-LIST"
3597*7c478bd9Sstevel@tonic-gate 						   : shortenstring(e->e_to,
3598*7c478bd9Sstevel@tonic-gate 								   MAXSHORTSTR),
3599*7c478bd9Sstevel@tonic-gate 				  m->m_name);
3600*7c478bd9Sstevel@tonic-gate 		checkfd012(wbuf);
3601*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
3602*7c478bd9Sstevel@tonic-gate 
3603*7c478bd9Sstevel@tonic-gate 		errno = 0;
3604*7c478bd9Sstevel@tonic-gate 
3605*7c478bd9Sstevel@tonic-gate 		/*
3606*7c478bd9Sstevel@tonic-gate 		**  It was originally necessary to set macro 'g' to NULL
3607*7c478bd9Sstevel@tonic-gate 		**  because it previously pointed to an auto buffer.
3608*7c478bd9Sstevel@tonic-gate 		**  We don't do this any more, so this may be unnecessary.
3609*7c478bd9Sstevel@tonic-gate 		*/
3610*7c478bd9Sstevel@tonic-gate 
3611*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
3612*7c478bd9Sstevel@tonic-gate 		e->e_to = NULL;
3613*7c478bd9Sstevel@tonic-gate 	}
3614*7c478bd9Sstevel@tonic-gate 	SM_END_TRY
3615*7c478bd9Sstevel@tonic-gate 	return rcode;
3616*7c478bd9Sstevel@tonic-gate }
3617*7c478bd9Sstevel@tonic-gate 
3618*7c478bd9Sstevel@tonic-gate /*
3619*7c478bd9Sstevel@tonic-gate **  MARKFAILURE -- mark a failure on a specific address.
3620*7c478bd9Sstevel@tonic-gate **
3621*7c478bd9Sstevel@tonic-gate **	Parameters:
3622*7c478bd9Sstevel@tonic-gate **		e -- the envelope we are sending.
3623*7c478bd9Sstevel@tonic-gate **		q -- the address to mark.
3624*7c478bd9Sstevel@tonic-gate **		mci -- mailer connection information.
3625*7c478bd9Sstevel@tonic-gate **		rcode -- the code signifying the particular failure.
3626*7c478bd9Sstevel@tonic-gate **		ovr -- override an existing code?
3627*7c478bd9Sstevel@tonic-gate **
3628*7c478bd9Sstevel@tonic-gate **	Returns:
3629*7c478bd9Sstevel@tonic-gate **		none.
3630*7c478bd9Sstevel@tonic-gate **
3631*7c478bd9Sstevel@tonic-gate **	Side Effects:
3632*7c478bd9Sstevel@tonic-gate **		marks the address (and possibly the envelope) with the
3633*7c478bd9Sstevel@tonic-gate **			failure so that an error will be returned or
3634*7c478bd9Sstevel@tonic-gate **			the message will be queued, as appropriate.
3635*7c478bd9Sstevel@tonic-gate */
3636*7c478bd9Sstevel@tonic-gate 
3637*7c478bd9Sstevel@tonic-gate void
3638*7c478bd9Sstevel@tonic-gate markfailure(e, q, mci, rcode, ovr)
3639*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
3640*7c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
3641*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
3642*7c478bd9Sstevel@tonic-gate 	int rcode;
3643*7c478bd9Sstevel@tonic-gate 	bool ovr;
3644*7c478bd9Sstevel@tonic-gate {
3645*7c478bd9Sstevel@tonic-gate 	int save_errno = errno;
3646*7c478bd9Sstevel@tonic-gate 	char *status = NULL;
3647*7c478bd9Sstevel@tonic-gate 	char *rstatus = NULL;
3648*7c478bd9Sstevel@tonic-gate 
3649*7c478bd9Sstevel@tonic-gate 	switch (rcode)
3650*7c478bd9Sstevel@tonic-gate 	{
3651*7c478bd9Sstevel@tonic-gate 	  case EX_OK:
3652*7c478bd9Sstevel@tonic-gate 		break;
3653*7c478bd9Sstevel@tonic-gate 
3654*7c478bd9Sstevel@tonic-gate 	  case EX_TEMPFAIL:
3655*7c478bd9Sstevel@tonic-gate 	  case EX_IOERR:
3656*7c478bd9Sstevel@tonic-gate 	  case EX_OSERR:
3657*7c478bd9Sstevel@tonic-gate 		q->q_state = QS_QUEUEUP;
3658*7c478bd9Sstevel@tonic-gate 		break;
3659*7c478bd9Sstevel@tonic-gate 
3660*7c478bd9Sstevel@tonic-gate 	  default:
3661*7c478bd9Sstevel@tonic-gate 		q->q_state = QS_BADADDR;
3662*7c478bd9Sstevel@tonic-gate 		break;
3663*7c478bd9Sstevel@tonic-gate 	}
3664*7c478bd9Sstevel@tonic-gate 
3665*7c478bd9Sstevel@tonic-gate 	/* find most specific error code possible */
3666*7c478bd9Sstevel@tonic-gate 	if (mci != NULL && mci->mci_status != NULL)
3667*7c478bd9Sstevel@tonic-gate 	{
3668*7c478bd9Sstevel@tonic-gate 		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
3669*7c478bd9Sstevel@tonic-gate 		if (mci->mci_rstatus != NULL)
3670*7c478bd9Sstevel@tonic-gate 			rstatus = sm_rpool_strdup_x(e->e_rpool,
3671*7c478bd9Sstevel@tonic-gate 						    mci->mci_rstatus);
3672*7c478bd9Sstevel@tonic-gate 		else
3673*7c478bd9Sstevel@tonic-gate 			rstatus = NULL;
3674*7c478bd9Sstevel@tonic-gate 	}
3675*7c478bd9Sstevel@tonic-gate 	else if (e->e_status != NULL)
3676*7c478bd9Sstevel@tonic-gate 	{
3677*7c478bd9Sstevel@tonic-gate 		status = e->e_status;
3678*7c478bd9Sstevel@tonic-gate 		rstatus = NULL;
3679*7c478bd9Sstevel@tonic-gate 	}
3680*7c478bd9Sstevel@tonic-gate 	else
3681*7c478bd9Sstevel@tonic-gate 	{
3682*7c478bd9Sstevel@tonic-gate 		switch (rcode)
3683*7c478bd9Sstevel@tonic-gate 		{
3684*7c478bd9Sstevel@tonic-gate 		  case EX_USAGE:
3685*7c478bd9Sstevel@tonic-gate 			status = "5.5.4";
3686*7c478bd9Sstevel@tonic-gate 			break;
3687*7c478bd9Sstevel@tonic-gate 
3688*7c478bd9Sstevel@tonic-gate 		  case EX_DATAERR:
3689*7c478bd9Sstevel@tonic-gate 			status = "5.5.2";
3690*7c478bd9Sstevel@tonic-gate 			break;
3691*7c478bd9Sstevel@tonic-gate 
3692*7c478bd9Sstevel@tonic-gate 		  case EX_NOUSER:
3693*7c478bd9Sstevel@tonic-gate 			status = "5.1.1";
3694*7c478bd9Sstevel@tonic-gate 			break;
3695*7c478bd9Sstevel@tonic-gate 
3696*7c478bd9Sstevel@tonic-gate 		  case EX_NOHOST:
3697*7c478bd9Sstevel@tonic-gate 			status = "5.1.2";
3698*7c478bd9Sstevel@tonic-gate 			break;
3699*7c478bd9Sstevel@tonic-gate 
3700*7c478bd9Sstevel@tonic-gate 		  case EX_NOINPUT:
3701*7c478bd9Sstevel@tonic-gate 		  case EX_CANTCREAT:
3702*7c478bd9Sstevel@tonic-gate 		  case EX_NOPERM:
3703*7c478bd9Sstevel@tonic-gate 			status = "5.3.0";
3704*7c478bd9Sstevel@tonic-gate 			break;
3705*7c478bd9Sstevel@tonic-gate 
3706*7c478bd9Sstevel@tonic-gate 		  case EX_UNAVAILABLE:
3707*7c478bd9Sstevel@tonic-gate 		  case EX_SOFTWARE:
3708*7c478bd9Sstevel@tonic-gate 		  case EX_OSFILE:
3709*7c478bd9Sstevel@tonic-gate 		  case EX_PROTOCOL:
3710*7c478bd9Sstevel@tonic-gate 		  case EX_CONFIG:
3711*7c478bd9Sstevel@tonic-gate 			status = "5.5.0";
3712*7c478bd9Sstevel@tonic-gate 			break;
3713*7c478bd9Sstevel@tonic-gate 
3714*7c478bd9Sstevel@tonic-gate 		  case EX_OSERR:
3715*7c478bd9Sstevel@tonic-gate 		  case EX_IOERR:
3716*7c478bd9Sstevel@tonic-gate 			status = "4.5.0";
3717*7c478bd9Sstevel@tonic-gate 			break;
3718*7c478bd9Sstevel@tonic-gate 
3719*7c478bd9Sstevel@tonic-gate 		  case EX_TEMPFAIL:
3720*7c478bd9Sstevel@tonic-gate 			status = "4.2.0";
3721*7c478bd9Sstevel@tonic-gate 			break;
3722*7c478bd9Sstevel@tonic-gate 		}
3723*7c478bd9Sstevel@tonic-gate 	}
3724*7c478bd9Sstevel@tonic-gate 
3725*7c478bd9Sstevel@tonic-gate 	/* new status? */
3726*7c478bd9Sstevel@tonic-gate 	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
3727*7c478bd9Sstevel@tonic-gate 	    *q->q_status == '\0' || *q->q_status < *status))
3728*7c478bd9Sstevel@tonic-gate 	{
3729*7c478bd9Sstevel@tonic-gate 		q->q_status = status;
3730*7c478bd9Sstevel@tonic-gate 		q->q_rstatus = rstatus;
3731*7c478bd9Sstevel@tonic-gate 	}
3732*7c478bd9Sstevel@tonic-gate 	if (rcode != EX_OK && q->q_rstatus == NULL &&
3733*7c478bd9Sstevel@tonic-gate 	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
3734*7c478bd9Sstevel@tonic-gate 	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
3735*7c478bd9Sstevel@tonic-gate 	{
3736*7c478bd9Sstevel@tonic-gate 		char buf[16];
3737*7c478bd9Sstevel@tonic-gate 
3738*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(buf, sizeof buf, "%d", rcode);
3739*7c478bd9Sstevel@tonic-gate 		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
3740*7c478bd9Sstevel@tonic-gate 	}
3741*7c478bd9Sstevel@tonic-gate 
3742*7c478bd9Sstevel@tonic-gate 	q->q_statdate = curtime();
3743*7c478bd9Sstevel@tonic-gate 	if (CurHostName != NULL && CurHostName[0] != '\0' &&
3744*7c478bd9Sstevel@tonic-gate 	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
3745*7c478bd9Sstevel@tonic-gate 		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
3746*7c478bd9Sstevel@tonic-gate 
3747*7c478bd9Sstevel@tonic-gate 	/* restore errno */
3748*7c478bd9Sstevel@tonic-gate 	errno = save_errno;
3749*7c478bd9Sstevel@tonic-gate }
3750*7c478bd9Sstevel@tonic-gate /*
3751*7c478bd9Sstevel@tonic-gate **  ENDMAILER -- Wait for mailer to terminate.
3752*7c478bd9Sstevel@tonic-gate **
3753*7c478bd9Sstevel@tonic-gate **	We should never get fatal errors (e.g., segmentation
3754*7c478bd9Sstevel@tonic-gate **	violation), so we report those specially.  For other
3755*7c478bd9Sstevel@tonic-gate **	errors, we choose a status message (into statmsg),
3756*7c478bd9Sstevel@tonic-gate **	and if it represents an error, we print it.
3757*7c478bd9Sstevel@tonic-gate **
3758*7c478bd9Sstevel@tonic-gate **	Parameters:
3759*7c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
3760*7c478bd9Sstevel@tonic-gate **		e -- the current envelope.
3761*7c478bd9Sstevel@tonic-gate **		pv -- the parameter vector that invoked the mailer
3762*7c478bd9Sstevel@tonic-gate **			(for error messages).
3763*7c478bd9Sstevel@tonic-gate **
3764*7c478bd9Sstevel@tonic-gate **	Returns:
3765*7c478bd9Sstevel@tonic-gate **		exit code of mailer.
3766*7c478bd9Sstevel@tonic-gate **
3767*7c478bd9Sstevel@tonic-gate **	Side Effects:
3768*7c478bd9Sstevel@tonic-gate **		none.
3769*7c478bd9Sstevel@tonic-gate */
3770*7c478bd9Sstevel@tonic-gate 
3771*7c478bd9Sstevel@tonic-gate static jmp_buf	EndWaitTimeout;
3772*7c478bd9Sstevel@tonic-gate 
3773*7c478bd9Sstevel@tonic-gate static void
3774*7c478bd9Sstevel@tonic-gate endwaittimeout(ignore)
3775*7c478bd9Sstevel@tonic-gate 	int ignore;
3776*7c478bd9Sstevel@tonic-gate {
3777*7c478bd9Sstevel@tonic-gate 	/*
3778*7c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3779*7c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3780*7c478bd9Sstevel@tonic-gate 	**	DOING.
3781*7c478bd9Sstevel@tonic-gate 	*/
3782*7c478bd9Sstevel@tonic-gate 
3783*7c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
3784*7c478bd9Sstevel@tonic-gate 	longjmp(EndWaitTimeout, 1);
3785*7c478bd9Sstevel@tonic-gate }
3786*7c478bd9Sstevel@tonic-gate 
3787*7c478bd9Sstevel@tonic-gate int
3788*7c478bd9Sstevel@tonic-gate endmailer(mci, e, pv)
3789*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
3790*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
3791*7c478bd9Sstevel@tonic-gate 	char **pv;
3792*7c478bd9Sstevel@tonic-gate {
3793*7c478bd9Sstevel@tonic-gate 	int st;
3794*7c478bd9Sstevel@tonic-gate 	int save_errno = errno;
3795*7c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3796*7c478bd9Sstevel@tonic-gate 	SM_EVENT *ev = NULL;
3797*7c478bd9Sstevel@tonic-gate 
3798*7c478bd9Sstevel@tonic-gate 
3799*7c478bd9Sstevel@tonic-gate 	mci_unlock_host(mci);
3800*7c478bd9Sstevel@tonic-gate 
3801*7c478bd9Sstevel@tonic-gate 	/* close output to mailer */
3802*7c478bd9Sstevel@tonic-gate 	if (mci->mci_out != NULL)
3803*7c478bd9Sstevel@tonic-gate 	{
3804*7c478bd9Sstevel@tonic-gate 		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
3805*7c478bd9Sstevel@tonic-gate 		mci->mci_out = NULL;
3806*7c478bd9Sstevel@tonic-gate 	}
3807*7c478bd9Sstevel@tonic-gate 
3808*7c478bd9Sstevel@tonic-gate 	/* copy any remaining input to transcript */
3809*7c478bd9Sstevel@tonic-gate 	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
3810*7c478bd9Sstevel@tonic-gate 	    e->e_xfp != NULL)
3811*7c478bd9Sstevel@tonic-gate 	{
3812*7c478bd9Sstevel@tonic-gate 		while (sfgets(buf, sizeof buf, mci->mci_in,
3813*7c478bd9Sstevel@tonic-gate 			      TimeOuts.to_quit, "Draining Input") != NULL)
3814*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
3815*7c478bd9Sstevel@tonic-gate 	}
3816*7c478bd9Sstevel@tonic-gate 
3817*7c478bd9Sstevel@tonic-gate #if SASL
3818*7c478bd9Sstevel@tonic-gate 	/* close SASL connection */
3819*7c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_AUTHACT, mci->mci_flags))
3820*7c478bd9Sstevel@tonic-gate 	{
3821*7c478bd9Sstevel@tonic-gate 		sasl_dispose(&mci->mci_conn);
3822*7c478bd9Sstevel@tonic-gate 		mci->mci_flags &= ~MCIF_AUTHACT;
3823*7c478bd9Sstevel@tonic-gate 	}
3824*7c478bd9Sstevel@tonic-gate #endif /* SASL */
3825*7c478bd9Sstevel@tonic-gate 
3826*7c478bd9Sstevel@tonic-gate #if STARTTLS
3827*7c478bd9Sstevel@tonic-gate 	/* shutdown TLS */
3828*7c478bd9Sstevel@tonic-gate 	(void) endtlsclt(mci);
3829*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
3830*7c478bd9Sstevel@tonic-gate 
3831*7c478bd9Sstevel@tonic-gate 	/* now close the input */
3832*7c478bd9Sstevel@tonic-gate 	if (mci->mci_in != NULL)
3833*7c478bd9Sstevel@tonic-gate 	{
3834*7c478bd9Sstevel@tonic-gate 		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
3835*7c478bd9Sstevel@tonic-gate 		mci->mci_in = NULL;
3836*7c478bd9Sstevel@tonic-gate 	}
3837*7c478bd9Sstevel@tonic-gate 	mci->mci_state = MCIS_CLOSED;
3838*7c478bd9Sstevel@tonic-gate 
3839*7c478bd9Sstevel@tonic-gate 	errno = save_errno;
3840*7c478bd9Sstevel@tonic-gate 
3841*7c478bd9Sstevel@tonic-gate 	/* in the IPC case there is nothing to wait for */
3842*7c478bd9Sstevel@tonic-gate 	if (mci->mci_pid == 0)
3843*7c478bd9Sstevel@tonic-gate 		return EX_OK;
3844*7c478bd9Sstevel@tonic-gate 
3845*7c478bd9Sstevel@tonic-gate 	/* put a timeout around the wait */
3846*7c478bd9Sstevel@tonic-gate 	if (mci->mci_mailer->m_wait > 0)
3847*7c478bd9Sstevel@tonic-gate 	{
3848*7c478bd9Sstevel@tonic-gate 		if (setjmp(EndWaitTimeout) == 0)
3849*7c478bd9Sstevel@tonic-gate 			ev = sm_setevent(mci->mci_mailer->m_wait,
3850*7c478bd9Sstevel@tonic-gate 					 endwaittimeout, 0);
3851*7c478bd9Sstevel@tonic-gate 		else
3852*7c478bd9Sstevel@tonic-gate 		{
3853*7c478bd9Sstevel@tonic-gate 			syserr("endmailer %s: wait timeout (%ld)",
3854*7c478bd9Sstevel@tonic-gate 			       mci->mci_mailer->m_name,
3855*7c478bd9Sstevel@tonic-gate 			       (long) mci->mci_mailer->m_wait);
3856*7c478bd9Sstevel@tonic-gate 			return EX_TEMPFAIL;
3857*7c478bd9Sstevel@tonic-gate 		}
3858*7c478bd9Sstevel@tonic-gate 	}
3859*7c478bd9Sstevel@tonic-gate 
3860*7c478bd9Sstevel@tonic-gate 	/* wait for the mailer process, collect status */
3861*7c478bd9Sstevel@tonic-gate 	st = waitfor(mci->mci_pid);
3862*7c478bd9Sstevel@tonic-gate 	save_errno = errno;
3863*7c478bd9Sstevel@tonic-gate 	if (ev != NULL)
3864*7c478bd9Sstevel@tonic-gate 		sm_clrevent(ev);
3865*7c478bd9Sstevel@tonic-gate 	errno = save_errno;
3866*7c478bd9Sstevel@tonic-gate 
3867*7c478bd9Sstevel@tonic-gate 	if (st == -1)
3868*7c478bd9Sstevel@tonic-gate 	{
3869*7c478bd9Sstevel@tonic-gate 		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
3870*7c478bd9Sstevel@tonic-gate 		return EX_SOFTWARE;
3871*7c478bd9Sstevel@tonic-gate 	}
3872*7c478bd9Sstevel@tonic-gate 
3873*7c478bd9Sstevel@tonic-gate 	if (WIFEXITED(st))
3874*7c478bd9Sstevel@tonic-gate 	{
3875*7c478bd9Sstevel@tonic-gate 		/* normal death -- return status */
3876*7c478bd9Sstevel@tonic-gate 		return (WEXITSTATUS(st));
3877*7c478bd9Sstevel@tonic-gate 	}
3878*7c478bd9Sstevel@tonic-gate 
3879*7c478bd9Sstevel@tonic-gate 	/* it died a horrid death */
3880*7c478bd9Sstevel@tonic-gate 	syserr("451 4.3.0 mailer %s died with signal %d%s",
3881*7c478bd9Sstevel@tonic-gate 		mci->mci_mailer->m_name, WTERMSIG(st),
3882*7c478bd9Sstevel@tonic-gate 		WCOREDUMP(st) ? " (core dumped)" :
3883*7c478bd9Sstevel@tonic-gate 		(WIFSTOPPED(st) ? " (stopped)" : ""));
3884*7c478bd9Sstevel@tonic-gate 
3885*7c478bd9Sstevel@tonic-gate 	/* log the arguments */
3886*7c478bd9Sstevel@tonic-gate 	if (pv != NULL && e->e_xfp != NULL)
3887*7c478bd9Sstevel@tonic-gate 	{
3888*7c478bd9Sstevel@tonic-gate 		register char **av;
3889*7c478bd9Sstevel@tonic-gate 
3890*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
3891*7c478bd9Sstevel@tonic-gate 		for (av = pv; *av != NULL; av++)
3892*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
3893*7c478bd9Sstevel@tonic-gate 					     *av);
3894*7c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
3895*7c478bd9Sstevel@tonic-gate 	}
3896*7c478bd9Sstevel@tonic-gate 
3897*7c478bd9Sstevel@tonic-gate 	ExitStat = EX_TEMPFAIL;
3898*7c478bd9Sstevel@tonic-gate 	return EX_TEMPFAIL;
3899*7c478bd9Sstevel@tonic-gate }
3900*7c478bd9Sstevel@tonic-gate /*
3901*7c478bd9Sstevel@tonic-gate **  GIVERESPONSE -- Interpret an error response from a mailer
3902*7c478bd9Sstevel@tonic-gate **
3903*7c478bd9Sstevel@tonic-gate **	Parameters:
3904*7c478bd9Sstevel@tonic-gate **		status -- the status code from the mailer (high byte
3905*7c478bd9Sstevel@tonic-gate **			only; core dumps must have been taken care of
3906*7c478bd9Sstevel@tonic-gate **			already).
3907*7c478bd9Sstevel@tonic-gate **		dsn -- the DSN associated with the address, if any.
3908*7c478bd9Sstevel@tonic-gate **		m -- the mailer info for this mailer.
3909*7c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info -- can be NULL if the
3910*7c478bd9Sstevel@tonic-gate **			response is given before the connection is made.
3911*7c478bd9Sstevel@tonic-gate **		ctladdr -- the controlling address for the recipient
3912*7c478bd9Sstevel@tonic-gate **			address(es).
3913*7c478bd9Sstevel@tonic-gate **		xstart -- the transaction start time, for computing
3914*7c478bd9Sstevel@tonic-gate **			transaction delays.
3915*7c478bd9Sstevel@tonic-gate **		e -- the current envelope.
3916*7c478bd9Sstevel@tonic-gate **		to -- the current recipient (NULL if none).
3917*7c478bd9Sstevel@tonic-gate **
3918*7c478bd9Sstevel@tonic-gate **	Returns:
3919*7c478bd9Sstevel@tonic-gate **		none.
3920*7c478bd9Sstevel@tonic-gate **
3921*7c478bd9Sstevel@tonic-gate **	Side Effects:
3922*7c478bd9Sstevel@tonic-gate **		Errors may be incremented.
3923*7c478bd9Sstevel@tonic-gate **		ExitStat may be set.
3924*7c478bd9Sstevel@tonic-gate */
3925*7c478bd9Sstevel@tonic-gate 
3926*7c478bd9Sstevel@tonic-gate void
3927*7c478bd9Sstevel@tonic-gate giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
3928*7c478bd9Sstevel@tonic-gate 	int status;
3929*7c478bd9Sstevel@tonic-gate 	char *dsn;
3930*7c478bd9Sstevel@tonic-gate 	register MAILER *m;
3931*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
3932*7c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
3933*7c478bd9Sstevel@tonic-gate 	time_t xstart;
3934*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3935*7c478bd9Sstevel@tonic-gate 	ADDRESS *to;
3936*7c478bd9Sstevel@tonic-gate {
3937*7c478bd9Sstevel@tonic-gate 	register const char *statmsg;
3938*7c478bd9Sstevel@tonic-gate 	int errnum = errno;
3939*7c478bd9Sstevel@tonic-gate 	int off = 4;
3940*7c478bd9Sstevel@tonic-gate 	bool usestat = false;
3941*7c478bd9Sstevel@tonic-gate 	char dsnbuf[ENHSCLEN];
3942*7c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3943*7c478bd9Sstevel@tonic-gate 	char *exmsg;
3944*7c478bd9Sstevel@tonic-gate 
3945*7c478bd9Sstevel@tonic-gate 	if (e == NULL)
3946*7c478bd9Sstevel@tonic-gate 		syserr("giveresponse: null envelope");
3947*7c478bd9Sstevel@tonic-gate 
3948*7c478bd9Sstevel@tonic-gate 	/*
3949*7c478bd9Sstevel@tonic-gate 	**  Compute status message from code.
3950*7c478bd9Sstevel@tonic-gate 	*/
3951*7c478bd9Sstevel@tonic-gate 
3952*7c478bd9Sstevel@tonic-gate 	exmsg = sm_sysexmsg(status);
3953*7c478bd9Sstevel@tonic-gate 	if (status == 0)
3954*7c478bd9Sstevel@tonic-gate 	{
3955*7c478bd9Sstevel@tonic-gate 		statmsg = "250 2.0.0 Sent";
3956*7c478bd9Sstevel@tonic-gate 		if (e->e_statmsg != NULL)
3957*7c478bd9Sstevel@tonic-gate 		{
3958*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf, "%s (%s)",
3959*7c478bd9Sstevel@tonic-gate 					   statmsg,
3960*7c478bd9Sstevel@tonic-gate 					   shortenstring(e->e_statmsg, 403));
3961*7c478bd9Sstevel@tonic-gate 			statmsg = buf;
3962*7c478bd9Sstevel@tonic-gate 		}
3963*7c478bd9Sstevel@tonic-gate 	}
3964*7c478bd9Sstevel@tonic-gate 	else if (exmsg == NULL)
3965*7c478bd9Sstevel@tonic-gate 	{
3966*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(buf, sizeof buf,
3967*7c478bd9Sstevel@tonic-gate 				   "554 5.3.0 unknown mailer error %d",
3968*7c478bd9Sstevel@tonic-gate 				   status);
3969*7c478bd9Sstevel@tonic-gate 		status = EX_UNAVAILABLE;
3970*7c478bd9Sstevel@tonic-gate 		statmsg = buf;
3971*7c478bd9Sstevel@tonic-gate 		usestat = true;
3972*7c478bd9Sstevel@tonic-gate 	}
3973*7c478bd9Sstevel@tonic-gate 	else if (status == EX_TEMPFAIL)
3974*7c478bd9Sstevel@tonic-gate 	{
3975*7c478bd9Sstevel@tonic-gate 		char *bp = buf;
3976*7c478bd9Sstevel@tonic-gate 
3977*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
3978*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
3979*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
3980*7c478bd9Sstevel@tonic-gate 		if (h_errno == TRY_AGAIN)
3981*7c478bd9Sstevel@tonic-gate 			statmsg = sm_errstring(h_errno + E_DNSBASE);
3982*7c478bd9Sstevel@tonic-gate 		else
3983*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
3984*7c478bd9Sstevel@tonic-gate 		{
3985*7c478bd9Sstevel@tonic-gate 			if (errnum != 0)
3986*7c478bd9Sstevel@tonic-gate 				statmsg = sm_errstring(errnum);
3987*7c478bd9Sstevel@tonic-gate 			else
3988*7c478bd9Sstevel@tonic-gate 				statmsg = SmtpError;
3989*7c478bd9Sstevel@tonic-gate 		}
3990*7c478bd9Sstevel@tonic-gate 		if (statmsg != NULL && statmsg[0] != '\0')
3991*7c478bd9Sstevel@tonic-gate 		{
3992*7c478bd9Sstevel@tonic-gate 			switch (errnum)
3993*7c478bd9Sstevel@tonic-gate 			{
3994*7c478bd9Sstevel@tonic-gate #ifdef ENETDOWN
3995*7c478bd9Sstevel@tonic-gate 			  case ENETDOWN:	/* Network is down */
3996*7c478bd9Sstevel@tonic-gate #endif /* ENETDOWN */
3997*7c478bd9Sstevel@tonic-gate #ifdef ENETUNREACH
3998*7c478bd9Sstevel@tonic-gate 			  case ENETUNREACH:	/* Network is unreachable */
3999*7c478bd9Sstevel@tonic-gate #endif /* ENETUNREACH */
4000*7c478bd9Sstevel@tonic-gate #ifdef ENETRESET
4001*7c478bd9Sstevel@tonic-gate 			  case ENETRESET:	/* Network dropped connection on reset */
4002*7c478bd9Sstevel@tonic-gate #endif /* ENETRESET */
4003*7c478bd9Sstevel@tonic-gate #ifdef ECONNABORTED
4004*7c478bd9Sstevel@tonic-gate 			  case ECONNABORTED:	/* Software caused connection abort */
4005*7c478bd9Sstevel@tonic-gate #endif /* ECONNABORTED */
4006*7c478bd9Sstevel@tonic-gate #ifdef EHOSTDOWN
4007*7c478bd9Sstevel@tonic-gate 			  case EHOSTDOWN:	/* Host is down */
4008*7c478bd9Sstevel@tonic-gate #endif /* EHOSTDOWN */
4009*7c478bd9Sstevel@tonic-gate #ifdef EHOSTUNREACH
4010*7c478bd9Sstevel@tonic-gate 			  case EHOSTUNREACH:	/* No route to host */
4011*7c478bd9Sstevel@tonic-gate #endif /* EHOSTUNREACH */
4012*7c478bd9Sstevel@tonic-gate 				if (mci != NULL && mci->mci_host != NULL)
4013*7c478bd9Sstevel@tonic-gate 				{
4014*7c478bd9Sstevel@tonic-gate 					(void) sm_strlcpyn(bp,
4015*7c478bd9Sstevel@tonic-gate 							   SPACELEFT(buf, bp),
4016*7c478bd9Sstevel@tonic-gate 							   2, ": ",
4017*7c478bd9Sstevel@tonic-gate 							   mci->mci_host);
4018*7c478bd9Sstevel@tonic-gate 					bp += strlen(bp);
4019*7c478bd9Sstevel@tonic-gate 				}
4020*7c478bd9Sstevel@tonic-gate 				break;
4021*7c478bd9Sstevel@tonic-gate 			}
4022*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
4023*7c478bd9Sstevel@tonic-gate 					   statmsg);
4024*7c478bd9Sstevel@tonic-gate 			usestat = true;
4025*7c478bd9Sstevel@tonic-gate 		}
4026*7c478bd9Sstevel@tonic-gate 		statmsg = buf;
4027*7c478bd9Sstevel@tonic-gate 	}
4028*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
4029*7c478bd9Sstevel@tonic-gate 	else if (status == EX_NOHOST && h_errno != 0)
4030*7c478bd9Sstevel@tonic-gate 	{
4031*7c478bd9Sstevel@tonic-gate 		statmsg = sm_errstring(h_errno + E_DNSBASE);
4032*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1,
4033*7c478bd9Sstevel@tonic-gate 				   statmsg);
4034*7c478bd9Sstevel@tonic-gate 		statmsg = buf;
4035*7c478bd9Sstevel@tonic-gate 		usestat = true;
4036*7c478bd9Sstevel@tonic-gate 	}
4037*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
4038*7c478bd9Sstevel@tonic-gate 	else
4039*7c478bd9Sstevel@tonic-gate 	{
4040*7c478bd9Sstevel@tonic-gate 		statmsg = exmsg;
4041*7c478bd9Sstevel@tonic-gate 		if (*statmsg++ == ':' && errnum != 0)
4042*7c478bd9Sstevel@tonic-gate 		{
4043*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg,
4044*7c478bd9Sstevel@tonic-gate 					   sm_errstring(errnum));
4045*7c478bd9Sstevel@tonic-gate 			statmsg = buf;
4046*7c478bd9Sstevel@tonic-gate 			usestat = true;
4047*7c478bd9Sstevel@tonic-gate 		}
4048*7c478bd9Sstevel@tonic-gate 		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
4049*7c478bd9Sstevel@tonic-gate 		{
4050*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg,
4051*7c478bd9Sstevel@tonic-gate 					   shortenstring(e->e_statmsg, 403));
4052*7c478bd9Sstevel@tonic-gate 			statmsg = buf;
4053*7c478bd9Sstevel@tonic-gate 			usestat = true;
4054*7c478bd9Sstevel@tonic-gate 		}
4055*7c478bd9Sstevel@tonic-gate 	}
4056*7c478bd9Sstevel@tonic-gate 
4057*7c478bd9Sstevel@tonic-gate 	/*
4058*7c478bd9Sstevel@tonic-gate 	**  Print the message as appropriate
4059*7c478bd9Sstevel@tonic-gate 	*/
4060*7c478bd9Sstevel@tonic-gate 
4061*7c478bd9Sstevel@tonic-gate 	if (status == EX_OK || status == EX_TEMPFAIL)
4062*7c478bd9Sstevel@tonic-gate 	{
4063*7c478bd9Sstevel@tonic-gate 		extern char MsgBuf[];
4064*7c478bd9Sstevel@tonic-gate 
4065*7c478bd9Sstevel@tonic-gate 		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
4066*7c478bd9Sstevel@tonic-gate 		{
4067*7c478bd9Sstevel@tonic-gate 			if (dsn == NULL)
4068*7c478bd9Sstevel@tonic-gate 			{
4069*7c478bd9Sstevel@tonic-gate 				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
4070*7c478bd9Sstevel@tonic-gate 						   "%.*s", off, statmsg + 4);
4071*7c478bd9Sstevel@tonic-gate 				dsn = dsnbuf;
4072*7c478bd9Sstevel@tonic-gate 			}
4073*7c478bd9Sstevel@tonic-gate 			off += 5;
4074*7c478bd9Sstevel@tonic-gate 		}
4075*7c478bd9Sstevel@tonic-gate 		else
4076*7c478bd9Sstevel@tonic-gate 		{
4077*7c478bd9Sstevel@tonic-gate 			off = 4;
4078*7c478bd9Sstevel@tonic-gate 		}
4079*7c478bd9Sstevel@tonic-gate 		message("%s", statmsg + off);
4080*7c478bd9Sstevel@tonic-gate 		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
4081*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
4082*7c478bd9Sstevel@tonic-gate 					     &MsgBuf[4]);
4083*7c478bd9Sstevel@tonic-gate 	}
4084*7c478bd9Sstevel@tonic-gate 	else
4085*7c478bd9Sstevel@tonic-gate 	{
4086*7c478bd9Sstevel@tonic-gate 		char mbuf[ENHSCLEN + 4];
4087*7c478bd9Sstevel@tonic-gate 
4088*7c478bd9Sstevel@tonic-gate 		Errors++;
4089*7c478bd9Sstevel@tonic-gate 		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
4090*7c478bd9Sstevel@tonic-gate 		    off < sizeof mbuf - 4)
4091*7c478bd9Sstevel@tonic-gate 		{
4092*7c478bd9Sstevel@tonic-gate 			if (dsn == NULL)
4093*7c478bd9Sstevel@tonic-gate 			{
4094*7c478bd9Sstevel@tonic-gate 				(void) sm_snprintf(dsnbuf, sizeof dsnbuf,
4095*7c478bd9Sstevel@tonic-gate 						   "%.*s", off, statmsg + 4);
4096*7c478bd9Sstevel@tonic-gate 				dsn = dsnbuf;
4097*7c478bd9Sstevel@tonic-gate 			}
4098*7c478bd9Sstevel@tonic-gate 			off += 5;
4099*7c478bd9Sstevel@tonic-gate 
4100*7c478bd9Sstevel@tonic-gate 			/* copy only part of statmsg to mbuf */
4101*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(mbuf, statmsg, off);
4102*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(mbuf, " %s", sizeof mbuf);
4103*7c478bd9Sstevel@tonic-gate 		}
4104*7c478bd9Sstevel@tonic-gate 		else
4105*7c478bd9Sstevel@tonic-gate 		{
4106*7c478bd9Sstevel@tonic-gate 			dsnbuf[0] = '\0';
4107*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s",
4108*7c478bd9Sstevel@tonic-gate 					   statmsg);
4109*7c478bd9Sstevel@tonic-gate 			off = 4;
4110*7c478bd9Sstevel@tonic-gate 		}
4111*7c478bd9Sstevel@tonic-gate 		usrerr(mbuf, &statmsg[off]);
4112*7c478bd9Sstevel@tonic-gate 	}
4113*7c478bd9Sstevel@tonic-gate 
4114*7c478bd9Sstevel@tonic-gate 	/*
4115*7c478bd9Sstevel@tonic-gate 	**  Final cleanup.
4116*7c478bd9Sstevel@tonic-gate 	**	Log a record of the transaction.  Compute the new
4117*7c478bd9Sstevel@tonic-gate 	**	ExitStat -- if we already had an error, stick with
4118*7c478bd9Sstevel@tonic-gate 	**	that.
4119*7c478bd9Sstevel@tonic-gate 	*/
4120*7c478bd9Sstevel@tonic-gate 
4121*7c478bd9Sstevel@tonic-gate 	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
4122*7c478bd9Sstevel@tonic-gate 	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
4123*7c478bd9Sstevel@tonic-gate 		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
4124*7c478bd9Sstevel@tonic-gate 
4125*7c478bd9Sstevel@tonic-gate 	if (tTd(11, 2))
4126*7c478bd9Sstevel@tonic-gate 		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
4127*7c478bd9Sstevel@tonic-gate 			   status,
4128*7c478bd9Sstevel@tonic-gate 			   dsn == NULL ? "<NULL>" : dsn,
4129*7c478bd9Sstevel@tonic-gate 			   e->e_message == NULL ? "<NULL>" : e->e_message,
4130*7c478bd9Sstevel@tonic-gate 			   errnum);
4131*7c478bd9Sstevel@tonic-gate 
4132*7c478bd9Sstevel@tonic-gate 	if (status != EX_TEMPFAIL)
4133*7c478bd9Sstevel@tonic-gate 		setstat(status);
4134*7c478bd9Sstevel@tonic-gate 	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
4135*7c478bd9Sstevel@tonic-gate 		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
4136*7c478bd9Sstevel@tonic-gate 	if (status != EX_OK && to != NULL && to->q_message == NULL)
4137*7c478bd9Sstevel@tonic-gate 	{
4138*7c478bd9Sstevel@tonic-gate 		if (!usestat && e->e_message != NULL)
4139*7c478bd9Sstevel@tonic-gate 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
4140*7c478bd9Sstevel@tonic-gate 							  e->e_message);
4141*7c478bd9Sstevel@tonic-gate 		else
4142*7c478bd9Sstevel@tonic-gate 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
4143*7c478bd9Sstevel@tonic-gate 							  statmsg + off);
4144*7c478bd9Sstevel@tonic-gate 	}
4145*7c478bd9Sstevel@tonic-gate 	errno = 0;
4146*7c478bd9Sstevel@tonic-gate 	SM_SET_H_ERRNO(0);
4147*7c478bd9Sstevel@tonic-gate }
4148*7c478bd9Sstevel@tonic-gate /*
4149*7c478bd9Sstevel@tonic-gate **  LOGDELIVERY -- log the delivery in the system log
4150*7c478bd9Sstevel@tonic-gate **
4151*7c478bd9Sstevel@tonic-gate **	Care is taken to avoid logging lines that are too long, because
4152*7c478bd9Sstevel@tonic-gate **	some versions of syslog have an unfortunate proclivity for core
4153*7c478bd9Sstevel@tonic-gate **	dumping.  This is a hack, to be sure, that is at best empirical.
4154*7c478bd9Sstevel@tonic-gate **
4155*7c478bd9Sstevel@tonic-gate **	Parameters:
4156*7c478bd9Sstevel@tonic-gate **		m -- the mailer info.  Can be NULL for initial queue.
4157*7c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info -- can be NULL if the
4158*7c478bd9Sstevel@tonic-gate **			log is occurring when no connection is active.
4159*7c478bd9Sstevel@tonic-gate **		dsn -- the DSN attached to the status.
4160*7c478bd9Sstevel@tonic-gate **		status -- the message to print for the status.
4161*7c478bd9Sstevel@tonic-gate **		ctladdr -- the controlling address for the to list.
4162*7c478bd9Sstevel@tonic-gate **		xstart -- the transaction start time, used for
4163*7c478bd9Sstevel@tonic-gate **			computing transaction delay.
4164*7c478bd9Sstevel@tonic-gate **		e -- the current envelope.
4165*7c478bd9Sstevel@tonic-gate **
4166*7c478bd9Sstevel@tonic-gate **	Returns:
4167*7c478bd9Sstevel@tonic-gate **		none
4168*7c478bd9Sstevel@tonic-gate **
4169*7c478bd9Sstevel@tonic-gate **	Side Effects:
4170*7c478bd9Sstevel@tonic-gate **		none
4171*7c478bd9Sstevel@tonic-gate */
4172*7c478bd9Sstevel@tonic-gate 
4173*7c478bd9Sstevel@tonic-gate void
4174*7c478bd9Sstevel@tonic-gate logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
4175*7c478bd9Sstevel@tonic-gate 	MAILER *m;
4176*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
4177*7c478bd9Sstevel@tonic-gate 	char *dsn;
4178*7c478bd9Sstevel@tonic-gate 	const char *status;
4179*7c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
4180*7c478bd9Sstevel@tonic-gate 	time_t xstart;
4181*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
4182*7c478bd9Sstevel@tonic-gate {
4183*7c478bd9Sstevel@tonic-gate 	register char *bp;
4184*7c478bd9Sstevel@tonic-gate 	register char *p;
4185*7c478bd9Sstevel@tonic-gate 	int l;
4186*7c478bd9Sstevel@tonic-gate 	time_t now = curtime();
4187*7c478bd9Sstevel@tonic-gate 	char buf[1024];
4188*7c478bd9Sstevel@tonic-gate 
4189*7c478bd9Sstevel@tonic-gate #if (SYSLOG_BUFSIZE) >= 256
4190*7c478bd9Sstevel@tonic-gate 	/* ctladdr: max 106 bytes */
4191*7c478bd9Sstevel@tonic-gate 	bp = buf;
4192*7c478bd9Sstevel@tonic-gate 	if (ctladdr != NULL)
4193*7c478bd9Sstevel@tonic-gate 	{
4194*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
4195*7c478bd9Sstevel@tonic-gate 				   shortenstring(ctladdr->q_paddr, 83));
4196*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4197*7c478bd9Sstevel@tonic-gate 		if (bitset(QGOODUID, ctladdr->q_flags))
4198*7c478bd9Sstevel@tonic-gate 		{
4199*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
4200*7c478bd9Sstevel@tonic-gate 					   (int) ctladdr->q_uid,
4201*7c478bd9Sstevel@tonic-gate 					   (int) ctladdr->q_gid);
4202*7c478bd9Sstevel@tonic-gate 			bp += strlen(bp);
4203*7c478bd9Sstevel@tonic-gate 		}
4204*7c478bd9Sstevel@tonic-gate 	}
4205*7c478bd9Sstevel@tonic-gate 
4206*7c478bd9Sstevel@tonic-gate 	/* delay & xdelay: max 41 bytes */
4207*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
4208*7c478bd9Sstevel@tonic-gate 			   pintvl(now - e->e_ctime, true));
4209*7c478bd9Sstevel@tonic-gate 	bp += strlen(bp);
4210*7c478bd9Sstevel@tonic-gate 
4211*7c478bd9Sstevel@tonic-gate 	if (xstart != (time_t) 0)
4212*7c478bd9Sstevel@tonic-gate 	{
4213*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
4214*7c478bd9Sstevel@tonic-gate 				   pintvl(now - xstart, true));
4215*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4216*7c478bd9Sstevel@tonic-gate 	}
4217*7c478bd9Sstevel@tonic-gate 
4218*7c478bd9Sstevel@tonic-gate 	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
4219*7c478bd9Sstevel@tonic-gate 	if (m != NULL)
4220*7c478bd9Sstevel@tonic-gate 	{
4221*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
4222*7c478bd9Sstevel@tonic-gate 				   m->m_name);
4223*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4224*7c478bd9Sstevel@tonic-gate 	}
4225*7c478bd9Sstevel@tonic-gate 
4226*7c478bd9Sstevel@tonic-gate 	/* pri: changes with each delivery attempt */
4227*7c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
4228*7c478bd9Sstevel@tonic-gate 		e->e_msgpriority);
4229*7c478bd9Sstevel@tonic-gate 	bp += strlen(bp);
4230*7c478bd9Sstevel@tonic-gate 
4231*7c478bd9Sstevel@tonic-gate 	/* relay: max 66 bytes for IPv4 addresses */
4232*7c478bd9Sstevel@tonic-gate 	if (mci != NULL && mci->mci_host != NULL)
4233*7c478bd9Sstevel@tonic-gate 	{
4234*7c478bd9Sstevel@tonic-gate 		extern SOCKADDR CurHostAddr;
4235*7c478bd9Sstevel@tonic-gate 
4236*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
4237*7c478bd9Sstevel@tonic-gate 				   shortenstring(mci->mci_host, 40));
4238*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4239*7c478bd9Sstevel@tonic-gate 
4240*7c478bd9Sstevel@tonic-gate 		if (CurHostAddr.sa.sa_family != 0)
4241*7c478bd9Sstevel@tonic-gate 		{
4242*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
4243*7c478bd9Sstevel@tonic-gate 					   anynet_ntoa(&CurHostAddr));
4244*7c478bd9Sstevel@tonic-gate 		}
4245*7c478bd9Sstevel@tonic-gate 	}
4246*7c478bd9Sstevel@tonic-gate 	else if (strcmp(status, "quarantined") == 0)
4247*7c478bd9Sstevel@tonic-gate 	{
4248*7c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
4249*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4250*7c478bd9Sstevel@tonic-gate 					   ", quarantine=%s",
4251*7c478bd9Sstevel@tonic-gate 					   shortenstring(e->e_quarmsg, 40));
4252*7c478bd9Sstevel@tonic-gate 	}
4253*7c478bd9Sstevel@tonic-gate 	else if (strcmp(status, "queued") != 0)
4254*7c478bd9Sstevel@tonic-gate 	{
4255*7c478bd9Sstevel@tonic-gate 		p = macvalue('h', e);
4256*7c478bd9Sstevel@tonic-gate 		if (p != NULL && p[0] != '\0')
4257*7c478bd9Sstevel@tonic-gate 		{
4258*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4259*7c478bd9Sstevel@tonic-gate 					   ", relay=%s", shortenstring(p, 40));
4260*7c478bd9Sstevel@tonic-gate 		}
4261*7c478bd9Sstevel@tonic-gate 	}
4262*7c478bd9Sstevel@tonic-gate 	bp += strlen(bp);
4263*7c478bd9Sstevel@tonic-gate 
4264*7c478bd9Sstevel@tonic-gate 	/* dsn */
4265*7c478bd9Sstevel@tonic-gate 	if (dsn != NULL && *dsn != '\0')
4266*7c478bd9Sstevel@tonic-gate 	{
4267*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
4268*7c478bd9Sstevel@tonic-gate 				   shortenstring(dsn, ENHSCLEN));
4269*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4270*7c478bd9Sstevel@tonic-gate 	}
4271*7c478bd9Sstevel@tonic-gate 
4272*7c478bd9Sstevel@tonic-gate #if _FFR_LOG_NTRIES
4273*7c478bd9Sstevel@tonic-gate 	/* ntries */
4274*7c478bd9Sstevel@tonic-gate 	if (e->e_ntries >= 0)
4275*7c478bd9Sstevel@tonic-gate 	{
4276*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4277*7c478bd9Sstevel@tonic-gate 				   ", ntries=%d", e->e_ntries + 1);
4278*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4279*7c478bd9Sstevel@tonic-gate 	}
4280*7c478bd9Sstevel@tonic-gate #endif /* _FFR_LOG_NTRIES */
4281*7c478bd9Sstevel@tonic-gate 
4282*7c478bd9Sstevel@tonic-gate # define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
4283*7c478bd9Sstevel@tonic-gate # if (STATLEN) < 63
4284*7c478bd9Sstevel@tonic-gate #  undef STATLEN
4285*7c478bd9Sstevel@tonic-gate #  define STATLEN	63
4286*7c478bd9Sstevel@tonic-gate # endif /* (STATLEN) < 63 */
4287*7c478bd9Sstevel@tonic-gate # if (STATLEN) > 203
4288*7c478bd9Sstevel@tonic-gate #  undef STATLEN
4289*7c478bd9Sstevel@tonic-gate #  define STATLEN	203
4290*7c478bd9Sstevel@tonic-gate # endif /* (STATLEN) > 203 */
4291*7c478bd9Sstevel@tonic-gate 
4292*7c478bd9Sstevel@tonic-gate 	/* stat: max 210 bytes */
4293*7c478bd9Sstevel@tonic-gate 	if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
4294*7c478bd9Sstevel@tonic-gate 	{
4295*7c478bd9Sstevel@tonic-gate 		/* desperation move -- truncate data */
4296*7c478bd9Sstevel@tonic-gate 		bp = buf + sizeof buf - ((STATLEN) + 17);
4297*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
4298*7c478bd9Sstevel@tonic-gate 		bp += 3;
4299*7c478bd9Sstevel@tonic-gate 	}
4300*7c478bd9Sstevel@tonic-gate 
4301*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
4302*7c478bd9Sstevel@tonic-gate 	bp += strlen(bp);
4303*7c478bd9Sstevel@tonic-gate 
4304*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
4305*7c478bd9Sstevel@tonic-gate 			  SPACELEFT(buf, bp));
4306*7c478bd9Sstevel@tonic-gate 
4307*7c478bd9Sstevel@tonic-gate 	/* id, to: max 13 + TOBUFSIZE bytes */
4308*7c478bd9Sstevel@tonic-gate 	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
4309*7c478bd9Sstevel@tonic-gate 	if (l < 0)
4310*7c478bd9Sstevel@tonic-gate 		l = 0;
4311*7c478bd9Sstevel@tonic-gate 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
4312*7c478bd9Sstevel@tonic-gate 	while (strlen(p) >= l)
4313*7c478bd9Sstevel@tonic-gate 	{
4314*7c478bd9Sstevel@tonic-gate 		register char *q;
4315*7c478bd9Sstevel@tonic-gate 
4316*7c478bd9Sstevel@tonic-gate 		for (q = p + l; q > p; q--)
4317*7c478bd9Sstevel@tonic-gate 		{
4318*7c478bd9Sstevel@tonic-gate 			if (*q == ',')
4319*7c478bd9Sstevel@tonic-gate 				break;
4320*7c478bd9Sstevel@tonic-gate 		}
4321*7c478bd9Sstevel@tonic-gate 		if (p == q)
4322*7c478bd9Sstevel@tonic-gate 			break;
4323*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
4324*7c478bd9Sstevel@tonic-gate 			  (int) (++q - p), p, buf);
4325*7c478bd9Sstevel@tonic-gate 		p = q;
4326*7c478bd9Sstevel@tonic-gate 	}
4327*7c478bd9Sstevel@tonic-gate 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
4328*7c478bd9Sstevel@tonic-gate 
4329*7c478bd9Sstevel@tonic-gate #else /* (SYSLOG_BUFSIZE) >= 256 */
4330*7c478bd9Sstevel@tonic-gate 
4331*7c478bd9Sstevel@tonic-gate 	l = SYSLOG_BUFSIZE - 85;
4332*7c478bd9Sstevel@tonic-gate 	if (l < 0)
4333*7c478bd9Sstevel@tonic-gate 		l = 0;
4334*7c478bd9Sstevel@tonic-gate 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
4335*7c478bd9Sstevel@tonic-gate 	while (strlen(p) >= l)
4336*7c478bd9Sstevel@tonic-gate 	{
4337*7c478bd9Sstevel@tonic-gate 		register char *q;
4338*7c478bd9Sstevel@tonic-gate 
4339*7c478bd9Sstevel@tonic-gate 		for (q = p + l; q > p; q--)
4340*7c478bd9Sstevel@tonic-gate 		{
4341*7c478bd9Sstevel@tonic-gate 			if (*q == ',')
4342*7c478bd9Sstevel@tonic-gate 				break;
4343*7c478bd9Sstevel@tonic-gate 		}
4344*7c478bd9Sstevel@tonic-gate 		if (p == q)
4345*7c478bd9Sstevel@tonic-gate 			break;
4346*7c478bd9Sstevel@tonic-gate 
4347*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
4348*7c478bd9Sstevel@tonic-gate 			  (int) (++q - p), p);
4349*7c478bd9Sstevel@tonic-gate 		p = q;
4350*7c478bd9Sstevel@tonic-gate 	}
4351*7c478bd9Sstevel@tonic-gate 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
4352*7c478bd9Sstevel@tonic-gate 
4353*7c478bd9Sstevel@tonic-gate 	if (ctladdr != NULL)
4354*7c478bd9Sstevel@tonic-gate 	{
4355*7c478bd9Sstevel@tonic-gate 		bp = buf;
4356*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
4357*7c478bd9Sstevel@tonic-gate 				   shortenstring(ctladdr->q_paddr, 83));
4358*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4359*7c478bd9Sstevel@tonic-gate 		if (bitset(QGOODUID, ctladdr->q_flags))
4360*7c478bd9Sstevel@tonic-gate 		{
4361*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
4362*7c478bd9Sstevel@tonic-gate 					   ctladdr->q_uid, ctladdr->q_gid);
4363*7c478bd9Sstevel@tonic-gate 			bp += strlen(bp);
4364*7c478bd9Sstevel@tonic-gate 		}
4365*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
4366*7c478bd9Sstevel@tonic-gate 	}
4367*7c478bd9Sstevel@tonic-gate 	bp = buf;
4368*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
4369*7c478bd9Sstevel@tonic-gate 			   pintvl(now - e->e_ctime, true));
4370*7c478bd9Sstevel@tonic-gate 	bp += strlen(bp);
4371*7c478bd9Sstevel@tonic-gate 	if (xstart != (time_t) 0)
4372*7c478bd9Sstevel@tonic-gate 	{
4373*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
4374*7c478bd9Sstevel@tonic-gate 				   pintvl(now - xstart, true));
4375*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4376*7c478bd9Sstevel@tonic-gate 	}
4377*7c478bd9Sstevel@tonic-gate 
4378*7c478bd9Sstevel@tonic-gate 	if (m != NULL)
4379*7c478bd9Sstevel@tonic-gate 	{
4380*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
4381*7c478bd9Sstevel@tonic-gate 				   m->m_name);
4382*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4383*7c478bd9Sstevel@tonic-gate 	}
4384*7c478bd9Sstevel@tonic-gate 	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4385*7c478bd9Sstevel@tonic-gate 
4386*7c478bd9Sstevel@tonic-gate 	buf[0] = '\0';
4387*7c478bd9Sstevel@tonic-gate 	bp = buf;
4388*7c478bd9Sstevel@tonic-gate 	if (mci != NULL && mci->mci_host != NULL)
4389*7c478bd9Sstevel@tonic-gate 	{
4390*7c478bd9Sstevel@tonic-gate 		extern SOCKADDR CurHostAddr;
4391*7c478bd9Sstevel@tonic-gate 
4392*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
4393*7c478bd9Sstevel@tonic-gate 				   mci->mci_host);
4394*7c478bd9Sstevel@tonic-gate 		bp += strlen(bp);
4395*7c478bd9Sstevel@tonic-gate 
4396*7c478bd9Sstevel@tonic-gate 		if (CurHostAddr.sa.sa_family != 0)
4397*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4398*7c478bd9Sstevel@tonic-gate 					   " [%.100s]",
4399*7c478bd9Sstevel@tonic-gate 					   anynet_ntoa(&CurHostAddr));
4400*7c478bd9Sstevel@tonic-gate 	}
4401*7c478bd9Sstevel@tonic-gate 	else if (strcmp(status, "quarantined") == 0)
4402*7c478bd9Sstevel@tonic-gate 	{
4403*7c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
4404*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4405*7c478bd9Sstevel@tonic-gate 					   ", quarantine=%.100s",
4406*7c478bd9Sstevel@tonic-gate 					   e->e_quarmsg);
4407*7c478bd9Sstevel@tonic-gate 	}
4408*7c478bd9Sstevel@tonic-gate 	else if (strcmp(status, "queued") != 0)
4409*7c478bd9Sstevel@tonic-gate 	{
4410*7c478bd9Sstevel@tonic-gate 		p = macvalue('h', e);
4411*7c478bd9Sstevel@tonic-gate 		if (p != NULL && p[0] != '\0')
4412*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p);
4413*7c478bd9Sstevel@tonic-gate 	}
4414*7c478bd9Sstevel@tonic-gate 	if (buf[0] != '\0')
4415*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4416*7c478bd9Sstevel@tonic-gate 
4417*7c478bd9Sstevel@tonic-gate 	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
4418*7c478bd9Sstevel@tonic-gate #endif /* (SYSLOG_BUFSIZE) >= 256 */
4419*7c478bd9Sstevel@tonic-gate }
4420*7c478bd9Sstevel@tonic-gate /*
4421*7c478bd9Sstevel@tonic-gate **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
4422*7c478bd9Sstevel@tonic-gate **
4423*7c478bd9Sstevel@tonic-gate **	This can be made an arbitrary message separator by changing $l
4424*7c478bd9Sstevel@tonic-gate **
4425*7c478bd9Sstevel@tonic-gate **	One of the ugliest hacks seen by human eyes is contained herein:
4426*7c478bd9Sstevel@tonic-gate **	UUCP wants those stupid "remote from <host>" lines.  Why oh why
4427*7c478bd9Sstevel@tonic-gate **	does a well-meaning programmer such as myself have to deal with
4428*7c478bd9Sstevel@tonic-gate **	this kind of antique garbage????
4429*7c478bd9Sstevel@tonic-gate **
4430*7c478bd9Sstevel@tonic-gate **	Parameters:
4431*7c478bd9Sstevel@tonic-gate **		mci -- the connection information.
4432*7c478bd9Sstevel@tonic-gate **		e -- the envelope.
4433*7c478bd9Sstevel@tonic-gate **
4434*7c478bd9Sstevel@tonic-gate **	Returns:
4435*7c478bd9Sstevel@tonic-gate **		none
4436*7c478bd9Sstevel@tonic-gate **
4437*7c478bd9Sstevel@tonic-gate **	Side Effects:
4438*7c478bd9Sstevel@tonic-gate **		outputs some text to fp.
4439*7c478bd9Sstevel@tonic-gate */
4440*7c478bd9Sstevel@tonic-gate 
4441*7c478bd9Sstevel@tonic-gate void
4442*7c478bd9Sstevel@tonic-gate putfromline(mci, e)
4443*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
4444*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4445*7c478bd9Sstevel@tonic-gate {
4446*7c478bd9Sstevel@tonic-gate 	char *template = UnixFromLine;
4447*7c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
4448*7c478bd9Sstevel@tonic-gate 	char xbuf[MAXLINE];
4449*7c478bd9Sstevel@tonic-gate 
4450*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
4451*7c478bd9Sstevel@tonic-gate 		return;
4452*7c478bd9Sstevel@tonic-gate 
4453*7c478bd9Sstevel@tonic-gate 	mci->mci_flags |= MCIF_INHEADER;
4454*7c478bd9Sstevel@tonic-gate 
4455*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
4456*7c478bd9Sstevel@tonic-gate 	{
4457*7c478bd9Sstevel@tonic-gate 		char *bang;
4458*7c478bd9Sstevel@tonic-gate 
4459*7c478bd9Sstevel@tonic-gate 		expand("\201g", buf, sizeof buf, e);
4460*7c478bd9Sstevel@tonic-gate 		bang = strchr(buf, '!');
4461*7c478bd9Sstevel@tonic-gate 		if (bang == NULL)
4462*7c478bd9Sstevel@tonic-gate 		{
4463*7c478bd9Sstevel@tonic-gate 			char *at;
4464*7c478bd9Sstevel@tonic-gate 			char hname[MAXNAME];
4465*7c478bd9Sstevel@tonic-gate 
4466*7c478bd9Sstevel@tonic-gate 			/*
4467*7c478bd9Sstevel@tonic-gate 			**  If we can construct a UUCP path, do so
4468*7c478bd9Sstevel@tonic-gate 			*/
4469*7c478bd9Sstevel@tonic-gate 
4470*7c478bd9Sstevel@tonic-gate 			at = strrchr(buf, '@');
4471*7c478bd9Sstevel@tonic-gate 			if (at == NULL)
4472*7c478bd9Sstevel@tonic-gate 			{
4473*7c478bd9Sstevel@tonic-gate 				expand("\201k", hname, sizeof hname, e);
4474*7c478bd9Sstevel@tonic-gate 				at = hname;
4475*7c478bd9Sstevel@tonic-gate 			}
4476*7c478bd9Sstevel@tonic-gate 			else
4477*7c478bd9Sstevel@tonic-gate 				*at++ = '\0';
4478*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(xbuf, sizeof xbuf,
4479*7c478bd9Sstevel@tonic-gate 					   "From %.800s  \201d remote from %.100s\n",
4480*7c478bd9Sstevel@tonic-gate 					   buf, at);
4481*7c478bd9Sstevel@tonic-gate 		}
4482*7c478bd9Sstevel@tonic-gate 		else
4483*7c478bd9Sstevel@tonic-gate 		{
4484*7c478bd9Sstevel@tonic-gate 			*bang++ = '\0';
4485*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(xbuf, sizeof xbuf,
4486*7c478bd9Sstevel@tonic-gate 					   "From %.800s  \201d remote from %.100s\n",
4487*7c478bd9Sstevel@tonic-gate 					   bang, buf);
4488*7c478bd9Sstevel@tonic-gate 			template = xbuf;
4489*7c478bd9Sstevel@tonic-gate 		}
4490*7c478bd9Sstevel@tonic-gate 	}
4491*7c478bd9Sstevel@tonic-gate 	expand(template, buf, sizeof buf, e);
4492*7c478bd9Sstevel@tonic-gate 	putxline(buf, strlen(buf), mci, PXLF_HEADER);
4493*7c478bd9Sstevel@tonic-gate }
4494*7c478bd9Sstevel@tonic-gate /*
4495*7c478bd9Sstevel@tonic-gate **  PUTBODY -- put the body of a message.
4496*7c478bd9Sstevel@tonic-gate **
4497*7c478bd9Sstevel@tonic-gate **	Parameters:
4498*7c478bd9Sstevel@tonic-gate **		mci -- the connection information.
4499*7c478bd9Sstevel@tonic-gate **		e -- the envelope to put out.
4500*7c478bd9Sstevel@tonic-gate **		separator -- if non-NULL, a message separator that must
4501*7c478bd9Sstevel@tonic-gate **			not be permitted in the resulting message.
4502*7c478bd9Sstevel@tonic-gate **
4503*7c478bd9Sstevel@tonic-gate **	Returns:
4504*7c478bd9Sstevel@tonic-gate **		none.
4505*7c478bd9Sstevel@tonic-gate **
4506*7c478bd9Sstevel@tonic-gate **	Side Effects:
4507*7c478bd9Sstevel@tonic-gate **		The message is written onto fp.
4508*7c478bd9Sstevel@tonic-gate */
4509*7c478bd9Sstevel@tonic-gate 
4510*7c478bd9Sstevel@tonic-gate /* values for output state variable */
4511*7c478bd9Sstevel@tonic-gate #define OS_HEAD		0	/* at beginning of line */
4512*7c478bd9Sstevel@tonic-gate #define OS_CR		1	/* read a carriage return */
4513*7c478bd9Sstevel@tonic-gate #define OS_INLINE	2	/* putting rest of line */
4514*7c478bd9Sstevel@tonic-gate 
4515*7c478bd9Sstevel@tonic-gate void
4516*7c478bd9Sstevel@tonic-gate putbody(mci, e, separator)
4517*7c478bd9Sstevel@tonic-gate 	register MCI *mci;
4518*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
4519*7c478bd9Sstevel@tonic-gate 	char *separator;
4520*7c478bd9Sstevel@tonic-gate {
4521*7c478bd9Sstevel@tonic-gate 	bool dead = false;
4522*7c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
4523*7c478bd9Sstevel@tonic-gate #if MIME8TO7
4524*7c478bd9Sstevel@tonic-gate 	char *boundaries[MAXMIMENESTING + 1];
4525*7c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */
4526*7c478bd9Sstevel@tonic-gate 
4527*7c478bd9Sstevel@tonic-gate 	/*
4528*7c478bd9Sstevel@tonic-gate 	**  Output the body of the message
4529*7c478bd9Sstevel@tonic-gate 	*/
4530*7c478bd9Sstevel@tonic-gate 
4531*7c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
4532*7c478bd9Sstevel@tonic-gate 	{
4533*7c478bd9Sstevel@tonic-gate 		char *df = queuename(e, DATAFL_LETTER);
4534*7c478bd9Sstevel@tonic-gate 
4535*7c478bd9Sstevel@tonic-gate 		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4536*7c478bd9Sstevel@tonic-gate 				      SM_IO_RDONLY_B, NULL);
4537*7c478bd9Sstevel@tonic-gate 		if (e->e_dfp == NULL)
4538*7c478bd9Sstevel@tonic-gate 		{
4539*7c478bd9Sstevel@tonic-gate 			char *msg = "!putbody: Cannot open %s for %s from %s";
4540*7c478bd9Sstevel@tonic-gate 
4541*7c478bd9Sstevel@tonic-gate 			if (errno == ENOENT)
4542*7c478bd9Sstevel@tonic-gate 				msg++;
4543*7c478bd9Sstevel@tonic-gate 			syserr(msg, df, e->e_to, e->e_from.q_paddr);
4544*7c478bd9Sstevel@tonic-gate 		}
4545*7c478bd9Sstevel@tonic-gate 
4546*7c478bd9Sstevel@tonic-gate 	}
4547*7c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL)
4548*7c478bd9Sstevel@tonic-gate 	{
4549*7c478bd9Sstevel@tonic-gate 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4550*7c478bd9Sstevel@tonic-gate 		{
4551*7c478bd9Sstevel@tonic-gate 			putline("", mci);
4552*7c478bd9Sstevel@tonic-gate 			mci->mci_flags &= ~MCIF_INHEADER;
4553*7c478bd9Sstevel@tonic-gate 		}
4554*7c478bd9Sstevel@tonic-gate 		putline("<<< No Message Collected >>>", mci);
4555*7c478bd9Sstevel@tonic-gate 		goto endofmessage;
4556*7c478bd9Sstevel@tonic-gate 	}
4557*7c478bd9Sstevel@tonic-gate 
4558*7c478bd9Sstevel@tonic-gate 	if (e->e_dfino == (ino_t) 0)
4559*7c478bd9Sstevel@tonic-gate 	{
4560*7c478bd9Sstevel@tonic-gate 		struct stat stbuf;
4561*7c478bd9Sstevel@tonic-gate 
4562*7c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
4563*7c478bd9Sstevel@tonic-gate 		    < 0)
4564*7c478bd9Sstevel@tonic-gate 			e->e_dfino = -1;
4565*7c478bd9Sstevel@tonic-gate 		else
4566*7c478bd9Sstevel@tonic-gate 		{
4567*7c478bd9Sstevel@tonic-gate 			e->e_dfdev = stbuf.st_dev;
4568*7c478bd9Sstevel@tonic-gate 			e->e_dfino = stbuf.st_ino;
4569*7c478bd9Sstevel@tonic-gate 		}
4570*7c478bd9Sstevel@tonic-gate 	}
4571*7c478bd9Sstevel@tonic-gate 
4572*7c478bd9Sstevel@tonic-gate 	/* paranoia: the data file should always be in a rewound state */
4573*7c478bd9Sstevel@tonic-gate 	(void) bfrewind(e->e_dfp);
4574*7c478bd9Sstevel@tonic-gate 
4575*7c478bd9Sstevel@tonic-gate #if MIME8TO7
4576*7c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
4577*7c478bd9Sstevel@tonic-gate 	{
4578*7c478bd9Sstevel@tonic-gate 		/*
4579*7c478bd9Sstevel@tonic-gate 		**  Do 8 to 7 bit MIME conversion.
4580*7c478bd9Sstevel@tonic-gate 		*/
4581*7c478bd9Sstevel@tonic-gate 
4582*7c478bd9Sstevel@tonic-gate 		/* make sure it looks like a MIME message */
4583*7c478bd9Sstevel@tonic-gate 		if (hvalue("MIME-Version", e->e_header) == NULL)
4584*7c478bd9Sstevel@tonic-gate 			putline("MIME-Version: 1.0", mci);
4585*7c478bd9Sstevel@tonic-gate 
4586*7c478bd9Sstevel@tonic-gate 		if (hvalue("Content-Type", e->e_header) == NULL)
4587*7c478bd9Sstevel@tonic-gate 		{
4588*7c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf,
4589*7c478bd9Sstevel@tonic-gate 					   "Content-Type: text/plain; charset=%s",
4590*7c478bd9Sstevel@tonic-gate 					   defcharset(e));
4591*7c478bd9Sstevel@tonic-gate 			putline(buf, mci);
4592*7c478bd9Sstevel@tonic-gate 		}
4593*7c478bd9Sstevel@tonic-gate 
4594*7c478bd9Sstevel@tonic-gate 		/* now do the hard work */
4595*7c478bd9Sstevel@tonic-gate 		boundaries[0] = NULL;
4596*7c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_INHEADER;
4597*7c478bd9Sstevel@tonic-gate 		(void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
4598*7c478bd9Sstevel@tonic-gate 	}
4599*7c478bd9Sstevel@tonic-gate # if MIME7TO8
4600*7c478bd9Sstevel@tonic-gate 	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
4601*7c478bd9Sstevel@tonic-gate 	{
4602*7c478bd9Sstevel@tonic-gate 		(void) mime7to8(mci, e->e_header, e);
4603*7c478bd9Sstevel@tonic-gate 	}
4604*7c478bd9Sstevel@tonic-gate # endif /* MIME7TO8 */
4605*7c478bd9Sstevel@tonic-gate 	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
4606*7c478bd9Sstevel@tonic-gate 	{
4607*7c478bd9Sstevel@tonic-gate 		bool oldsuprerrs = SuprErrs;
4608*7c478bd9Sstevel@tonic-gate 
4609*7c478bd9Sstevel@tonic-gate 		/* Use mime8to7 to check multipart for MIME header overflows */
4610*7c478bd9Sstevel@tonic-gate 		boundaries[0] = NULL;
4611*7c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_INHEADER;
4612*7c478bd9Sstevel@tonic-gate 
4613*7c478bd9Sstevel@tonic-gate 		/*
4614*7c478bd9Sstevel@tonic-gate 		**  If EF_DONT_MIME is set, we have a broken MIME message
4615*7c478bd9Sstevel@tonic-gate 		**  and don't want to generate a new bounce message whose
4616*7c478bd9Sstevel@tonic-gate 		**  body propagates the broken MIME.  We can't just not call
4617*7c478bd9Sstevel@tonic-gate 		**  mime8to7() as is done above since we need the security
4618*7c478bd9Sstevel@tonic-gate 		**  checks.  The best we can do is suppress the errors.
4619*7c478bd9Sstevel@tonic-gate 		*/
4620*7c478bd9Sstevel@tonic-gate 
4621*7c478bd9Sstevel@tonic-gate 		if (bitset(EF_DONT_MIME, e->e_flags))
4622*7c478bd9Sstevel@tonic-gate 			SuprErrs = true;
4623*7c478bd9Sstevel@tonic-gate 
4624*7c478bd9Sstevel@tonic-gate 		(void) mime8to7(mci, e->e_header, e, boundaries,
4625*7c478bd9Sstevel@tonic-gate 				M87F_OUTER|M87F_NO8TO7);
4626*7c478bd9Sstevel@tonic-gate 
4627*7c478bd9Sstevel@tonic-gate 		/* restore SuprErrs */
4628*7c478bd9Sstevel@tonic-gate 		SuprErrs = oldsuprerrs;
4629*7c478bd9Sstevel@tonic-gate 	}
4630*7c478bd9Sstevel@tonic-gate 	else
4631*7c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */
4632*7c478bd9Sstevel@tonic-gate 	{
4633*7c478bd9Sstevel@tonic-gate 		int ostate;
4634*7c478bd9Sstevel@tonic-gate 		register char *bp;
4635*7c478bd9Sstevel@tonic-gate 		register char *pbp;
4636*7c478bd9Sstevel@tonic-gate 		register int c;
4637*7c478bd9Sstevel@tonic-gate 		register char *xp;
4638*7c478bd9Sstevel@tonic-gate 		int padc;
4639*7c478bd9Sstevel@tonic-gate 		char *buflim;
4640*7c478bd9Sstevel@tonic-gate 		int pos = 0;
4641*7c478bd9Sstevel@tonic-gate 		char peekbuf[12];
4642*7c478bd9Sstevel@tonic-gate 
4643*7c478bd9Sstevel@tonic-gate 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4644*7c478bd9Sstevel@tonic-gate 		{
4645*7c478bd9Sstevel@tonic-gate 			putline("", mci);
4646*7c478bd9Sstevel@tonic-gate 			mci->mci_flags &= ~MCIF_INHEADER;
4647*7c478bd9Sstevel@tonic-gate 		}
4648*7c478bd9Sstevel@tonic-gate 
4649*7c478bd9Sstevel@tonic-gate 		/* determine end of buffer; allow for short mailer lines */
4650*7c478bd9Sstevel@tonic-gate 		buflim = &buf[sizeof buf - 1];
4651*7c478bd9Sstevel@tonic-gate 		if (mci->mci_mailer->m_linelimit > 0 &&
4652*7c478bd9Sstevel@tonic-gate 		    mci->mci_mailer->m_linelimit < sizeof buf - 1)
4653*7c478bd9Sstevel@tonic-gate 			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
4654*7c478bd9Sstevel@tonic-gate 
4655*7c478bd9Sstevel@tonic-gate 		/* copy temp file to output with mapping */
4656*7c478bd9Sstevel@tonic-gate 		ostate = OS_HEAD;
4657*7c478bd9Sstevel@tonic-gate 		bp = buf;
4658*7c478bd9Sstevel@tonic-gate 		pbp = peekbuf;
4659*7c478bd9Sstevel@tonic-gate 		while (!sm_io_error(mci->mci_out) && !dead)
4660*7c478bd9Sstevel@tonic-gate 		{
4661*7c478bd9Sstevel@tonic-gate 			if (pbp > peekbuf)
4662*7c478bd9Sstevel@tonic-gate 				c = *--pbp;
4663*7c478bd9Sstevel@tonic-gate 			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
4664*7c478bd9Sstevel@tonic-gate 				 == SM_IO_EOF)
4665*7c478bd9Sstevel@tonic-gate 				break;
4666*7c478bd9Sstevel@tonic-gate 			if (bitset(MCIF_7BIT, mci->mci_flags))
4667*7c478bd9Sstevel@tonic-gate 				c &= 0x7f;
4668*7c478bd9Sstevel@tonic-gate 			switch (ostate)
4669*7c478bd9Sstevel@tonic-gate 			{
4670*7c478bd9Sstevel@tonic-gate 			  case OS_HEAD:
4671*7c478bd9Sstevel@tonic-gate 				if (c == '\0' &&
4672*7c478bd9Sstevel@tonic-gate 				    bitnset(M_NONULLS,
4673*7c478bd9Sstevel@tonic-gate 					    mci->mci_mailer->m_flags))
4674*7c478bd9Sstevel@tonic-gate 					break;
4675*7c478bd9Sstevel@tonic-gate 				if (c != '\r' && c != '\n' && bp < buflim)
4676*7c478bd9Sstevel@tonic-gate 				{
4677*7c478bd9Sstevel@tonic-gate 					*bp++ = c;
4678*7c478bd9Sstevel@tonic-gate 					break;
4679*7c478bd9Sstevel@tonic-gate 				}
4680*7c478bd9Sstevel@tonic-gate 
4681*7c478bd9Sstevel@tonic-gate 				/* check beginning of line for special cases */
4682*7c478bd9Sstevel@tonic-gate 				*bp = '\0';
4683*7c478bd9Sstevel@tonic-gate 				pos = 0;
4684*7c478bd9Sstevel@tonic-gate 				padc = SM_IO_EOF;
4685*7c478bd9Sstevel@tonic-gate 				if (buf[0] == 'F' &&
4686*7c478bd9Sstevel@tonic-gate 				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
4687*7c478bd9Sstevel@tonic-gate 				    && strncmp(buf, "From ", 5) == 0)
4688*7c478bd9Sstevel@tonic-gate 				{
4689*7c478bd9Sstevel@tonic-gate 					padc = '>';
4690*7c478bd9Sstevel@tonic-gate 				}
4691*7c478bd9Sstevel@tonic-gate 				if (buf[0] == '-' && buf[1] == '-' &&
4692*7c478bd9Sstevel@tonic-gate 				    separator != NULL)
4693*7c478bd9Sstevel@tonic-gate 				{
4694*7c478bd9Sstevel@tonic-gate 					/* possible separator */
4695*7c478bd9Sstevel@tonic-gate 					int sl = strlen(separator);
4696*7c478bd9Sstevel@tonic-gate 
4697*7c478bd9Sstevel@tonic-gate 					if (strncmp(&buf[2], separator, sl)
4698*7c478bd9Sstevel@tonic-gate 					    == 0)
4699*7c478bd9Sstevel@tonic-gate 						padc = ' ';
4700*7c478bd9Sstevel@tonic-gate 				}
4701*7c478bd9Sstevel@tonic-gate 				if (buf[0] == '.' &&
4702*7c478bd9Sstevel@tonic-gate 				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
4703*7c478bd9Sstevel@tonic-gate 				{
4704*7c478bd9Sstevel@tonic-gate 					padc = '.';
4705*7c478bd9Sstevel@tonic-gate 				}
4706*7c478bd9Sstevel@tonic-gate 
4707*7c478bd9Sstevel@tonic-gate 				/* now copy out saved line */
4708*7c478bd9Sstevel@tonic-gate 				if (TrafficLogFile != NULL)
4709*7c478bd9Sstevel@tonic-gate 				{
4710*7c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(TrafficLogFile,
4711*7c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
4712*7c478bd9Sstevel@tonic-gate 							     "%05d >>> ",
4713*7c478bd9Sstevel@tonic-gate 							     (int) CurrentPid);
4714*7c478bd9Sstevel@tonic-gate 					if (padc != SM_IO_EOF)
4715*7c478bd9Sstevel@tonic-gate 						(void) sm_io_putc(TrafficLogFile,
4716*7c478bd9Sstevel@tonic-gate 								  SM_TIME_DEFAULT,
4717*7c478bd9Sstevel@tonic-gate 								  padc);
4718*7c478bd9Sstevel@tonic-gate 					for (xp = buf; xp < bp; xp++)
4719*7c478bd9Sstevel@tonic-gate 						(void) sm_io_putc(TrafficLogFile,
4720*7c478bd9Sstevel@tonic-gate 								  SM_TIME_DEFAULT,
4721*7c478bd9Sstevel@tonic-gate 								  (unsigned char) *xp);
4722*7c478bd9Sstevel@tonic-gate 					if (c == '\n')
4723*7c478bd9Sstevel@tonic-gate 						(void) sm_io_fputs(TrafficLogFile,
4724*7c478bd9Sstevel@tonic-gate 								   SM_TIME_DEFAULT,
4725*7c478bd9Sstevel@tonic-gate 								   mci->mci_mailer->m_eol);
4726*7c478bd9Sstevel@tonic-gate 				}
4727*7c478bd9Sstevel@tonic-gate 				if (padc != SM_IO_EOF)
4728*7c478bd9Sstevel@tonic-gate 				{
4729*7c478bd9Sstevel@tonic-gate 					if (sm_io_putc(mci->mci_out,
4730*7c478bd9Sstevel@tonic-gate 						       SM_TIME_DEFAULT, padc)
4731*7c478bd9Sstevel@tonic-gate 					    == SM_IO_EOF)
4732*7c478bd9Sstevel@tonic-gate 					{
4733*7c478bd9Sstevel@tonic-gate 						dead = true;
4734*7c478bd9Sstevel@tonic-gate 						continue;
4735*7c478bd9Sstevel@tonic-gate 					}
4736*7c478bd9Sstevel@tonic-gate 					else
4737*7c478bd9Sstevel@tonic-gate 					{
4738*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4739*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4740*7c478bd9Sstevel@tonic-gate 					}
4741*7c478bd9Sstevel@tonic-gate 					pos++;
4742*7c478bd9Sstevel@tonic-gate 				}
4743*7c478bd9Sstevel@tonic-gate 				for (xp = buf; xp < bp; xp++)
4744*7c478bd9Sstevel@tonic-gate 				{
4745*7c478bd9Sstevel@tonic-gate 					if (sm_io_putc(mci->mci_out,
4746*7c478bd9Sstevel@tonic-gate 						       SM_TIME_DEFAULT,
4747*7c478bd9Sstevel@tonic-gate 						       (unsigned char) *xp)
4748*7c478bd9Sstevel@tonic-gate 					    == SM_IO_EOF)
4749*7c478bd9Sstevel@tonic-gate 					{
4750*7c478bd9Sstevel@tonic-gate 						dead = true;
4751*7c478bd9Sstevel@tonic-gate 						break;
4752*7c478bd9Sstevel@tonic-gate 					}
4753*7c478bd9Sstevel@tonic-gate 					else
4754*7c478bd9Sstevel@tonic-gate 					{
4755*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4756*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4757*7c478bd9Sstevel@tonic-gate 					}
4758*7c478bd9Sstevel@tonic-gate 				}
4759*7c478bd9Sstevel@tonic-gate 				if (dead)
4760*7c478bd9Sstevel@tonic-gate 					continue;
4761*7c478bd9Sstevel@tonic-gate 				if (c == '\n')
4762*7c478bd9Sstevel@tonic-gate 				{
4763*7c478bd9Sstevel@tonic-gate 					if (sm_io_fputs(mci->mci_out,
4764*7c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
4765*7c478bd9Sstevel@tonic-gate 							mci->mci_mailer->m_eol)
4766*7c478bd9Sstevel@tonic-gate 							== SM_IO_EOF)
4767*7c478bd9Sstevel@tonic-gate 						break;
4768*7c478bd9Sstevel@tonic-gate 					else
4769*7c478bd9Sstevel@tonic-gate 					{
4770*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4771*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4772*7c478bd9Sstevel@tonic-gate 					}
4773*7c478bd9Sstevel@tonic-gate 					pos = 0;
4774*7c478bd9Sstevel@tonic-gate 				}
4775*7c478bd9Sstevel@tonic-gate 				else
4776*7c478bd9Sstevel@tonic-gate 				{
4777*7c478bd9Sstevel@tonic-gate 					pos += bp - buf;
4778*7c478bd9Sstevel@tonic-gate 					if (c != '\r')
4779*7c478bd9Sstevel@tonic-gate 					{
4780*7c478bd9Sstevel@tonic-gate 						SM_ASSERT(pbp < peekbuf +
4781*7c478bd9Sstevel@tonic-gate 								sizeof(peekbuf));
4782*7c478bd9Sstevel@tonic-gate 						*pbp++ = c;
4783*7c478bd9Sstevel@tonic-gate 					}
4784*7c478bd9Sstevel@tonic-gate 				}
4785*7c478bd9Sstevel@tonic-gate 
4786*7c478bd9Sstevel@tonic-gate 				bp = buf;
4787*7c478bd9Sstevel@tonic-gate 
4788*7c478bd9Sstevel@tonic-gate 				/* determine next state */
4789*7c478bd9Sstevel@tonic-gate 				if (c == '\n')
4790*7c478bd9Sstevel@tonic-gate 					ostate = OS_HEAD;
4791*7c478bd9Sstevel@tonic-gate 				else if (c == '\r')
4792*7c478bd9Sstevel@tonic-gate 					ostate = OS_CR;
4793*7c478bd9Sstevel@tonic-gate 				else
4794*7c478bd9Sstevel@tonic-gate 					ostate = OS_INLINE;
4795*7c478bd9Sstevel@tonic-gate 				continue;
4796*7c478bd9Sstevel@tonic-gate 
4797*7c478bd9Sstevel@tonic-gate 			  case OS_CR:
4798*7c478bd9Sstevel@tonic-gate 				if (c == '\n')
4799*7c478bd9Sstevel@tonic-gate 				{
4800*7c478bd9Sstevel@tonic-gate 					/* got CRLF */
4801*7c478bd9Sstevel@tonic-gate 					if (sm_io_fputs(mci->mci_out,
4802*7c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
4803*7c478bd9Sstevel@tonic-gate 							mci->mci_mailer->m_eol)
4804*7c478bd9Sstevel@tonic-gate 							== SM_IO_EOF)
4805*7c478bd9Sstevel@tonic-gate 						continue;
4806*7c478bd9Sstevel@tonic-gate 					else
4807*7c478bd9Sstevel@tonic-gate 					{
4808*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4809*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4810*7c478bd9Sstevel@tonic-gate 					}
4811*7c478bd9Sstevel@tonic-gate 
4812*7c478bd9Sstevel@tonic-gate 					if (TrafficLogFile != NULL)
4813*7c478bd9Sstevel@tonic-gate 					{
4814*7c478bd9Sstevel@tonic-gate 						(void) sm_io_fputs(TrafficLogFile,
4815*7c478bd9Sstevel@tonic-gate 								   SM_TIME_DEFAULT,
4816*7c478bd9Sstevel@tonic-gate 								   mci->mci_mailer->m_eol);
4817*7c478bd9Sstevel@tonic-gate 					}
4818*7c478bd9Sstevel@tonic-gate 					ostate = OS_HEAD;
4819*7c478bd9Sstevel@tonic-gate 					continue;
4820*7c478bd9Sstevel@tonic-gate 				}
4821*7c478bd9Sstevel@tonic-gate 
4822*7c478bd9Sstevel@tonic-gate 				/* had a naked carriage return */
4823*7c478bd9Sstevel@tonic-gate 				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
4824*7c478bd9Sstevel@tonic-gate 				*pbp++ = c;
4825*7c478bd9Sstevel@tonic-gate 				c = '\r';
4826*7c478bd9Sstevel@tonic-gate 				ostate = OS_INLINE;
4827*7c478bd9Sstevel@tonic-gate 				goto putch;
4828*7c478bd9Sstevel@tonic-gate 
4829*7c478bd9Sstevel@tonic-gate 			  case OS_INLINE:
4830*7c478bd9Sstevel@tonic-gate 				if (c == '\r')
4831*7c478bd9Sstevel@tonic-gate 				{
4832*7c478bd9Sstevel@tonic-gate 					ostate = OS_CR;
4833*7c478bd9Sstevel@tonic-gate 					continue;
4834*7c478bd9Sstevel@tonic-gate 				}
4835*7c478bd9Sstevel@tonic-gate 				if (c == '\0' &&
4836*7c478bd9Sstevel@tonic-gate 				    bitnset(M_NONULLS,
4837*7c478bd9Sstevel@tonic-gate 					    mci->mci_mailer->m_flags))
4838*7c478bd9Sstevel@tonic-gate 					break;
4839*7c478bd9Sstevel@tonic-gate putch:
4840*7c478bd9Sstevel@tonic-gate 				if (mci->mci_mailer->m_linelimit > 0 &&
4841*7c478bd9Sstevel@tonic-gate 				    pos >= mci->mci_mailer->m_linelimit - 1 &&
4842*7c478bd9Sstevel@tonic-gate 				    c != '\n')
4843*7c478bd9Sstevel@tonic-gate 				{
4844*7c478bd9Sstevel@tonic-gate 					int d;
4845*7c478bd9Sstevel@tonic-gate 
4846*7c478bd9Sstevel@tonic-gate 					/* check next character for EOL */
4847*7c478bd9Sstevel@tonic-gate 					if (pbp > peekbuf)
4848*7c478bd9Sstevel@tonic-gate 						d = *(pbp - 1);
4849*7c478bd9Sstevel@tonic-gate 					else if ((d = sm_io_getc(e->e_dfp,
4850*7c478bd9Sstevel@tonic-gate 								 SM_TIME_DEFAULT))
4851*7c478bd9Sstevel@tonic-gate 						 != SM_IO_EOF)
4852*7c478bd9Sstevel@tonic-gate 					{
4853*7c478bd9Sstevel@tonic-gate 						SM_ASSERT(pbp < peekbuf +
4854*7c478bd9Sstevel@tonic-gate 								sizeof(peekbuf));
4855*7c478bd9Sstevel@tonic-gate 						*pbp++ = d;
4856*7c478bd9Sstevel@tonic-gate 					}
4857*7c478bd9Sstevel@tonic-gate 
4858*7c478bd9Sstevel@tonic-gate 					if (d == '\n' || d == SM_IO_EOF)
4859*7c478bd9Sstevel@tonic-gate 					{
4860*7c478bd9Sstevel@tonic-gate 						if (TrafficLogFile != NULL)
4861*7c478bd9Sstevel@tonic-gate 							(void) sm_io_putc(TrafficLogFile,
4862*7c478bd9Sstevel@tonic-gate 									  SM_TIME_DEFAULT,
4863*7c478bd9Sstevel@tonic-gate 									  (unsigned char) c);
4864*7c478bd9Sstevel@tonic-gate 						if (sm_io_putc(mci->mci_out,
4865*7c478bd9Sstevel@tonic-gate 							       SM_TIME_DEFAULT,
4866*7c478bd9Sstevel@tonic-gate 							       (unsigned char) c)
4867*7c478bd9Sstevel@tonic-gate 							       == SM_IO_EOF)
4868*7c478bd9Sstevel@tonic-gate 						{
4869*7c478bd9Sstevel@tonic-gate 							dead = true;
4870*7c478bd9Sstevel@tonic-gate 							continue;
4871*7c478bd9Sstevel@tonic-gate 						}
4872*7c478bd9Sstevel@tonic-gate 						else
4873*7c478bd9Sstevel@tonic-gate 						{
4874*7c478bd9Sstevel@tonic-gate 							/* record progress for DATA timeout */
4875*7c478bd9Sstevel@tonic-gate 							DataProgress = true;
4876*7c478bd9Sstevel@tonic-gate 						}
4877*7c478bd9Sstevel@tonic-gate 						pos++;
4878*7c478bd9Sstevel@tonic-gate 						continue;
4879*7c478bd9Sstevel@tonic-gate 					}
4880*7c478bd9Sstevel@tonic-gate 
4881*7c478bd9Sstevel@tonic-gate 					if (sm_io_putc(mci->mci_out,
4882*7c478bd9Sstevel@tonic-gate 						       SM_TIME_DEFAULT, '!')
4883*7c478bd9Sstevel@tonic-gate 					    == SM_IO_EOF ||
4884*7c478bd9Sstevel@tonic-gate 					    sm_io_fputs(mci->mci_out,
4885*7c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
4886*7c478bd9Sstevel@tonic-gate 							mci->mci_mailer->m_eol)
4887*7c478bd9Sstevel@tonic-gate 					    == SM_IO_EOF)
4888*7c478bd9Sstevel@tonic-gate 					{
4889*7c478bd9Sstevel@tonic-gate 						dead = true;
4890*7c478bd9Sstevel@tonic-gate 						continue;
4891*7c478bd9Sstevel@tonic-gate 					}
4892*7c478bd9Sstevel@tonic-gate 					else
4893*7c478bd9Sstevel@tonic-gate 					{
4894*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4895*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4896*7c478bd9Sstevel@tonic-gate 					}
4897*7c478bd9Sstevel@tonic-gate 
4898*7c478bd9Sstevel@tonic-gate 					if (TrafficLogFile != NULL)
4899*7c478bd9Sstevel@tonic-gate 					{
4900*7c478bd9Sstevel@tonic-gate 						(void) sm_io_fprintf(TrafficLogFile,
4901*7c478bd9Sstevel@tonic-gate 								     SM_TIME_DEFAULT,
4902*7c478bd9Sstevel@tonic-gate 								     "!%s",
4903*7c478bd9Sstevel@tonic-gate 								     mci->mci_mailer->m_eol);
4904*7c478bd9Sstevel@tonic-gate 					}
4905*7c478bd9Sstevel@tonic-gate 					ostate = OS_HEAD;
4906*7c478bd9Sstevel@tonic-gate 					SM_ASSERT(pbp < peekbuf +
4907*7c478bd9Sstevel@tonic-gate 							sizeof(peekbuf));
4908*7c478bd9Sstevel@tonic-gate 					*pbp++ = c;
4909*7c478bd9Sstevel@tonic-gate 					continue;
4910*7c478bd9Sstevel@tonic-gate 				}
4911*7c478bd9Sstevel@tonic-gate 				if (c == '\n')
4912*7c478bd9Sstevel@tonic-gate 				{
4913*7c478bd9Sstevel@tonic-gate 					if (TrafficLogFile != NULL)
4914*7c478bd9Sstevel@tonic-gate 						(void) sm_io_fputs(TrafficLogFile,
4915*7c478bd9Sstevel@tonic-gate 								   SM_TIME_DEFAULT,
4916*7c478bd9Sstevel@tonic-gate 								   mci->mci_mailer->m_eol);
4917*7c478bd9Sstevel@tonic-gate 					if (sm_io_fputs(mci->mci_out,
4918*7c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
4919*7c478bd9Sstevel@tonic-gate 							mci->mci_mailer->m_eol)
4920*7c478bd9Sstevel@tonic-gate 							== SM_IO_EOF)
4921*7c478bd9Sstevel@tonic-gate 						continue;
4922*7c478bd9Sstevel@tonic-gate 					else
4923*7c478bd9Sstevel@tonic-gate 					{
4924*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4925*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4926*7c478bd9Sstevel@tonic-gate 					}
4927*7c478bd9Sstevel@tonic-gate 					pos = 0;
4928*7c478bd9Sstevel@tonic-gate 					ostate = OS_HEAD;
4929*7c478bd9Sstevel@tonic-gate 				}
4930*7c478bd9Sstevel@tonic-gate 				else
4931*7c478bd9Sstevel@tonic-gate 				{
4932*7c478bd9Sstevel@tonic-gate 					if (TrafficLogFile != NULL)
4933*7c478bd9Sstevel@tonic-gate 						(void) sm_io_putc(TrafficLogFile,
4934*7c478bd9Sstevel@tonic-gate 								  SM_TIME_DEFAULT,
4935*7c478bd9Sstevel@tonic-gate 								  (unsigned char) c);
4936*7c478bd9Sstevel@tonic-gate 					if (sm_io_putc(mci->mci_out,
4937*7c478bd9Sstevel@tonic-gate 						       SM_TIME_DEFAULT,
4938*7c478bd9Sstevel@tonic-gate 						       (unsigned char) c)
4939*7c478bd9Sstevel@tonic-gate 					    == SM_IO_EOF)
4940*7c478bd9Sstevel@tonic-gate 					{
4941*7c478bd9Sstevel@tonic-gate 						dead = true;
4942*7c478bd9Sstevel@tonic-gate 						continue;
4943*7c478bd9Sstevel@tonic-gate 					}
4944*7c478bd9Sstevel@tonic-gate 					else
4945*7c478bd9Sstevel@tonic-gate 					{
4946*7c478bd9Sstevel@tonic-gate 						/* record progress for DATA timeout */
4947*7c478bd9Sstevel@tonic-gate 						DataProgress = true;
4948*7c478bd9Sstevel@tonic-gate 					}
4949*7c478bd9Sstevel@tonic-gate 					pos++;
4950*7c478bd9Sstevel@tonic-gate 					ostate = OS_INLINE;
4951*7c478bd9Sstevel@tonic-gate 				}
4952*7c478bd9Sstevel@tonic-gate 				break;
4953*7c478bd9Sstevel@tonic-gate 			}
4954*7c478bd9Sstevel@tonic-gate 		}
4955*7c478bd9Sstevel@tonic-gate 
4956*7c478bd9Sstevel@tonic-gate 		/* make sure we are at the beginning of a line */
4957*7c478bd9Sstevel@tonic-gate 		if (bp > buf)
4958*7c478bd9Sstevel@tonic-gate 		{
4959*7c478bd9Sstevel@tonic-gate 			if (TrafficLogFile != NULL)
4960*7c478bd9Sstevel@tonic-gate 			{
4961*7c478bd9Sstevel@tonic-gate 				for (xp = buf; xp < bp; xp++)
4962*7c478bd9Sstevel@tonic-gate 					(void) sm_io_putc(TrafficLogFile,
4963*7c478bd9Sstevel@tonic-gate 							  SM_TIME_DEFAULT,
4964*7c478bd9Sstevel@tonic-gate 							  (unsigned char) *xp);
4965*7c478bd9Sstevel@tonic-gate 			}
4966*7c478bd9Sstevel@tonic-gate 			for (xp = buf; xp < bp; xp++)
4967*7c478bd9Sstevel@tonic-gate 			{
4968*7c478bd9Sstevel@tonic-gate 				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
4969*7c478bd9Sstevel@tonic-gate 					       (unsigned char) *xp)
4970*7c478bd9Sstevel@tonic-gate 				    == SM_IO_EOF)
4971*7c478bd9Sstevel@tonic-gate 				{
4972*7c478bd9Sstevel@tonic-gate 					dead = true;
4973*7c478bd9Sstevel@tonic-gate 					break;
4974*7c478bd9Sstevel@tonic-gate 				}
4975*7c478bd9Sstevel@tonic-gate 				else
4976*7c478bd9Sstevel@tonic-gate 				{
4977*7c478bd9Sstevel@tonic-gate 					/* record progress for DATA timeout */
4978*7c478bd9Sstevel@tonic-gate 					DataProgress = true;
4979*7c478bd9Sstevel@tonic-gate 				}
4980*7c478bd9Sstevel@tonic-gate 			}
4981*7c478bd9Sstevel@tonic-gate 			pos += bp - buf;
4982*7c478bd9Sstevel@tonic-gate 		}
4983*7c478bd9Sstevel@tonic-gate 		if (!dead && pos > 0)
4984*7c478bd9Sstevel@tonic-gate 		{
4985*7c478bd9Sstevel@tonic-gate 			if (TrafficLogFile != NULL)
4986*7c478bd9Sstevel@tonic-gate 				(void) sm_io_fputs(TrafficLogFile,
4987*7c478bd9Sstevel@tonic-gate 						   SM_TIME_DEFAULT,
4988*7c478bd9Sstevel@tonic-gate 						   mci->mci_mailer->m_eol);
4989*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
4990*7c478bd9Sstevel@tonic-gate 					   mci->mci_mailer->m_eol);
4991*7c478bd9Sstevel@tonic-gate 
4992*7c478bd9Sstevel@tonic-gate 			/* record progress for DATA timeout */
4993*7c478bd9Sstevel@tonic-gate 			DataProgress = true;
4994*7c478bd9Sstevel@tonic-gate 		}
4995*7c478bd9Sstevel@tonic-gate 	}
4996*7c478bd9Sstevel@tonic-gate 
4997*7c478bd9Sstevel@tonic-gate 	if (sm_io_error(e->e_dfp))
4998*7c478bd9Sstevel@tonic-gate 	{
4999*7c478bd9Sstevel@tonic-gate 		syserr("putbody: %s/%cf%s: read error",
5000*7c478bd9Sstevel@tonic-gate 		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
5001*7c478bd9Sstevel@tonic-gate 		       DATAFL_LETTER, e->e_id);
5002*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
5003*7c478bd9Sstevel@tonic-gate 	}
5004*7c478bd9Sstevel@tonic-gate 
5005*7c478bd9Sstevel@tonic-gate endofmessage:
5006*7c478bd9Sstevel@tonic-gate 	/*
5007*7c478bd9Sstevel@tonic-gate 	**  Since mailfile() uses e_dfp in a child process,
5008*7c478bd9Sstevel@tonic-gate 	**  the file offset in the stdio library for the
5009*7c478bd9Sstevel@tonic-gate 	**  parent process will not agree with the in-kernel
5010*7c478bd9Sstevel@tonic-gate 	**  file offset since the file descriptor is shared
5011*7c478bd9Sstevel@tonic-gate 	**  between the processes.  Therefore, it is vital
5012*7c478bd9Sstevel@tonic-gate 	**  that the file always be rewound.  This forces the
5013*7c478bd9Sstevel@tonic-gate 	**  kernel offset (lseek) and stdio library (ftell)
5014*7c478bd9Sstevel@tonic-gate 	**  offset to match.
5015*7c478bd9Sstevel@tonic-gate 	*/
5016*7c478bd9Sstevel@tonic-gate 
5017*7c478bd9Sstevel@tonic-gate 	if (e->e_dfp != NULL)
5018*7c478bd9Sstevel@tonic-gate 		(void) bfrewind(e->e_dfp);
5019*7c478bd9Sstevel@tonic-gate 
5020*7c478bd9Sstevel@tonic-gate 	/* some mailers want extra blank line at end of message */
5021*7c478bd9Sstevel@tonic-gate 	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
5022*7c478bd9Sstevel@tonic-gate 	    buf[0] != '\0' && buf[0] != '\n')
5023*7c478bd9Sstevel@tonic-gate 		putline("", mci);
5024*7c478bd9Sstevel@tonic-gate 
5025*7c478bd9Sstevel@tonic-gate 	(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
5026*7c478bd9Sstevel@tonic-gate 	if (sm_io_error(mci->mci_out) && errno != EPIPE)
5027*7c478bd9Sstevel@tonic-gate 	{
5028*7c478bd9Sstevel@tonic-gate 		syserr("putbody: write error");
5029*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_IOERR;
5030*7c478bd9Sstevel@tonic-gate 	}
5031*7c478bd9Sstevel@tonic-gate 
5032*7c478bd9Sstevel@tonic-gate 	errno = 0;
5033*7c478bd9Sstevel@tonic-gate }
5034*7c478bd9Sstevel@tonic-gate /*
5035*7c478bd9Sstevel@tonic-gate **  MAILFILE -- Send a message to a file.
5036*7c478bd9Sstevel@tonic-gate **
5037*7c478bd9Sstevel@tonic-gate **	If the file has the set-user-ID/set-group-ID bits set, but NO
5038*7c478bd9Sstevel@tonic-gate **	execute bits, sendmail will try to become the owner of that file
5039*7c478bd9Sstevel@tonic-gate **	rather than the real user.  Obviously, this only works if
5040*7c478bd9Sstevel@tonic-gate **	sendmail runs as root.
5041*7c478bd9Sstevel@tonic-gate **
5042*7c478bd9Sstevel@tonic-gate **	This could be done as a subordinate mailer, except that it
5043*7c478bd9Sstevel@tonic-gate **	is used implicitly to save messages in ~/dead.letter.  We
5044*7c478bd9Sstevel@tonic-gate **	view this as being sufficiently important as to include it
5045*7c478bd9Sstevel@tonic-gate **	here.  For example, if the system is dying, we shouldn't have
5046*7c478bd9Sstevel@tonic-gate **	to create another process plus some pipes to save the message.
5047*7c478bd9Sstevel@tonic-gate **
5048*7c478bd9Sstevel@tonic-gate **	Parameters:
5049*7c478bd9Sstevel@tonic-gate **		filename -- the name of the file to send to.
5050*7c478bd9Sstevel@tonic-gate **		mailer -- mailer definition for recipient -- if NULL,
5051*7c478bd9Sstevel@tonic-gate **			use FileMailer.
5052*7c478bd9Sstevel@tonic-gate **		ctladdr -- the controlling address header -- includes
5053*7c478bd9Sstevel@tonic-gate **			the userid/groupid to be when sending.
5054*7c478bd9Sstevel@tonic-gate **		sfflags -- flags for opening.
5055*7c478bd9Sstevel@tonic-gate **		e -- the current envelope.
5056*7c478bd9Sstevel@tonic-gate **
5057*7c478bd9Sstevel@tonic-gate **	Returns:
5058*7c478bd9Sstevel@tonic-gate **		The exit code associated with the operation.
5059*7c478bd9Sstevel@tonic-gate **
5060*7c478bd9Sstevel@tonic-gate **	Side Effects:
5061*7c478bd9Sstevel@tonic-gate **		none.
5062*7c478bd9Sstevel@tonic-gate */
5063*7c478bd9Sstevel@tonic-gate 
5064*7c478bd9Sstevel@tonic-gate # define RETURN(st)			exit(st);
5065*7c478bd9Sstevel@tonic-gate 
5066*7c478bd9Sstevel@tonic-gate static jmp_buf	CtxMailfileTimeout;
5067*7c478bd9Sstevel@tonic-gate 
5068*7c478bd9Sstevel@tonic-gate int
5069*7c478bd9Sstevel@tonic-gate mailfile(filename, mailer, ctladdr, sfflags, e)
5070*7c478bd9Sstevel@tonic-gate 	char *volatile filename;
5071*7c478bd9Sstevel@tonic-gate 	MAILER *volatile mailer;
5072*7c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
5073*7c478bd9Sstevel@tonic-gate 	volatile long sfflags;
5074*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
5075*7c478bd9Sstevel@tonic-gate {
5076*7c478bd9Sstevel@tonic-gate 	register SM_FILE_T *f;
5077*7c478bd9Sstevel@tonic-gate 	register pid_t pid = -1;
5078*7c478bd9Sstevel@tonic-gate 	volatile int mode;
5079*7c478bd9Sstevel@tonic-gate 	int len;
5080*7c478bd9Sstevel@tonic-gate 	off_t curoff;
5081*7c478bd9Sstevel@tonic-gate 	bool suidwarn = geteuid() == 0;
5082*7c478bd9Sstevel@tonic-gate 	char *p;
5083*7c478bd9Sstevel@tonic-gate 	char *volatile realfile;
5084*7c478bd9Sstevel@tonic-gate 	SM_EVENT *ev;
5085*7c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
5086*7c478bd9Sstevel@tonic-gate 	char targetfile[MAXPATHLEN];
5087*7c478bd9Sstevel@tonic-gate 
5088*7c478bd9Sstevel@tonic-gate 	if (tTd(11, 1))
5089*7c478bd9Sstevel@tonic-gate 	{
5090*7c478bd9Sstevel@tonic-gate 		sm_dprintf("mailfile %s\n  ctladdr=", filename);
5091*7c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), ctladdr, false);
5092*7c478bd9Sstevel@tonic-gate 	}
5093*7c478bd9Sstevel@tonic-gate 
5094*7c478bd9Sstevel@tonic-gate 	if (mailer == NULL)
5095*7c478bd9Sstevel@tonic-gate 		mailer = FileMailer;
5096*7c478bd9Sstevel@tonic-gate 
5097*7c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
5098*7c478bd9Sstevel@tonic-gate 		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
5099*7c478bd9Sstevel@tonic-gate 
5100*7c478bd9Sstevel@tonic-gate 	/*
5101*7c478bd9Sstevel@tonic-gate 	**  Special case /dev/null.  This allows us to restrict file
5102*7c478bd9Sstevel@tonic-gate 	**  delivery to regular files only.
5103*7c478bd9Sstevel@tonic-gate 	*/
5104*7c478bd9Sstevel@tonic-gate 
5105*7c478bd9Sstevel@tonic-gate 	if (sm_path_isdevnull(filename))
5106*7c478bd9Sstevel@tonic-gate 		return EX_OK;
5107*7c478bd9Sstevel@tonic-gate 
5108*7c478bd9Sstevel@tonic-gate 	/* check for 8-bit available */
5109*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags) &&
5110*7c478bd9Sstevel@tonic-gate 	    bitnset(M_7BITS, mailer->m_flags) &&
5111*7c478bd9Sstevel@tonic-gate 	    (bitset(EF_DONT_MIME, e->e_flags) ||
5112*7c478bd9Sstevel@tonic-gate 	     !(bitset(MM_MIME8BIT, MimeMode) ||
5113*7c478bd9Sstevel@tonic-gate 	       (bitset(EF_IS_MIME, e->e_flags) &&
5114*7c478bd9Sstevel@tonic-gate 		bitset(MM_CVTMIME, MimeMode)))))
5115*7c478bd9Sstevel@tonic-gate 	{
5116*7c478bd9Sstevel@tonic-gate 		e->e_status = "5.6.3";
5117*7c478bd9Sstevel@tonic-gate 		usrerrenh(e->e_status,
5118*7c478bd9Sstevel@tonic-gate 			  "554 Cannot send 8-bit data to 7-bit destination");
5119*7c478bd9Sstevel@tonic-gate 		errno = 0;
5120*7c478bd9Sstevel@tonic-gate 		return EX_DATAERR;
5121*7c478bd9Sstevel@tonic-gate 	}
5122*7c478bd9Sstevel@tonic-gate 
5123*7c478bd9Sstevel@tonic-gate 	/* Find the actual file */
5124*7c478bd9Sstevel@tonic-gate 	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
5125*7c478bd9Sstevel@tonic-gate 	{
5126*7c478bd9Sstevel@tonic-gate 		len = strlen(SafeFileEnv);
5127*7c478bd9Sstevel@tonic-gate 
5128*7c478bd9Sstevel@tonic-gate 		if (strncmp(SafeFileEnv, filename, len) == 0)
5129*7c478bd9Sstevel@tonic-gate 			filename += len;
5130*7c478bd9Sstevel@tonic-gate 
5131*7c478bd9Sstevel@tonic-gate 		if (len + strlen(filename) + 1 >= sizeof targetfile)
5132*7c478bd9Sstevel@tonic-gate 		{
5133*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: filename too long (%s/%s)",
5134*7c478bd9Sstevel@tonic-gate 			       SafeFileEnv, filename);
5135*7c478bd9Sstevel@tonic-gate 			return EX_CANTCREAT;
5136*7c478bd9Sstevel@tonic-gate 		}
5137*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile);
5138*7c478bd9Sstevel@tonic-gate 		realfile = targetfile + len;
5139*7c478bd9Sstevel@tonic-gate 		if (*filename == '/')
5140*7c478bd9Sstevel@tonic-gate 			filename++;
5141*7c478bd9Sstevel@tonic-gate 		if (*filename != '\0')
5142*7c478bd9Sstevel@tonic-gate 		{
5143*7c478bd9Sstevel@tonic-gate 			/* paranoia: trailing / should be removed in readcf */
5144*7c478bd9Sstevel@tonic-gate 			if (targetfile[len - 1] != '/')
5145*7c478bd9Sstevel@tonic-gate 				(void) sm_strlcat(targetfile,
5146*7c478bd9Sstevel@tonic-gate 						  "/", sizeof targetfile);
5147*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(targetfile, filename,
5148*7c478bd9Sstevel@tonic-gate 					  sizeof targetfile);
5149*7c478bd9Sstevel@tonic-gate 		}
5150*7c478bd9Sstevel@tonic-gate 	}
5151*7c478bd9Sstevel@tonic-gate 	else if (mailer->m_rootdir != NULL)
5152*7c478bd9Sstevel@tonic-gate 	{
5153*7c478bd9Sstevel@tonic-gate 		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);
5154*7c478bd9Sstevel@tonic-gate 		len = strlen(targetfile);
5155*7c478bd9Sstevel@tonic-gate 
5156*7c478bd9Sstevel@tonic-gate 		if (strncmp(targetfile, filename, len) == 0)
5157*7c478bd9Sstevel@tonic-gate 			filename += len;
5158*7c478bd9Sstevel@tonic-gate 
5159*7c478bd9Sstevel@tonic-gate 		if (len + strlen(filename) + 1 >= sizeof targetfile)
5160*7c478bd9Sstevel@tonic-gate 		{
5161*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: filename too long (%s/%s)",
5162*7c478bd9Sstevel@tonic-gate 			       targetfile, filename);
5163*7c478bd9Sstevel@tonic-gate 			return EX_CANTCREAT;
5164*7c478bd9Sstevel@tonic-gate 		}
5165*7c478bd9Sstevel@tonic-gate 		realfile = targetfile + len;
5166*7c478bd9Sstevel@tonic-gate 		if (targetfile[len - 1] != '/')
5167*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(targetfile, "/", sizeof targetfile);
5168*7c478bd9Sstevel@tonic-gate 		if (*filename == '/')
5169*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(targetfile, filename + 1,
5170*7c478bd9Sstevel@tonic-gate 					  sizeof targetfile);
5171*7c478bd9Sstevel@tonic-gate 		else
5172*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(targetfile, filename,
5173*7c478bd9Sstevel@tonic-gate 					  sizeof targetfile);
5174*7c478bd9Sstevel@tonic-gate 	}
5175*7c478bd9Sstevel@tonic-gate 	else
5176*7c478bd9Sstevel@tonic-gate 	{
5177*7c478bd9Sstevel@tonic-gate 		if (sm_strlcpy(targetfile, filename, sizeof targetfile) >=
5178*7c478bd9Sstevel@tonic-gate 		    sizeof targetfile)
5179*7c478bd9Sstevel@tonic-gate 		{
5180*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: filename too long (%s)", filename);
5181*7c478bd9Sstevel@tonic-gate 			return EX_CANTCREAT;
5182*7c478bd9Sstevel@tonic-gate 		}
5183*7c478bd9Sstevel@tonic-gate 		realfile = targetfile;
5184*7c478bd9Sstevel@tonic-gate 	}
5185*7c478bd9Sstevel@tonic-gate 
5186*7c478bd9Sstevel@tonic-gate 	/*
5187*7c478bd9Sstevel@tonic-gate 	**  Fork so we can change permissions here.
5188*7c478bd9Sstevel@tonic-gate 	**	Note that we MUST use fork, not vfork, because of
5189*7c478bd9Sstevel@tonic-gate 	**	the complications of calling subroutines, etc.
5190*7c478bd9Sstevel@tonic-gate 	*/
5191*7c478bd9Sstevel@tonic-gate 
5192*7c478bd9Sstevel@tonic-gate 
5193*7c478bd9Sstevel@tonic-gate 	/*
5194*7c478bd9Sstevel@tonic-gate 	**  Dispose of SIGCHLD signal catchers that may be laying
5195*7c478bd9Sstevel@tonic-gate 	**  around so that the waitfor() below will get it.
5196*7c478bd9Sstevel@tonic-gate 	*/
5197*7c478bd9Sstevel@tonic-gate 
5198*7c478bd9Sstevel@tonic-gate 	(void) sm_signal(SIGCHLD, SIG_DFL);
5199*7c478bd9Sstevel@tonic-gate 
5200*7c478bd9Sstevel@tonic-gate 	DOFORK(fork);
5201*7c478bd9Sstevel@tonic-gate 
5202*7c478bd9Sstevel@tonic-gate 	if (pid < 0)
5203*7c478bd9Sstevel@tonic-gate 		return EX_OSERR;
5204*7c478bd9Sstevel@tonic-gate 	else if (pid == 0)
5205*7c478bd9Sstevel@tonic-gate 	{
5206*7c478bd9Sstevel@tonic-gate 		/* child -- actually write to file */
5207*7c478bd9Sstevel@tonic-gate 		struct stat stb;
5208*7c478bd9Sstevel@tonic-gate 		MCI mcibuf;
5209*7c478bd9Sstevel@tonic-gate 		int err;
5210*7c478bd9Sstevel@tonic-gate 		volatile int oflags = O_WRONLY|O_APPEND;
5211*7c478bd9Sstevel@tonic-gate 
5212*7c478bd9Sstevel@tonic-gate 		/* Reset global flags */
5213*7c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
5214*7c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
5215*7c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
5216*7c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
5217*7c478bd9Sstevel@tonic-gate 		CurrentPid = getpid();
5218*7c478bd9Sstevel@tonic-gate 
5219*7c478bd9Sstevel@tonic-gate 		if (e->e_lockfp != NULL)
5220*7c478bd9Sstevel@tonic-gate 			(void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
5221*7c478bd9Sstevel@tonic-gate 				     NULL));
5222*7c478bd9Sstevel@tonic-gate 
5223*7c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGINT, SIG_DFL);
5224*7c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGHUP, SIG_DFL);
5225*7c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGTERM, SIG_DFL);
5226*7c478bd9Sstevel@tonic-gate 		(void) umask(OldUmask);
5227*7c478bd9Sstevel@tonic-gate 		e->e_to = filename;
5228*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
5229*7c478bd9Sstevel@tonic-gate 
5230*7c478bd9Sstevel@tonic-gate 		if (setjmp(CtxMailfileTimeout) != 0)
5231*7c478bd9Sstevel@tonic-gate 		{
5232*7c478bd9Sstevel@tonic-gate 			RETURN(EX_TEMPFAIL);
5233*7c478bd9Sstevel@tonic-gate 		}
5234*7c478bd9Sstevel@tonic-gate 
5235*7c478bd9Sstevel@tonic-gate 		if (TimeOuts.to_fileopen > 0)
5236*7c478bd9Sstevel@tonic-gate 			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
5237*7c478bd9Sstevel@tonic-gate 					 0);
5238*7c478bd9Sstevel@tonic-gate 		else
5239*7c478bd9Sstevel@tonic-gate 			ev = NULL;
5240*7c478bd9Sstevel@tonic-gate 
5241*7c478bd9Sstevel@tonic-gate 		/* check file mode to see if set-user-ID */
5242*7c478bd9Sstevel@tonic-gate 		if (stat(targetfile, &stb) < 0)
5243*7c478bd9Sstevel@tonic-gate 			mode = FileMode;
5244*7c478bd9Sstevel@tonic-gate 		else
5245*7c478bd9Sstevel@tonic-gate 			mode = stb.st_mode;
5246*7c478bd9Sstevel@tonic-gate 
5247*7c478bd9Sstevel@tonic-gate 		/* limit the errors to those actually caused in the child */
5248*7c478bd9Sstevel@tonic-gate 		errno = 0;
5249*7c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
5250*7c478bd9Sstevel@tonic-gate 
5251*7c478bd9Sstevel@tonic-gate 		/* Allow alias expansions to use the S_IS{U,G}ID bits */
5252*7c478bd9Sstevel@tonic-gate 		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
5253*7c478bd9Sstevel@tonic-gate 		    bitset(SFF_RUNASREALUID, sfflags))
5254*7c478bd9Sstevel@tonic-gate 		{
5255*7c478bd9Sstevel@tonic-gate 			/* ignore set-user-ID and set-group-ID bits */
5256*7c478bd9Sstevel@tonic-gate 			mode &= ~(S_ISGID|S_ISUID);
5257*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 20))
5258*7c478bd9Sstevel@tonic-gate 				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
5259*7c478bd9Sstevel@tonic-gate 		}
5260*7c478bd9Sstevel@tonic-gate 
5261*7c478bd9Sstevel@tonic-gate 		/* we have to open the data file BEFORE setuid() */
5262*7c478bd9Sstevel@tonic-gate 		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
5263*7c478bd9Sstevel@tonic-gate 		{
5264*7c478bd9Sstevel@tonic-gate 			char *df = queuename(e, DATAFL_LETTER);
5265*7c478bd9Sstevel@tonic-gate 
5266*7c478bd9Sstevel@tonic-gate 			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5267*7c478bd9Sstevel@tonic-gate 					      SM_IO_RDONLY_B, NULL);
5268*7c478bd9Sstevel@tonic-gate 			if (e->e_dfp == NULL)
5269*7c478bd9Sstevel@tonic-gate 			{
5270*7c478bd9Sstevel@tonic-gate 				syserr("mailfile: Cannot open %s for %s from %s",
5271*7c478bd9Sstevel@tonic-gate 					df, e->e_to, e->e_from.q_paddr);
5272*7c478bd9Sstevel@tonic-gate 			}
5273*7c478bd9Sstevel@tonic-gate 		}
5274*7c478bd9Sstevel@tonic-gate 
5275*7c478bd9Sstevel@tonic-gate 		/* select a new user to run as */
5276*7c478bd9Sstevel@tonic-gate 		if (!bitset(SFF_RUNASREALUID, sfflags))
5277*7c478bd9Sstevel@tonic-gate 		{
5278*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
5279*7c478bd9Sstevel@tonic-gate 			{
5280*7c478bd9Sstevel@tonic-gate 				RealUserName = NULL;
5281*7c478bd9Sstevel@tonic-gate 				if (mailer->m_uid == NO_UID)
5282*7c478bd9Sstevel@tonic-gate 					RealUid = RunAsUid;
5283*7c478bd9Sstevel@tonic-gate 				else
5284*7c478bd9Sstevel@tonic-gate 					RealUid = mailer->m_uid;
5285*7c478bd9Sstevel@tonic-gate 				if (RunAsUid != 0 && RealUid != RunAsUid)
5286*7c478bd9Sstevel@tonic-gate 				{
5287*7c478bd9Sstevel@tonic-gate 					/* Only root can change the uid */
5288*7c478bd9Sstevel@tonic-gate 					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
5289*7c478bd9Sstevel@tonic-gate 						(int) RunAsUid, (int) RealUid);
5290*7c478bd9Sstevel@tonic-gate 					RETURN(EX_TEMPFAIL);
5291*7c478bd9Sstevel@tonic-gate 				}
5292*7c478bd9Sstevel@tonic-gate 			}
5293*7c478bd9Sstevel@tonic-gate 			else if (bitset(S_ISUID, mode))
5294*7c478bd9Sstevel@tonic-gate 			{
5295*7c478bd9Sstevel@tonic-gate 				RealUserName = NULL;
5296*7c478bd9Sstevel@tonic-gate 				RealUid = stb.st_uid;
5297*7c478bd9Sstevel@tonic-gate 			}
5298*7c478bd9Sstevel@tonic-gate 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5299*7c478bd9Sstevel@tonic-gate 			{
5300*7c478bd9Sstevel@tonic-gate 				if (ctladdr->q_ruser != NULL)
5301*7c478bd9Sstevel@tonic-gate 					RealUserName = ctladdr->q_ruser;
5302*7c478bd9Sstevel@tonic-gate 				else
5303*7c478bd9Sstevel@tonic-gate 					RealUserName = ctladdr->q_user;
5304*7c478bd9Sstevel@tonic-gate 				RealUid = ctladdr->q_uid;
5305*7c478bd9Sstevel@tonic-gate 			}
5306*7c478bd9Sstevel@tonic-gate 			else if (mailer != NULL && mailer->m_uid != NO_UID)
5307*7c478bd9Sstevel@tonic-gate 			{
5308*7c478bd9Sstevel@tonic-gate 				RealUserName = DefUser;
5309*7c478bd9Sstevel@tonic-gate 				RealUid = mailer->m_uid;
5310*7c478bd9Sstevel@tonic-gate 			}
5311*7c478bd9Sstevel@tonic-gate 			else
5312*7c478bd9Sstevel@tonic-gate 			{
5313*7c478bd9Sstevel@tonic-gate 				RealUserName = DefUser;
5314*7c478bd9Sstevel@tonic-gate 				RealUid = DefUid;
5315*7c478bd9Sstevel@tonic-gate 			}
5316*7c478bd9Sstevel@tonic-gate 
5317*7c478bd9Sstevel@tonic-gate 			/* select a new group to run as */
5318*7c478bd9Sstevel@tonic-gate 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
5319*7c478bd9Sstevel@tonic-gate 			{
5320*7c478bd9Sstevel@tonic-gate 				if (mailer->m_gid == NO_GID)
5321*7c478bd9Sstevel@tonic-gate 					RealGid = RunAsGid;
5322*7c478bd9Sstevel@tonic-gate 				else
5323*7c478bd9Sstevel@tonic-gate 					RealGid = mailer->m_gid;
5324*7c478bd9Sstevel@tonic-gate 				if (RunAsUid != 0 &&
5325*7c478bd9Sstevel@tonic-gate 				    (RealGid != getgid() ||
5326*7c478bd9Sstevel@tonic-gate 				     RealGid != getegid()))
5327*7c478bd9Sstevel@tonic-gate 				{
5328*7c478bd9Sstevel@tonic-gate 					/* Only root can change the gid */
5329*7c478bd9Sstevel@tonic-gate 					syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
5330*7c478bd9Sstevel@tonic-gate 					       (int) RealGid, (int) RunAsUid,
5331*7c478bd9Sstevel@tonic-gate 					       (int) getgid(), (int) getegid());
5332*7c478bd9Sstevel@tonic-gate 					RETURN(EX_TEMPFAIL);
5333*7c478bd9Sstevel@tonic-gate 				}
5334*7c478bd9Sstevel@tonic-gate 			}
5335*7c478bd9Sstevel@tonic-gate 			else if (bitset(S_ISGID, mode))
5336*7c478bd9Sstevel@tonic-gate 				RealGid = stb.st_gid;
5337*7c478bd9Sstevel@tonic-gate 			else if (ctladdr != NULL &&
5338*7c478bd9Sstevel@tonic-gate 				 ctladdr->q_uid == DefUid &&
5339*7c478bd9Sstevel@tonic-gate 				 ctladdr->q_gid == 0)
5340*7c478bd9Sstevel@tonic-gate 			{
5341*7c478bd9Sstevel@tonic-gate 				/*
5342*7c478bd9Sstevel@tonic-gate 				**  Special case:  This means it is an
5343*7c478bd9Sstevel@tonic-gate 				**  alias and we should act as DefaultUser.
5344*7c478bd9Sstevel@tonic-gate 				**  See alias()'s comments.
5345*7c478bd9Sstevel@tonic-gate 				*/
5346*7c478bd9Sstevel@tonic-gate 
5347*7c478bd9Sstevel@tonic-gate 				RealGid = DefGid;
5348*7c478bd9Sstevel@tonic-gate 				RealUserName = DefUser;
5349*7c478bd9Sstevel@tonic-gate 			}
5350*7c478bd9Sstevel@tonic-gate 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5351*7c478bd9Sstevel@tonic-gate 				RealGid = ctladdr->q_gid;
5352*7c478bd9Sstevel@tonic-gate 			else if (mailer != NULL && mailer->m_gid != NO_GID)
5353*7c478bd9Sstevel@tonic-gate 				RealGid = mailer->m_gid;
5354*7c478bd9Sstevel@tonic-gate 			else
5355*7c478bd9Sstevel@tonic-gate 				RealGid = DefGid;
5356*7c478bd9Sstevel@tonic-gate 		}
5357*7c478bd9Sstevel@tonic-gate 
5358*7c478bd9Sstevel@tonic-gate 		/* last ditch */
5359*7c478bd9Sstevel@tonic-gate 		if (!bitset(SFF_ROOTOK, sfflags))
5360*7c478bd9Sstevel@tonic-gate 		{
5361*7c478bd9Sstevel@tonic-gate 			if (RealUid == 0)
5362*7c478bd9Sstevel@tonic-gate 				RealUid = DefUid;
5363*7c478bd9Sstevel@tonic-gate 			if (RealGid == 0)
5364*7c478bd9Sstevel@tonic-gate 				RealGid = DefGid;
5365*7c478bd9Sstevel@tonic-gate 		}
5366*7c478bd9Sstevel@tonic-gate 
5367*7c478bd9Sstevel@tonic-gate 		/* set group id list (needs /etc/group access) */
5368*7c478bd9Sstevel@tonic-gate 		if (RealUserName != NULL && !DontInitGroups)
5369*7c478bd9Sstevel@tonic-gate 		{
5370*7c478bd9Sstevel@tonic-gate 			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
5371*7c478bd9Sstevel@tonic-gate 			{
5372*7c478bd9Sstevel@tonic-gate 				syserr("mailfile: initgroups(%s, %d) failed",
5373*7c478bd9Sstevel@tonic-gate 					RealUserName, RealGid);
5374*7c478bd9Sstevel@tonic-gate 				RETURN(EX_TEMPFAIL);
5375*7c478bd9Sstevel@tonic-gate 			}
5376*7c478bd9Sstevel@tonic-gate 		}
5377*7c478bd9Sstevel@tonic-gate 		else
5378*7c478bd9Sstevel@tonic-gate 		{
5379*7c478bd9Sstevel@tonic-gate 			GIDSET_T gidset[1];
5380*7c478bd9Sstevel@tonic-gate 
5381*7c478bd9Sstevel@tonic-gate 			gidset[0] = RealGid;
5382*7c478bd9Sstevel@tonic-gate 			if (setgroups(1, gidset) == -1 && suidwarn)
5383*7c478bd9Sstevel@tonic-gate 			{
5384*7c478bd9Sstevel@tonic-gate 				syserr("mailfile: setgroups() failed");
5385*7c478bd9Sstevel@tonic-gate 				RETURN(EX_TEMPFAIL);
5386*7c478bd9Sstevel@tonic-gate 			}
5387*7c478bd9Sstevel@tonic-gate 		}
5388*7c478bd9Sstevel@tonic-gate 
5389*7c478bd9Sstevel@tonic-gate 		/*
5390*7c478bd9Sstevel@tonic-gate 		**  If you have a safe environment, go into it.
5391*7c478bd9Sstevel@tonic-gate 		*/
5392*7c478bd9Sstevel@tonic-gate 
5393*7c478bd9Sstevel@tonic-gate 		if (realfile != targetfile)
5394*7c478bd9Sstevel@tonic-gate 		{
5395*7c478bd9Sstevel@tonic-gate 			char save;
5396*7c478bd9Sstevel@tonic-gate 
5397*7c478bd9Sstevel@tonic-gate 			save = *realfile;
5398*7c478bd9Sstevel@tonic-gate 			*realfile = '\0';
5399*7c478bd9Sstevel@tonic-gate 			if (tTd(11, 20))
5400*7c478bd9Sstevel@tonic-gate 				sm_dprintf("mailfile: chroot %s\n", targetfile);
5401*7c478bd9Sstevel@tonic-gate 			if (chroot(targetfile) < 0)
5402*7c478bd9Sstevel@tonic-gate 			{
5403*7c478bd9Sstevel@tonic-gate 				syserr("mailfile: Cannot chroot(%s)",
5404*7c478bd9Sstevel@tonic-gate 				       targetfile);
5405*7c478bd9Sstevel@tonic-gate 				RETURN(EX_CANTCREAT);
5406*7c478bd9Sstevel@tonic-gate 			}
5407*7c478bd9Sstevel@tonic-gate 			*realfile = save;
5408*7c478bd9Sstevel@tonic-gate 		}
5409*7c478bd9Sstevel@tonic-gate 
5410*7c478bd9Sstevel@tonic-gate 		if (tTd(11, 40))
5411*7c478bd9Sstevel@tonic-gate 			sm_dprintf("mailfile: deliver to %s\n", realfile);
5412*7c478bd9Sstevel@tonic-gate 
5413*7c478bd9Sstevel@tonic-gate 		if (chdir("/") < 0)
5414*7c478bd9Sstevel@tonic-gate 		{
5415*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: cannot chdir(/)");
5416*7c478bd9Sstevel@tonic-gate 			RETURN(EX_CANTCREAT);
5417*7c478bd9Sstevel@tonic-gate 		}
5418*7c478bd9Sstevel@tonic-gate 
5419*7c478bd9Sstevel@tonic-gate 		/* now reset the group and user ids */
5420*7c478bd9Sstevel@tonic-gate 		endpwent();
5421*7c478bd9Sstevel@tonic-gate 		sm_mbdb_terminate();
5422*7c478bd9Sstevel@tonic-gate 		if (setgid(RealGid) < 0 && suidwarn)
5423*7c478bd9Sstevel@tonic-gate 		{
5424*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
5425*7c478bd9Sstevel@tonic-gate 			RETURN(EX_TEMPFAIL);
5426*7c478bd9Sstevel@tonic-gate 		}
5427*7c478bd9Sstevel@tonic-gate 		vendor_set_uid(RealUid);
5428*7c478bd9Sstevel@tonic-gate 		if (setuid(RealUid) < 0 && suidwarn)
5429*7c478bd9Sstevel@tonic-gate 		{
5430*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
5431*7c478bd9Sstevel@tonic-gate 			RETURN(EX_TEMPFAIL);
5432*7c478bd9Sstevel@tonic-gate 		}
5433*7c478bd9Sstevel@tonic-gate 
5434*7c478bd9Sstevel@tonic-gate 		if (tTd(11, 2))
5435*7c478bd9Sstevel@tonic-gate 			sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",
5436*7c478bd9Sstevel@tonic-gate 				(int) getuid(), (int) geteuid(),
5437*7c478bd9Sstevel@tonic-gate 				(int) getgid(), (int) getegid());
5438*7c478bd9Sstevel@tonic-gate 
5439*7c478bd9Sstevel@tonic-gate 
5440*7c478bd9Sstevel@tonic-gate 		/* move into some "safe" directory */
5441*7c478bd9Sstevel@tonic-gate 		if (mailer->m_execdir != NULL)
5442*7c478bd9Sstevel@tonic-gate 		{
5443*7c478bd9Sstevel@tonic-gate 			char *q;
5444*7c478bd9Sstevel@tonic-gate 
5445*7c478bd9Sstevel@tonic-gate 			for (p = mailer->m_execdir; p != NULL; p = q)
5446*7c478bd9Sstevel@tonic-gate 			{
5447*7c478bd9Sstevel@tonic-gate 				q = strchr(p, ':');
5448*7c478bd9Sstevel@tonic-gate 				if (q != NULL)
5449*7c478bd9Sstevel@tonic-gate 					*q = '\0';
5450*7c478bd9Sstevel@tonic-gate 				expand(p, buf, sizeof buf, e);
5451*7c478bd9Sstevel@tonic-gate 				if (q != NULL)
5452*7c478bd9Sstevel@tonic-gate 					*q++ = ':';
5453*7c478bd9Sstevel@tonic-gate 				if (tTd(11, 20))
5454*7c478bd9Sstevel@tonic-gate 					sm_dprintf("mailfile: trydir %s\n",
5455*7c478bd9Sstevel@tonic-gate 						   buf);
5456*7c478bd9Sstevel@tonic-gate 				if (buf[0] != '\0' && chdir(buf) >= 0)
5457*7c478bd9Sstevel@tonic-gate 					break;
5458*7c478bd9Sstevel@tonic-gate 			}
5459*7c478bd9Sstevel@tonic-gate 		}
5460*7c478bd9Sstevel@tonic-gate 
5461*7c478bd9Sstevel@tonic-gate 		/*
5462*7c478bd9Sstevel@tonic-gate 		**  Recheck the file after we have assumed the ID of the
5463*7c478bd9Sstevel@tonic-gate 		**  delivery user to make sure we can deliver to it as
5464*7c478bd9Sstevel@tonic-gate 		**  that user.  This is necessary if sendmail is running
5465*7c478bd9Sstevel@tonic-gate 		**  as root and the file is on an NFS mount which treats
5466*7c478bd9Sstevel@tonic-gate 		**  root as nobody.
5467*7c478bd9Sstevel@tonic-gate 		*/
5468*7c478bd9Sstevel@tonic-gate 
5469*7c478bd9Sstevel@tonic-gate #if HASLSTAT
5470*7c478bd9Sstevel@tonic-gate 		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
5471*7c478bd9Sstevel@tonic-gate 			err = stat(realfile, &stb);
5472*7c478bd9Sstevel@tonic-gate 		else
5473*7c478bd9Sstevel@tonic-gate 			err = lstat(realfile, &stb);
5474*7c478bd9Sstevel@tonic-gate #else /* HASLSTAT */
5475*7c478bd9Sstevel@tonic-gate 		err = stat(realfile, &stb);
5476*7c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
5477*7c478bd9Sstevel@tonic-gate 
5478*7c478bd9Sstevel@tonic-gate 		if (err < 0)
5479*7c478bd9Sstevel@tonic-gate 		{
5480*7c478bd9Sstevel@tonic-gate 			stb.st_mode = ST_MODE_NOFILE;
5481*7c478bd9Sstevel@tonic-gate 			mode = FileMode;
5482*7c478bd9Sstevel@tonic-gate 			oflags |= O_CREAT|O_EXCL;
5483*7c478bd9Sstevel@tonic-gate 		}
5484*7c478bd9Sstevel@tonic-gate 		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
5485*7c478bd9Sstevel@tonic-gate 			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
5486*7c478bd9Sstevel@tonic-gate 				   DontBlameSendmail) &&
5487*7c478bd9Sstevel@tonic-gate 			  stb.st_nlink != 1) ||
5488*7c478bd9Sstevel@tonic-gate 			 (realfile != targetfile && !S_ISREG(mode)))
5489*7c478bd9Sstevel@tonic-gate 			exit(EX_CANTCREAT);
5490*7c478bd9Sstevel@tonic-gate 		else
5491*7c478bd9Sstevel@tonic-gate 			mode = stb.st_mode;
5492*7c478bd9Sstevel@tonic-gate 
5493*7c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
5494*7c478bd9Sstevel@tonic-gate 			sfflags |= SFF_NOSLINK;
5495*7c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
5496*7c478bd9Sstevel@tonic-gate 			sfflags |= SFF_NOHLINK;
5497*7c478bd9Sstevel@tonic-gate 		sfflags &= ~SFF_OPENASROOT;
5498*7c478bd9Sstevel@tonic-gate 		f = safefopen(realfile, oflags, mode, sfflags);
5499*7c478bd9Sstevel@tonic-gate 		if (f == NULL)
5500*7c478bd9Sstevel@tonic-gate 		{
5501*7c478bd9Sstevel@tonic-gate 			if (transienterror(errno))
5502*7c478bd9Sstevel@tonic-gate 			{
5503*7c478bd9Sstevel@tonic-gate 				usrerr("454 4.3.0 cannot open %s: %s",
5504*7c478bd9Sstevel@tonic-gate 				       shortenstring(realfile, MAXSHORTSTR),
5505*7c478bd9Sstevel@tonic-gate 				       sm_errstring(errno));
5506*7c478bd9Sstevel@tonic-gate 				RETURN(EX_TEMPFAIL);
5507*7c478bd9Sstevel@tonic-gate 			}
5508*7c478bd9Sstevel@tonic-gate 			else
5509*7c478bd9Sstevel@tonic-gate 			{
5510*7c478bd9Sstevel@tonic-gate 				usrerr("554 5.3.0 cannot open %s: %s",
5511*7c478bd9Sstevel@tonic-gate 				       shortenstring(realfile, MAXSHORTSTR),
5512*7c478bd9Sstevel@tonic-gate 				       sm_errstring(errno));
5513*7c478bd9Sstevel@tonic-gate 				RETURN(EX_CANTCREAT);
5514*7c478bd9Sstevel@tonic-gate 			}
5515*7c478bd9Sstevel@tonic-gate 		}
5516*7c478bd9Sstevel@tonic-gate 		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
5517*7c478bd9Sstevel@tonic-gate 		    &stb))
5518*7c478bd9Sstevel@tonic-gate 		{
5519*7c478bd9Sstevel@tonic-gate 			syserr("554 5.3.0 file changed after open");
5520*7c478bd9Sstevel@tonic-gate 			RETURN(EX_CANTCREAT);
5521*7c478bd9Sstevel@tonic-gate 		}
5522*7c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
5523*7c478bd9Sstevel@tonic-gate 		{
5524*7c478bd9Sstevel@tonic-gate 			syserr("554 5.3.0 cannot fstat %s",
5525*7c478bd9Sstevel@tonic-gate 				sm_errstring(errno));
5526*7c478bd9Sstevel@tonic-gate 			RETURN(EX_CANTCREAT);
5527*7c478bd9Sstevel@tonic-gate 		}
5528*7c478bd9Sstevel@tonic-gate 
5529*7c478bd9Sstevel@tonic-gate 		curoff = stb.st_size;
5530*7c478bd9Sstevel@tonic-gate 
5531*7c478bd9Sstevel@tonic-gate 		if (ev != NULL)
5532*7c478bd9Sstevel@tonic-gate 			sm_clrevent(ev);
5533*7c478bd9Sstevel@tonic-gate 
5534*7c478bd9Sstevel@tonic-gate 		memset(&mcibuf, '\0', sizeof mcibuf);
5535*7c478bd9Sstevel@tonic-gate 		mcibuf.mci_mailer = mailer;
5536*7c478bd9Sstevel@tonic-gate 		mcibuf.mci_out = f;
5537*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_7BITS, mailer->m_flags))
5538*7c478bd9Sstevel@tonic-gate 			mcibuf.mci_flags |= MCIF_7BIT;
5539*7c478bd9Sstevel@tonic-gate 
5540*7c478bd9Sstevel@tonic-gate 		/* clear out per-message flags from connection structure */
5541*7c478bd9Sstevel@tonic-gate 		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
5542*7c478bd9Sstevel@tonic-gate 
5543*7c478bd9Sstevel@tonic-gate 		if (bitset(EF_HAS8BIT, e->e_flags) &&
5544*7c478bd9Sstevel@tonic-gate 		    !bitset(EF_DONT_MIME, e->e_flags) &&
5545*7c478bd9Sstevel@tonic-gate 		    bitnset(M_7BITS, mailer->m_flags))
5546*7c478bd9Sstevel@tonic-gate 			mcibuf.mci_flags |= MCIF_CVT8TO7;
5547*7c478bd9Sstevel@tonic-gate 
5548*7c478bd9Sstevel@tonic-gate #if MIME7TO8
5549*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
5550*7c478bd9Sstevel@tonic-gate 		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
5551*7c478bd9Sstevel@tonic-gate 		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
5552*7c478bd9Sstevel@tonic-gate 		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
5553*7c478bd9Sstevel@tonic-gate 		     sm_strcasecmp(p, "base64") == 0) &&
5554*7c478bd9Sstevel@tonic-gate 		    (p = hvalue("Content-Type", e->e_header)) != NULL)
5555*7c478bd9Sstevel@tonic-gate 		{
5556*7c478bd9Sstevel@tonic-gate 			/* may want to convert 7 -> 8 */
5557*7c478bd9Sstevel@tonic-gate 			/* XXX should really parse it here -- and use a class XXX */
5558*7c478bd9Sstevel@tonic-gate 			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
5559*7c478bd9Sstevel@tonic-gate 			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
5560*7c478bd9Sstevel@tonic-gate 				mcibuf.mci_flags |= MCIF_CVT7TO8;
5561*7c478bd9Sstevel@tonic-gate 		}
5562*7c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */
5563*7c478bd9Sstevel@tonic-gate 
5564*7c478bd9Sstevel@tonic-gate 		putfromline(&mcibuf, e);
5565*7c478bd9Sstevel@tonic-gate 		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
5566*7c478bd9Sstevel@tonic-gate 		(*e->e_putbody)(&mcibuf, e, NULL);
5567*7c478bd9Sstevel@tonic-gate 		putline("\n", &mcibuf);
5568*7c478bd9Sstevel@tonic-gate 		if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
5569*7c478bd9Sstevel@tonic-gate 		    (SuperSafe != SAFE_NO &&
5570*7c478bd9Sstevel@tonic-gate 		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
5571*7c478bd9Sstevel@tonic-gate 		    sm_io_error(f))
5572*7c478bd9Sstevel@tonic-gate 		{
5573*7c478bd9Sstevel@tonic-gate 			setstat(EX_IOERR);
5574*7c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE
5575*7c478bd9Sstevel@tonic-gate 			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
5576*7c478bd9Sstevel@tonic-gate 					 curoff);
5577*7c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */
5578*7c478bd9Sstevel@tonic-gate 		}
5579*7c478bd9Sstevel@tonic-gate 
5580*7c478bd9Sstevel@tonic-gate 		/* reset ISUID & ISGID bits for paranoid systems */
5581*7c478bd9Sstevel@tonic-gate #if HASFCHMOD
5582*7c478bd9Sstevel@tonic-gate 		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
5583*7c478bd9Sstevel@tonic-gate 			      (MODE_T) mode);
5584*7c478bd9Sstevel@tonic-gate #else /* HASFCHMOD */
5585*7c478bd9Sstevel@tonic-gate 		(void) chmod(filename, (MODE_T) mode);
5586*7c478bd9Sstevel@tonic-gate #endif /* HASFCHMOD */
5587*7c478bd9Sstevel@tonic-gate 		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
5588*7c478bd9Sstevel@tonic-gate 			setstat(EX_IOERR);
5589*7c478bd9Sstevel@tonic-gate 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
5590*7c478bd9Sstevel@tonic-gate 		(void) setuid(RealUid);
5591*7c478bd9Sstevel@tonic-gate 		exit(ExitStat);
5592*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
5593*7c478bd9Sstevel@tonic-gate 	}
5594*7c478bd9Sstevel@tonic-gate 	else
5595*7c478bd9Sstevel@tonic-gate 	{
5596*7c478bd9Sstevel@tonic-gate 		/* parent -- wait for exit status */
5597*7c478bd9Sstevel@tonic-gate 		int st;
5598*7c478bd9Sstevel@tonic-gate 
5599*7c478bd9Sstevel@tonic-gate 		st = waitfor(pid);
5600*7c478bd9Sstevel@tonic-gate 		if (st == -1)
5601*7c478bd9Sstevel@tonic-gate 		{
5602*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: %s: wait", mailer->m_name);
5603*7c478bd9Sstevel@tonic-gate 			return EX_SOFTWARE;
5604*7c478bd9Sstevel@tonic-gate 		}
5605*7c478bd9Sstevel@tonic-gate 		if (WIFEXITED(st))
5606*7c478bd9Sstevel@tonic-gate 		{
5607*7c478bd9Sstevel@tonic-gate 			errno = 0;
5608*7c478bd9Sstevel@tonic-gate 			return (WEXITSTATUS(st));
5609*7c478bd9Sstevel@tonic-gate 		}
5610*7c478bd9Sstevel@tonic-gate 		else
5611*7c478bd9Sstevel@tonic-gate 		{
5612*7c478bd9Sstevel@tonic-gate 			syserr("mailfile: %s: child died on signal %d",
5613*7c478bd9Sstevel@tonic-gate 			       mailer->m_name, st);
5614*7c478bd9Sstevel@tonic-gate 			return EX_UNAVAILABLE;
5615*7c478bd9Sstevel@tonic-gate 		}
5616*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
5617*7c478bd9Sstevel@tonic-gate 	}
5618*7c478bd9Sstevel@tonic-gate 	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
5619*7c478bd9Sstevel@tonic-gate }
5620*7c478bd9Sstevel@tonic-gate 
5621*7c478bd9Sstevel@tonic-gate static void
5622*7c478bd9Sstevel@tonic-gate mailfiletimeout(ignore)
5623*7c478bd9Sstevel@tonic-gate 	int ignore;
5624*7c478bd9Sstevel@tonic-gate {
5625*7c478bd9Sstevel@tonic-gate 	/*
5626*7c478bd9Sstevel@tonic-gate 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
5627*7c478bd9Sstevel@tonic-gate 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
5628*7c478bd9Sstevel@tonic-gate 	**	DOING.
5629*7c478bd9Sstevel@tonic-gate 	*/
5630*7c478bd9Sstevel@tonic-gate 
5631*7c478bd9Sstevel@tonic-gate 	errno = ETIMEDOUT;
5632*7c478bd9Sstevel@tonic-gate 	longjmp(CtxMailfileTimeout, 1);
5633*7c478bd9Sstevel@tonic-gate }
5634*7c478bd9Sstevel@tonic-gate /*
5635*7c478bd9Sstevel@tonic-gate **  HOSTSIGNATURE -- return the "signature" for a host.
5636*7c478bd9Sstevel@tonic-gate **
5637*7c478bd9Sstevel@tonic-gate **	The signature describes how we are going to send this -- it
5638*7c478bd9Sstevel@tonic-gate **	can be just the hostname (for non-Internet hosts) or can be
5639*7c478bd9Sstevel@tonic-gate **	an ordered list of MX hosts.
5640*7c478bd9Sstevel@tonic-gate **
5641*7c478bd9Sstevel@tonic-gate **	Parameters:
5642*7c478bd9Sstevel@tonic-gate **		m -- the mailer describing this host.
5643*7c478bd9Sstevel@tonic-gate **		host -- the host name.
5644*7c478bd9Sstevel@tonic-gate **
5645*7c478bd9Sstevel@tonic-gate **	Returns:
5646*7c478bd9Sstevel@tonic-gate **		The signature for this host.
5647*7c478bd9Sstevel@tonic-gate **
5648*7c478bd9Sstevel@tonic-gate **	Side Effects:
5649*7c478bd9Sstevel@tonic-gate **		Can tweak the symbol table.
5650*7c478bd9Sstevel@tonic-gate */
5651*7c478bd9Sstevel@tonic-gate 
5652*7c478bd9Sstevel@tonic-gate #define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
5653*7c478bd9Sstevel@tonic-gate 
5654*7c478bd9Sstevel@tonic-gate char *
5655*7c478bd9Sstevel@tonic-gate hostsignature(m, host)
5656*7c478bd9Sstevel@tonic-gate 	register MAILER *m;
5657*7c478bd9Sstevel@tonic-gate 	char *host;
5658*7c478bd9Sstevel@tonic-gate {
5659*7c478bd9Sstevel@tonic-gate 	register char *p;
5660*7c478bd9Sstevel@tonic-gate 	register STAB *s;
5661*7c478bd9Sstevel@tonic-gate 	time_t now;
5662*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
5663*7c478bd9Sstevel@tonic-gate 	char sep = ':';
5664*7c478bd9Sstevel@tonic-gate 	char prevsep = ':';
5665*7c478bd9Sstevel@tonic-gate 	int i;
5666*7c478bd9Sstevel@tonic-gate 	int len;
5667*7c478bd9Sstevel@tonic-gate 	int nmx;
5668*7c478bd9Sstevel@tonic-gate 	int hl;
5669*7c478bd9Sstevel@tonic-gate 	char *hp;
5670*7c478bd9Sstevel@tonic-gate 	char *endp;
5671*7c478bd9Sstevel@tonic-gate 	int oldoptions = _res.options;
5672*7c478bd9Sstevel@tonic-gate 	char *mxhosts[MAXMXHOSTS + 1];
5673*7c478bd9Sstevel@tonic-gate 	unsigned short mxprefs[MAXMXHOSTS + 1];
5674*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
5675*7c478bd9Sstevel@tonic-gate 
5676*7c478bd9Sstevel@tonic-gate 	if (tTd(17, 3))
5677*7c478bd9Sstevel@tonic-gate 		sm_dprintf("hostsignature(%s)\n", host);
5678*7c478bd9Sstevel@tonic-gate 
5679*7c478bd9Sstevel@tonic-gate 	/*
5680*7c478bd9Sstevel@tonic-gate 	**  If local delivery (and not remote), just return a constant.
5681*7c478bd9Sstevel@tonic-gate 	*/
5682*7c478bd9Sstevel@tonic-gate 
5683*7c478bd9Sstevel@tonic-gate 	if (bitnset(M_LOCALMAILER, m->m_flags) &&
5684*7c478bd9Sstevel@tonic-gate 	    strcmp(m->m_mailer, "[IPC]") != 0 &&
5685*7c478bd9Sstevel@tonic-gate 	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
5686*7c478bd9Sstevel@tonic-gate 		return "localhost";
5687*7c478bd9Sstevel@tonic-gate 
5688*7c478bd9Sstevel@tonic-gate 	/* an empty host does not have MX records */
5689*7c478bd9Sstevel@tonic-gate 	if (*host == '\0')
5690*7c478bd9Sstevel@tonic-gate 		return "_empty_";
5691*7c478bd9Sstevel@tonic-gate 
5692*7c478bd9Sstevel@tonic-gate 	/*
5693*7c478bd9Sstevel@tonic-gate 	**  Check to see if this uses IPC -- if not, it can't have MX records.
5694*7c478bd9Sstevel@tonic-gate 	*/
5695*7c478bd9Sstevel@tonic-gate 
5696*7c478bd9Sstevel@tonic-gate 	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
5697*7c478bd9Sstevel@tonic-gate 	    CurEnv->e_sendmode == SM_DEFER)
5698*7c478bd9Sstevel@tonic-gate 	{
5699*7c478bd9Sstevel@tonic-gate 		/* just an ordinary mailer or deferred mode */
5700*7c478bd9Sstevel@tonic-gate 		return host;
5701*7c478bd9Sstevel@tonic-gate 	}
5702*7c478bd9Sstevel@tonic-gate #if NETUNIX
5703*7c478bd9Sstevel@tonic-gate 	else if (m->m_argv[0] != NULL &&
5704*7c478bd9Sstevel@tonic-gate 		 strcmp(m->m_argv[0], "FILE") == 0)
5705*7c478bd9Sstevel@tonic-gate 	{
5706*7c478bd9Sstevel@tonic-gate 		/* rendezvous in the file system, no MX records */
5707*7c478bd9Sstevel@tonic-gate 		return host;
5708*7c478bd9Sstevel@tonic-gate 	}
5709*7c478bd9Sstevel@tonic-gate #endif /* NETUNIX */
5710*7c478bd9Sstevel@tonic-gate 
5711*7c478bd9Sstevel@tonic-gate 	/*
5712*7c478bd9Sstevel@tonic-gate 	**  Look it up in the symbol table.
5713*7c478bd9Sstevel@tonic-gate 	*/
5714*7c478bd9Sstevel@tonic-gate 
5715*7c478bd9Sstevel@tonic-gate 	now = curtime();
5716*7c478bd9Sstevel@tonic-gate 	s = stab(host, ST_HOSTSIG, ST_ENTER);
5717*7c478bd9Sstevel@tonic-gate 	if (s->s_hostsig.hs_sig != NULL)
5718*7c478bd9Sstevel@tonic-gate 	{
5719*7c478bd9Sstevel@tonic-gate 		if (s->s_hostsig.hs_exp >= now)
5720*7c478bd9Sstevel@tonic-gate 		{
5721*7c478bd9Sstevel@tonic-gate 			if (tTd(17, 3))
5722*7c478bd9Sstevel@tonic-gate 				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
5723*7c478bd9Sstevel@tonic-gate 					   s->s_hostsig.hs_sig);
5724*7c478bd9Sstevel@tonic-gate 			return s->s_hostsig.hs_sig;
5725*7c478bd9Sstevel@tonic-gate 		}
5726*7c478bd9Sstevel@tonic-gate 
5727*7c478bd9Sstevel@tonic-gate 		/* signature is expired: clear it */
5728*7c478bd9Sstevel@tonic-gate 		sm_free(s->s_hostsig.hs_sig);
5729*7c478bd9Sstevel@tonic-gate 		s->s_hostsig.hs_sig = NULL;
5730*7c478bd9Sstevel@tonic-gate 	}
5731*7c478bd9Sstevel@tonic-gate 
5732*7c478bd9Sstevel@tonic-gate 	/* set default TTL */
5733*7c478bd9Sstevel@tonic-gate 	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
5734*7c478bd9Sstevel@tonic-gate 
5735*7c478bd9Sstevel@tonic-gate 	/*
5736*7c478bd9Sstevel@tonic-gate 	**  Not already there or expired -- create a signature.
5737*7c478bd9Sstevel@tonic-gate 	*/
5738*7c478bd9Sstevel@tonic-gate 
5739*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
5740*7c478bd9Sstevel@tonic-gate 	if (ConfigLevel < 2)
5741*7c478bd9Sstevel@tonic-gate 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
5742*7c478bd9Sstevel@tonic-gate 
5743*7c478bd9Sstevel@tonic-gate 	for (hp = host; hp != NULL; hp = endp)
5744*7c478bd9Sstevel@tonic-gate 	{
5745*7c478bd9Sstevel@tonic-gate #if NETINET6
5746*7c478bd9Sstevel@tonic-gate 		if (*hp == '[')
5747*7c478bd9Sstevel@tonic-gate 		{
5748*7c478bd9Sstevel@tonic-gate 			endp = strchr(hp + 1, ']');
5749*7c478bd9Sstevel@tonic-gate 			if (endp != NULL)
5750*7c478bd9Sstevel@tonic-gate 				endp = strpbrk(endp + 1, ":,");
5751*7c478bd9Sstevel@tonic-gate 		}
5752*7c478bd9Sstevel@tonic-gate 		else
5753*7c478bd9Sstevel@tonic-gate 			endp = strpbrk(hp, ":,");
5754*7c478bd9Sstevel@tonic-gate #else /* NETINET6 */
5755*7c478bd9Sstevel@tonic-gate 		endp = strpbrk(hp, ":,");
5756*7c478bd9Sstevel@tonic-gate #endif /* NETINET6 */
5757*7c478bd9Sstevel@tonic-gate 		if (endp != NULL)
5758*7c478bd9Sstevel@tonic-gate 		{
5759*7c478bd9Sstevel@tonic-gate 			sep = *endp;
5760*7c478bd9Sstevel@tonic-gate 			*endp = '\0';
5761*7c478bd9Sstevel@tonic-gate 		}
5762*7c478bd9Sstevel@tonic-gate 
5763*7c478bd9Sstevel@tonic-gate 		if (bitnset(M_NOMX, m->m_flags))
5764*7c478bd9Sstevel@tonic-gate 		{
5765*7c478bd9Sstevel@tonic-gate 			/* skip MX lookups */
5766*7c478bd9Sstevel@tonic-gate 			nmx = 1;
5767*7c478bd9Sstevel@tonic-gate 			mxhosts[0] = hp;
5768*7c478bd9Sstevel@tonic-gate 		}
5769*7c478bd9Sstevel@tonic-gate 		else
5770*7c478bd9Sstevel@tonic-gate 		{
5771*7c478bd9Sstevel@tonic-gate 			auto int rcode;
5772*7c478bd9Sstevel@tonic-gate 			int ttl;
5773*7c478bd9Sstevel@tonic-gate 
5774*7c478bd9Sstevel@tonic-gate 			nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
5775*7c478bd9Sstevel@tonic-gate 				      &ttl);
5776*7c478bd9Sstevel@tonic-gate 			if (nmx <= 0)
5777*7c478bd9Sstevel@tonic-gate 			{
5778*7c478bd9Sstevel@tonic-gate 				int save_errno;
5779*7c478bd9Sstevel@tonic-gate 				register MCI *mci;
5780*7c478bd9Sstevel@tonic-gate 
5781*7c478bd9Sstevel@tonic-gate 				/* update the connection info for this host */
5782*7c478bd9Sstevel@tonic-gate 				save_errno = errno;
5783*7c478bd9Sstevel@tonic-gate 				mci = mci_get(hp, m);
5784*7c478bd9Sstevel@tonic-gate 				mci->mci_errno = save_errno;
5785*7c478bd9Sstevel@tonic-gate 				mci->mci_herrno = h_errno;
5786*7c478bd9Sstevel@tonic-gate 				mci->mci_lastuse = now;
5787*7c478bd9Sstevel@tonic-gate 				if (rcode == EX_NOHOST)
5788*7c478bd9Sstevel@tonic-gate 					mci_setstat(mci, rcode, "5.1.2",
5789*7c478bd9Sstevel@tonic-gate 						    "550 Host unknown");
5790*7c478bd9Sstevel@tonic-gate 				else
5791*7c478bd9Sstevel@tonic-gate 					mci_setstat(mci, rcode, NULL, NULL);
5792*7c478bd9Sstevel@tonic-gate 
5793*7c478bd9Sstevel@tonic-gate 				/* use the original host name as signature */
5794*7c478bd9Sstevel@tonic-gate 				nmx = 1;
5795*7c478bd9Sstevel@tonic-gate 				mxhosts[0] = hp;
5796*7c478bd9Sstevel@tonic-gate 			}
5797*7c478bd9Sstevel@tonic-gate 			if (tTd(17, 3))
5798*7c478bd9Sstevel@tonic-gate 				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
5799*7c478bd9Sstevel@tonic-gate 					   nmx, mxhosts[0]);
5800*7c478bd9Sstevel@tonic-gate 
5801*7c478bd9Sstevel@tonic-gate 			/*
5802*7c478bd9Sstevel@tonic-gate 			**  Set new TTL: we use only one!
5803*7c478bd9Sstevel@tonic-gate 			**	We could try to use the minimum instead.
5804*7c478bd9Sstevel@tonic-gate 			*/
5805*7c478bd9Sstevel@tonic-gate 
5806*7c478bd9Sstevel@tonic-gate 			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
5807*7c478bd9Sstevel@tonic-gate 		}
5808*7c478bd9Sstevel@tonic-gate 
5809*7c478bd9Sstevel@tonic-gate 		len = 0;
5810*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < nmx; i++)
5811*7c478bd9Sstevel@tonic-gate 			len += strlen(mxhosts[i]) + 1;
5812*7c478bd9Sstevel@tonic-gate 		if (s->s_hostsig.hs_sig != NULL)
5813*7c478bd9Sstevel@tonic-gate 			len += strlen(s->s_hostsig.hs_sig) + 1;
5814*7c478bd9Sstevel@tonic-gate 		if (len < 0 || len >= MAXHOSTSIGNATURE)
5815*7c478bd9Sstevel@tonic-gate 		{
5816*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
5817*7c478bd9Sstevel@tonic-gate 				  host, MAXHOSTSIGNATURE, len);
5818*7c478bd9Sstevel@tonic-gate 			len = MAXHOSTSIGNATURE;
5819*7c478bd9Sstevel@tonic-gate 		}
5820*7c478bd9Sstevel@tonic-gate 		p = sm_pmalloc_x(len);
5821*7c478bd9Sstevel@tonic-gate 		if (s->s_hostsig.hs_sig != NULL)
5822*7c478bd9Sstevel@tonic-gate 		{
5823*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
5824*7c478bd9Sstevel@tonic-gate 			sm_free(s->s_hostsig.hs_sig); /* XXX */
5825*7c478bd9Sstevel@tonic-gate 			s->s_hostsig.hs_sig = p;
5826*7c478bd9Sstevel@tonic-gate 			hl = strlen(p);
5827*7c478bd9Sstevel@tonic-gate 			p += hl;
5828*7c478bd9Sstevel@tonic-gate 			*p++ = prevsep;
5829*7c478bd9Sstevel@tonic-gate 			len -= hl + 1;
5830*7c478bd9Sstevel@tonic-gate 		}
5831*7c478bd9Sstevel@tonic-gate 		else
5832*7c478bd9Sstevel@tonic-gate 			s->s_hostsig.hs_sig = p;
5833*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < nmx; i++)
5834*7c478bd9Sstevel@tonic-gate 		{
5835*7c478bd9Sstevel@tonic-gate 			hl = strlen(mxhosts[i]);
5836*7c478bd9Sstevel@tonic-gate 			if (len - 1 < hl || len <= 1)
5837*7c478bd9Sstevel@tonic-gate 			{
5838*7c478bd9Sstevel@tonic-gate 				/* force to drop out of outer loop */
5839*7c478bd9Sstevel@tonic-gate 				len = -1;
5840*7c478bd9Sstevel@tonic-gate 				break;
5841*7c478bd9Sstevel@tonic-gate 			}
5842*7c478bd9Sstevel@tonic-gate 			if (i != 0)
5843*7c478bd9Sstevel@tonic-gate 			{
5844*7c478bd9Sstevel@tonic-gate 				if (mxprefs[i] == mxprefs[i - 1])
5845*7c478bd9Sstevel@tonic-gate 					*p++ = ',';
5846*7c478bd9Sstevel@tonic-gate 				else
5847*7c478bd9Sstevel@tonic-gate 					*p++ = ':';
5848*7c478bd9Sstevel@tonic-gate 				len--;
5849*7c478bd9Sstevel@tonic-gate 			}
5850*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(p, mxhosts[i], len);
5851*7c478bd9Sstevel@tonic-gate 			p += hl;
5852*7c478bd9Sstevel@tonic-gate 			len -= hl;
5853*7c478bd9Sstevel@tonic-gate 		}
5854*7c478bd9Sstevel@tonic-gate 
5855*7c478bd9Sstevel@tonic-gate 		/*
5856*7c478bd9Sstevel@tonic-gate 		**  break out of loop if len exceeded MAXHOSTSIGNATURE
5857*7c478bd9Sstevel@tonic-gate 		**  because we won't have more space for further hosts
5858*7c478bd9Sstevel@tonic-gate 		**  anyway (separated by : in the .cf file).
5859*7c478bd9Sstevel@tonic-gate 		*/
5860*7c478bd9Sstevel@tonic-gate 
5861*7c478bd9Sstevel@tonic-gate 		if (len < 0)
5862*7c478bd9Sstevel@tonic-gate 			break;
5863*7c478bd9Sstevel@tonic-gate 		if (endp != NULL)
5864*7c478bd9Sstevel@tonic-gate 			*endp++ = sep;
5865*7c478bd9Sstevel@tonic-gate 		prevsep = sep;
5866*7c478bd9Sstevel@tonic-gate 	}
5867*7c478bd9Sstevel@tonic-gate 	makelower(s->s_hostsig.hs_sig);
5868*7c478bd9Sstevel@tonic-gate 	if (ConfigLevel < 2)
5869*7c478bd9Sstevel@tonic-gate 		_res.options = oldoptions;
5870*7c478bd9Sstevel@tonic-gate #else /* NAMED_BIND */
5871*7c478bd9Sstevel@tonic-gate 	/* not using BIND -- the signature is just the host name */
5872*7c478bd9Sstevel@tonic-gate 	/*
5873*7c478bd9Sstevel@tonic-gate 	**  'host' points to storage that will be freed after we are
5874*7c478bd9Sstevel@tonic-gate 	**  done processing the current envelope, so we copy it.
5875*7c478bd9Sstevel@tonic-gate 	*/
5876*7c478bd9Sstevel@tonic-gate 	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
5877*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
5878*7c478bd9Sstevel@tonic-gate 	if (tTd(17, 1))
5879*7c478bd9Sstevel@tonic-gate 		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
5880*7c478bd9Sstevel@tonic-gate 	return s->s_hostsig.hs_sig;
5881*7c478bd9Sstevel@tonic-gate }
5882*7c478bd9Sstevel@tonic-gate /*
5883*7c478bd9Sstevel@tonic-gate **  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
5884*7c478bd9Sstevel@tonic-gate **
5885*7c478bd9Sstevel@tonic-gate **	The signature describes how we are going to send this -- it
5886*7c478bd9Sstevel@tonic-gate **	can be just the hostname (for non-Internet hosts) or can be
5887*7c478bd9Sstevel@tonic-gate **	an ordered list of MX hosts which must be randomized for equal
5888*7c478bd9Sstevel@tonic-gate **	MX preference values.
5889*7c478bd9Sstevel@tonic-gate **
5890*7c478bd9Sstevel@tonic-gate **	Parameters:
5891*7c478bd9Sstevel@tonic-gate **		sig -- the host signature.
5892*7c478bd9Sstevel@tonic-gate **		mxhosts -- array to populate.
5893*7c478bd9Sstevel@tonic-gate **		mailer -- mailer.
5894*7c478bd9Sstevel@tonic-gate **
5895*7c478bd9Sstevel@tonic-gate **	Returns:
5896*7c478bd9Sstevel@tonic-gate **		The number of hosts inserted into mxhosts array.
5897*7c478bd9Sstevel@tonic-gate **
5898*7c478bd9Sstevel@tonic-gate **	Side Effects:
5899*7c478bd9Sstevel@tonic-gate **		Randomizes equal MX preference hosts in mxhosts.
5900*7c478bd9Sstevel@tonic-gate */
5901*7c478bd9Sstevel@tonic-gate 
5902*7c478bd9Sstevel@tonic-gate static int
5903*7c478bd9Sstevel@tonic-gate parse_hostsignature(sig, mxhosts, mailer)
5904*7c478bd9Sstevel@tonic-gate 	char *sig;
5905*7c478bd9Sstevel@tonic-gate 	char **mxhosts;
5906*7c478bd9Sstevel@tonic-gate 	MAILER *mailer;
5907*7c478bd9Sstevel@tonic-gate {
5908*7c478bd9Sstevel@tonic-gate 	unsigned short curpref = 0;
5909*7c478bd9Sstevel@tonic-gate 	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
5910*7c478bd9Sstevel@tonic-gate 	char *hp, *endp;
5911*7c478bd9Sstevel@tonic-gate 	unsigned short prefer[MAXMXHOSTS];
5912*7c478bd9Sstevel@tonic-gate 	long rndm[MAXMXHOSTS];
5913*7c478bd9Sstevel@tonic-gate 
5914*7c478bd9Sstevel@tonic-gate 	for (hp = sig; hp != NULL; hp = endp)
5915*7c478bd9Sstevel@tonic-gate 	{
5916*7c478bd9Sstevel@tonic-gate 		char sep = ':';
5917*7c478bd9Sstevel@tonic-gate 
5918*7c478bd9Sstevel@tonic-gate #if NETINET6
5919*7c478bd9Sstevel@tonic-gate 		if (*hp == '[')
5920*7c478bd9Sstevel@tonic-gate 		{
5921*7c478bd9Sstevel@tonic-gate 			endp = strchr(hp + 1, ']');
5922*7c478bd9Sstevel@tonic-gate 			if (endp != NULL)
5923*7c478bd9Sstevel@tonic-gate 				endp = strpbrk(endp + 1, ":,");
5924*7c478bd9Sstevel@tonic-gate 		}
5925*7c478bd9Sstevel@tonic-gate 		else
5926*7c478bd9Sstevel@tonic-gate 			endp = strpbrk(hp, ":,");
5927*7c478bd9Sstevel@tonic-gate #else /* NETINET6 */
5928*7c478bd9Sstevel@tonic-gate 		endp = strpbrk(hp, ":,");
5929*7c478bd9Sstevel@tonic-gate #endif /* NETINET6 */
5930*7c478bd9Sstevel@tonic-gate 		if (endp != NULL)
5931*7c478bd9Sstevel@tonic-gate 		{
5932*7c478bd9Sstevel@tonic-gate 			sep = *endp;
5933*7c478bd9Sstevel@tonic-gate 			*endp = '\0';
5934*7c478bd9Sstevel@tonic-gate 		}
5935*7c478bd9Sstevel@tonic-gate 
5936*7c478bd9Sstevel@tonic-gate 		mxhosts[nmx] = hp;
5937*7c478bd9Sstevel@tonic-gate 		prefer[nmx] = curpref;
5938*7c478bd9Sstevel@tonic-gate 		if (mci_match(hp, mailer))
5939*7c478bd9Sstevel@tonic-gate 			rndm[nmx] = 0;
5940*7c478bd9Sstevel@tonic-gate 		else
5941*7c478bd9Sstevel@tonic-gate 			rndm[nmx] = get_random();
5942*7c478bd9Sstevel@tonic-gate 
5943*7c478bd9Sstevel@tonic-gate 		if (endp != NULL)
5944*7c478bd9Sstevel@tonic-gate 		{
5945*7c478bd9Sstevel@tonic-gate 			/*
5946*7c478bd9Sstevel@tonic-gate 			**  Since we don't have the original MX prefs,
5947*7c478bd9Sstevel@tonic-gate 			**  make our own.  If the separator is a ':', that
5948*7c478bd9Sstevel@tonic-gate 			**  means the preference for the next host will be
5949*7c478bd9Sstevel@tonic-gate 			**  higher than this one, so simply increment curpref.
5950*7c478bd9Sstevel@tonic-gate 			*/
5951*7c478bd9Sstevel@tonic-gate 
5952*7c478bd9Sstevel@tonic-gate 			if (sep == ':')
5953*7c478bd9Sstevel@tonic-gate 				curpref++;
5954*7c478bd9Sstevel@tonic-gate 
5955*7c478bd9Sstevel@tonic-gate 			*endp++ = sep;
5956*7c478bd9Sstevel@tonic-gate 		}
5957*7c478bd9Sstevel@tonic-gate 		if (++nmx >= MAXMXHOSTS)
5958*7c478bd9Sstevel@tonic-gate 			break;
5959*7c478bd9Sstevel@tonic-gate 	}
5960*7c478bd9Sstevel@tonic-gate 
5961*7c478bd9Sstevel@tonic-gate 	/* sort the records using the random factor for equal preferences */
5962*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nmx; i++)
5963*7c478bd9Sstevel@tonic-gate 	{
5964*7c478bd9Sstevel@tonic-gate 		for (j = i + 1; j < nmx; j++)
5965*7c478bd9Sstevel@tonic-gate 		{
5966*7c478bd9Sstevel@tonic-gate 			/*
5967*7c478bd9Sstevel@tonic-gate 			**  List is already sorted by MX preference, only
5968*7c478bd9Sstevel@tonic-gate 			**  need to look for equal preference MX records
5969*7c478bd9Sstevel@tonic-gate 			*/
5970*7c478bd9Sstevel@tonic-gate 
5971*7c478bd9Sstevel@tonic-gate 			if (prefer[i] < prefer[j])
5972*7c478bd9Sstevel@tonic-gate 				break;
5973*7c478bd9Sstevel@tonic-gate 
5974*7c478bd9Sstevel@tonic-gate 			if (prefer[i] > prefer[j] ||
5975*7c478bd9Sstevel@tonic-gate 			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
5976*7c478bd9Sstevel@tonic-gate 			{
5977*7c478bd9Sstevel@tonic-gate 				register unsigned short tempp;
5978*7c478bd9Sstevel@tonic-gate 				register long tempr;
5979*7c478bd9Sstevel@tonic-gate 				register char *temp1;
5980*7c478bd9Sstevel@tonic-gate 
5981*7c478bd9Sstevel@tonic-gate 				tempp = prefer[i];
5982*7c478bd9Sstevel@tonic-gate 				prefer[i] = prefer[j];
5983*7c478bd9Sstevel@tonic-gate 				prefer[j] = tempp;
5984*7c478bd9Sstevel@tonic-gate 				temp1 = mxhosts[i];
5985*7c478bd9Sstevel@tonic-gate 				mxhosts[i] = mxhosts[j];
5986*7c478bd9Sstevel@tonic-gate 				mxhosts[j] = temp1;
5987*7c478bd9Sstevel@tonic-gate 				tempr = rndm[i];
5988*7c478bd9Sstevel@tonic-gate 				rndm[i] = rndm[j];
5989*7c478bd9Sstevel@tonic-gate 				rndm[j] = tempr;
5990*7c478bd9Sstevel@tonic-gate 			}
5991*7c478bd9Sstevel@tonic-gate 		}
5992*7c478bd9Sstevel@tonic-gate 	}
5993*7c478bd9Sstevel@tonic-gate 	return nmx;
5994*7c478bd9Sstevel@tonic-gate }
5995*7c478bd9Sstevel@tonic-gate 
5996*7c478bd9Sstevel@tonic-gate # if STARTTLS
5997*7c478bd9Sstevel@tonic-gate static SSL_CTX	*clt_ctx = NULL;
5998*7c478bd9Sstevel@tonic-gate static bool	tls_ok_clt = true;
5999*7c478bd9Sstevel@tonic-gate 
6000*7c478bd9Sstevel@tonic-gate /*
6001*7c478bd9Sstevel@tonic-gate **  SETCLTTLS -- client side TLS: allow/disallow.
6002*7c478bd9Sstevel@tonic-gate **
6003*7c478bd9Sstevel@tonic-gate **	Parameters:
6004*7c478bd9Sstevel@tonic-gate **		tls_ok -- should tls be done?
6005*7c478bd9Sstevel@tonic-gate **
6006*7c478bd9Sstevel@tonic-gate **	Returns:
6007*7c478bd9Sstevel@tonic-gate **		none.
6008*7c478bd9Sstevel@tonic-gate **
6009*7c478bd9Sstevel@tonic-gate **	Side Effects:
6010*7c478bd9Sstevel@tonic-gate **		sets tls_ok_clt (static variable in this module)
6011*7c478bd9Sstevel@tonic-gate */
6012*7c478bd9Sstevel@tonic-gate 
6013*7c478bd9Sstevel@tonic-gate void
6014*7c478bd9Sstevel@tonic-gate setclttls(tls_ok)
6015*7c478bd9Sstevel@tonic-gate 	bool tls_ok;
6016*7c478bd9Sstevel@tonic-gate {
6017*7c478bd9Sstevel@tonic-gate 	tls_ok_clt = tls_ok;
6018*7c478bd9Sstevel@tonic-gate 	return;
6019*7c478bd9Sstevel@tonic-gate }
6020*7c478bd9Sstevel@tonic-gate /*
6021*7c478bd9Sstevel@tonic-gate **  INITCLTTLS -- initialize client side TLS
6022*7c478bd9Sstevel@tonic-gate **
6023*7c478bd9Sstevel@tonic-gate **	Parameters:
6024*7c478bd9Sstevel@tonic-gate **		tls_ok -- should tls initialization be done?
6025*7c478bd9Sstevel@tonic-gate **
6026*7c478bd9Sstevel@tonic-gate **	Returns:
6027*7c478bd9Sstevel@tonic-gate **		succeeded?
6028*7c478bd9Sstevel@tonic-gate **
6029*7c478bd9Sstevel@tonic-gate **	Side Effects:
6030*7c478bd9Sstevel@tonic-gate **		sets tls_ok_clt (static variable in this module)
6031*7c478bd9Sstevel@tonic-gate */
6032*7c478bd9Sstevel@tonic-gate 
6033*7c478bd9Sstevel@tonic-gate bool
6034*7c478bd9Sstevel@tonic-gate initclttls(tls_ok)
6035*7c478bd9Sstevel@tonic-gate 	bool tls_ok;
6036*7c478bd9Sstevel@tonic-gate {
6037*7c478bd9Sstevel@tonic-gate 	if (!tls_ok_clt)
6038*7c478bd9Sstevel@tonic-gate 		return false;
6039*7c478bd9Sstevel@tonic-gate 	tls_ok_clt = tls_ok;
6040*7c478bd9Sstevel@tonic-gate 	if (!tls_ok_clt)
6041*7c478bd9Sstevel@tonic-gate 		return false;
6042*7c478bd9Sstevel@tonic-gate 	if (clt_ctx != NULL)
6043*7c478bd9Sstevel@tonic-gate 		return true;	/* already done */
6044*7c478bd9Sstevel@tonic-gate 	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile,
6045*7c478bd9Sstevel@tonic-gate 			     CltKeyFile, CACertPath, CACertFile, DHParams);
6046*7c478bd9Sstevel@tonic-gate 	return tls_ok_clt;
6047*7c478bd9Sstevel@tonic-gate }
6048*7c478bd9Sstevel@tonic-gate 
6049*7c478bd9Sstevel@tonic-gate /*
6050*7c478bd9Sstevel@tonic-gate **  STARTTLS -- try to start secure connection (client side)
6051*7c478bd9Sstevel@tonic-gate **
6052*7c478bd9Sstevel@tonic-gate **	Parameters:
6053*7c478bd9Sstevel@tonic-gate **		m -- the mailer.
6054*7c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
6055*7c478bd9Sstevel@tonic-gate **		e -- the envelope.
6056*7c478bd9Sstevel@tonic-gate **
6057*7c478bd9Sstevel@tonic-gate **	Returns:
6058*7c478bd9Sstevel@tonic-gate **		success?
6059*7c478bd9Sstevel@tonic-gate **		(maybe this should be some other code than EX_
6060*7c478bd9Sstevel@tonic-gate **		that denotes which stage failed.)
6061*7c478bd9Sstevel@tonic-gate */
6062*7c478bd9Sstevel@tonic-gate 
6063*7c478bd9Sstevel@tonic-gate static int
6064*7c478bd9Sstevel@tonic-gate starttls(m, mci, e)
6065*7c478bd9Sstevel@tonic-gate 	MAILER *m;
6066*7c478bd9Sstevel@tonic-gate 	MCI *mci;
6067*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
6068*7c478bd9Sstevel@tonic-gate {
6069*7c478bd9Sstevel@tonic-gate 	int smtpresult;
6070*7c478bd9Sstevel@tonic-gate 	int result = 0;
6071*7c478bd9Sstevel@tonic-gate 	int rfd, wfd;
6072*7c478bd9Sstevel@tonic-gate 	SSL *clt_ssl = NULL;
6073*7c478bd9Sstevel@tonic-gate 	time_t tlsstart;
6074*7c478bd9Sstevel@tonic-gate 
6075*7c478bd9Sstevel@tonic-gate 	if (clt_ctx == NULL && !initclttls(true))
6076*7c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
6077*7c478bd9Sstevel@tonic-gate 	smtpmessage("STARTTLS", m, mci);
6078*7c478bd9Sstevel@tonic-gate 
6079*7c478bd9Sstevel@tonic-gate 	/* get the reply */
6080*7c478bd9Sstevel@tonic-gate 	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
6081*7c478bd9Sstevel@tonic-gate 			XS_STARTTLS);
6082*7c478bd9Sstevel@tonic-gate 
6083*7c478bd9Sstevel@tonic-gate 	/* check return code from server */
6084*7c478bd9Sstevel@tonic-gate 	if (smtpresult == 454)
6085*7c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
6086*7c478bd9Sstevel@tonic-gate 	if (smtpresult == 501)
6087*7c478bd9Sstevel@tonic-gate 		return EX_USAGE;
6088*7c478bd9Sstevel@tonic-gate 	if (smtpresult == -1)
6089*7c478bd9Sstevel@tonic-gate 		return smtpresult;
6090*7c478bd9Sstevel@tonic-gate 	if (smtpresult != 220)
6091*7c478bd9Sstevel@tonic-gate 		return EX_PROTOCOL;
6092*7c478bd9Sstevel@tonic-gate 
6093*7c478bd9Sstevel@tonic-gate 	if (LogLevel > 13)
6094*7c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
6095*7c478bd9Sstevel@tonic-gate 
6096*7c478bd9Sstevel@tonic-gate 	/* start connection */
6097*7c478bd9Sstevel@tonic-gate 	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
6098*7c478bd9Sstevel@tonic-gate 	{
6099*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 5)
6100*7c478bd9Sstevel@tonic-gate 		{
6101*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, NOQID,
6102*7c478bd9Sstevel@tonic-gate 				  "STARTTLS=client, error: SSL_new failed");
6103*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 9)
6104*7c478bd9Sstevel@tonic-gate 				tlslogerr("client");
6105*7c478bd9Sstevel@tonic-gate 		}
6106*7c478bd9Sstevel@tonic-gate 		return EX_SOFTWARE;
6107*7c478bd9Sstevel@tonic-gate 	}
6108*7c478bd9Sstevel@tonic-gate 
6109*7c478bd9Sstevel@tonic-gate 	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
6110*7c478bd9Sstevel@tonic-gate 	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
6111*7c478bd9Sstevel@tonic-gate 
6112*7c478bd9Sstevel@tonic-gate 	/* SSL_clear(clt_ssl); ? */
6113*7c478bd9Sstevel@tonic-gate 	if (rfd < 0 || wfd < 0 ||
6114*7c478bd9Sstevel@tonic-gate 	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
6115*7c478bd9Sstevel@tonic-gate 	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
6116*7c478bd9Sstevel@tonic-gate 	{
6117*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 5)
6118*7c478bd9Sstevel@tonic-gate 		{
6119*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, NOQID,
6120*7c478bd9Sstevel@tonic-gate 				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
6121*7c478bd9Sstevel@tonic-gate 				  result);
6122*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 9)
6123*7c478bd9Sstevel@tonic-gate 				tlslogerr("client");
6124*7c478bd9Sstevel@tonic-gate 		}
6125*7c478bd9Sstevel@tonic-gate 		return EX_SOFTWARE;
6126*7c478bd9Sstevel@tonic-gate 	}
6127*7c478bd9Sstevel@tonic-gate 	SSL_set_connect_state(clt_ssl);
6128*7c478bd9Sstevel@tonic-gate 	tlsstart = curtime();
6129*7c478bd9Sstevel@tonic-gate 
6130*7c478bd9Sstevel@tonic-gate ssl_retry:
6131*7c478bd9Sstevel@tonic-gate 	if ((result = SSL_connect(clt_ssl)) <= 0)
6132*7c478bd9Sstevel@tonic-gate 	{
6133*7c478bd9Sstevel@tonic-gate 		int i;
6134*7c478bd9Sstevel@tonic-gate 		bool timedout;
6135*7c478bd9Sstevel@tonic-gate 		time_t left;
6136*7c478bd9Sstevel@tonic-gate 		time_t now = curtime();
6137*7c478bd9Sstevel@tonic-gate 		struct timeval tv;
6138*7c478bd9Sstevel@tonic-gate 
6139*7c478bd9Sstevel@tonic-gate 		/* what to do in this case? */
6140*7c478bd9Sstevel@tonic-gate 		i = SSL_get_error(clt_ssl, result);
6141*7c478bd9Sstevel@tonic-gate 
6142*7c478bd9Sstevel@tonic-gate 		/*
6143*7c478bd9Sstevel@tonic-gate 		**  For SSL_ERROR_WANT_{READ,WRITE}:
6144*7c478bd9Sstevel@tonic-gate 		**  There is not a complete SSL record available yet
6145*7c478bd9Sstevel@tonic-gate 		**  or there is only a partial SSL record removed from
6146*7c478bd9Sstevel@tonic-gate 		**  the network (socket) buffer into the SSL buffer.
6147*7c478bd9Sstevel@tonic-gate 		**  The SSL_connect will only succeed when a full
6148*7c478bd9Sstevel@tonic-gate 		**  SSL record is available (assuming a "real" error
6149*7c478bd9Sstevel@tonic-gate 		**  doesn't happen). To handle when a "real" error
6150*7c478bd9Sstevel@tonic-gate 		**  does happen the select is set for exceptions too.
6151*7c478bd9Sstevel@tonic-gate 		**  The connection may be re-negotiated during this time
6152*7c478bd9Sstevel@tonic-gate 		**  so both read and write "want errors" need to be handled.
6153*7c478bd9Sstevel@tonic-gate 		**  A select() exception loops back so that a proper SSL
6154*7c478bd9Sstevel@tonic-gate 		**  error message can be gotten.
6155*7c478bd9Sstevel@tonic-gate 		*/
6156*7c478bd9Sstevel@tonic-gate 
6157*7c478bd9Sstevel@tonic-gate 		left = TimeOuts.to_starttls - (now - tlsstart);
6158*7c478bd9Sstevel@tonic-gate 		timedout = left <= 0;
6159*7c478bd9Sstevel@tonic-gate 		if (!timedout)
6160*7c478bd9Sstevel@tonic-gate 		{
6161*7c478bd9Sstevel@tonic-gate 			tv.tv_sec = left;
6162*7c478bd9Sstevel@tonic-gate 			tv.tv_usec = 0;
6163*7c478bd9Sstevel@tonic-gate 		}
6164*7c478bd9Sstevel@tonic-gate 
6165*7c478bd9Sstevel@tonic-gate 		if (!timedout && FD_SETSIZE > 0 &&
6166*7c478bd9Sstevel@tonic-gate 		    (rfd >= FD_SETSIZE ||
6167*7c478bd9Sstevel@tonic-gate 		     (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
6168*7c478bd9Sstevel@tonic-gate 		{
6169*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
6170*7c478bd9Sstevel@tonic-gate 			{
6171*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
6172*7c478bd9Sstevel@tonic-gate 					  "STARTTLS=client, error: fd %d/%d too large",
6173*7c478bd9Sstevel@tonic-gate 					  rfd, wfd);
6174*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
6175*7c478bd9Sstevel@tonic-gate 				tlslogerr("client");
6176*7c478bd9Sstevel@tonic-gate 			}
6177*7c478bd9Sstevel@tonic-gate 			errno = EINVAL;
6178*7c478bd9Sstevel@tonic-gate 			goto tlsfail;
6179*7c478bd9Sstevel@tonic-gate 		}
6180*7c478bd9Sstevel@tonic-gate 		if (!timedout && i == SSL_ERROR_WANT_READ)
6181*7c478bd9Sstevel@tonic-gate 		{
6182*7c478bd9Sstevel@tonic-gate 			fd_set ssl_maskr, ssl_maskx;
6183*7c478bd9Sstevel@tonic-gate 
6184*7c478bd9Sstevel@tonic-gate 			FD_ZERO(&ssl_maskr);
6185*7c478bd9Sstevel@tonic-gate 			FD_SET(rfd, &ssl_maskr);
6186*7c478bd9Sstevel@tonic-gate 			FD_ZERO(&ssl_maskx);
6187*7c478bd9Sstevel@tonic-gate 			FD_SET(rfd, &ssl_maskx);
6188*7c478bd9Sstevel@tonic-gate 			if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
6189*7c478bd9Sstevel@tonic-gate 			    > 0)
6190*7c478bd9Sstevel@tonic-gate 				goto ssl_retry;
6191*7c478bd9Sstevel@tonic-gate 		}
6192*7c478bd9Sstevel@tonic-gate 		if (!timedout && i == SSL_ERROR_WANT_WRITE)
6193*7c478bd9Sstevel@tonic-gate 		{
6194*7c478bd9Sstevel@tonic-gate 			fd_set ssl_maskw, ssl_maskx;
6195*7c478bd9Sstevel@tonic-gate 
6196*7c478bd9Sstevel@tonic-gate 			FD_ZERO(&ssl_maskw);
6197*7c478bd9Sstevel@tonic-gate 			FD_SET(wfd, &ssl_maskw);
6198*7c478bd9Sstevel@tonic-gate 			FD_ZERO(&ssl_maskx);
6199*7c478bd9Sstevel@tonic-gate 			FD_SET(rfd, &ssl_maskx);
6200*7c478bd9Sstevel@tonic-gate 			if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
6201*7c478bd9Sstevel@tonic-gate 			    > 0)
6202*7c478bd9Sstevel@tonic-gate 				goto ssl_retry;
6203*7c478bd9Sstevel@tonic-gate 		}
6204*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 5)
6205*7c478bd9Sstevel@tonic-gate 		{
6206*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, e->e_id,
6207*7c478bd9Sstevel@tonic-gate 				  "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d",
6208*7c478bd9Sstevel@tonic-gate 				  result, i, (int) timedout, errno);
6209*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
6210*7c478bd9Sstevel@tonic-gate 				tlslogerr("client");
6211*7c478bd9Sstevel@tonic-gate 		}
6212*7c478bd9Sstevel@tonic-gate tlsfail:
6213*7c478bd9Sstevel@tonic-gate 		SSL_free(clt_ssl);
6214*7c478bd9Sstevel@tonic-gate 		clt_ssl = NULL;
6215*7c478bd9Sstevel@tonic-gate 		return EX_SOFTWARE;
6216*7c478bd9Sstevel@tonic-gate 	}
6217*7c478bd9Sstevel@tonic-gate 	mci->mci_ssl = clt_ssl;
6218*7c478bd9Sstevel@tonic-gate 	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
6219*7c478bd9Sstevel@tonic-gate 			      &mci->mci_macro, true);
6220*7c478bd9Sstevel@tonic-gate 
6221*7c478bd9Sstevel@tonic-gate 	/* switch to use TLS... */
6222*7c478bd9Sstevel@tonic-gate 	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
6223*7c478bd9Sstevel@tonic-gate 		return EX_OK;
6224*7c478bd9Sstevel@tonic-gate 
6225*7c478bd9Sstevel@tonic-gate 	/* failure */
6226*7c478bd9Sstevel@tonic-gate 	SSL_free(clt_ssl);
6227*7c478bd9Sstevel@tonic-gate 	clt_ssl = NULL;
6228*7c478bd9Sstevel@tonic-gate 	return EX_SOFTWARE;
6229*7c478bd9Sstevel@tonic-gate }
6230*7c478bd9Sstevel@tonic-gate /*
6231*7c478bd9Sstevel@tonic-gate **  ENDTLSCLT -- shutdown secure connection (client side)
6232*7c478bd9Sstevel@tonic-gate **
6233*7c478bd9Sstevel@tonic-gate **	Parameters:
6234*7c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
6235*7c478bd9Sstevel@tonic-gate **
6236*7c478bd9Sstevel@tonic-gate **	Returns:
6237*7c478bd9Sstevel@tonic-gate **		success?
6238*7c478bd9Sstevel@tonic-gate */
6239*7c478bd9Sstevel@tonic-gate 
6240*7c478bd9Sstevel@tonic-gate static int
6241*7c478bd9Sstevel@tonic-gate endtlsclt(mci)
6242*7c478bd9Sstevel@tonic-gate 	MCI *mci;
6243*7c478bd9Sstevel@tonic-gate {
6244*7c478bd9Sstevel@tonic-gate 	int r;
6245*7c478bd9Sstevel@tonic-gate 
6246*7c478bd9Sstevel@tonic-gate 	if (!bitset(MCIF_TLSACT, mci->mci_flags))
6247*7c478bd9Sstevel@tonic-gate 		return EX_OK;
6248*7c478bd9Sstevel@tonic-gate 	r = endtls(mci->mci_ssl, "client");
6249*7c478bd9Sstevel@tonic-gate 	mci->mci_flags &= ~MCIF_TLSACT;
6250*7c478bd9Sstevel@tonic-gate 	return r;
6251*7c478bd9Sstevel@tonic-gate }
6252*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */
6253*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL
6254*7c478bd9Sstevel@tonic-gate /*
6255*7c478bd9Sstevel@tonic-gate **  ISCLTFLGSET -- check whether client flag is set.
6256*7c478bd9Sstevel@tonic-gate **
6257*7c478bd9Sstevel@tonic-gate **	Parameters:
6258*7c478bd9Sstevel@tonic-gate **		e -- envelope.
6259*7c478bd9Sstevel@tonic-gate **		flag -- flag to check in {client_flags}
6260*7c478bd9Sstevel@tonic-gate **
6261*7c478bd9Sstevel@tonic-gate **	Returns:
6262*7c478bd9Sstevel@tonic-gate **		true iff flag is set.
6263*7c478bd9Sstevel@tonic-gate */
6264*7c478bd9Sstevel@tonic-gate 
6265*7c478bd9Sstevel@tonic-gate static bool
6266*7c478bd9Sstevel@tonic-gate iscltflgset(e, flag)
6267*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
6268*7c478bd9Sstevel@tonic-gate 	int flag;
6269*7c478bd9Sstevel@tonic-gate {
6270*7c478bd9Sstevel@tonic-gate 	char *p;
6271*7c478bd9Sstevel@tonic-gate 
6272*7c478bd9Sstevel@tonic-gate 	p = macvalue(macid("{client_flags}"), e);
6273*7c478bd9Sstevel@tonic-gate 	if (p == NULL)
6274*7c478bd9Sstevel@tonic-gate 		return false;
6275*7c478bd9Sstevel@tonic-gate 	for (; *p != '\0'; p++)
6276*7c478bd9Sstevel@tonic-gate 	{
6277*7c478bd9Sstevel@tonic-gate 		/* look for just this one flag */
6278*7c478bd9Sstevel@tonic-gate 		if (*p == (char) flag)
6279*7c478bd9Sstevel@tonic-gate 			return true;
6280*7c478bd9Sstevel@tonic-gate 	}
6281*7c478bd9Sstevel@tonic-gate 	return false;
6282*7c478bd9Sstevel@tonic-gate }
6283*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */
6284