xref: /freebsd/contrib/sendmail/src/deliver.c (revision 5b0945b57059d1cde0831d3afea7ec56c7d79508)
1c2aa98e2SPeter Wemm /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 1998-2010, 2012 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
154e4196cbSGregory Neil Shapiro #include <sm/time.h>
1606f25ae9SGregory Neil Shapiro 
174313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 ca Exp $")
18c2aa98e2SPeter Wemm 
19c2aa98e2SPeter Wemm #if HASSETUSERCONTEXT
20c2aa98e2SPeter Wemm # include <login_cap.h>
21*5b0945b5SGregory Neil Shapiro #endif
2206f25ae9SGregory Neil Shapiro 
23605302a5SGregory Neil Shapiro #if NETINET || NETINET6
24605302a5SGregory Neil Shapiro # include <arpa/inet.h>
25*5b0945b5SGregory Neil Shapiro #endif
26605302a5SGregory Neil Shapiro 
2740266059SGregory Neil Shapiro #if STARTTLS || SASL
2806f25ae9SGregory Neil Shapiro # include "sfsasl.h"
29*5b0945b5SGregory Neil Shapiro # include "tls.h"
30*5b0945b5SGregory Neil Shapiro #endif
3106f25ae9SGregory Neil Shapiro 
3206f25ae9SGregory Neil Shapiro static int	deliver __P((ENVELOPE *, ADDRESS *));
3306f25ae9SGregory Neil Shapiro static void	dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
34b6bacd31SGregory Neil Shapiro static void	mailfiletimeout __P((int));
35b6bacd31SGregory Neil Shapiro static void	endwaittimeout __P((int));
3606f25ae9SGregory Neil Shapiro static int	parse_hostsignature __P((char *, char **, MAILER *));
3706f25ae9SGregory Neil Shapiro static void	sendenvelope __P((ENVELOPE *, int));
3840266059SGregory Neil Shapiro static int	coloncmp __P((const char *, const char *));
39c2aa98e2SPeter Wemm 
4006f25ae9SGregory Neil Shapiro #if STARTTLS
41ba00ec3dSGregory Neil Shapiro # include <openssl/err.h>
42*5b0945b5SGregory Neil Shapiro # if DANE
43*5b0945b5SGregory Neil Shapiro static int	starttls __P((MAILER *, MCI *, ENVELOPE *, dane_vrfy_ctx_P));
44*5b0945b5SGregory Neil Shapiro # else
4506f25ae9SGregory Neil Shapiro static int	starttls __P((MAILER *, MCI *, ENVELOPE *));
46*5b0945b5SGregory Neil Shapiro # endif
4740266059SGregory Neil Shapiro static int	endtlsclt __P((MCI *));
4806f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
4940266059SGregory Neil Shapiro #if STARTTLS || SASL
5040266059SGregory Neil Shapiro static bool	iscltflgset __P((ENVELOPE *, int));
51*5b0945b5SGregory Neil Shapiro #endif
52*5b0945b5SGregory Neil Shapiro 
53*5b0945b5SGregory Neil Shapiro #if _FFR_OCC
54*5b0945b5SGregory Neil Shapiro # include <ratectrl.h>
55*5b0945b5SGregory Neil Shapiro #endif
56c2aa98e2SPeter Wemm 
57c2aa98e2SPeter Wemm /*
58c2aa98e2SPeter Wemm **  SENDALL -- actually send all the messages.
59c2aa98e2SPeter Wemm **
60c2aa98e2SPeter Wemm **	Parameters:
61c2aa98e2SPeter Wemm **		e -- the envelope to send.
62c2aa98e2SPeter Wemm **		mode -- the delivery mode to use.  If SM_DEFAULT, use
63c2aa98e2SPeter Wemm **			the current e->e_sendmode.
64c2aa98e2SPeter Wemm **
65c2aa98e2SPeter Wemm **	Returns:
66c2aa98e2SPeter Wemm **		none.
67c2aa98e2SPeter Wemm **
68c2aa98e2SPeter Wemm **	Side Effects:
69c2aa98e2SPeter Wemm **		Scans the send lists and sends everything it finds.
70c2aa98e2SPeter Wemm **		Delivers any appropriate error messages.
71c2aa98e2SPeter Wemm **		If we are running in a non-interactive mode, takes the
72c2aa98e2SPeter Wemm **			appropriate action.
73c2aa98e2SPeter Wemm */
74c2aa98e2SPeter Wemm 
75c2aa98e2SPeter Wemm void
76c2aa98e2SPeter Wemm sendall(e, mode)
77c2aa98e2SPeter Wemm 	ENVELOPE *e;
78c2aa98e2SPeter Wemm 	int mode;
79c2aa98e2SPeter Wemm {
80c2aa98e2SPeter Wemm 	register ADDRESS *q;
81c2aa98e2SPeter Wemm 	char *owner;
82c2aa98e2SPeter Wemm 	int otherowners;
8306f25ae9SGregory Neil Shapiro 	int save_errno;
84c2aa98e2SPeter Wemm 	register ENVELOPE *ee;
85c2aa98e2SPeter Wemm 	ENVELOPE *splitenv = NULL;
86c2aa98e2SPeter Wemm 	int oldverbose = Verbose;
8740266059SGregory Neil Shapiro 	bool somedeliveries = false, expensive = false;
88c2aa98e2SPeter Wemm 	pid_t pid;
89c2aa98e2SPeter Wemm 
90c2aa98e2SPeter Wemm 	/*
91c2aa98e2SPeter Wemm 	**  If this message is to be discarded, don't bother sending
92c2aa98e2SPeter Wemm 	**  the message at all.
93c2aa98e2SPeter Wemm 	*/
94c2aa98e2SPeter Wemm 
95c2aa98e2SPeter Wemm 	if (bitset(EF_DISCARD, e->e_flags))
96c2aa98e2SPeter Wemm 	{
97c2aa98e2SPeter Wemm 		if (tTd(13, 1))
9840266059SGregory Neil Shapiro 			sm_dprintf("sendall: discarding id %s\n", e->e_id);
99c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
10040266059SGregory Neil Shapiro 		if (LogLevel > 9)
10140266059SGregory Neil Shapiro 			logundelrcpts(e, "discarded", 9, true);
10240266059SGregory Neil Shapiro 		else if (LogLevel > 4)
103c2aa98e2SPeter Wemm 			sm_syslog(LOG_INFO, e->e_id, "discarded");
10440266059SGregory Neil Shapiro 		markstats(e, NULL, STATS_REJECT);
105c2aa98e2SPeter Wemm 		return;
106c2aa98e2SPeter Wemm 	}
107c2aa98e2SPeter Wemm 
108c2aa98e2SPeter Wemm 	/*
109c2aa98e2SPeter Wemm 	**  If we have had global, fatal errors, don't bother sending
110c2aa98e2SPeter Wemm 	**  the message at all if we are in SMTP mode.  Local errors
111c2aa98e2SPeter Wemm 	**  (e.g., a single address failing) will still cause the other
112c2aa98e2SPeter Wemm 	**  addresses to be sent.
113c2aa98e2SPeter Wemm 	*/
114c2aa98e2SPeter Wemm 
115c2aa98e2SPeter Wemm 	if (bitset(EF_FATALERRS, e->e_flags) &&
116c2aa98e2SPeter Wemm 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
117c2aa98e2SPeter Wemm 	{
118c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
119c2aa98e2SPeter Wemm 		return;
120c2aa98e2SPeter Wemm 	}
121c2aa98e2SPeter Wemm 
122c2aa98e2SPeter Wemm 	/* determine actual delivery mode */
123c2aa98e2SPeter Wemm 	if (mode == SM_DEFAULT)
124c2aa98e2SPeter Wemm 	{
125c2aa98e2SPeter Wemm 		mode = e->e_sendmode;
126c2aa98e2SPeter Wemm 		if (mode != SM_VERIFY && mode != SM_DEFER &&
127c2aa98e2SPeter Wemm 		    shouldqueue(e->e_msgpriority, e->e_ctime))
128c2aa98e2SPeter Wemm 			mode = SM_QUEUE;
129c2aa98e2SPeter Wemm 	}
130c2aa98e2SPeter Wemm 
131c2aa98e2SPeter Wemm 	if (tTd(13, 1))
132c2aa98e2SPeter Wemm 	{
13340266059SGregory Neil Shapiro 		sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
134c2aa98e2SPeter Wemm 			mode, e->e_id);
135e92d3f3fSGregory Neil Shapiro 		printaddr(sm_debug_file(), &e->e_from, false);
13640266059SGregory Neil Shapiro 		sm_dprintf("\te_flags = ");
137c2aa98e2SPeter Wemm 		printenvflags(e);
13840266059SGregory Neil Shapiro 		sm_dprintf("sendqueue:\n");
139e92d3f3fSGregory Neil Shapiro 		printaddr(sm_debug_file(), e->e_sendqueue, true);
140c2aa98e2SPeter Wemm 	}
141c2aa98e2SPeter Wemm 
142c2aa98e2SPeter Wemm 	/*
143c2aa98e2SPeter Wemm 	**  Do any preprocessing necessary for the mode we are running.
144c2aa98e2SPeter Wemm 	**	Check to make sure the hop count is reasonable.
145c2aa98e2SPeter Wemm 	**	Delete sends to the sender in mailing lists.
146c2aa98e2SPeter Wemm 	*/
147c2aa98e2SPeter Wemm 
148c2aa98e2SPeter Wemm 	CurEnv = e;
149c2aa98e2SPeter Wemm 	if (tTd(62, 1))
150c2aa98e2SPeter Wemm 		checkfds(NULL);
151c2aa98e2SPeter Wemm 
152c2aa98e2SPeter Wemm 	if (e->e_hopcount > MaxHopCount)
153c2aa98e2SPeter Wemm 	{
1548774250cSGregory Neil Shapiro 		char *recip;
1558774250cSGregory Neil Shapiro 
1568774250cSGregory Neil Shapiro 		if (e->e_sendqueue != NULL &&
1578774250cSGregory Neil Shapiro 		    e->e_sendqueue->q_paddr != NULL)
1588774250cSGregory Neil Shapiro 			recip = e->e_sendqueue->q_paddr;
1598774250cSGregory Neil Shapiro 		else
1608774250cSGregory Neil Shapiro 			recip = "(nobody)";
1618774250cSGregory Neil Shapiro 
162c2aa98e2SPeter Wemm 		errno = 0;
16340266059SGregory Neil Shapiro 		queueup(e, WILL_BE_QUEUED(mode), false);
164c2aa98e2SPeter Wemm 		e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
16506f25ae9SGregory Neil Shapiro 		ExitStat = EX_UNAVAILABLE;
1668774250cSGregory Neil Shapiro 		syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
167c2aa98e2SPeter Wemm 		       e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
168c2aa98e2SPeter Wemm 		       RealHostName == NULL ? "localhost" : RealHostName,
1698774250cSGregory Neil Shapiro 		       recip);
17006f25ae9SGregory Neil Shapiro 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
17106f25ae9SGregory Neil Shapiro 		{
17206f25ae9SGregory Neil Shapiro 			if (QS_IS_DEAD(q->q_state))
17306f25ae9SGregory Neil Shapiro 				continue;
17406f25ae9SGregory Neil Shapiro 			q->q_state = QS_BADADDR;
17506f25ae9SGregory Neil Shapiro 			q->q_status = "5.4.6";
1768774250cSGregory Neil Shapiro 			q->q_rstatus = "554 5.4.6 Too many hops";
17706f25ae9SGregory Neil Shapiro 		}
178c2aa98e2SPeter Wemm 		return;
179c2aa98e2SPeter Wemm 	}
180c2aa98e2SPeter Wemm 
181c2aa98e2SPeter Wemm 	/*
182c2aa98e2SPeter Wemm 	**  Do sender deletion.
183c2aa98e2SPeter Wemm 	**
18406f25ae9SGregory Neil Shapiro 	**	If the sender should be queued up, skip this.
185c2aa98e2SPeter Wemm 	**	This can happen if the name server is hosed when you
186c2aa98e2SPeter Wemm 	**	are trying to send mail.  The result is that the sender
187c2aa98e2SPeter Wemm 	**	is instantiated in the queue as a recipient.
188c2aa98e2SPeter Wemm 	*/
189c2aa98e2SPeter Wemm 
190c2aa98e2SPeter Wemm 	if (!bitset(EF_METOO, e->e_flags) &&
19106f25ae9SGregory Neil Shapiro 	    !QS_IS_QUEUEUP(e->e_from.q_state))
192c2aa98e2SPeter Wemm 	{
193c2aa98e2SPeter Wemm 		if (tTd(13, 5))
194c2aa98e2SPeter Wemm 		{
19540266059SGregory Neil Shapiro 			sm_dprintf("sendall: QS_SENDER ");
196e92d3f3fSGregory Neil Shapiro 			printaddr(sm_debug_file(), &e->e_from, false);
197c2aa98e2SPeter Wemm 		}
19806f25ae9SGregory Neil Shapiro 		e->e_from.q_state = QS_SENDER;
199c2aa98e2SPeter Wemm 		(void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
200c2aa98e2SPeter Wemm 	}
201c2aa98e2SPeter Wemm 
202c2aa98e2SPeter Wemm 	/*
203c2aa98e2SPeter Wemm 	**  Handle alias owners.
204c2aa98e2SPeter Wemm 	**
205c2aa98e2SPeter Wemm 	**	We scan up the q_alias chain looking for owners.
206c2aa98e2SPeter Wemm 	**	We discard owners that are the same as the return path.
207c2aa98e2SPeter Wemm 	*/
208c2aa98e2SPeter Wemm 
209c2aa98e2SPeter Wemm 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
210c2aa98e2SPeter Wemm 	{
211c2aa98e2SPeter Wemm 		register struct address *a;
212c2aa98e2SPeter Wemm 
213c2aa98e2SPeter Wemm 		for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
214c2aa98e2SPeter Wemm 			continue;
215c2aa98e2SPeter Wemm 		if (a != NULL)
216c2aa98e2SPeter Wemm 			q->q_owner = a->q_owner;
217c2aa98e2SPeter Wemm 
218c2aa98e2SPeter Wemm 		if (q->q_owner != NULL &&
21906f25ae9SGregory Neil Shapiro 		    !QS_IS_DEAD(q->q_state) &&
220c2aa98e2SPeter Wemm 		    strcmp(q->q_owner, e->e_from.q_paddr) == 0)
221c2aa98e2SPeter Wemm 			q->q_owner = NULL;
222c2aa98e2SPeter Wemm 	}
223c2aa98e2SPeter Wemm 
224c2aa98e2SPeter Wemm 	if (tTd(13, 25))
225c2aa98e2SPeter Wemm 	{
22640266059SGregory Neil Shapiro 		sm_dprintf("\nAfter first owner pass, sendq =\n");
227e92d3f3fSGregory Neil Shapiro 		printaddr(sm_debug_file(), e->e_sendqueue, true);
228c2aa98e2SPeter Wemm 	}
229c2aa98e2SPeter Wemm 
230c2aa98e2SPeter Wemm 	owner = "";
231c2aa98e2SPeter Wemm 	otherowners = 1;
232c2aa98e2SPeter Wemm 	while (owner != NULL && otherowners > 0)
233c2aa98e2SPeter Wemm 	{
234c2aa98e2SPeter Wemm 		if (tTd(13, 28))
23540266059SGregory Neil Shapiro 			sm_dprintf("owner = \"%s\", otherowners = %d\n",
236c2aa98e2SPeter Wemm 				   owner, otherowners);
237c2aa98e2SPeter Wemm 		owner = NULL;
238c2aa98e2SPeter Wemm 		otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
239c2aa98e2SPeter Wemm 
240c2aa98e2SPeter Wemm 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
241c2aa98e2SPeter Wemm 		{
242c2aa98e2SPeter Wemm 			if (tTd(13, 30))
243c2aa98e2SPeter Wemm 			{
24440266059SGregory Neil Shapiro 				sm_dprintf("Checking ");
245e92d3f3fSGregory Neil Shapiro 				printaddr(sm_debug_file(), q, false);
246c2aa98e2SPeter Wemm 			}
24706f25ae9SGregory Neil Shapiro 			if (QS_IS_DEAD(q->q_state))
248c2aa98e2SPeter Wemm 			{
249c2aa98e2SPeter Wemm 				if (tTd(13, 30))
25040266059SGregory Neil Shapiro 					sm_dprintf("    ... QS_IS_DEAD\n");
251c2aa98e2SPeter Wemm 				continue;
252c2aa98e2SPeter Wemm 			}
253c2aa98e2SPeter Wemm 			if (tTd(13, 29) && !tTd(13, 30))
254c2aa98e2SPeter Wemm 			{
25540266059SGregory Neil Shapiro 				sm_dprintf("Checking ");
256e92d3f3fSGregory Neil Shapiro 				printaddr(sm_debug_file(), q, false);
257c2aa98e2SPeter Wemm 			}
258c2aa98e2SPeter Wemm 
259c2aa98e2SPeter Wemm 			if (q->q_owner != NULL)
260c2aa98e2SPeter Wemm 			{
261c2aa98e2SPeter Wemm 				if (owner == NULL)
262c2aa98e2SPeter Wemm 				{
263c2aa98e2SPeter Wemm 					if (tTd(13, 40))
26440266059SGregory Neil Shapiro 						sm_dprintf("    ... First owner = \"%s\"\n",
265c2aa98e2SPeter Wemm 							   q->q_owner);
266c2aa98e2SPeter Wemm 					owner = q->q_owner;
267c2aa98e2SPeter Wemm 				}
268c2aa98e2SPeter Wemm 				else if (owner != q->q_owner)
269c2aa98e2SPeter Wemm 				{
270c2aa98e2SPeter Wemm 					if (strcmp(owner, q->q_owner) == 0)
271c2aa98e2SPeter Wemm 					{
272c2aa98e2SPeter Wemm 						if (tTd(13, 40))
27340266059SGregory Neil Shapiro 							sm_dprintf("    ... Same owner = \"%s\"\n",
274c2aa98e2SPeter Wemm 								   owner);
275c2aa98e2SPeter Wemm 
276c2aa98e2SPeter Wemm 						/* make future comparisons cheap */
277c2aa98e2SPeter Wemm 						q->q_owner = owner;
278c2aa98e2SPeter Wemm 					}
279c2aa98e2SPeter Wemm 					else
280c2aa98e2SPeter Wemm 					{
281c2aa98e2SPeter Wemm 						if (tTd(13, 40))
28240266059SGregory Neil Shapiro 							sm_dprintf("    ... Another owner \"%s\"\n",
283c2aa98e2SPeter Wemm 								   q->q_owner);
284c2aa98e2SPeter Wemm 						otherowners++;
285c2aa98e2SPeter Wemm 					}
286c2aa98e2SPeter Wemm 					owner = q->q_owner;
287c2aa98e2SPeter Wemm 				}
288c2aa98e2SPeter Wemm 				else if (tTd(13, 40))
28940266059SGregory Neil Shapiro 					sm_dprintf("    ... Same owner = \"%s\"\n",
290c2aa98e2SPeter Wemm 						   owner);
291c2aa98e2SPeter Wemm 			}
292c2aa98e2SPeter Wemm 			else
293c2aa98e2SPeter Wemm 			{
294c2aa98e2SPeter Wemm 				if (tTd(13, 40))
29540266059SGregory Neil Shapiro 					sm_dprintf("    ... Null owner\n");
296c2aa98e2SPeter Wemm 				otherowners++;
297c2aa98e2SPeter Wemm 			}
298c2aa98e2SPeter Wemm 
29906f25ae9SGregory Neil Shapiro 			if (QS_IS_BADADDR(q->q_state))
30006f25ae9SGregory Neil Shapiro 			{
30106f25ae9SGregory Neil Shapiro 				if (tTd(13, 30))
30240266059SGregory Neil Shapiro 					sm_dprintf("    ... QS_IS_BADADDR\n");
30306f25ae9SGregory Neil Shapiro 				continue;
30406f25ae9SGregory Neil Shapiro 			}
30506f25ae9SGregory Neil Shapiro 
30606f25ae9SGregory Neil Shapiro 			if (QS_IS_QUEUEUP(q->q_state))
30706f25ae9SGregory Neil Shapiro 			{
30806f25ae9SGregory Neil Shapiro 				MAILER *m = q->q_mailer;
30906f25ae9SGregory Neil Shapiro 
31006f25ae9SGregory Neil Shapiro 				/*
31106f25ae9SGregory Neil Shapiro 				**  If we have temporary address failures
31206f25ae9SGregory Neil Shapiro 				**  (e.g., dns failure) and a fallback MX is
31306f25ae9SGregory Neil Shapiro 				**  set, send directly to the fallback MX host.
31406f25ae9SGregory Neil Shapiro 				*/
31506f25ae9SGregory Neil Shapiro 
316e92d3f3fSGregory Neil Shapiro 				if (FallbackMX != NULL &&
317e92d3f3fSGregory Neil Shapiro 				    !wordinclass(FallbackMX, 'w') &&
31806f25ae9SGregory Neil Shapiro 				    mode != SM_VERIFY &&
31940266059SGregory Neil Shapiro 				    !bitnset(M_NOMX, m->m_flags) &&
32040266059SGregory Neil Shapiro 				    strcmp(m->m_mailer, "[IPC]") == 0 &&
32106f25ae9SGregory Neil Shapiro 				    m->m_argv[0] != NULL &&
32240266059SGregory Neil Shapiro 				    strcmp(m->m_argv[0], "TCP") == 0)
32306f25ae9SGregory Neil Shapiro 				{
32406f25ae9SGregory Neil Shapiro 					int len;
32506f25ae9SGregory Neil Shapiro 					char *p;
32606f25ae9SGregory Neil Shapiro 
32706f25ae9SGregory Neil Shapiro 					if (tTd(13, 30))
328e92d3f3fSGregory Neil Shapiro 						sm_dprintf("    ... FallbackMX\n");
32906f25ae9SGregory Neil Shapiro 
330e92d3f3fSGregory Neil Shapiro 					len = strlen(FallbackMX) + 1;
33140266059SGregory Neil Shapiro 					p = sm_rpool_malloc_x(e->e_rpool, len);
332e92d3f3fSGregory Neil Shapiro 					(void) sm_strlcpy(p, FallbackMX, len);
33306f25ae9SGregory Neil Shapiro 					q->q_state = QS_OK;
33406f25ae9SGregory Neil Shapiro 					q->q_host = p;
33506f25ae9SGregory Neil Shapiro 				}
33606f25ae9SGregory Neil Shapiro 				else
33706f25ae9SGregory Neil Shapiro 				{
33806f25ae9SGregory Neil Shapiro 					if (tTd(13, 30))
33940266059SGregory Neil Shapiro 						sm_dprintf("    ... QS_IS_QUEUEUP\n");
34006f25ae9SGregory Neil Shapiro 					continue;
34106f25ae9SGregory Neil Shapiro 				}
34206f25ae9SGregory Neil Shapiro 			}
34306f25ae9SGregory Neil Shapiro 
344c2aa98e2SPeter Wemm 			/*
345c2aa98e2SPeter Wemm 			**  If this mailer is expensive, and if we don't
346c2aa98e2SPeter Wemm 			**  want to make connections now, just mark these
347c2aa98e2SPeter Wemm 			**  addresses and return.  This is useful if we
348c2aa98e2SPeter Wemm 			**  want to batch connections to reduce load.  This
349c2aa98e2SPeter Wemm 			**  will cause the messages to be queued up, and a
350c2aa98e2SPeter Wemm 			**  daemon will come along to send the messages later.
351c2aa98e2SPeter Wemm 			*/
352c2aa98e2SPeter Wemm 
353c2aa98e2SPeter Wemm 			if (NoConnect && !Verbose &&
354c2aa98e2SPeter Wemm 			    bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
355c2aa98e2SPeter Wemm 			{
356c2aa98e2SPeter Wemm 				if (tTd(13, 30))
35740266059SGregory Neil Shapiro 					sm_dprintf("    ... expensive\n");
35806f25ae9SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
35940266059SGregory Neil Shapiro 				expensive = true;
36006f25ae9SGregory Neil Shapiro 			}
36106f25ae9SGregory Neil Shapiro 			else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
36206f25ae9SGregory Neil Shapiro 				 QueueLimitId == NULL &&
36306f25ae9SGregory Neil Shapiro 				 QueueLimitSender == NULL &&
36406f25ae9SGregory Neil Shapiro 				 QueueLimitRecipient == NULL)
36506f25ae9SGregory Neil Shapiro 			{
36606f25ae9SGregory Neil Shapiro 				if (tTd(13, 30))
36740266059SGregory Neil Shapiro 					sm_dprintf("    ... hold\n");
36806f25ae9SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
36940266059SGregory Neil Shapiro 				expensive = true;
370c2aa98e2SPeter Wemm 			}
37140266059SGregory Neil Shapiro 			else if (QueueMode != QM_QUARANTINE &&
37240266059SGregory Neil Shapiro 				 e->e_quarmsg != NULL)
37340266059SGregory Neil Shapiro 			{
37440266059SGregory Neil Shapiro 				if (tTd(13, 30))
37540266059SGregory Neil Shapiro 					sm_dprintf("    ... quarantine: %s\n",
37640266059SGregory Neil Shapiro 						   e->e_quarmsg);
37740266059SGregory Neil Shapiro 				q->q_state = QS_QUEUEUP;
37840266059SGregory Neil Shapiro 				expensive = true;
37940266059SGregory Neil Shapiro 			}
380c2aa98e2SPeter Wemm 			else
381c2aa98e2SPeter Wemm 			{
382c2aa98e2SPeter Wemm 				if (tTd(13, 30))
38340266059SGregory Neil Shapiro 					sm_dprintf("    ... deliverable\n");
38440266059SGregory Neil Shapiro 				somedeliveries = true;
385c2aa98e2SPeter Wemm 			}
386c2aa98e2SPeter Wemm 		}
387c2aa98e2SPeter Wemm 
388c2aa98e2SPeter Wemm 		if (owner != NULL && otherowners > 0)
389c2aa98e2SPeter Wemm 		{
390c2aa98e2SPeter Wemm 			/*
391c2aa98e2SPeter Wemm 			**  Split this envelope into two.
392c2aa98e2SPeter Wemm 			*/
393c2aa98e2SPeter Wemm 
39440266059SGregory Neil Shapiro 			ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
395d0cef73dSGregory Neil Shapiro 							    sizeof(*ee));
39640266059SGregory Neil Shapiro 			STRUCTCOPY(*e, *ee);
39706f25ae9SGregory Neil Shapiro 			ee->e_message = NULL;
398c2aa98e2SPeter Wemm 			ee->e_id = NULL;
39906f25ae9SGregory Neil Shapiro 			assign_queueid(ee);
400c2aa98e2SPeter Wemm 
401c2aa98e2SPeter Wemm 			if (tTd(13, 1))
40240266059SGregory Neil Shapiro 				sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
40340266059SGregory Neil Shapiro 					   e->e_id, ee->e_id, owner,
40440266059SGregory Neil Shapiro 					   otherowners);
405c2aa98e2SPeter Wemm 
40640266059SGregory Neil Shapiro 			ee->e_header = copyheader(e->e_header, ee->e_rpool);
40740266059SGregory Neil Shapiro 			ee->e_sendqueue = copyqueue(e->e_sendqueue,
40840266059SGregory Neil Shapiro 						    ee->e_rpool);
40940266059SGregory Neil Shapiro 			ee->e_errorqueue = copyqueue(e->e_errorqueue,
41040266059SGregory Neil Shapiro 						     ee->e_rpool);
411c2aa98e2SPeter Wemm 			ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
412c2aa98e2SPeter Wemm 			ee->e_flags |= EF_NORECEIPT;
41340266059SGregory Neil Shapiro 			setsender(owner, ee, NULL, '\0', true);
414c2aa98e2SPeter Wemm 			if (tTd(13, 5))
415c2aa98e2SPeter Wemm 			{
41640266059SGregory Neil Shapiro 				sm_dprintf("sendall(split): QS_SENDER ");
417e92d3f3fSGregory Neil Shapiro 				printaddr(sm_debug_file(), &ee->e_from, false);
418c2aa98e2SPeter Wemm 			}
41906f25ae9SGregory Neil Shapiro 			ee->e_from.q_state = QS_SENDER;
420c2aa98e2SPeter Wemm 			ee->e_dfp = NULL;
42106f25ae9SGregory Neil Shapiro 			ee->e_lockfp = NULL;
422c2aa98e2SPeter Wemm 			ee->e_xfp = NULL;
42340266059SGregory Neil Shapiro 			ee->e_qgrp = e->e_qgrp;
42440266059SGregory Neil Shapiro 			ee->e_qdir = e->e_qdir;
425c2aa98e2SPeter Wemm 			ee->e_errormode = EM_MAIL;
426c2aa98e2SPeter Wemm 			ee->e_sibling = splitenv;
42706f25ae9SGregory Neil Shapiro 			ee->e_statmsg = NULL;
42840266059SGregory Neil Shapiro 			if (e->e_quarmsg != NULL)
42940266059SGregory Neil Shapiro 				ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
43040266059SGregory Neil Shapiro 								  e->e_quarmsg);
431c2aa98e2SPeter Wemm 			splitenv = ee;
432c2aa98e2SPeter Wemm 
433c2aa98e2SPeter Wemm 			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
434c2aa98e2SPeter Wemm 			{
435c2aa98e2SPeter Wemm 				if (q->q_owner == owner)
436c2aa98e2SPeter Wemm 				{
43706f25ae9SGregory Neil Shapiro 					q->q_state = QS_CLONED;
438c2aa98e2SPeter Wemm 					if (tTd(13, 6))
43940266059SGregory Neil Shapiro 						sm_dprintf("\t... stripping %s from original envelope\n",
440c2aa98e2SPeter Wemm 							   q->q_paddr);
441c2aa98e2SPeter Wemm 				}
442c2aa98e2SPeter Wemm 			}
443c2aa98e2SPeter Wemm 			for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
444c2aa98e2SPeter Wemm 			{
445c2aa98e2SPeter Wemm 				if (q->q_owner != owner)
446c2aa98e2SPeter Wemm 				{
44706f25ae9SGregory Neil Shapiro 					q->q_state = QS_CLONED;
448c2aa98e2SPeter Wemm 					if (tTd(13, 6))
44940266059SGregory Neil Shapiro 						sm_dprintf("\t... dropping %s from cloned envelope\n",
450c2aa98e2SPeter Wemm 							   q->q_paddr);
451c2aa98e2SPeter Wemm 				}
452c2aa98e2SPeter Wemm 				else
453c2aa98e2SPeter Wemm 				{
454c2aa98e2SPeter Wemm 					/* clear DSN parameters */
455c2aa98e2SPeter Wemm 					q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
456c2aa98e2SPeter Wemm 					q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
457c2aa98e2SPeter Wemm 					if (tTd(13, 6))
45840266059SGregory Neil Shapiro 						sm_dprintf("\t... moving %s to cloned envelope\n",
459c2aa98e2SPeter Wemm 							   q->q_paddr);
460c2aa98e2SPeter Wemm 				}
461c2aa98e2SPeter Wemm 			}
462c2aa98e2SPeter Wemm 
463c2aa98e2SPeter Wemm 			if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
46440266059SGregory Neil Shapiro 				dup_queue_file(e, ee, DATAFL_LETTER);
46506f25ae9SGregory Neil Shapiro 
46606f25ae9SGregory Neil Shapiro 			/*
46706f25ae9SGregory Neil Shapiro 			**  Give the split envelope access to the parent
46806f25ae9SGregory Neil Shapiro 			**  transcript file for errors obtained while
46906f25ae9SGregory Neil Shapiro 			**  processing the recipients (done before the
47006f25ae9SGregory Neil Shapiro 			**  envelope splitting).
47106f25ae9SGregory Neil Shapiro 			*/
47206f25ae9SGregory Neil Shapiro 
47306f25ae9SGregory Neil Shapiro 			if (e->e_xfp != NULL)
47440266059SGregory Neil Shapiro 				ee->e_xfp = sm_io_dup(e->e_xfp);
47506f25ae9SGregory Neil Shapiro 
47606f25ae9SGregory Neil Shapiro 			/* failed to dup e->e_xfp, start a new transcript */
47706f25ae9SGregory Neil Shapiro 			if (ee->e_xfp == NULL)
478c2aa98e2SPeter Wemm 				openxscript(ee);
47906f25ae9SGregory Neil Shapiro 
480065a643dSPeter Wemm 			if (mode != SM_VERIFY && LogLevel > 4)
48140266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
48240266059SGregory Neil Shapiro 					  "%s: clone: owner=%s",
48340266059SGregory Neil Shapiro 					  ee->e_id, owner);
484c2aa98e2SPeter Wemm 		}
485c2aa98e2SPeter Wemm 	}
486c2aa98e2SPeter Wemm 
487c2aa98e2SPeter Wemm 	if (owner != NULL)
488c2aa98e2SPeter Wemm 	{
48940266059SGregory Neil Shapiro 		setsender(owner, e, NULL, '\0', true);
490c2aa98e2SPeter Wemm 		if (tTd(13, 5))
491c2aa98e2SPeter Wemm 		{
49240266059SGregory Neil Shapiro 			sm_dprintf("sendall(owner): QS_SENDER ");
493e92d3f3fSGregory Neil Shapiro 			printaddr(sm_debug_file(), &e->e_from, false);
494c2aa98e2SPeter Wemm 		}
49506f25ae9SGregory Neil Shapiro 		e->e_from.q_state = QS_SENDER;
496c2aa98e2SPeter Wemm 		e->e_errormode = EM_MAIL;
497c2aa98e2SPeter Wemm 		e->e_flags |= EF_NORECEIPT;
498c2aa98e2SPeter Wemm 		e->e_flags &= ~EF_FATALERRS;
499c2aa98e2SPeter Wemm 	}
500c2aa98e2SPeter Wemm 
501c2aa98e2SPeter Wemm 	/* if nothing to be delivered, just queue up everything */
50240266059SGregory Neil Shapiro 	if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
503c2aa98e2SPeter Wemm 	    mode != SM_VERIFY)
504c2aa98e2SPeter Wemm 	{
50540266059SGregory Neil Shapiro 		time_t now;
506193538b7SGregory Neil Shapiro 
507c2aa98e2SPeter Wemm 		if (tTd(13, 29))
508ffb83623SGregory Neil Shapiro 			sm_dprintf("No deliveries: auto-queueing\n");
509c2aa98e2SPeter Wemm 		mode = SM_QUEUE;
51040266059SGregory Neil Shapiro 		now = curtime();
511c2aa98e2SPeter Wemm 
512c2aa98e2SPeter Wemm 		/* treat this as a delivery in terms of counting tries */
513193538b7SGregory Neil Shapiro 		e->e_dtime = now;
514c2aa98e2SPeter Wemm 		if (!expensive)
515c2aa98e2SPeter Wemm 			e->e_ntries++;
516c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
517c2aa98e2SPeter Wemm 		{
518193538b7SGregory Neil Shapiro 			ee->e_dtime = now;
519c2aa98e2SPeter Wemm 			if (!expensive)
520c2aa98e2SPeter Wemm 				ee->e_ntries++;
521c2aa98e2SPeter Wemm 		}
522c2aa98e2SPeter Wemm 	}
523c2aa98e2SPeter Wemm 
52440266059SGregory Neil Shapiro 	if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
525e92d3f3fSGregory Neil Shapiro 	     (mode != SM_VERIFY &&
526e92d3f3fSGregory Neil Shapiro 	      (SuperSafe == SAFE_REALLY ||
527e92d3f3fSGregory Neil Shapiro 	       SuperSafe == SAFE_REALLY_POSTMILTER))) &&
528c2aa98e2SPeter Wemm 	    (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
529c2aa98e2SPeter Wemm 	{
53040266059SGregory Neil Shapiro 		bool msync;
53140266059SGregory Neil Shapiro 
53242e5d165SGregory Neil Shapiro 		/*
53342e5d165SGregory Neil Shapiro 		**  Be sure everything is instantiated in the queue.
53442e5d165SGregory Neil Shapiro 		**  Split envelopes first in case the machine crashes.
53542e5d165SGregory Neil Shapiro 		**  If the original were done first, we may lose
53642e5d165SGregory Neil Shapiro 		**  recipients.
53742e5d165SGregory Neil Shapiro 		*/
53842e5d165SGregory Neil Shapiro 
53940266059SGregory Neil Shapiro #if !HASFLOCK
54040266059SGregory Neil Shapiro 		msync = false;
541*5b0945b5SGregory Neil Shapiro #else
54240266059SGregory Neil Shapiro 		msync = mode == SM_FORK;
543*5b0945b5SGregory Neil Shapiro #endif
54440266059SGregory Neil Shapiro 
545c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
54640266059SGregory Neil Shapiro 			queueup(ee, WILL_BE_QUEUED(mode), msync);
54740266059SGregory Neil Shapiro 		queueup(e, WILL_BE_QUEUED(mode), msync);
548c2aa98e2SPeter Wemm 	}
549c2aa98e2SPeter Wemm 
550c2aa98e2SPeter Wemm 	if (tTd(62, 10))
551c2aa98e2SPeter Wemm 		checkfds("after envelope splitting");
552c2aa98e2SPeter Wemm 
553c2aa98e2SPeter Wemm 	/*
554c2aa98e2SPeter Wemm 	**  If we belong in background, fork now.
555c2aa98e2SPeter Wemm 	*/
556c2aa98e2SPeter Wemm 
557c2aa98e2SPeter Wemm 	if (tTd(13, 20))
558c2aa98e2SPeter Wemm 	{
55940266059SGregory Neil Shapiro 		sm_dprintf("sendall: final mode = %c\n", mode);
560c2aa98e2SPeter Wemm 		if (tTd(13, 21))
561c2aa98e2SPeter Wemm 		{
56240266059SGregory Neil Shapiro 			sm_dprintf("\n================ Final Send Queue(s) =====================\n");
56340266059SGregory Neil Shapiro 			sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
564c2aa98e2SPeter Wemm 				   e->e_id, e->e_from.q_paddr);
565e92d3f3fSGregory Neil Shapiro 			printaddr(sm_debug_file(), e->e_sendqueue, true);
566c2aa98e2SPeter Wemm 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
567c2aa98e2SPeter Wemm 			{
56840266059SGregory Neil Shapiro 				sm_dprintf("\n  *** Envelope %s, e_from=%s ***\n",
569c2aa98e2SPeter Wemm 					   ee->e_id, ee->e_from.q_paddr);
570e92d3f3fSGregory Neil Shapiro 				printaddr(sm_debug_file(), ee->e_sendqueue, true);
571c2aa98e2SPeter Wemm 			}
57240266059SGregory Neil Shapiro 			sm_dprintf("==========================================================\n\n");
573c2aa98e2SPeter Wemm 		}
574c2aa98e2SPeter Wemm 	}
575c2aa98e2SPeter Wemm 	switch (mode)
576c2aa98e2SPeter Wemm 	{
577c2aa98e2SPeter Wemm 	  case SM_VERIFY:
578c2aa98e2SPeter Wemm 		Verbose = 2;
579c2aa98e2SPeter Wemm 		break;
580c2aa98e2SPeter Wemm 
581c2aa98e2SPeter Wemm 	  case SM_QUEUE:
582c2aa98e2SPeter Wemm 	  case SM_DEFER:
583c2aa98e2SPeter Wemm #if HASFLOCK
584c2aa98e2SPeter Wemm   queueonly:
585*5b0945b5SGregory Neil Shapiro #endif
586c2aa98e2SPeter Wemm 		if (e->e_nrcpts > 0)
587c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
5889bd497b8SGregory Neil Shapiro 		(void) dropenvelope(e, splitenv != NULL, true);
589c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
590c2aa98e2SPeter Wemm 		{
591c2aa98e2SPeter Wemm 			if (ee->e_nrcpts > 0)
592c2aa98e2SPeter Wemm 				ee->e_flags |= EF_INQUEUE;
5939bd497b8SGregory Neil Shapiro 			(void) dropenvelope(ee, false, true);
594c2aa98e2SPeter Wemm 		}
595c2aa98e2SPeter Wemm 		return;
596c2aa98e2SPeter Wemm 
597c2aa98e2SPeter Wemm 	  case SM_FORK:
598c2aa98e2SPeter Wemm 		if (e->e_xfp != NULL)
59940266059SGregory Neil Shapiro 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
600c2aa98e2SPeter Wemm 
601c2aa98e2SPeter Wemm #if !HASFLOCK
602c2aa98e2SPeter Wemm 		/*
603c2aa98e2SPeter Wemm 		**  Since fcntl locking has the interesting semantic that
604c2aa98e2SPeter Wemm 		**  the lock is owned by a process, not by an open file
605c2aa98e2SPeter Wemm 		**  descriptor, we have to flush this to the queue, and
606c2aa98e2SPeter Wemm 		**  then restart from scratch in the child.
607c2aa98e2SPeter Wemm 		*/
608c2aa98e2SPeter Wemm 
609c2aa98e2SPeter Wemm 		{
610c2aa98e2SPeter Wemm 			/* save id for future use */
611c2aa98e2SPeter Wemm 			char *qid = e->e_id;
612c2aa98e2SPeter Wemm 
613c2aa98e2SPeter Wemm 			/* now drop the envelope in the parent */
614c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
6159bd497b8SGregory Neil Shapiro 			(void) dropenvelope(e, splitenv != NULL, false);
616c2aa98e2SPeter Wemm 
617c2aa98e2SPeter Wemm 			/* arrange to reacquire lock after fork */
618c2aa98e2SPeter Wemm 			e->e_id = qid;
619c2aa98e2SPeter Wemm 		}
620c2aa98e2SPeter Wemm 
621c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
622c2aa98e2SPeter Wemm 		{
623c2aa98e2SPeter Wemm 			/* save id for future use */
624c2aa98e2SPeter Wemm 			char *qid = ee->e_id;
625c2aa98e2SPeter Wemm 
626c2aa98e2SPeter Wemm 			/* drop envelope in parent */
627c2aa98e2SPeter Wemm 			ee->e_flags |= EF_INQUEUE;
6289bd497b8SGregory Neil Shapiro 			(void) dropenvelope(ee, false, false);
629c2aa98e2SPeter Wemm 
630c2aa98e2SPeter Wemm 			/* and save qid for reacquisition */
631c2aa98e2SPeter Wemm 			ee->e_id = qid;
632c2aa98e2SPeter Wemm 		}
633e92d3f3fSGregory Neil Shapiro 
634c2aa98e2SPeter Wemm #endif /* !HASFLOCK */
635c2aa98e2SPeter Wemm 
63606f25ae9SGregory Neil Shapiro 		/*
63706f25ae9SGregory Neil Shapiro 		**  Since the delivery may happen in a child and the parent
63806f25ae9SGregory Neil Shapiro 		**  does not wait, the parent may close the maps thereby
63906f25ae9SGregory Neil Shapiro 		**  removing any shared memory used by the map.  Therefore,
64006f25ae9SGregory Neil Shapiro 		**  close the maps now so the child will dynamically open
64106f25ae9SGregory Neil Shapiro 		**  them if necessary.
64206f25ae9SGregory Neil Shapiro 		*/
64306f25ae9SGregory Neil Shapiro 
64440266059SGregory Neil Shapiro 		closemaps(false);
64506f25ae9SGregory Neil Shapiro 
646c2aa98e2SPeter Wemm 		pid = fork();
647c2aa98e2SPeter Wemm 		if (pid < 0)
648c2aa98e2SPeter Wemm 		{
64906f25ae9SGregory Neil Shapiro 			syserr("deliver: fork 1");
650c2aa98e2SPeter Wemm #if HASFLOCK
651c2aa98e2SPeter Wemm 			goto queueonly;
65206f25ae9SGregory Neil Shapiro #else /* HASFLOCK */
653c2aa98e2SPeter Wemm 			e->e_id = NULL;
654c2aa98e2SPeter Wemm 			for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
655c2aa98e2SPeter Wemm 				ee->e_id = NULL;
656c2aa98e2SPeter Wemm 			return;
657c2aa98e2SPeter Wemm #endif /* HASFLOCK */
658c2aa98e2SPeter Wemm 		}
659c2aa98e2SPeter Wemm 		else if (pid > 0)
660c2aa98e2SPeter Wemm 		{
661c2aa98e2SPeter Wemm #if HASFLOCK
662c2aa98e2SPeter Wemm 			/* be sure we leave the temp files to our child */
663c2aa98e2SPeter Wemm 			/* close any random open files in the envelope */
664c2aa98e2SPeter Wemm 			closexscript(e);
665c2aa98e2SPeter Wemm 			if (e->e_dfp != NULL)
66640266059SGregory Neil Shapiro 				(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
667c2aa98e2SPeter Wemm 			e->e_dfp = NULL;
668c2aa98e2SPeter Wemm 			e->e_flags &= ~EF_HAS_DF;
669c2aa98e2SPeter Wemm 
670c2aa98e2SPeter Wemm 			/* can't call unlockqueue to avoid unlink of xfp */
671c2aa98e2SPeter Wemm 			if (e->e_lockfp != NULL)
67240266059SGregory Neil Shapiro 				(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
67306f25ae9SGregory Neil Shapiro 			else
67406f25ae9SGregory Neil Shapiro 				syserr("%s: sendall: null lockfp", e->e_id);
675c2aa98e2SPeter Wemm 			e->e_lockfp = NULL;
67606f25ae9SGregory Neil Shapiro #endif /* HASFLOCK */
677c2aa98e2SPeter Wemm 
678c2aa98e2SPeter Wemm 			/* make sure the parent doesn't own the envelope */
679c2aa98e2SPeter Wemm 			e->e_id = NULL;
680c2aa98e2SPeter Wemm 
68140266059SGregory Neil Shapiro #if USE_DOUBLE_FORK
682c2aa98e2SPeter Wemm 			/* catch intermediate zombie */
683c2aa98e2SPeter Wemm 			(void) waitfor(pid);
684*5b0945b5SGregory Neil Shapiro #endif
685c2aa98e2SPeter Wemm 			return;
686c2aa98e2SPeter Wemm 		}
687c2aa98e2SPeter Wemm 
6888774250cSGregory Neil Shapiro 		/* Reset global flags */
6898774250cSGregory Neil Shapiro 		RestartRequest = NULL;
69040266059SGregory Neil Shapiro 		RestartWorkGroup = false;
6918774250cSGregory Neil Shapiro 		ShutdownRequest = NULL;
6928774250cSGregory Neil Shapiro 		PendingSignal = 0;
6938774250cSGregory Neil Shapiro 
69442e5d165SGregory Neil Shapiro 		/*
69540266059SGregory Neil Shapiro 		**  Initialize exception stack and default exception
69640266059SGregory Neil Shapiro 		**  handler for child process.
69740266059SGregory Neil Shapiro 		*/
69840266059SGregory Neil Shapiro 
69940266059SGregory Neil Shapiro 		sm_exc_newthread(fatal_error);
70040266059SGregory Neil Shapiro 
70140266059SGregory Neil Shapiro 		/*
70242e5d165SGregory Neil Shapiro 		**  Since we have accepted responsbility for the message,
70342e5d165SGregory Neil Shapiro 		**  change the SIGTERM handler.  intsig() (the old handler)
70442e5d165SGregory Neil Shapiro 		**  would remove the envelope if this was a command line
70542e5d165SGregory Neil Shapiro 		**  message submission.
70642e5d165SGregory Neil Shapiro 		*/
70742e5d165SGregory Neil Shapiro 
70840266059SGregory Neil Shapiro 		(void) sm_signal(SIGTERM, SIG_DFL);
70942e5d165SGregory Neil Shapiro 
71040266059SGregory Neil Shapiro #if USE_DOUBLE_FORK
711c2aa98e2SPeter Wemm 		/* double fork to avoid zombies */
712c2aa98e2SPeter Wemm 		pid = fork();
713c2aa98e2SPeter Wemm 		if (pid > 0)
714c2aa98e2SPeter Wemm 			exit(EX_OK);
71506f25ae9SGregory Neil Shapiro 		save_errno = errno;
71640266059SGregory Neil Shapiro #endif /* USE_DOUBLE_FORK */
71740266059SGregory Neil Shapiro 
71840266059SGregory Neil Shapiro 		CurrentPid = getpid();
719c2aa98e2SPeter Wemm 
720c2aa98e2SPeter Wemm 		/* be sure we are immune from the terminal */
721c2aa98e2SPeter Wemm 		disconnect(2, e);
72206f25ae9SGregory Neil Shapiro 		clearstats();
723c2aa98e2SPeter Wemm 
724c2aa98e2SPeter Wemm 		/* prevent parent from waiting if there was an error */
725c2aa98e2SPeter Wemm 		if (pid < 0)
726c2aa98e2SPeter Wemm 		{
72706f25ae9SGregory Neil Shapiro 			errno = save_errno;
72806f25ae9SGregory Neil Shapiro 			syserr("deliver: fork 2");
729c2aa98e2SPeter Wemm #if HASFLOCK
730c2aa98e2SPeter Wemm 			e->e_flags |= EF_INQUEUE;
731*5b0945b5SGregory Neil Shapiro #else
732c2aa98e2SPeter Wemm 			e->e_id = NULL;
733*5b0945b5SGregory Neil Shapiro #endif
73440266059SGregory Neil Shapiro 			finis(true, true, ExitStat);
735c2aa98e2SPeter Wemm 		}
736c2aa98e2SPeter Wemm 
737c2aa98e2SPeter Wemm 		/* be sure to give error messages in child */
73840266059SGregory Neil Shapiro 		QuickAbort = false;
739c2aa98e2SPeter Wemm 
740c2aa98e2SPeter Wemm 		/*
741c2aa98e2SPeter Wemm 		**  Close any cached connections.
742c2aa98e2SPeter Wemm 		**
743c2aa98e2SPeter Wemm 		**	We don't send the QUIT protocol because the parent
744c2aa98e2SPeter Wemm 		**	still knows about the connection.
745c2aa98e2SPeter Wemm 		**
746c2aa98e2SPeter Wemm 		**	This should only happen when delivering an error
747c2aa98e2SPeter Wemm 		**	message.
748c2aa98e2SPeter Wemm 		*/
749c2aa98e2SPeter Wemm 
75040266059SGregory Neil Shapiro 		mci_flush(false, NULL);
751c2aa98e2SPeter Wemm 
752c2aa98e2SPeter Wemm #if HASFLOCK
753c2aa98e2SPeter Wemm 		break;
75406f25ae9SGregory Neil Shapiro #else /* HASFLOCK */
755c2aa98e2SPeter Wemm 
756c2aa98e2SPeter Wemm 		/*
757c2aa98e2SPeter Wemm 		**  Now reacquire and run the various queue files.
758c2aa98e2SPeter Wemm 		*/
759c2aa98e2SPeter Wemm 
760c2aa98e2SPeter Wemm 		for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
761c2aa98e2SPeter Wemm 		{
762c2aa98e2SPeter Wemm 			ENVELOPE *sibling = ee->e_sibling;
763c2aa98e2SPeter Wemm 
76440266059SGregory Neil Shapiro 			(void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
76540266059SGregory Neil Shapiro 				      false, false, ee);
766c2aa98e2SPeter Wemm 			ee->e_sibling = sibling;
767c2aa98e2SPeter Wemm 		}
76840266059SGregory Neil Shapiro 		(void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
76940266059SGregory Neil Shapiro 			      false, false, e);
77040266059SGregory Neil Shapiro 		finis(true, true, ExitStat);
77106f25ae9SGregory Neil Shapiro #endif /* HASFLOCK */
772c2aa98e2SPeter Wemm 	}
773c2aa98e2SPeter Wemm 
774c2aa98e2SPeter Wemm 	sendenvelope(e, mode);
7759bd497b8SGregory Neil Shapiro 	(void) dropenvelope(e, true, true);
776c2aa98e2SPeter Wemm 	for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
777c2aa98e2SPeter Wemm 	{
778c2aa98e2SPeter Wemm 		CurEnv = ee;
779c2aa98e2SPeter Wemm 		if (mode != SM_VERIFY)
780c2aa98e2SPeter Wemm 			openxscript(ee);
781c2aa98e2SPeter Wemm 		sendenvelope(ee, mode);
7829bd497b8SGregory Neil Shapiro 		(void) dropenvelope(ee, true, true);
783c2aa98e2SPeter Wemm 	}
784c2aa98e2SPeter Wemm 	CurEnv = e;
785c2aa98e2SPeter Wemm 
786c2aa98e2SPeter Wemm 	Verbose = oldverbose;
787c2aa98e2SPeter Wemm 	if (mode == SM_FORK)
78840266059SGregory Neil Shapiro 		finis(true, true, ExitStat);
789c2aa98e2SPeter Wemm }
790c2aa98e2SPeter Wemm 
79106f25ae9SGregory Neil Shapiro static void
792c2aa98e2SPeter Wemm sendenvelope(e, mode)
793c2aa98e2SPeter Wemm 	register ENVELOPE *e;
794c2aa98e2SPeter Wemm 	int mode;
795c2aa98e2SPeter Wemm {
796c2aa98e2SPeter Wemm 	register ADDRESS *q;
797c2aa98e2SPeter Wemm 	bool didany;
798c2aa98e2SPeter Wemm 
799c2aa98e2SPeter Wemm 	if (tTd(13, 10))
80040266059SGregory Neil Shapiro 		sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
801c2aa98e2SPeter Wemm 			   e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
802c2aa98e2SPeter Wemm 			   e->e_flags);
803c2aa98e2SPeter Wemm 	if (LogLevel > 80)
804c2aa98e2SPeter Wemm 		sm_syslog(LOG_DEBUG, e->e_id,
80506f25ae9SGregory Neil Shapiro 			  "sendenvelope, flags=0x%lx",
806c2aa98e2SPeter Wemm 			  e->e_flags);
807c2aa98e2SPeter Wemm 
808c2aa98e2SPeter Wemm 	/*
809c2aa98e2SPeter Wemm 	**  If we have had global, fatal errors, don't bother sending
810c2aa98e2SPeter Wemm 	**  the message at all if we are in SMTP mode.  Local errors
811c2aa98e2SPeter Wemm 	**  (e.g., a single address failing) will still cause the other
812c2aa98e2SPeter Wemm 	**  addresses to be sent.
813c2aa98e2SPeter Wemm 	*/
814c2aa98e2SPeter Wemm 
815c2aa98e2SPeter Wemm 	if (bitset(EF_FATALERRS, e->e_flags) &&
816c2aa98e2SPeter Wemm 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
817c2aa98e2SPeter Wemm 	{
818c2aa98e2SPeter Wemm 		e->e_flags |= EF_CLRQUEUE;
819c2aa98e2SPeter Wemm 		return;
820c2aa98e2SPeter Wemm 	}
821c2aa98e2SPeter Wemm 
82240266059SGregory Neil Shapiro 	/*
82340266059SGregory Neil Shapiro 	**  Don't attempt deliveries if we want to bounce now
82440266059SGregory Neil Shapiro 	**  or if deliver-by time is exceeded.
82540266059SGregory Neil Shapiro 	*/
82640266059SGregory Neil Shapiro 
82706f25ae9SGregory Neil Shapiro 	if (!bitset(EF_RESPONSE, e->e_flags) &&
82840266059SGregory Neil Shapiro 	    (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
82940266059SGregory Neil Shapiro 	     (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
83040266059SGregory Neil Shapiro 	      curtime() > e->e_ctime + e->e_deliver_by)))
83106f25ae9SGregory Neil Shapiro 		return;
83206f25ae9SGregory Neil Shapiro 
833c2aa98e2SPeter Wemm 	/*
834c2aa98e2SPeter Wemm 	**  Run through the list and send everything.
835c2aa98e2SPeter Wemm 	**
836c2aa98e2SPeter Wemm 	**	Set EF_GLOBALERRS so that error messages during delivery
837c2aa98e2SPeter Wemm 	**	result in returned mail.
838c2aa98e2SPeter Wemm 	*/
839c2aa98e2SPeter Wemm 
840c2aa98e2SPeter Wemm 	e->e_nsent = 0;
841c2aa98e2SPeter Wemm 	e->e_flags |= EF_GLOBALERRS;
84206f25ae9SGregory Neil Shapiro 
84340266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
84440266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
84540266059SGregory Neil Shapiro 	didany = false;
84640266059SGregory Neil Shapiro 
84740266059SGregory Neil Shapiro 	if (!bitset(EF_SPLIT, e->e_flags))
84840266059SGregory Neil Shapiro 	{
84940266059SGregory Neil Shapiro 		ENVELOPE *oldsib;
85040266059SGregory Neil Shapiro 		ENVELOPE *ee;
85140266059SGregory Neil Shapiro 
85240266059SGregory Neil Shapiro 		/*
85340266059SGregory Neil Shapiro 		**  Save old sibling and set it to NULL to avoid
85440266059SGregory Neil Shapiro 		**  queueing up the same envelopes again.
85540266059SGregory Neil Shapiro 		**  This requires that envelopes in that list have
85640266059SGregory Neil Shapiro 		**  been take care of before (or at some other place).
85740266059SGregory Neil Shapiro 		*/
85840266059SGregory Neil Shapiro 
85940266059SGregory Neil Shapiro 		oldsib = e->e_sibling;
86040266059SGregory Neil Shapiro 		e->e_sibling = NULL;
86140266059SGregory Neil Shapiro 		if (!split_by_recipient(e) &&
86240266059SGregory Neil Shapiro 		    bitset(EF_FATALERRS, e->e_flags))
86340266059SGregory Neil Shapiro 		{
86440266059SGregory Neil Shapiro 			if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
86540266059SGregory Neil Shapiro 				e->e_flags |= EF_CLRQUEUE;
86640266059SGregory Neil Shapiro 			return;
86740266059SGregory Neil Shapiro 		}
86840266059SGregory Neil Shapiro 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
86940266059SGregory Neil Shapiro 			queueup(ee, false, true);
87040266059SGregory Neil Shapiro 
87140266059SGregory Neil Shapiro 		/* clean up */
87240266059SGregory Neil Shapiro 		for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
87340266059SGregory Neil Shapiro 		{
87440266059SGregory Neil Shapiro 			/* now unlock the job */
87540266059SGregory Neil Shapiro 			closexscript(ee);
87640266059SGregory Neil Shapiro 			unlockqueue(ee);
87740266059SGregory Neil Shapiro 
87840266059SGregory Neil Shapiro 			/* this envelope is marked unused */
87940266059SGregory Neil Shapiro 			if (ee->e_dfp != NULL)
88040266059SGregory Neil Shapiro 			{
88140266059SGregory Neil Shapiro 				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
88240266059SGregory Neil Shapiro 				ee->e_dfp = NULL;
88340266059SGregory Neil Shapiro 			}
88440266059SGregory Neil Shapiro 			ee->e_id = NULL;
88540266059SGregory Neil Shapiro 			ee->e_flags &= ~EF_HAS_DF;
88640266059SGregory Neil Shapiro 		}
88740266059SGregory Neil Shapiro 		e->e_sibling = oldsib;
88840266059SGregory Neil Shapiro 	}
889c2aa98e2SPeter Wemm 
890c2aa98e2SPeter Wemm 	/* now run through the queue */
891c2aa98e2SPeter Wemm 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
892c2aa98e2SPeter Wemm 	{
893c2aa98e2SPeter Wemm #if XDEBUG
894c2aa98e2SPeter Wemm 		char wbuf[MAXNAME + 20];
895c2aa98e2SPeter Wemm 
896d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)",
897c2aa98e2SPeter Wemm 				   MAXNAME, q->q_paddr);
898c2aa98e2SPeter Wemm 		checkfd012(wbuf);
89906f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
900c2aa98e2SPeter Wemm 		if (mode == SM_VERIFY)
901c2aa98e2SPeter Wemm 		{
902c2aa98e2SPeter Wemm 			e->e_to = q->q_paddr;
90306f25ae9SGregory Neil Shapiro 			if (QS_IS_SENDABLE(q->q_state))
904c2aa98e2SPeter Wemm 			{
905c2aa98e2SPeter Wemm 				if (q->q_host != NULL && q->q_host[0] != '\0')
906c2aa98e2SPeter Wemm 					message("deliverable: mailer %s, host %s, user %s",
907c2aa98e2SPeter Wemm 						q->q_mailer->m_name,
908c2aa98e2SPeter Wemm 						q->q_host,
909c2aa98e2SPeter Wemm 						q->q_user);
910c2aa98e2SPeter Wemm 				else
911c2aa98e2SPeter Wemm 					message("deliverable: mailer %s, user %s",
912c2aa98e2SPeter Wemm 						q->q_mailer->m_name,
913c2aa98e2SPeter Wemm 						q->q_user);
914c2aa98e2SPeter Wemm 			}
915c2aa98e2SPeter Wemm 		}
91606f25ae9SGregory Neil Shapiro 		else if (QS_IS_OK(q->q_state))
917c2aa98e2SPeter Wemm 		{
918c2aa98e2SPeter Wemm 			/*
919c2aa98e2SPeter Wemm 			**  Checkpoint the send list every few addresses
920c2aa98e2SPeter Wemm 			*/
921c2aa98e2SPeter Wemm 
92242e5d165SGregory Neil Shapiro 			if (CheckpointInterval > 0 &&
92342e5d165SGregory Neil Shapiro 			    e->e_nsent >= CheckpointInterval)
924c2aa98e2SPeter Wemm 			{
92540266059SGregory Neil Shapiro 				queueup(e, false, false);
926c2aa98e2SPeter Wemm 				e->e_nsent = 0;
927c2aa98e2SPeter Wemm 			}
928c2aa98e2SPeter Wemm 			(void) deliver(e, q);
92940266059SGregory Neil Shapiro 			didany = true;
930c2aa98e2SPeter Wemm 		}
931c2aa98e2SPeter Wemm 	}
932c2aa98e2SPeter Wemm 	if (didany)
933c2aa98e2SPeter Wemm 	{
934c2aa98e2SPeter Wemm 		e->e_dtime = curtime();
935c2aa98e2SPeter Wemm 		e->e_ntries++;
936c2aa98e2SPeter Wemm 	}
937c2aa98e2SPeter Wemm 
938c2aa98e2SPeter Wemm #if XDEBUG
939c2aa98e2SPeter Wemm 	checkfd012("end of sendenvelope");
940*5b0945b5SGregory Neil Shapiro #endif
941c2aa98e2SPeter Wemm }
94240266059SGregory Neil Shapiro 
94340266059SGregory Neil Shapiro #if REQUIRES_DIR_FSYNC
94440266059SGregory Neil Shapiro /*
94540266059SGregory Neil Shapiro **  SYNC_DIR -- fsync a directory based on a filename
94640266059SGregory Neil Shapiro **
94740266059SGregory Neil Shapiro **	Parameters:
94840266059SGregory Neil Shapiro **		filename -- path of file
94940266059SGregory Neil Shapiro **		panic -- panic?
95040266059SGregory Neil Shapiro **
95140266059SGregory Neil Shapiro **	Returns:
95240266059SGregory Neil Shapiro **		none
95340266059SGregory Neil Shapiro */
95440266059SGregory Neil Shapiro 
95540266059SGregory Neil Shapiro void
95640266059SGregory Neil Shapiro sync_dir(filename, panic)
95740266059SGregory Neil Shapiro 	char *filename;
95840266059SGregory Neil Shapiro 	bool panic;
95940266059SGregory Neil Shapiro {
96040266059SGregory Neil Shapiro 	int dirfd;
96140266059SGregory Neil Shapiro 	char *dirp;
96240266059SGregory Neil Shapiro 	char dir[MAXPATHLEN];
96340266059SGregory Neil Shapiro 
96413bd1963SGregory Neil Shapiro 	if (!RequiresDirfsync)
96513bd1963SGregory Neil Shapiro 		return;
96613bd1963SGregory Neil Shapiro 
96740266059SGregory Neil Shapiro 	/* filesystems which require the directory be synced */
96840266059SGregory Neil Shapiro 	dirp = strrchr(filename, '/');
96940266059SGregory Neil Shapiro 	if (dirp != NULL)
97040266059SGregory Neil Shapiro 	{
971d0cef73dSGregory Neil Shapiro 		if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir))
97240266059SGregory Neil Shapiro 			return;
97340266059SGregory Neil Shapiro 		dir[dirp - filename] = '\0';
97440266059SGregory Neil Shapiro 		dirp = dir;
97540266059SGregory Neil Shapiro 	}
97640266059SGregory Neil Shapiro 	else
97740266059SGregory Neil Shapiro 		dirp = ".";
97840266059SGregory Neil Shapiro 	dirfd = open(dirp, O_RDONLY, 0700);
97940266059SGregory Neil Shapiro 	if (tTd(40,32))
98040266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
98140266059SGregory Neil Shapiro 			  dirp, dirfd);
98240266059SGregory Neil Shapiro 	if (dirfd >= 0)
98340266059SGregory Neil Shapiro 	{
98440266059SGregory Neil Shapiro 		if (fsync(dirfd) < 0)
98540266059SGregory Neil Shapiro 		{
98640266059SGregory Neil Shapiro 			if (panic)
98740266059SGregory Neil Shapiro 				syserr("!sync_dir: cannot fsync directory %s",
98840266059SGregory Neil Shapiro 				       dirp);
98940266059SGregory Neil Shapiro 			else if (LogLevel > 1)
99040266059SGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
99140266059SGregory Neil Shapiro 					  "sync_dir: cannot fsync directory %s: %s",
99240266059SGregory Neil Shapiro 					  dirp, sm_errstring(errno));
99340266059SGregory Neil Shapiro 		}
99440266059SGregory Neil Shapiro 		(void) close(dirfd);
99540266059SGregory Neil Shapiro 	}
99640266059SGregory Neil Shapiro }
99740266059SGregory Neil Shapiro #endif /* REQUIRES_DIR_FSYNC */
99840266059SGregory Neil Shapiro /*
999c2aa98e2SPeter Wemm **  DUP_QUEUE_FILE -- duplicate a queue file into a split queue
1000c2aa98e2SPeter Wemm **
1001c2aa98e2SPeter Wemm **	Parameters:
1002c2aa98e2SPeter Wemm **		e -- the existing envelope
1003c2aa98e2SPeter Wemm **		ee -- the new envelope
100440266059SGregory Neil Shapiro **		type -- the queue file type (e.g., DATAFL_LETTER)
1005c2aa98e2SPeter Wemm **
1006c2aa98e2SPeter Wemm **	Returns:
1007c2aa98e2SPeter Wemm **		none
1008c2aa98e2SPeter Wemm */
1009c2aa98e2SPeter Wemm 
101006f25ae9SGregory Neil Shapiro static void
1011c2aa98e2SPeter Wemm dup_queue_file(e, ee, type)
101240266059SGregory Neil Shapiro 	ENVELOPE *e, *ee;
1013c2aa98e2SPeter Wemm 	int type;
1014c2aa98e2SPeter Wemm {
101506f25ae9SGregory Neil Shapiro 	char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
1016c2aa98e2SPeter Wemm 
1017c2aa98e2SPeter Wemm 	ee->e_dfp = NULL;
1018c2aa98e2SPeter Wemm 	ee->e_xfp = NULL;
101906f25ae9SGregory Neil Shapiro 
102006f25ae9SGregory Neil Shapiro 	/*
102106f25ae9SGregory Neil Shapiro 	**  Make sure both are in the same directory.
102206f25ae9SGregory Neil Shapiro 	*/
102306f25ae9SGregory Neil Shapiro 
1024d0cef73dSGregory Neil Shapiro 	(void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf));
1025d0cef73dSGregory Neil Shapiro 	(void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf));
1026959366dcSGregory Neil Shapiro 
1027959366dcSGregory Neil Shapiro 	/* Force the df to disk if it's not there yet */
1028959366dcSGregory Neil Shapiro 	if (type == DATAFL_LETTER && e->e_dfp != NULL &&
1029959366dcSGregory Neil Shapiro 	    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
1030959366dcSGregory Neil Shapiro 	    errno != EINVAL)
1031959366dcSGregory Neil Shapiro 	{
1032959366dcSGregory Neil Shapiro 		syserr("!dup_queue_file: can't commit %s", f1buf);
1033959366dcSGregory Neil Shapiro 		/* NOTREACHED */
1034959366dcSGregory Neil Shapiro 	}
1035959366dcSGregory Neil Shapiro 
1036c2aa98e2SPeter Wemm 	if (link(f1buf, f2buf) < 0)
1037c2aa98e2SPeter Wemm 	{
103806f25ae9SGregory Neil Shapiro 		int save_errno = errno;
1039c2aa98e2SPeter Wemm 
1040c2aa98e2SPeter Wemm 		syserr("sendall: link(%s, %s)", f1buf, f2buf);
104106f25ae9SGregory Neil Shapiro 		if (save_errno == EEXIST)
1042c2aa98e2SPeter Wemm 		{
1043c2aa98e2SPeter Wemm 			if (unlink(f2buf) < 0)
1044c2aa98e2SPeter Wemm 			{
1045c2aa98e2SPeter Wemm 				syserr("!sendall: unlink(%s): permanent",
1046c2aa98e2SPeter Wemm 				       f2buf);
1047c2aa98e2SPeter Wemm 				/* NOTREACHED */
1048c2aa98e2SPeter Wemm 			}
1049c2aa98e2SPeter Wemm 			if (link(f1buf, f2buf) < 0)
1050c2aa98e2SPeter Wemm 			{
1051c2aa98e2SPeter Wemm 				syserr("!sendall: link(%s, %s): permanent",
1052c2aa98e2SPeter Wemm 				       f1buf, f2buf);
1053c2aa98e2SPeter Wemm 				/* NOTREACHED */
1054c2aa98e2SPeter Wemm 			}
1055c2aa98e2SPeter Wemm 		}
1056c2aa98e2SPeter Wemm 	}
105740266059SGregory Neil Shapiro 	SYNC_DIR(f2buf, true);
1058c2aa98e2SPeter Wemm }
105940266059SGregory Neil Shapiro /*
1060c2aa98e2SPeter Wemm **  DOFORK -- do a fork, retrying a couple of times on failure.
1061c2aa98e2SPeter Wemm **
1062c2aa98e2SPeter Wemm **	This MUST be a macro, since after a vfork we are running
1063c2aa98e2SPeter Wemm **	two processes on the same stack!!!
1064c2aa98e2SPeter Wemm **
1065c2aa98e2SPeter Wemm **	Parameters:
1066c2aa98e2SPeter Wemm **		none.
1067c2aa98e2SPeter Wemm **
1068c2aa98e2SPeter Wemm **	Returns:
1069c2aa98e2SPeter Wemm **		From a macro???  You've got to be kidding!
1070c2aa98e2SPeter Wemm **
1071c2aa98e2SPeter Wemm **	Side Effects:
1072c2aa98e2SPeter Wemm **		Modifies the ==> LOCAL <== variable 'pid', leaving:
1073c2aa98e2SPeter Wemm **			pid of child in parent, zero in child.
1074c2aa98e2SPeter Wemm **			-1 on unrecoverable error.
1075c2aa98e2SPeter Wemm **
1076c2aa98e2SPeter Wemm **	Notes:
1077c2aa98e2SPeter Wemm **		I'm awfully sorry this looks so awful.  That's
1078c2aa98e2SPeter Wemm **		vfork for you.....
1079c2aa98e2SPeter Wemm */
1080c2aa98e2SPeter Wemm 
1081c2aa98e2SPeter Wemm #define NFORKTRIES	5
1082c2aa98e2SPeter Wemm 
1083c2aa98e2SPeter Wemm #ifndef FORK
1084c2aa98e2SPeter Wemm # define FORK	fork
1085*5b0945b5SGregory Neil Shapiro #endif
1086c2aa98e2SPeter Wemm 
1087c2aa98e2SPeter Wemm #define DOFORK(fORKfN) \
1088c2aa98e2SPeter Wemm {\
1089c2aa98e2SPeter Wemm 	register int i;\
1090c2aa98e2SPeter Wemm \
1091c2aa98e2SPeter Wemm 	for (i = NFORKTRIES; --i >= 0; )\
1092c2aa98e2SPeter Wemm 	{\
1093c2aa98e2SPeter Wemm 		pid = fORKfN();\
1094c2aa98e2SPeter Wemm 		if (pid >= 0)\
1095c2aa98e2SPeter Wemm 			break;\
1096c2aa98e2SPeter Wemm 		if (i > 0)\
109706f25ae9SGregory Neil Shapiro 			(void) sleep((unsigned) NFORKTRIES - i);\
1098c2aa98e2SPeter Wemm 	}\
1099c2aa98e2SPeter Wemm }
110040266059SGregory Neil Shapiro /*
1101c2aa98e2SPeter Wemm **  DOFORK -- simple fork interface to DOFORK.
1102c2aa98e2SPeter Wemm **
1103c2aa98e2SPeter Wemm **	Parameters:
1104c2aa98e2SPeter Wemm **		none.
1105c2aa98e2SPeter Wemm **
1106c2aa98e2SPeter Wemm **	Returns:
1107c2aa98e2SPeter Wemm **		pid of child in parent.
1108c2aa98e2SPeter Wemm **		zero in child.
1109c2aa98e2SPeter Wemm **		-1 on error.
1110c2aa98e2SPeter Wemm **
1111c2aa98e2SPeter Wemm **	Side Effects:
1112c2aa98e2SPeter Wemm **		returns twice, once in parent and once in child.
1113c2aa98e2SPeter Wemm */
1114c2aa98e2SPeter Wemm 
11158774250cSGregory Neil Shapiro pid_t
1116c2aa98e2SPeter Wemm dofork()
1117c2aa98e2SPeter Wemm {
1118c2aa98e2SPeter Wemm 	register pid_t pid = -1;
1119c2aa98e2SPeter Wemm 
1120c2aa98e2SPeter Wemm 	DOFORK(fork);
112106f25ae9SGregory Neil Shapiro 	return pid;
1122c2aa98e2SPeter Wemm }
112340266059SGregory Neil Shapiro 
112440266059SGregory Neil Shapiro /*
112540266059SGregory Neil Shapiro **  COLONCMP -- compare host-signatures up to first ':' or EOS
112640266059SGregory Neil Shapiro **
112740266059SGregory Neil Shapiro **	This takes two strings which happen to be host-signatures and
112840266059SGregory Neil Shapiro **	compares them. If the lowest preference portions of the MX-RR's
112940266059SGregory Neil Shapiro **	match (up to ':' or EOS, whichever is first), then we have
113040266059SGregory Neil Shapiro **	match. This is used for coattail-piggybacking messages during
113140266059SGregory Neil Shapiro **	message delivery.
113240266059SGregory Neil Shapiro **	If the signatures are the same up to the first ':' the remainder of
113340266059SGregory Neil Shapiro **	the signatures are then compared with a normal strcmp(). This saves
113440266059SGregory Neil Shapiro **	re-examining the first part of the signatures.
113540266059SGregory Neil Shapiro **
113640266059SGregory Neil Shapiro **	Parameters:
113740266059SGregory Neil Shapiro **		a - first host-signature
113840266059SGregory Neil Shapiro **		b - second host-signature
113940266059SGregory Neil Shapiro **
114040266059SGregory Neil Shapiro **	Returns:
114140266059SGregory Neil Shapiro **		HS_MATCH_NO -- no "match".
114240266059SGregory Neil Shapiro **		HS_MATCH_FIRST -- "match" for the first MX preference
114340266059SGregory Neil Shapiro **			(up to the first colon (':')).
114440266059SGregory Neil Shapiro **		HS_MATCH_FULL -- match for the entire MX record.
114540266059SGregory Neil Shapiro **
114640266059SGregory Neil Shapiro **	Side Effects:
114740266059SGregory Neil Shapiro **		none.
114840266059SGregory Neil Shapiro */
114940266059SGregory Neil Shapiro 
115040266059SGregory Neil Shapiro #define HS_MATCH_NO	0
115140266059SGregory Neil Shapiro #define HS_MATCH_FIRST	1
115240266059SGregory Neil Shapiro #define HS_MATCH_FULL	2
115340266059SGregory Neil Shapiro 
115440266059SGregory Neil Shapiro static int
115540266059SGregory Neil Shapiro coloncmp(a, b)
115640266059SGregory Neil Shapiro 	register const char *a;
115740266059SGregory Neil Shapiro 	register const char *b;
115840266059SGregory Neil Shapiro {
115940266059SGregory Neil Shapiro 	int ret = HS_MATCH_NO;
116040266059SGregory Neil Shapiro 	int braclev = 0;
116140266059SGregory Neil Shapiro 
116240266059SGregory Neil Shapiro 	while (*a == *b++)
116340266059SGregory Neil Shapiro 	{
116440266059SGregory Neil Shapiro 		/* Need to account for IPv6 bracketed addresses */
116540266059SGregory Neil Shapiro 		if (*a == '[')
116640266059SGregory Neil Shapiro 			braclev++;
11675ef517c0SGregory Neil Shapiro 		else if (*a == ']' && braclev > 0)
116840266059SGregory Neil Shapiro 			braclev--;
116940266059SGregory Neil Shapiro 		else if (*a == ':' && braclev <= 0)
117040266059SGregory Neil Shapiro 		{
117140266059SGregory Neil Shapiro 			ret = HS_MATCH_FIRST;
117240266059SGregory Neil Shapiro 			a++;
117340266059SGregory Neil Shapiro 			break;
117440266059SGregory Neil Shapiro 		}
117540266059SGregory Neil Shapiro 		else if (*a == '\0')
117640266059SGregory Neil Shapiro 			return HS_MATCH_FULL; /* a full match */
117740266059SGregory Neil Shapiro 		a++;
117840266059SGregory Neil Shapiro 	}
117940266059SGregory Neil Shapiro 	if (ret == HS_MATCH_NO &&
118040266059SGregory Neil Shapiro 	    braclev <= 0 &&
118140266059SGregory Neil Shapiro 	    ((*a == '\0' && *(b - 1) == ':') ||
118240266059SGregory Neil Shapiro 	     (*a == ':' && *(b - 1) == '\0')))
118340266059SGregory Neil Shapiro 		return HS_MATCH_FIRST;
118440266059SGregory Neil Shapiro 	if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
118540266059SGregory Neil Shapiro 		return HS_MATCH_FULL;
118640266059SGregory Neil Shapiro 
118740266059SGregory Neil Shapiro 	return ret;
118840266059SGregory Neil Shapiro }
1189e92d3f3fSGregory Neil Shapiro 
1190e92d3f3fSGregory Neil Shapiro /*
1191e92d3f3fSGregory Neil Shapiro **  SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
1192e92d3f3fSGregory Neil Shapiro **
1193e92d3f3fSGregory Neil Shapiro **	Parameters:
1194e92d3f3fSGregory Neil Shapiro **		e -- envelope
1195e92d3f3fSGregory Neil Shapiro **		tried_fallbacksmarthost -- has been tried already? (in/out)
1196e92d3f3fSGregory Neil Shapiro **		hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
1197e92d3f3fSGregory Neil Shapiro **		hbsz -- size of hostbuf
1198e92d3f3fSGregory Neil Shapiro **		status -- current delivery status
1199e92d3f3fSGregory Neil Shapiro **
1200e92d3f3fSGregory Neil Shapiro **	Returns:
1201e92d3f3fSGregory Neil Shapiro **		true iff FallbackSmartHost should be tried.
1202e92d3f3fSGregory Neil Shapiro */
1203e92d3f3fSGregory Neil Shapiro 
1204d0cef73dSGregory Neil Shapiro static bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int));
1205d0cef73dSGregory Neil Shapiro 
1206e92d3f3fSGregory Neil Shapiro static bool
1207e92d3f3fSGregory Neil Shapiro should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
1208e92d3f3fSGregory Neil Shapiro 	ENVELOPE *e;
1209e92d3f3fSGregory Neil Shapiro 	bool *tried_fallbacksmarthost;
1210e92d3f3fSGregory Neil Shapiro 	char *hostbuf;
1211e92d3f3fSGregory Neil Shapiro 	size_t hbsz;
1212e92d3f3fSGregory Neil Shapiro 	int status;
1213e92d3f3fSGregory Neil Shapiro {
1214e92d3f3fSGregory Neil Shapiro 	/*
12154e4196cbSGregory Neil Shapiro 	**  If the host was not found or a temporary failure occurred
12164e4196cbSGregory Neil Shapiro 	**  and a FallbackSmartHost is defined (and we have not yet
12174e4196cbSGregory Neil Shapiro 	**  tried it), then make one last try with it as the host.
1218e92d3f3fSGregory Neil Shapiro 	*/
1219e92d3f3fSGregory Neil Shapiro 
12204e4196cbSGregory Neil Shapiro 	if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
12214e4196cbSGregory Neil Shapiro 	    FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
1222e92d3f3fSGregory Neil Shapiro 	{
1223e92d3f3fSGregory Neil Shapiro 		*tried_fallbacksmarthost = true;
1224e92d3f3fSGregory Neil Shapiro 		expand(FallbackSmartHost, hostbuf, hbsz, e);
1225e92d3f3fSGregory Neil Shapiro 		if (!wordinclass(hostbuf, 'w'))
1226e92d3f3fSGregory Neil Shapiro 		{
1227e92d3f3fSGregory Neil Shapiro 			if (tTd(11, 1))
1228e92d3f3fSGregory Neil Shapiro 				sm_dprintf("one last try with FallbackSmartHost %s\n",
1229e92d3f3fSGregory Neil Shapiro 					   hostbuf);
1230e92d3f3fSGregory Neil Shapiro 			return true;
1231e92d3f3fSGregory Neil Shapiro 		}
1232e92d3f3fSGregory Neil Shapiro 	}
1233e92d3f3fSGregory Neil Shapiro 	return false;
1234e92d3f3fSGregory Neil Shapiro }
1235da7d7b9cSGregory Neil Shapiro 
123640266059SGregory Neil Shapiro /*
1237c2aa98e2SPeter Wemm **  DELIVER -- Deliver a message to a list of addresses.
1238c2aa98e2SPeter Wemm **
1239c2aa98e2SPeter Wemm **	This routine delivers to everyone on the same host as the
1240c2aa98e2SPeter Wemm **	user on the head of the list.  It is clever about mailers
1241c2aa98e2SPeter Wemm **	that don't handle multiple users.  It is NOT guaranteed
1242c2aa98e2SPeter Wemm **	that it will deliver to all these addresses however -- so
1243*5b0945b5SGregory Neil Shapiro **	deliver should be called once for each address on the list.
124440266059SGregory Neil Shapiro **	Deliver tries to be as opportunistic as possible about piggybacking
124540266059SGregory Neil Shapiro **	messages. Some definitions to make understanding easier follow below.
124640266059SGregory Neil Shapiro **	Piggybacking occurs when an existing connection to a mail host can
124740266059SGregory Neil Shapiro **	be used to send the same message to more than one recipient at the
124840266059SGregory Neil Shapiro **	same time. So "no piggybacking" means one message for one recipient
124940266059SGregory Neil Shapiro **	per connection. "Intentional piggybacking" happens when the
125040266059SGregory Neil Shapiro **	recipients' host address (not the mail host address) is used to
125140266059SGregory Neil Shapiro **	attempt piggybacking. Recipients with the same host address
125240266059SGregory Neil Shapiro **	have the same mail host. "Coincidental piggybacking" relies on
125340266059SGregory Neil Shapiro **	piggybacking based on all the mail host addresses in the MX-RR. This
125440266059SGregory Neil Shapiro **	is "coincidental" in the fact it could not be predicted until the
125540266059SGregory Neil Shapiro **	MX Resource Records for the hosts were obtained and examined. For
125640266059SGregory Neil Shapiro **	example (preference order and equivalence is important, not values):
125740266059SGregory Neil Shapiro **		domain1 IN MX 10 mxhost-A
125840266059SGregory Neil Shapiro **			IN MX 20 mxhost-B
125940266059SGregory Neil Shapiro **		domain2 IN MX  4 mxhost-A
126040266059SGregory Neil Shapiro **			IN MX  8 mxhost-B
126140266059SGregory Neil Shapiro **	Domain1 and domain2 can piggyback the same message to mxhost-A or
126240266059SGregory Neil Shapiro **	mxhost-B (if mxhost-A cannot be reached).
126340266059SGregory Neil Shapiro **	"Coattail piggybacking" relaxes the strictness of "coincidental
126440266059SGregory Neil Shapiro **	piggybacking" in the hope that most significant (lowest value)
126540266059SGregory Neil Shapiro **	MX preference host(s) can create more piggybacking. For example
126640266059SGregory Neil Shapiro **	(again, preference order and equivalence is important, not values):
126740266059SGregory Neil Shapiro **		domain3 IN MX 100 mxhost-C
126840266059SGregory Neil Shapiro **			IN MX 100 mxhost-D
126940266059SGregory Neil Shapiro **			IN MX 200 mxhost-E
127040266059SGregory Neil Shapiro **		domain4 IN MX  50 mxhost-C
127140266059SGregory Neil Shapiro **			IN MX  50 mxhost-D
127240266059SGregory Neil Shapiro **			IN MX  80 mxhost-F
127340266059SGregory Neil Shapiro **	A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
127440266059SGregory Neil Shapiro **	is available. Same with mxhost-D because in both RR's the preference
127540266059SGregory Neil Shapiro **	value is the same as mxhost-C, respectively.
127640266059SGregory Neil Shapiro **	So deliver attempts coattail piggybacking when possible. If the
127740266059SGregory Neil Shapiro **	first MX preference level hosts cannot be used then the piggybacking
127840266059SGregory Neil Shapiro **	reverts to coincidental piggybacking. Using the above example you
127940266059SGregory Neil Shapiro **	cannot deliver to mxhost-F for domain3 regardless of preference value.
128040266059SGregory Neil Shapiro **	("Coattail" from "riding on the coattails of your predecessor" meaning
128140266059SGregory Neil Shapiro **	gaining benefit from a predecessor effort with no or little addition
128240266059SGregory Neil Shapiro **	effort. The predecessor here being the preceding MX RR).
1283c2aa98e2SPeter Wemm **
1284c2aa98e2SPeter Wemm **	Parameters:
1285c2aa98e2SPeter Wemm **		e -- the envelope to deliver.
1286c2aa98e2SPeter Wemm **		firstto -- head of the address list to deliver to.
1287c2aa98e2SPeter Wemm **
1288c2aa98e2SPeter Wemm **	Returns:
1289c2aa98e2SPeter Wemm **		zero -- successfully delivered.
1290c2aa98e2SPeter Wemm **		else -- some failure, see ExitStat for more info.
1291c2aa98e2SPeter Wemm **
1292c2aa98e2SPeter Wemm **	Side Effects:
1293c2aa98e2SPeter Wemm **		The standard input is passed off to someone.
1294c2aa98e2SPeter Wemm */
1295c2aa98e2SPeter Wemm 
129606f25ae9SGregory Neil Shapiro static int
1297c2aa98e2SPeter Wemm deliver(e, firstto)
1298c2aa98e2SPeter Wemm 	register ENVELOPE *e;
1299c2aa98e2SPeter Wemm 	ADDRESS *firstto;
1300c2aa98e2SPeter Wemm {
1301c2aa98e2SPeter Wemm 	char *host;			/* host being sent to */
1302c2aa98e2SPeter Wemm 	char *user;			/* user being sent to */
1303c2aa98e2SPeter Wemm 	char **pvp;
1304c2aa98e2SPeter Wemm 	register char **mvp;
1305c2aa98e2SPeter Wemm 	register char *p;
1306c2aa98e2SPeter Wemm 	register MAILER *m;		/* mailer for this recipient */
1307c2aa98e2SPeter Wemm 	ADDRESS *volatile ctladdr;
130840266059SGregory Neil Shapiro #if HASSETUSERCONTEXT
1309c2aa98e2SPeter Wemm 	ADDRESS *volatile contextaddr = NULL;
1310*5b0945b5SGregory Neil Shapiro #endif
1311c2aa98e2SPeter Wemm 	register MCI *volatile mci;
131240266059SGregory Neil Shapiro 	register ADDRESS *SM_NONVOLATILE to = firstto;
131340266059SGregory Neil Shapiro 	volatile bool clever = false;	/* running user smtp to this mailer */
1314c2aa98e2SPeter Wemm 	ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
1315c2aa98e2SPeter Wemm 	int rcode;			/* response code */
131640266059SGregory Neil Shapiro 	SM_NONVOLATILE int lmtp_rcode = EX_OK;
131740266059SGregory Neil Shapiro 	SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
131840266059SGregory Neil Shapiro 	SM_NONVOLATILE int hostnum = 0;	/* current MX host index */
1319c2aa98e2SPeter Wemm 	char *firstsig;			/* signature of firstto */
132040266059SGregory Neil Shapiro 	volatile pid_t pid = -1;
1321c2aa98e2SPeter Wemm 	char *volatile curhost;
132240266059SGregory Neil Shapiro 	SM_NONVOLATILE unsigned short port = 0;
132340266059SGregory Neil Shapiro 	SM_NONVOLATILE time_t enough = 0;
132406f25ae9SGregory Neil Shapiro #if NETUNIX
132540266059SGregory Neil Shapiro 	char *SM_NONVOLATILE mux_path = NULL;	/* path to UNIX domain socket */
1326*5b0945b5SGregory Neil Shapiro #endif
1327c2aa98e2SPeter Wemm 	time_t xstart;
1328c2aa98e2SPeter Wemm 	bool suidwarn;
1329c2aa98e2SPeter Wemm 	bool anyok;			/* at least one address was OK */
133040266059SGregory Neil Shapiro 	SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
133106f25ae9SGregory Neil Shapiro 	bool ovr;
133240266059SGregory Neil Shapiro 	bool quarantine;
1333*5b0945b5SGregory Neil Shapiro #if STARTTLS
1334*5b0945b5SGregory Neil Shapiro 	/* 0: try TLS, 1: try without TLS again, >1: don't try again */
1335*5b0945b5SGregory Neil Shapiro 	int tlsstate;
1336*5b0945b5SGregory Neil Shapiro # if DANE
1337*5b0945b5SGregory Neil Shapiro 	dane_vrfy_ctx_T	dane_vrfy_ctx;
1338*5b0945b5SGregory Neil Shapiro 	STAB *ste;
1339*5b0945b5SGregory Neil Shapiro # endif
1340*5b0945b5SGregory Neil Shapiro #endif
1341*5b0945b5SGregory Neil Shapiro #if STARTTLS || SASL
1342*5b0945b5SGregory Neil Shapiro 	int dotpos;
1343*5b0945b5SGregory Neil Shapiro 
1344*5b0945b5SGregory Neil Shapiro # define RM_TRAIL_DOT(name)				\
1345*5b0945b5SGregory Neil Shapiro 	do {						\
1346*5b0945b5SGregory Neil Shapiro 		dotpos = strlen(name) - 1;		\
1347*5b0945b5SGregory Neil Shapiro 		if (dotpos >= 0)			\
1348*5b0945b5SGregory Neil Shapiro 		{					\
1349*5b0945b5SGregory Neil Shapiro 			if (name[dotpos] == '.')	\
1350*5b0945b5SGregory Neil Shapiro 				name[dotpos] = '\0';	\
1351*5b0945b5SGregory Neil Shapiro 			else				\
1352*5b0945b5SGregory Neil Shapiro 				dotpos = -1;		\
1353*5b0945b5SGregory Neil Shapiro 		}					\
1354*5b0945b5SGregory Neil Shapiro 	} while (0)
1355*5b0945b5SGregory Neil Shapiro 
1356*5b0945b5SGregory Neil Shapiro # define FIX_TRAIL_DOT(name)				\
1357*5b0945b5SGregory Neil Shapiro 	do {						\
1358*5b0945b5SGregory Neil Shapiro 		if (dotpos >= 0)			\
1359*5b0945b5SGregory Neil Shapiro 			name[dotpos] = '.';		\
1360*5b0945b5SGregory Neil Shapiro 	} while (0)
1361*5b0945b5SGregory Neil Shapiro 
1362*5b0945b5SGregory Neil Shapiro #endif
136306f25ae9SGregory Neil Shapiro 	int strsize;
136406f25ae9SGregory Neil Shapiro 	int rcptcount;
136540266059SGregory Neil Shapiro 	int ret;
136606f25ae9SGregory Neil Shapiro 	static int tobufsize = 0;
136706f25ae9SGregory Neil Shapiro 	static char *tobuf = NULL;
136840266059SGregory Neil Shapiro 	char *rpath;	/* translated return path */
1369c2aa98e2SPeter Wemm 	int mpvect[2];
1370c2aa98e2SPeter Wemm 	int rpvect[2];
137106f25ae9SGregory Neil Shapiro 	char *mxhosts[MAXMXHOSTS + 1];
1372c2aa98e2SPeter Wemm 	char *pv[MAXPV + 1];
1373c2aa98e2SPeter Wemm 	char buf[MAXNAME + 1];
137494c01205SGregory Neil Shapiro 	char cbuf[MAXPATHLEN];
1375c2aa98e2SPeter Wemm 
1376c2aa98e2SPeter Wemm 	errno = 0;
1377d0cef73dSGregory Neil Shapiro 	SM_REQUIRE(firstto != NULL);	/* same as to */
137806f25ae9SGregory Neil Shapiro 	if (!QS_IS_OK(to->q_state))
137906f25ae9SGregory Neil Shapiro 		return 0;
1380c2aa98e2SPeter Wemm 
1381c2aa98e2SPeter Wemm 	suidwarn = geteuid() == 0;
1382c2aa98e2SPeter Wemm 
1383d0cef73dSGregory Neil Shapiro 	SM_REQUIRE(e != NULL);
1384c2aa98e2SPeter Wemm 	m = to->q_mailer;
1385c2aa98e2SPeter Wemm 	host = to->q_host;
1386c2aa98e2SPeter Wemm 	CurEnv = e;			/* just in case */
1387c2aa98e2SPeter Wemm 	e->e_statmsg = NULL;
1388c2aa98e2SPeter Wemm 	SmtpError[0] = '\0';
1389c2aa98e2SPeter Wemm 	xstart = curtime();
1390*5b0945b5SGregory Neil Shapiro #if STARTTLS
1391*5b0945b5SGregory Neil Shapiro 	tlsstate = 0;
1392*5b0945b5SGregory Neil Shapiro # if DANE
1393*5b0945b5SGregory Neil Shapiro 	memset(&dane_vrfy_ctx, '\0', sizeof(dane_vrfy_ctx));
1394*5b0945b5SGregory Neil Shapiro 	ste = NULL;
1395*5b0945b5SGregory Neil Shapiro # endif
1396*5b0945b5SGregory Neil Shapiro #endif
1397c2aa98e2SPeter Wemm 
1398c2aa98e2SPeter Wemm 	if (tTd(10, 1))
139940266059SGregory Neil Shapiro 		sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
1400c2aa98e2SPeter Wemm 			e->e_id, m->m_name, host, to->q_user);
1401c2aa98e2SPeter Wemm 	if (tTd(10, 100))
140240266059SGregory Neil Shapiro 		printopenfds(false);
1403c2aa98e2SPeter Wemm 
1404c2aa98e2SPeter Wemm 	/*
140540266059SGregory Neil Shapiro 	**  Clear {client_*} macros if this is a bounce message to
1406c2aa98e2SPeter Wemm 	**  prevent rejection by check_compat ruleset.
1407c2aa98e2SPeter Wemm 	*/
1408c2aa98e2SPeter Wemm 
1409c2aa98e2SPeter Wemm 	if (bitset(EF_RESPONSE, e->e_flags))
1410c2aa98e2SPeter Wemm 	{
141140266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
1412e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
141340266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
141440266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
141540266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
1416c2aa98e2SPeter Wemm 	}
1417c2aa98e2SPeter Wemm 
141840266059SGregory Neil Shapiro 	SM_TRY
141940266059SGregory Neil Shapiro 	{
142040266059SGregory Neil Shapiro 	ADDRESS *skip_back = NULL;
142140266059SGregory Neil Shapiro 
1422c2aa98e2SPeter Wemm 	/*
1423c2aa98e2SPeter Wemm 	**  Do initial argv setup.
1424c2aa98e2SPeter Wemm 	**	Insert the mailer name.  Notice that $x expansion is
1425c2aa98e2SPeter Wemm 	**	NOT done on the mailer name.  Then, if the mailer has
1426c2aa98e2SPeter Wemm 	**	a picky -f flag, we insert it as appropriate.  This
1427c2aa98e2SPeter Wemm 	**	code does not check for 'pv' overflow; this places a
1428c2aa98e2SPeter Wemm 	**	manifest lower limit of 4 for MAXPV.
1429c2aa98e2SPeter Wemm 	**		The from address rewrite is expected to make
1430c2aa98e2SPeter Wemm 	**		the address relative to the other end.
1431c2aa98e2SPeter Wemm 	*/
1432c2aa98e2SPeter Wemm 
1433c2aa98e2SPeter Wemm 	/* rewrite from address, using rewriting rules */
1434c2aa98e2SPeter Wemm 	rcode = EX_OK;
1435d0cef73dSGregory Neil Shapiro 	SM_ASSERT(e->e_from.q_mailer != NULL);
1436c2aa98e2SPeter Wemm 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
1437c2aa98e2SPeter Wemm 		p = e->e_sender;
1438c2aa98e2SPeter Wemm 	else
1439c2aa98e2SPeter Wemm 		p = e->e_from.q_paddr;
144040266059SGregory Neil Shapiro 	rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
1441da7d7b9cSGregory Neil Shapiro 	if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags))
1442da7d7b9cSGregory Neil Shapiro 		goto cleanup;
14439bd497b8SGregory Neil Shapiro 	if (strlen(rpath) > MAXNAME)
1444c2aa98e2SPeter Wemm 	{
144540266059SGregory Neil Shapiro 		rpath = shortenstring(rpath, MAXSHORTSTR);
144640266059SGregory Neil Shapiro 
144740266059SGregory Neil Shapiro 		/* avoid bogus errno */
144840266059SGregory Neil Shapiro 		errno = 0;
144940266059SGregory Neil Shapiro 		syserr("remotename: huge return path %s", rpath);
1450c2aa98e2SPeter Wemm 	}
145140266059SGregory Neil Shapiro 	rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
145240266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'g', rpath);
145340266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'h', host);
1454c2aa98e2SPeter Wemm 	Errors = 0;
1455c2aa98e2SPeter Wemm 	pvp = pv;
1456c2aa98e2SPeter Wemm 	*pvp++ = m->m_argv[0];
1457c2aa98e2SPeter Wemm 
1458e92d3f3fSGregory Neil Shapiro 	/* ignore long term host status information if mailer flag W is set */
1459e92d3f3fSGregory Neil Shapiro 	if (bitnset(M_NOHOSTSTAT, m->m_flags))
1460e92d3f3fSGregory Neil Shapiro 		IgnoreHostStatus = true;
1461e92d3f3fSGregory Neil Shapiro 
1462c2aa98e2SPeter Wemm 	/* insert -f or -r flag as appropriate */
146306f25ae9SGregory Neil Shapiro 	if (FromFlag &&
146406f25ae9SGregory Neil Shapiro 	    (bitnset(M_FOPT, m->m_flags) ||
146506f25ae9SGregory Neil Shapiro 	     bitnset(M_ROPT, m->m_flags)))
1466c2aa98e2SPeter Wemm 	{
1467c2aa98e2SPeter Wemm 		if (bitnset(M_FOPT, m->m_flags))
1468c2aa98e2SPeter Wemm 			*pvp++ = "-f";
1469c2aa98e2SPeter Wemm 		else
1470c2aa98e2SPeter Wemm 			*pvp++ = "-r";
147140266059SGregory Neil Shapiro 		*pvp++ = rpath;
1472c2aa98e2SPeter Wemm 	}
1473c2aa98e2SPeter Wemm 
1474c2aa98e2SPeter Wemm 	/*
1475c2aa98e2SPeter Wemm 	**  Append the other fixed parts of the argv.  These run
1476c2aa98e2SPeter Wemm 	**  up to the first entry containing "$u".  There can only
1477c2aa98e2SPeter Wemm 	**  be one of these, and there are only a few more slots
1478c2aa98e2SPeter Wemm 	**  in the pv after it.
1479c2aa98e2SPeter Wemm 	*/
1480c2aa98e2SPeter Wemm 
1481c2aa98e2SPeter Wemm 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1482c2aa98e2SPeter Wemm 	{
1483c2aa98e2SPeter Wemm 		/* can't use strchr here because of sign extension problems */
1484c2aa98e2SPeter Wemm 		while (*p != '\0')
1485c2aa98e2SPeter Wemm 		{
1486c2aa98e2SPeter Wemm 			if ((*p++ & 0377) == MACROEXPAND)
1487c2aa98e2SPeter Wemm 			{
1488c2aa98e2SPeter Wemm 				if (*p == 'u')
1489c2aa98e2SPeter Wemm 					break;
1490c2aa98e2SPeter Wemm 			}
1491c2aa98e2SPeter Wemm 		}
1492c2aa98e2SPeter Wemm 
1493c2aa98e2SPeter Wemm 		if (*p != '\0')
1494c2aa98e2SPeter Wemm 			break;
1495c2aa98e2SPeter Wemm 
1496c2aa98e2SPeter Wemm 		/* this entry is safe -- go ahead and process it */
1497d0cef73dSGregory Neil Shapiro 		expand(*mvp, buf, sizeof(buf), e);
149840266059SGregory Neil Shapiro 		*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1499c2aa98e2SPeter Wemm 		if (pvp >= &pv[MAXPV - 3])
1500c2aa98e2SPeter Wemm 		{
150106f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 Too many parameters to %s before $u",
150206f25ae9SGregory Neil Shapiro 			       pv[0]);
150340266059SGregory Neil Shapiro 			rcode = -1;
150440266059SGregory Neil Shapiro 			goto cleanup;
1505c2aa98e2SPeter Wemm 		}
1506c2aa98e2SPeter Wemm 	}
1507c2aa98e2SPeter Wemm 
1508c2aa98e2SPeter Wemm 	/*
1509c2aa98e2SPeter Wemm 	**  If we have no substitution for the user name in the argument
1510c2aa98e2SPeter Wemm 	**  list, we know that we must supply the names otherwise -- and
1511c2aa98e2SPeter Wemm 	**  SMTP is the answer!!
1512c2aa98e2SPeter Wemm 	*/
1513c2aa98e2SPeter Wemm 
1514c2aa98e2SPeter Wemm 	if (*mvp == NULL)
1515c2aa98e2SPeter Wemm 	{
1516602a2b1bSGregory Neil Shapiro 		/* running LMTP or SMTP */
151740266059SGregory Neil Shapiro 		clever = true;
1518c2aa98e2SPeter Wemm 		*pvp = NULL;
1519da7d7b9cSGregory Neil Shapiro 		setbitn(M_xSMTP, m->m_flags);
1520c2aa98e2SPeter Wemm 	}
1521602a2b1bSGregory Neil Shapiro 	else if (bitnset(M_LMTP, m->m_flags))
1522602a2b1bSGregory Neil Shapiro 	{
1523602a2b1bSGregory Neil Shapiro 		/* not running LMTP */
1524602a2b1bSGregory Neil Shapiro 		sm_syslog(LOG_ERR, NULL,
1525602a2b1bSGregory Neil Shapiro 			  "Warning: mailer %s: LMTP flag (F=z) turned off",
1526602a2b1bSGregory Neil Shapiro 			  m->m_name);
1527602a2b1bSGregory Neil Shapiro 		clrbitn(M_LMTP, m->m_flags);
1528602a2b1bSGregory Neil Shapiro 	}
1529c2aa98e2SPeter Wemm 
1530c2aa98e2SPeter Wemm 	/*
1531c2aa98e2SPeter Wemm 	**  At this point *mvp points to the argument with $u.  We
1532c2aa98e2SPeter Wemm 	**  run through our address list and append all the addresses
1533c2aa98e2SPeter Wemm 	**  we can.  If we run out of space, do not fret!  We can
1534c2aa98e2SPeter Wemm 	**  always send another copy later.
1535c2aa98e2SPeter Wemm 	*/
1536c2aa98e2SPeter Wemm 
153706f25ae9SGregory Neil Shapiro 	e->e_to = NULL;
153806f25ae9SGregory Neil Shapiro 	strsize = 2;
153906f25ae9SGregory Neil Shapiro 	rcptcount = 0;
1540c2aa98e2SPeter Wemm 	ctladdr = NULL;
154140266059SGregory Neil Shapiro 	if (firstto->q_signature == NULL)
154240266059SGregory Neil Shapiro 		firstto->q_signature = hostsignature(firstto->q_mailer,
1543*5b0945b5SGregory Neil Shapiro 						     firstto->q_host,
1544*5b0945b5SGregory Neil Shapiro 						     firstto->q_flags & QSECURE);
154540266059SGregory Neil Shapiro 	firstsig = firstto->q_signature;
154640266059SGregory Neil Shapiro 
1547c2aa98e2SPeter Wemm 	for (; to != NULL; to = to->q_next)
1548c2aa98e2SPeter Wemm 	{
1549c2aa98e2SPeter Wemm 		/* avoid sending multiple recipients to dumb mailers */
155006f25ae9SGregory Neil Shapiro 		if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
155106f25ae9SGregory Neil Shapiro 			break;
1552c2aa98e2SPeter Wemm 
1553c2aa98e2SPeter Wemm 		/* if already sent or not for this host, don't send */
155440266059SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
1555c2aa98e2SPeter Wemm 			continue;
1556c2aa98e2SPeter Wemm 
155740266059SGregory Neil Shapiro 		/*
155840266059SGregory Neil Shapiro 		**  Must be same mailer to keep grouping rcpts.
155940266059SGregory Neil Shapiro 		**  If mailers don't match: continue; sendqueue is not
156040266059SGregory Neil Shapiro 		**  sorted by mailers, so don't break;
156140266059SGregory Neil Shapiro 		*/
156240266059SGregory Neil Shapiro 
156340266059SGregory Neil Shapiro 		if (to->q_mailer != firstto->q_mailer)
156440266059SGregory Neil Shapiro 			continue;
156540266059SGregory Neil Shapiro 
156640266059SGregory Neil Shapiro 		if (to->q_signature == NULL) /* for safety */
156740266059SGregory Neil Shapiro 			to->q_signature = hostsignature(to->q_mailer,
1568*5b0945b5SGregory Neil Shapiro 							to->q_host,
1569*5b0945b5SGregory Neil Shapiro 							to->q_flags & QSECURE);
157040266059SGregory Neil Shapiro 
157140266059SGregory Neil Shapiro 		/*
157240266059SGregory Neil Shapiro 		**  This is for coincidental and tailcoat piggybacking messages
157340266059SGregory Neil Shapiro 		**  to the same mail host. While the signatures are identical
157440266059SGregory Neil Shapiro 		**  (that's the MX-RR's are identical) we can do coincidental
157540266059SGregory Neil Shapiro 		**  piggybacking. We try hard for coattail piggybacking
157640266059SGregory Neil Shapiro 		**  with the same mail host when the next recipient has the
157740266059SGregory Neil Shapiro 		**  same host at lowest preference. It may be that this
157840266059SGregory Neil Shapiro 		**  won't work out, so 'skip_back' is maintained if a backup
157940266059SGregory Neil Shapiro 		**  to coincidental piggybacking or full signature must happen.
158040266059SGregory Neil Shapiro 		*/
158140266059SGregory Neil Shapiro 
158240266059SGregory Neil Shapiro 		ret = firstto == to ? HS_MATCH_FULL :
158340266059SGregory Neil Shapiro 				      coloncmp(to->q_signature, firstsig);
158440266059SGregory Neil Shapiro 		if (ret == HS_MATCH_FULL)
158540266059SGregory Neil Shapiro 			skip_back = to;
158640266059SGregory Neil Shapiro 		else if (ret == HS_MATCH_NO)
158706f25ae9SGregory Neil Shapiro 			break;
158806f25ae9SGregory Neil Shapiro 
158940266059SGregory Neil Shapiro 		if (!clever)
159040266059SGregory Neil Shapiro 		{
159140266059SGregory Neil Shapiro 			/* avoid overflowing tobuf */
159240266059SGregory Neil Shapiro 			strsize += strlen(to->q_paddr) + 1;
159340266059SGregory Neil Shapiro 			if (strsize > TOBUFSIZE)
159440266059SGregory Neil Shapiro 				break;
159540266059SGregory Neil Shapiro 		}
159640266059SGregory Neil Shapiro 
159706f25ae9SGregory Neil Shapiro 		if (++rcptcount > to->q_mailer->m_maxrcpt)
159806f25ae9SGregory Neil Shapiro 			break;
1599c2aa98e2SPeter Wemm 
1600c2aa98e2SPeter Wemm 		if (tTd(10, 1))
1601c2aa98e2SPeter Wemm 		{
160240266059SGregory Neil Shapiro 			sm_dprintf("\nsend to ");
1603e92d3f3fSGregory Neil Shapiro 			printaddr(sm_debug_file(), to, false);
1604c2aa98e2SPeter Wemm 		}
1605c2aa98e2SPeter Wemm 
1606c2aa98e2SPeter Wemm 		/* compute effective uid/gid when sending */
1607c2aa98e2SPeter Wemm 		if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
160840266059SGregory Neil Shapiro #if HASSETUSERCONTEXT
1609c2aa98e2SPeter Wemm 			contextaddr = ctladdr = getctladdr(to);
1610*5b0945b5SGregory Neil Shapiro #else
161140266059SGregory Neil Shapiro 			ctladdr = getctladdr(to);
1612*5b0945b5SGregory Neil Shapiro #endif
1613c2aa98e2SPeter Wemm 
1614c2aa98e2SPeter Wemm 		if (tTd(10, 2))
1615c2aa98e2SPeter Wemm 		{
161640266059SGregory Neil Shapiro 			sm_dprintf("ctladdr=");
1617e92d3f3fSGregory Neil Shapiro 			printaddr(sm_debug_file(), ctladdr, false);
1618c2aa98e2SPeter Wemm 		}
1619c2aa98e2SPeter Wemm 
1620c2aa98e2SPeter Wemm 		user = to->q_user;
1621c2aa98e2SPeter Wemm 		e->e_to = to->q_paddr;
1622c2aa98e2SPeter Wemm 
1623c2aa98e2SPeter Wemm 		/*
1624c2aa98e2SPeter Wemm 		**  Check to see that these people are allowed to
1625c2aa98e2SPeter Wemm 		**  talk to each other.
162642e5d165SGregory Neil Shapiro 		**  Check also for overflow of e_msgsize.
1627c2aa98e2SPeter Wemm 		*/
1628c2aa98e2SPeter Wemm 
162942e5d165SGregory Neil Shapiro 		if (m->m_maxsize != 0 &&
163042e5d165SGregory Neil Shapiro 		    (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
1631c2aa98e2SPeter Wemm 		{
1632c2aa98e2SPeter Wemm 			e->e_flags |= EF_NO_BODY_RETN;
1633c2aa98e2SPeter Wemm 			if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
1634c2aa98e2SPeter Wemm 				to->q_status = "5.2.3";
1635c2aa98e2SPeter Wemm 			else
1636c2aa98e2SPeter Wemm 				to->q_status = "5.3.4";
163740266059SGregory Neil Shapiro 
163806f25ae9SGregory Neil Shapiro 			/* set to->q_rstatus = NULL; or to the following? */
163906f25ae9SGregory Neil Shapiro 			usrerrenh(to->q_status,
164006f25ae9SGregory Neil Shapiro 				  "552 Message is too large; %ld bytes max",
164106f25ae9SGregory Neil Shapiro 				  m->m_maxsize);
164240266059SGregory Neil Shapiro 			markfailure(e, to, NULL, EX_UNAVAILABLE, false);
164306f25ae9SGregory Neil Shapiro 			giveresponse(EX_UNAVAILABLE, to->q_status, m,
164440266059SGregory Neil Shapiro 				     NULL, ctladdr, xstart, e, to);
1645c2aa98e2SPeter Wemm 			continue;
1646c2aa98e2SPeter Wemm 		}
1647602a2b1bSGregory Neil Shapiro 		SM_SET_H_ERRNO(0);
164840266059SGregory Neil Shapiro 		ovr = true;
1649c2aa98e2SPeter Wemm 
1650c2aa98e2SPeter Wemm 		/* do config file checking of compatibility */
165140266059SGregory Neil Shapiro 		quarantine = (e->e_quarmsg != NULL);
165206f25ae9SGregory Neil Shapiro 		rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
1653959366dcSGregory Neil Shapiro 				e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
1654da7d7b9cSGregory Neil Shapiro 				e->e_id, NULL, NULL);
1655c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
1656c2aa98e2SPeter Wemm 		{
1657065a643dSPeter Wemm 			/* do in-code checking if not discarding */
1658065a643dSPeter Wemm 			if (!bitset(EF_DISCARD, e->e_flags))
165906f25ae9SGregory Neil Shapiro 			{
1660c2aa98e2SPeter Wemm 				rcode = checkcompat(to, e);
166140266059SGregory Neil Shapiro 				ovr = false;
166206f25ae9SGregory Neil Shapiro 			}
1663c2aa98e2SPeter Wemm 		}
1664c2aa98e2SPeter Wemm 		if (rcode != EX_OK)
1665c2aa98e2SPeter Wemm 		{
166606f25ae9SGregory Neil Shapiro 			markfailure(e, to, NULL, rcode, ovr);
166706f25ae9SGregory Neil Shapiro 			giveresponse(rcode, to->q_status, m,
166840266059SGregory Neil Shapiro 				     NULL, ctladdr, xstart, e, to);
1669c2aa98e2SPeter Wemm 			continue;
1670c2aa98e2SPeter Wemm 		}
167140266059SGregory Neil Shapiro 		if (!quarantine && e->e_quarmsg != NULL)
167240266059SGregory Neil Shapiro 		{
167340266059SGregory Neil Shapiro 			/*
167440266059SGregory Neil Shapiro 			**  check_compat or checkcompat() has tried
167540266059SGregory Neil Shapiro 			**  to quarantine but that isn't supported.
167640266059SGregory Neil Shapiro 			**  Revert the attempt.
167740266059SGregory Neil Shapiro 			*/
167840266059SGregory Neil Shapiro 
167940266059SGregory Neil Shapiro 			e->e_quarmsg = NULL;
168040266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
168140266059SGregory Neil Shapiro 				  macid("{quarantine}"), "");
168240266059SGregory Neil Shapiro 		}
1683065a643dSPeter Wemm 		if (bitset(EF_DISCARD, e->e_flags))
1684065a643dSPeter Wemm 		{
1685065a643dSPeter Wemm 			if (tTd(10, 5))
1686065a643dSPeter Wemm 			{
168740266059SGregory Neil Shapiro 				sm_dprintf("deliver: discarding recipient ");
1688e92d3f3fSGregory Neil Shapiro 				printaddr(sm_debug_file(), to, false);
1689065a643dSPeter Wemm 			}
1690065a643dSPeter Wemm 
169106f25ae9SGregory Neil Shapiro 			/* pretend the message was sent */
169206f25ae9SGregory Neil Shapiro 			/* XXX should we log something here? */
169306f25ae9SGregory Neil Shapiro 			to->q_state = QS_DISCARDED;
169406f25ae9SGregory Neil Shapiro 
1695065a643dSPeter Wemm 			/*
1696065a643dSPeter Wemm 			**  Remove discard bit to prevent discard of
169706f25ae9SGregory Neil Shapiro 			**  future recipients.  This is safe because the
169806f25ae9SGregory Neil Shapiro 			**  true "global discard" has been handled before
169906f25ae9SGregory Neil Shapiro 			**  we get here.
1700065a643dSPeter Wemm 			*/
1701065a643dSPeter Wemm 
170206f25ae9SGregory Neil Shapiro 			e->e_flags &= ~EF_DISCARD;
1703065a643dSPeter Wemm 			continue;
1704065a643dSPeter Wemm 		}
1705c2aa98e2SPeter Wemm 
1706c2aa98e2SPeter Wemm 		/*
1707c2aa98e2SPeter Wemm 		**  Strip quote bits from names if the mailer is dumb
1708c2aa98e2SPeter Wemm 		**	about them.
1709c2aa98e2SPeter Wemm 		*/
1710c2aa98e2SPeter Wemm 
1711c2aa98e2SPeter Wemm 		if (bitnset(M_STRIPQ, m->m_flags))
1712c2aa98e2SPeter Wemm 		{
1713c2aa98e2SPeter Wemm 			stripquotes(user);
1714c2aa98e2SPeter Wemm 			stripquotes(host);
1715c2aa98e2SPeter Wemm 		}
1716e92d3f3fSGregory Neil Shapiro 
171713bd1963SGregory Neil Shapiro 		/*
1718b6bacd31SGregory Neil Shapiro 		**  Strip all leading backslashes if requested and the
171913bd1963SGregory Neil Shapiro 		**  next character is alphanumerical (the latter can
172013bd1963SGregory Neil Shapiro 		**  probably relaxed a bit, see RFC2821).
172113bd1963SGregory Neil Shapiro 		*/
172213bd1963SGregory Neil Shapiro 
172313bd1963SGregory Neil Shapiro 		if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
172413bd1963SGregory Neil Shapiro 			stripbackslash(user);
1725c2aa98e2SPeter Wemm 
1726c2aa98e2SPeter Wemm 		/* hack attack -- delivermail compatibility */
1727c2aa98e2SPeter Wemm 		if (m == ProgMailer && *user == '|')
1728c2aa98e2SPeter Wemm 			user++;
1729c2aa98e2SPeter Wemm 
1730c2aa98e2SPeter Wemm 		/*
1731c2aa98e2SPeter Wemm 		**  If an error message has already been given, don't
1732c2aa98e2SPeter Wemm 		**	bother to send to this address.
1733c2aa98e2SPeter Wemm 		**
1734c2aa98e2SPeter Wemm 		**	>>>>>>>>>> This clause assumes that the local mailer
1735c2aa98e2SPeter Wemm 		**	>> NOTE >> cannot do any further aliasing; that
1736c2aa98e2SPeter Wemm 		**	>>>>>>>>>> function is subsumed by sendmail.
1737c2aa98e2SPeter Wemm 		*/
1738c2aa98e2SPeter Wemm 
173906f25ae9SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state))
1740c2aa98e2SPeter Wemm 			continue;
1741c2aa98e2SPeter Wemm 
1742c2aa98e2SPeter Wemm 		/*
1743c2aa98e2SPeter Wemm 		**  See if this user name is "special".
1744c2aa98e2SPeter Wemm 		**	If the user name has a slash in it, assume that this
1745c2aa98e2SPeter Wemm 		**	is a file -- send it off without further ado.  Note
1746c2aa98e2SPeter Wemm 		**	that this type of addresses is not processed along
1747c2aa98e2SPeter Wemm 		**	with the others, so we fudge on the To person.
1748c2aa98e2SPeter Wemm 		*/
1749c2aa98e2SPeter Wemm 
1750c2aa98e2SPeter Wemm 		if (strcmp(m->m_mailer, "[FILE]") == 0)
1751c2aa98e2SPeter Wemm 		{
175240266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 'u', user);
1753c2aa98e2SPeter Wemm 			p = to->q_home;
1754c2aa98e2SPeter Wemm 			if (p == NULL && ctladdr != NULL)
1755c2aa98e2SPeter Wemm 				p = ctladdr->q_home;
175640266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 'z', p);
1757d0cef73dSGregory Neil Shapiro 			expand(m->m_argv[1], buf, sizeof(buf), e);
1758c2aa98e2SPeter Wemm 			if (strlen(buf) > 0)
1759c2aa98e2SPeter Wemm 				rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
1760c2aa98e2SPeter Wemm 			else
1761c2aa98e2SPeter Wemm 			{
1762c2aa98e2SPeter Wemm 				syserr("empty filename specification for mailer %s",
1763c2aa98e2SPeter Wemm 				       m->m_name);
1764c2aa98e2SPeter Wemm 				rcode = EX_CONFIG;
1765c2aa98e2SPeter Wemm 			}
176606f25ae9SGregory Neil Shapiro 			giveresponse(rcode, to->q_status, m, NULL,
176740266059SGregory Neil Shapiro 				     ctladdr, xstart, e, to);
176840266059SGregory Neil Shapiro 			markfailure(e, to, NULL, rcode, true);
1769c2aa98e2SPeter Wemm 			e->e_nsent++;
1770c2aa98e2SPeter Wemm 			if (rcode == EX_OK)
1771c2aa98e2SPeter Wemm 			{
177206f25ae9SGregory Neil Shapiro 				to->q_state = QS_SENT;
1773c2aa98e2SPeter Wemm 				if (bitnset(M_LOCALMAILER, m->m_flags) &&
1774c2aa98e2SPeter Wemm 				    bitset(QPINGONSUCCESS, to->q_flags))
1775c2aa98e2SPeter Wemm 				{
1776c2aa98e2SPeter Wemm 					to->q_flags |= QDELIVERED;
1777c2aa98e2SPeter Wemm 					to->q_status = "2.1.5";
177840266059SGregory Neil Shapiro 					(void) sm_io_fprintf(e->e_xfp,
177940266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
178040266059SGregory Neil Shapiro 							     "%s... Successfully delivered\n",
1781c2aa98e2SPeter Wemm 							     to->q_paddr);
1782c2aa98e2SPeter Wemm 				}
1783c2aa98e2SPeter Wemm 			}
1784c2aa98e2SPeter Wemm 			to->q_statdate = curtime();
178540266059SGregory Neil Shapiro 			markstats(e, to, STATS_NORMAL);
1786c2aa98e2SPeter Wemm 			continue;
1787c2aa98e2SPeter Wemm 		}
1788c2aa98e2SPeter Wemm 
1789c2aa98e2SPeter Wemm 		/*
1790c2aa98e2SPeter Wemm 		**  Address is verified -- add this user to mailer
1791c2aa98e2SPeter Wemm 		**  argv, and add it to the print list of recipients.
1792c2aa98e2SPeter Wemm 		*/
1793c2aa98e2SPeter Wemm 
1794c2aa98e2SPeter Wemm 		/* link together the chain of recipients */
1795c2aa98e2SPeter Wemm 		to->q_tchain = tochain;
1796c2aa98e2SPeter Wemm 		tochain = to;
179706f25ae9SGregory Neil Shapiro 		e->e_to = "[CHAIN]";
179806f25ae9SGregory Neil Shapiro 
179940266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'u', user);  /* to user */
1800c2aa98e2SPeter Wemm 		p = to->q_home;
1801c2aa98e2SPeter Wemm 		if (p == NULL && ctladdr != NULL)
1802c2aa98e2SPeter Wemm 			p = ctladdr->q_home;
180340266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'z', p);  /* user's home */
1804c2aa98e2SPeter Wemm 
180506f25ae9SGregory Neil Shapiro 		/* set the ${dsn_notify} macro if applicable */
180606f25ae9SGregory Neil Shapiro 		if (bitset(QHASNOTIFY, to->q_flags))
180706f25ae9SGregory Neil Shapiro 		{
180806f25ae9SGregory Neil Shapiro 			char notify[MAXLINE];
180906f25ae9SGregory Neil Shapiro 
181006f25ae9SGregory Neil Shapiro 			notify[0] = '\0';
181106f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONSUCCESS, to->q_flags))
181240266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "SUCCESS,",
1813d0cef73dSGregory Neil Shapiro 						  sizeof(notify));
181406f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONFAILURE, to->q_flags))
181540266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "FAILURE,",
1816d0cef73dSGregory Neil Shapiro 						  sizeof(notify));
181706f25ae9SGregory Neil Shapiro 			if (bitset(QPINGONDELAY, to->q_flags))
181840266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "DELAY,",
1819d0cef73dSGregory Neil Shapiro 						  sizeof(notify));
182006f25ae9SGregory Neil Shapiro 
182106f25ae9SGregory Neil Shapiro 			/* Set to NEVER or drop trailing comma */
182206f25ae9SGregory Neil Shapiro 			if (notify[0] == '\0')
182340266059SGregory Neil Shapiro 				(void) sm_strlcat(notify, "NEVER",
1824d0cef73dSGregory Neil Shapiro 						  sizeof(notify));
182506f25ae9SGregory Neil Shapiro 			else
182606f25ae9SGregory Neil Shapiro 				notify[strlen(notify) - 1] = '\0';
182706f25ae9SGregory Neil Shapiro 
182840266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_TEMP,
182940266059SGregory Neil Shapiro 				macid("{dsn_notify}"), notify);
183006f25ae9SGregory Neil Shapiro 		}
183106f25ae9SGregory Neil Shapiro 		else
183240266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
183340266059SGregory Neil Shapiro 				macid("{dsn_notify}"), NULL);
183406f25ae9SGregory Neil Shapiro 
1835c2aa98e2SPeter Wemm 		/*
1836c2aa98e2SPeter Wemm 		**  Expand out this user into argument list.
1837c2aa98e2SPeter Wemm 		*/
1838c2aa98e2SPeter Wemm 
1839c2aa98e2SPeter Wemm 		if (!clever)
1840c2aa98e2SPeter Wemm 		{
1841d0cef73dSGregory Neil Shapiro 			expand(*mvp, buf, sizeof(buf), e);
184240266059SGregory Neil Shapiro 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1843c2aa98e2SPeter Wemm 			if (pvp >= &pv[MAXPV - 2])
1844c2aa98e2SPeter Wemm 			{
1845c2aa98e2SPeter Wemm 				/* allow some space for trailing parms */
1846c2aa98e2SPeter Wemm 				break;
1847c2aa98e2SPeter Wemm 			}
1848c2aa98e2SPeter Wemm 		}
1849c2aa98e2SPeter Wemm 	}
1850c2aa98e2SPeter Wemm 
1851c2aa98e2SPeter Wemm 	/* see if any addresses still exist */
185206f25ae9SGregory Neil Shapiro 	if (tochain == NULL)
1853c2aa98e2SPeter Wemm 	{
185440266059SGregory Neil Shapiro 		rcode = 0;
185540266059SGregory Neil Shapiro 		goto cleanup;
1856c2aa98e2SPeter Wemm 	}
1857c2aa98e2SPeter Wemm 
1858c2aa98e2SPeter Wemm 	/* print out messages as full list */
185940266059SGregory Neil Shapiro 	strsize = 1;
186006f25ae9SGregory Neil Shapiro 	for (to = tochain; to != NULL; to = to->q_tchain)
186140266059SGregory Neil Shapiro 		strsize += strlen(to->q_paddr) + 1;
186240266059SGregory Neil Shapiro 	if (strsize < TOBUFSIZE)
186340266059SGregory Neil Shapiro 		strsize = TOBUFSIZE;
186440266059SGregory Neil Shapiro 	if (strsize > tobufsize)
186506f25ae9SGregory Neil Shapiro 	{
1866*5b0945b5SGregory Neil Shapiro 		SM_FREE(tobuf);
186740266059SGregory Neil Shapiro 		tobuf = sm_pmalloc_x(strsize);
186840266059SGregory Neil Shapiro 		tobufsize = strsize;
186906f25ae9SGregory Neil Shapiro 	}
187040266059SGregory Neil Shapiro 	p = tobuf;
187140266059SGregory Neil Shapiro 	*p = '\0';
187206f25ae9SGregory Neil Shapiro 	for (to = tochain; to != NULL; to = to->q_tchain)
187306f25ae9SGregory Neil Shapiro 	{
187440266059SGregory Neil Shapiro 		(void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
187540266059SGregory Neil Shapiro 				   ",", to->q_paddr);
187640266059SGregory Neil Shapiro 		p += strlen(p);
187706f25ae9SGregory Neil Shapiro 	}
1878c2aa98e2SPeter Wemm 	e->e_to = tobuf + 1;
1879c2aa98e2SPeter Wemm 
1880c2aa98e2SPeter Wemm 	/*
1881c2aa98e2SPeter Wemm 	**  Fill out any parameters after the $u parameter.
1882c2aa98e2SPeter Wemm 	*/
1883c2aa98e2SPeter Wemm 
188440266059SGregory Neil Shapiro 	if (!clever)
188540266059SGregory Neil Shapiro 	{
188640266059SGregory Neil Shapiro 		while (*++mvp != NULL)
1887c2aa98e2SPeter Wemm 		{
1888d0cef73dSGregory Neil Shapiro 			expand(*mvp, buf, sizeof(buf), e);
188940266059SGregory Neil Shapiro 			*pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
1890c2aa98e2SPeter Wemm 			if (pvp >= &pv[MAXPV])
189106f25ae9SGregory Neil Shapiro 				syserr("554 5.3.0 deliver: pv overflow after $u for %s",
189206f25ae9SGregory Neil Shapiro 				       pv[0]);
1893c2aa98e2SPeter Wemm 		}
189440266059SGregory Neil Shapiro 	}
1895c2aa98e2SPeter Wemm 	*pvp++ = NULL;
1896c2aa98e2SPeter Wemm 
1897c2aa98e2SPeter Wemm 	/*
1898c2aa98e2SPeter Wemm 	**  Call the mailer.
1899c2aa98e2SPeter Wemm 	**	The argument vector gets built, pipes
1900c2aa98e2SPeter Wemm 	**	are created as necessary, and we fork & exec as
1901c2aa98e2SPeter Wemm 	**	appropriate.
1902c2aa98e2SPeter Wemm 	**	If we are running SMTP, we just need to clean up.
1903c2aa98e2SPeter Wemm 	*/
1904c2aa98e2SPeter Wemm 
19056f9c8e5bSGregory Neil Shapiro 	/* XXX this seems a bit weird */
1906c2aa98e2SPeter Wemm 	if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
1907c2aa98e2SPeter Wemm 	    bitset(QGOODUID, e->e_from.q_flags))
1908c2aa98e2SPeter Wemm 		ctladdr = &e->e_from;
1909c2aa98e2SPeter Wemm 
1910c2aa98e2SPeter Wemm #if NAMED_BIND
1911c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
1912c2aa98e2SPeter Wemm 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
1913*5b0945b5SGregory Neil Shapiro #endif
1914c2aa98e2SPeter Wemm 
1915c2aa98e2SPeter Wemm 	if (tTd(11, 1))
1916c2aa98e2SPeter Wemm 	{
191740266059SGregory Neil Shapiro 		sm_dprintf("openmailer:");
1918e92d3f3fSGregory Neil Shapiro 		printav(sm_debug_file(), pv);
1919c2aa98e2SPeter Wemm 	}
1920c2aa98e2SPeter Wemm 	errno = 0;
1921602a2b1bSGregory Neil Shapiro 	SM_SET_H_ERRNO(0);
1922c2aa98e2SPeter Wemm 	CurHostName = NULL;
1923c2aa98e2SPeter Wemm 
1924c2aa98e2SPeter Wemm 	/*
1925c2aa98e2SPeter Wemm 	**  Deal with the special case of mail handled through an IPC
1926c2aa98e2SPeter Wemm 	**  connection.
1927c2aa98e2SPeter Wemm 	**	In this case we don't actually fork.  We must be
1928c2aa98e2SPeter Wemm 	**	running SMTP for this to work.  We will return a
1929c2aa98e2SPeter Wemm 	**	zero pid to indicate that we are running IPC.
1930c2aa98e2SPeter Wemm 	**  We also handle a debug version that just talks to stdin/out.
1931c2aa98e2SPeter Wemm 	*/
1932c2aa98e2SPeter Wemm 
1933c2aa98e2SPeter Wemm 	curhost = NULL;
1934c2aa98e2SPeter Wemm 	SmtpPhase = NULL;
1935c2aa98e2SPeter Wemm 	mci = NULL;
1936c2aa98e2SPeter Wemm 
1937c2aa98e2SPeter Wemm #if XDEBUG
1938c2aa98e2SPeter Wemm 	{
1939c2aa98e2SPeter Wemm 		char wbuf[MAXLINE];
1940c2aa98e2SPeter Wemm 
1941c2aa98e2SPeter Wemm 		/* make absolutely certain 0, 1, and 2 are in use */
1942d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)",
194340266059SGregory Neil Shapiro 				   shortenstring(e->e_to, MAXSHORTSTR),
194440266059SGregory Neil Shapiro 				   m->m_name);
1945c2aa98e2SPeter Wemm 		checkfd012(wbuf);
1946c2aa98e2SPeter Wemm 	}
194706f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
1948c2aa98e2SPeter Wemm 
1949c2aa98e2SPeter Wemm 	/* check for 8-bit available */
1950c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
1951c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, m->m_flags) &&
1952c2aa98e2SPeter Wemm 	    (bitset(EF_DONT_MIME, e->e_flags) ||
1953c2aa98e2SPeter Wemm 	     !(bitset(MM_MIME8BIT, MimeMode) ||
1954c2aa98e2SPeter Wemm 	       (bitset(EF_IS_MIME, e->e_flags) &&
1955c2aa98e2SPeter Wemm 		bitset(MM_CVTMIME, MimeMode)))))
1956c2aa98e2SPeter Wemm 	{
1957c2aa98e2SPeter Wemm 		e->e_status = "5.6.3";
195806f25ae9SGregory Neil Shapiro 		usrerrenh(e->e_status,
195906f25ae9SGregory Neil Shapiro 			  "554 Cannot send 8-bit data to 7-bit destination");
196006f25ae9SGregory Neil Shapiro 		rcode = EX_DATAERR;
1961c2aa98e2SPeter Wemm 		goto give_up;
1962c2aa98e2SPeter Wemm 	}
1963c2aa98e2SPeter Wemm 
1964c2aa98e2SPeter Wemm 	if (tTd(62, 8))
1965c2aa98e2SPeter Wemm 		checkfds("before delivery");
1966c2aa98e2SPeter Wemm 
1967c2aa98e2SPeter Wemm 	/* check for Local Person Communication -- not for mortals!!! */
1968c2aa98e2SPeter Wemm 	if (strcmp(m->m_mailer, "[LPC]") == 0)
1969c2aa98e2SPeter Wemm 	{
197040266059SGregory Neil Shapiro 		if (clever)
197140266059SGregory Neil Shapiro 		{
197240266059SGregory Neil Shapiro 			/* flush any expired connections */
197340266059SGregory Neil Shapiro 			(void) mci_scan(NULL);
197440266059SGregory Neil Shapiro 
197540266059SGregory Neil Shapiro 			/* try to get a cached connection or just a slot */
197640266059SGregory Neil Shapiro 			mci = mci_get(m->m_name, m);
197740266059SGregory Neil Shapiro 			if (mci->mci_host == NULL)
197840266059SGregory Neil Shapiro 				mci->mci_host = m->m_name;
197940266059SGregory Neil Shapiro 			CurHostName = mci->mci_host;
198040266059SGregory Neil Shapiro 			if (mci->mci_state != MCIS_CLOSED)
198140266059SGregory Neil Shapiro 			{
198240266059SGregory Neil Shapiro 				message("Using cached SMTP/LPC connection for %s...",
198340266059SGregory Neil Shapiro 					m->m_name);
198440266059SGregory Neil Shapiro 				mci->mci_deliveries++;
198540266059SGregory Neil Shapiro 				goto do_transfer;
198640266059SGregory Neil Shapiro 			}
198740266059SGregory Neil Shapiro 		}
198840266059SGregory Neil Shapiro 		else
198940266059SGregory Neil Shapiro 		{
199040266059SGregory Neil Shapiro 			mci = mci_new(e->e_rpool);
199140266059SGregory Neil Shapiro 		}
199240266059SGregory Neil Shapiro 		mci->mci_in = smioin;
199340266059SGregory Neil Shapiro 		mci->mci_out = smioout;
199440266059SGregory Neil Shapiro 		mci->mci_mailer = m;
199540266059SGregory Neil Shapiro 		mci->mci_host = m->m_name;
199640266059SGregory Neil Shapiro 		if (clever)
199740266059SGregory Neil Shapiro 		{
199840266059SGregory Neil Shapiro 			mci->mci_state = MCIS_OPENING;
199940266059SGregory Neil Shapiro 			mci_cache(mci);
200040266059SGregory Neil Shapiro 		}
200140266059SGregory Neil Shapiro 		else
200240266059SGregory Neil Shapiro 			mci->mci_state = MCIS_OPEN;
2003c2aa98e2SPeter Wemm 	}
200440266059SGregory Neil Shapiro 	else if (strcmp(m->m_mailer, "[IPC]") == 0)
2005c2aa98e2SPeter Wemm 	{
2006c2aa98e2SPeter Wemm 		register int i;
2007c2aa98e2SPeter Wemm 
2008c2aa98e2SPeter Wemm 		if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
2009c2aa98e2SPeter Wemm 		{
201006f25ae9SGregory Neil Shapiro 			syserr("null destination for %s mailer", m->m_mailer);
2011c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
2012c2aa98e2SPeter Wemm 			goto give_up;
2013c2aa98e2SPeter Wemm 		}
2014c2aa98e2SPeter Wemm 
201506f25ae9SGregory Neil Shapiro #if NETUNIX
201606f25ae9SGregory Neil Shapiro 		if (strcmp(pv[0], "FILE") == 0)
201706f25ae9SGregory Neil Shapiro 		{
201806f25ae9SGregory Neil Shapiro 			curhost = CurHostName = "localhost";
201906f25ae9SGregory Neil Shapiro 			mux_path = pv[1];
202006f25ae9SGregory Neil Shapiro 		}
202106f25ae9SGregory Neil Shapiro 		else
202206f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
202306f25ae9SGregory Neil Shapiro 		{
2024c2aa98e2SPeter Wemm 			CurHostName = pv[1];
2025*5b0945b5SGregory Neil Shapiro 							/* XXX ??? */
2026*5b0945b5SGregory Neil Shapiro 			curhost = hostsignature(m, pv[1], firstto->q_flags & QSECURE);
202706f25ae9SGregory Neil Shapiro 		}
2028c2aa98e2SPeter Wemm 
2029c2aa98e2SPeter Wemm 		if (curhost == NULL || curhost[0] == '\0')
2030c2aa98e2SPeter Wemm 		{
2031c2aa98e2SPeter Wemm 			syserr("null host signature for %s", pv[1]);
2032c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
2033c2aa98e2SPeter Wemm 			goto give_up;
2034c2aa98e2SPeter Wemm 		}
2035c2aa98e2SPeter Wemm 
2036c2aa98e2SPeter Wemm 		if (!clever)
2037c2aa98e2SPeter Wemm 		{
203806f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 non-clever IPC");
2039c2aa98e2SPeter Wemm 			rcode = EX_CONFIG;
2040c2aa98e2SPeter Wemm 			goto give_up;
2041c2aa98e2SPeter Wemm 		}
204206f25ae9SGregory Neil Shapiro 		if (pv[2] != NULL
204306f25ae9SGregory Neil Shapiro #if NETUNIX
204406f25ae9SGregory Neil Shapiro 		    && mux_path == NULL
2045*5b0945b5SGregory Neil Shapiro #endif
204606f25ae9SGregory Neil Shapiro 		    )
2047c2aa98e2SPeter Wemm 		{
204840266059SGregory Neil Shapiro 			port = htons((unsigned short) atoi(pv[2]));
2049c2aa98e2SPeter Wemm 			if (port == 0)
2050c2aa98e2SPeter Wemm 			{
205106f25ae9SGregory Neil Shapiro #ifdef NO_GETSERVBYNAME
205206f25ae9SGregory Neil Shapiro 				syserr("Invalid port number: %s", pv[2]);
205306f25ae9SGregory Neil Shapiro #else /* NO_GETSERVBYNAME */
2054c2aa98e2SPeter Wemm 				struct servent *sp = getservbyname(pv[2], "tcp");
2055c2aa98e2SPeter Wemm 
2056c2aa98e2SPeter Wemm 				if (sp == NULL)
2057c2aa98e2SPeter Wemm 					syserr("Service %s unknown", pv[2]);
2058c2aa98e2SPeter Wemm 				else
2059c2aa98e2SPeter Wemm 					port = sp->s_port;
206006f25ae9SGregory Neil Shapiro #endif /* NO_GETSERVBYNAME */
2061c2aa98e2SPeter Wemm 			}
2062c2aa98e2SPeter Wemm 		}
2063c2aa98e2SPeter Wemm 
206406f25ae9SGregory Neil Shapiro 		nummxhosts = parse_hostsignature(curhost, mxhosts, m);
206540266059SGregory Neil Shapiro 		if (TimeOuts.to_aconnect > 0)
206640266059SGregory Neil Shapiro 			enough = curtime() + TimeOuts.to_aconnect;
206706f25ae9SGregory Neil Shapiro tryhost:
206806f25ae9SGregory Neil Shapiro 		while (hostnum < nummxhosts)
206906f25ae9SGregory Neil Shapiro 		{
207006f25ae9SGregory Neil Shapiro 			char sep = ':';
207106f25ae9SGregory Neil Shapiro 			char *endp;
207206f25ae9SGregory Neil Shapiro 			static char hostbuf[MAXNAME + 1];
2073e92d3f3fSGregory Neil Shapiro 			bool tried_fallbacksmarthost = false;
2074*5b0945b5SGregory Neil Shapiro #if DANE
2075*5b0945b5SGregory Neil Shapiro 			unsigned long tlsa_flags;
207606f25ae9SGregory Neil Shapiro 
2077*5b0945b5SGregory Neil Shapiro 			ste = NULL;
2078*5b0945b5SGregory Neil Shapiro 			tlsa_flags = 0;
2079*5b0945b5SGregory Neil Shapiro #endif
208006f25ae9SGregory Neil Shapiro #if NETINET6
208106f25ae9SGregory Neil Shapiro 			if (*mxhosts[hostnum] == '[')
208206f25ae9SGregory Neil Shapiro 			{
208306f25ae9SGregory Neil Shapiro 				endp = strchr(mxhosts[hostnum] + 1, ']');
208406f25ae9SGregory Neil Shapiro 				if (endp != NULL)
208506f25ae9SGregory Neil Shapiro 					endp = strpbrk(endp + 1, ":,");
208606f25ae9SGregory Neil Shapiro 			}
208706f25ae9SGregory Neil Shapiro 			else
208806f25ae9SGregory Neil Shapiro 				endp = strpbrk(mxhosts[hostnum], ":,");
208906f25ae9SGregory Neil Shapiro #else /* NETINET6 */
209006f25ae9SGregory Neil Shapiro 			endp = strpbrk(mxhosts[hostnum], ":,");
209106f25ae9SGregory Neil Shapiro #endif /* NETINET6 */
209206f25ae9SGregory Neil Shapiro 			if (endp != NULL)
209306f25ae9SGregory Neil Shapiro 			{
209406f25ae9SGregory Neil Shapiro 				sep = *endp;
209506f25ae9SGregory Neil Shapiro 				*endp = '\0';
209606f25ae9SGregory Neil Shapiro 			}
209706f25ae9SGregory Neil Shapiro 
209840266059SGregory Neil Shapiro 			if (hostnum == 1 && skip_back != NULL)
209940266059SGregory Neil Shapiro 			{
210040266059SGregory Neil Shapiro 				/*
210140266059SGregory Neil Shapiro 				**  Coattail piggybacking is no longer an
210240266059SGregory Neil Shapiro 				**  option with the mail host next to be tried
210340266059SGregory Neil Shapiro 				**  no longer the lowest MX preference
210440266059SGregory Neil Shapiro 				**  (hostnum == 1 meaning we're on the second
210540266059SGregory Neil Shapiro 				**  preference). We do not try to coattail
210640266059SGregory Neil Shapiro 				**  piggyback more than the first MX preference.
210740266059SGregory Neil Shapiro 				**  Revert 'tochain' to last location for
210840266059SGregory Neil Shapiro 				**  coincidental piggybacking. This works this
210940266059SGregory Neil Shapiro 				**  easily because the q_tchain kept getting
211040266059SGregory Neil Shapiro 				**  added to the top of the linked list.
211140266059SGregory Neil Shapiro 				*/
211240266059SGregory Neil Shapiro 
211340266059SGregory Neil Shapiro 				tochain = skip_back;
211440266059SGregory Neil Shapiro 			}
211540266059SGregory Neil Shapiro 
211606f25ae9SGregory Neil Shapiro 			if (*mxhosts[hostnum] == '\0')
2117c2aa98e2SPeter Wemm 			{
2118c2aa98e2SPeter Wemm 				syserr("deliver: null host name in signature");
211906f25ae9SGregory Neil Shapiro 				hostnum++;
212006f25ae9SGregory Neil Shapiro 				if (endp != NULL)
212106f25ae9SGregory Neil Shapiro 					*endp = sep;
2122c2aa98e2SPeter Wemm 				continue;
2123c2aa98e2SPeter Wemm 			}
212440266059SGregory Neil Shapiro 			(void) sm_strlcpy(hostbuf, mxhosts[hostnum],
2125d0cef73dSGregory Neil Shapiro 					  sizeof(hostbuf));
212606f25ae9SGregory Neil Shapiro 			hostnum++;
212706f25ae9SGregory Neil Shapiro 			if (endp != NULL)
212806f25ae9SGregory Neil Shapiro 				*endp = sep;
2129*5b0945b5SGregory Neil Shapiro #if STARTTLS
2130*5b0945b5SGregory Neil Shapiro 			tlsstate = 0;
2131*5b0945b5SGregory Neil Shapiro #endif
2132c2aa98e2SPeter Wemm 
2133e92d3f3fSGregory Neil Shapiro   one_last_try:
2134c2aa98e2SPeter Wemm 			/* see if we already know that this host is fried */
2135c2aa98e2SPeter Wemm 			CurHostName = hostbuf;
2136c2aa98e2SPeter Wemm 			mci = mci_get(hostbuf, m);
2137c2aa98e2SPeter Wemm 			if (mci->mci_state != MCIS_CLOSED)
2138c2aa98e2SPeter Wemm 			{
213940266059SGregory Neil Shapiro 				char *type;
214040266059SGregory Neil Shapiro 
2141c2aa98e2SPeter Wemm 				if (tTd(11, 1))
2142c2aa98e2SPeter Wemm 				{
214340266059SGregory Neil Shapiro 					sm_dprintf("openmailer: ");
2144e92d3f3fSGregory Neil Shapiro 					mci_dump(sm_debug_file(), mci, false);
2145c2aa98e2SPeter Wemm 				}
2146c2aa98e2SPeter Wemm 				CurHostName = mci->mci_host;
214740266059SGregory Neil Shapiro 				if (bitnset(M_LMTP, m->m_flags))
214840266059SGregory Neil Shapiro 					type = "L";
214940266059SGregory Neil Shapiro 				else if (bitset(MCIF_ESMTP, mci->mci_flags))
215040266059SGregory Neil Shapiro 					type = "ES";
215140266059SGregory Neil Shapiro 				else
215240266059SGregory Neil Shapiro 					type = "S";
215340266059SGregory Neil Shapiro 				message("Using cached %sMTP connection to %s via %s...",
215440266059SGregory Neil Shapiro 					type, hostbuf, m->m_name);
215506f25ae9SGregory Neil Shapiro 				mci->mci_deliveries++;
2156c2aa98e2SPeter Wemm 				break;
2157c2aa98e2SPeter Wemm 			}
2158c2aa98e2SPeter Wemm 			mci->mci_mailer = m;
2159*5b0945b5SGregory Neil Shapiro #if DANE
2160*5b0945b5SGregory Neil Shapiro 			tlsa_flags = 0;
2161*5b0945b5SGregory Neil Shapiro 			if (CHK_DANE(Dane))
2162*5b0945b5SGregory Neil Shapiro 				(void) GETTLSA(hostbuf, &ste, m->m_port);
2163*5b0945b5SGregory Neil Shapiro 
2164*5b0945b5SGregory Neil Shapiro 			/* XXX: check expiration! */
2165*5b0945b5SGregory Neil Shapiro 			if (ste != NULL && TLSA_RR_TEMPFAIL(ste->s_tlsa))
2166*5b0945b5SGregory Neil Shapiro 			{
2167*5b0945b5SGregory Neil Shapiro 				if (tTd(11, 1))
2168*5b0945b5SGregory Neil Shapiro 					sm_dprintf("skip: host=%s, TLSA_RR_lookup=%d\n"
2169*5b0945b5SGregory Neil Shapiro 						, hostbuf
2170*5b0945b5SGregory Neil Shapiro 						, ste->s_tlsa->dane_tlsa_dnsrc);
2171*5b0945b5SGregory Neil Shapiro 
2172*5b0945b5SGregory Neil Shapiro 				tlsa_flags |= TLSAFLTEMP;
2173*5b0945b5SGregory Neil Shapiro 			}
2174*5b0945b5SGregory Neil Shapiro #endif /* DANE */
2175*5b0945b5SGregory Neil Shapiro 
2176c2aa98e2SPeter Wemm 			if (mci->mci_exitstat != EX_OK)
2177c2aa98e2SPeter Wemm 			{
2178c2aa98e2SPeter Wemm 				if (mci->mci_exitstat == EX_TEMPFAIL)
217940266059SGregory Neil Shapiro 					goodmxfound = true;
2180e92d3f3fSGregory Neil Shapiro 
2181e92d3f3fSGregory Neil Shapiro 				/* Try FallbackSmartHost? */
2182e92d3f3fSGregory Neil Shapiro 				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2183d0cef73dSGregory Neil Shapiro 						    hostbuf, sizeof(hostbuf),
2184e92d3f3fSGregory Neil Shapiro 						    mci->mci_exitstat))
2185e92d3f3fSGregory Neil Shapiro 					goto one_last_try;
2186e92d3f3fSGregory Neil Shapiro 
2187c2aa98e2SPeter Wemm 				continue;
2188c2aa98e2SPeter Wemm 			}
2189c2aa98e2SPeter Wemm 
2190c2aa98e2SPeter Wemm 			if (mci_lock_host(mci) != EX_OK)
2191c2aa98e2SPeter Wemm 			{
2192c2aa98e2SPeter Wemm 				mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
219340266059SGregory Neil Shapiro 				goodmxfound = true;
2194c2aa98e2SPeter Wemm 				continue;
2195c2aa98e2SPeter Wemm 			}
2196c2aa98e2SPeter Wemm 
2197c2aa98e2SPeter Wemm 			/* try the connection */
219840266059SGregory Neil Shapiro 			sm_setproctitle(true, e, "%s %s: %s",
219906f25ae9SGregory Neil Shapiro 					qid_printname(e),
220006f25ae9SGregory Neil Shapiro 					hostbuf, "user open");
220106f25ae9SGregory Neil Shapiro #if NETUNIX
220206f25ae9SGregory Neil Shapiro 			if (mux_path != NULL)
220306f25ae9SGregory Neil Shapiro 			{
220406f25ae9SGregory Neil Shapiro 				message("Connecting to %s via %s...",
220506f25ae9SGregory Neil Shapiro 					mux_path, m->m_name);
220640266059SGregory Neil Shapiro 				i = makeconnection_ds((char *) mux_path, mci);
220706f25ae9SGregory Neil Shapiro 			}
220806f25ae9SGregory Neil Shapiro 			else
220906f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
221006f25ae9SGregory Neil Shapiro 			{
2211c2aa98e2SPeter Wemm 				if (port == 0)
2212c2aa98e2SPeter Wemm 					message("Connecting to %s via %s...",
2213c2aa98e2SPeter Wemm 						hostbuf, m->m_name);
2214c2aa98e2SPeter Wemm 				else
2215c2aa98e2SPeter Wemm 					message("Connecting to %s port %d via %s...",
221606f25ae9SGregory Neil Shapiro 						hostbuf, ntohs(port),
221706f25ae9SGregory Neil Shapiro 						m->m_name);
2218*5b0945b5SGregory Neil Shapiro #if DANE
2219*5b0945b5SGregory Neil Shapiro 				tlsa_flags |= (ste != NULL) ? Dane : DANE_NEVER;
2220*5b0945b5SGregory Neil Shapiro 				dane_vrfy_ctx.dane_vrfy_chk = tlsa_flags;
2221*5b0945b5SGregory Neil Shapiro 				dane_vrfy_ctx.dane_vrfy_port = m->m_port;
2222*5b0945b5SGregory Neil Shapiro 				if (tTd(11, 11))
2223*5b0945b5SGregory Neil Shapiro 					sm_dprintf("makeconnection: before: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
2224*5b0945b5SGregory Neil Shapiro #endif
222540266059SGregory Neil Shapiro 				i = makeconnection(hostbuf, port, mci, e,
2226*5b0945b5SGregory Neil Shapiro 						enough
2227*5b0945b5SGregory Neil Shapiro #if DANE
2228*5b0945b5SGregory Neil Shapiro 						, &tlsa_flags
2229*5b0945b5SGregory Neil Shapiro #endif
2230*5b0945b5SGregory Neil Shapiro 						);
2231*5b0945b5SGregory Neil Shapiro #if DANE
2232*5b0945b5SGregory Neil Shapiro 				if (tTd(11, 11))
2233*5b0945b5SGregory Neil Shapiro 					sm_dprintf("makeconnection: after: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags);
2234*5b0945b5SGregory Neil Shapiro 				if (dane_vrfy_ctx.dane_vrfy_chk != DANE_ALWAYS)
2235*5b0945b5SGregory Neil Shapiro 					dane_vrfy_ctx.dane_vrfy_chk = DANEMODE(tlsa_flags);
2236*5b0945b5SGregory Neil Shapiro 				if (EX_TEMPFAIL == i &&
2237*5b0945b5SGregory Neil Shapiro 				    ((tlsa_flags & (TLSAFLTEMP|DANE_SECURE)) ==
2238*5b0945b5SGregory Neil Shapiro 				     (TLSAFLTEMP|DANE_SECURE)))
2239*5b0945b5SGregory Neil Shapiro 				{
2240*5b0945b5SGregory Neil Shapiro 					(void) sm_strlcpy(SmtpError,
2241*5b0945b5SGregory Neil Shapiro 						" for TLSA RR",
2242*5b0945b5SGregory Neil Shapiro 						sizeof(SmtpError));
2243*5b0945b5SGregory Neil Shapiro # if NAMED_BIND
2244*5b0945b5SGregory Neil Shapiro 					SM_SET_H_ERRNO(TRY_AGAIN);
2245*5b0945b5SGregory Neil Shapiro # endif
2246*5b0945b5SGregory Neil Shapiro 				}
2247*5b0945b5SGregory Neil Shapiro #endif
224806f25ae9SGregory Neil Shapiro 			}
22498774250cSGregory Neil Shapiro 			mci->mci_errno = errno;
2250c2aa98e2SPeter Wemm 			mci->mci_lastuse = curtime();
225106f25ae9SGregory Neil Shapiro 			mci->mci_deliveries = 0;
2252c2aa98e2SPeter Wemm 			mci->mci_exitstat = i;
22536f9c8e5bSGregory Neil Shapiro 			mci_clr_extensions(mci);
2254c2aa98e2SPeter Wemm #if NAMED_BIND
2255c2aa98e2SPeter Wemm 			mci->mci_herrno = h_errno;
2256*5b0945b5SGregory Neil Shapiro #endif
225740266059SGregory Neil Shapiro 
225840266059SGregory Neil Shapiro 			/*
225940266059SGregory Neil Shapiro 			**  Have we tried long enough to get a connection?
226040266059SGregory Neil Shapiro 			**	If yes, skip to the fallback MX hosts
226140266059SGregory Neil Shapiro 			**	(if existent).
226240266059SGregory Neil Shapiro 			*/
226340266059SGregory Neil Shapiro 
226440266059SGregory Neil Shapiro 			if (enough > 0 && mci->mci_lastuse >= enough)
226540266059SGregory Neil Shapiro 			{
226640266059SGregory Neil Shapiro 				int h;
226740266059SGregory Neil Shapiro #if NAMED_BIND
2268e92d3f3fSGregory Neil Shapiro 				extern int NumFallbackMXHosts;
2269*5b0945b5SGregory Neil Shapiro #else
2270e92d3f3fSGregory Neil Shapiro 				const int NumFallbackMXHosts = 0;
2271*5b0945b5SGregory Neil Shapiro #endif
227240266059SGregory Neil Shapiro 
227340266059SGregory Neil Shapiro 				if (hostnum < nummxhosts && LogLevel > 9)
227440266059SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
227540266059SGregory Neil Shapiro 						  "Timeout.to_aconnect occurred before exhausting all addresses");
227640266059SGregory Neil Shapiro 
227740266059SGregory Neil Shapiro 				/* turn off timeout if fallback available */
2278e92d3f3fSGregory Neil Shapiro 				if (NumFallbackMXHosts > 0)
227940266059SGregory Neil Shapiro 					enough = 0;
228040266059SGregory Neil Shapiro 
228140266059SGregory Neil Shapiro 				/* skip to a fallback MX host */
2282e92d3f3fSGregory Neil Shapiro 				h = nummxhosts - NumFallbackMXHosts;
228340266059SGregory Neil Shapiro 				if (hostnum < h)
228440266059SGregory Neil Shapiro 					hostnum = h;
228540266059SGregory Neil Shapiro 			}
2286c2aa98e2SPeter Wemm 			if (i == EX_OK)
2287c2aa98e2SPeter Wemm 			{
228840266059SGregory Neil Shapiro 				goodmxfound = true;
2289605302a5SGregory Neil Shapiro 				markstats(e, firstto, STATS_CONNECT);
2290c2aa98e2SPeter Wemm 				mci->mci_state = MCIS_OPENING;
2291c2aa98e2SPeter Wemm 				mci_cache(mci);
2292c2aa98e2SPeter Wemm 				if (TrafficLogFile != NULL)
229340266059SGregory Neil Shapiro 					(void) sm_io_fprintf(TrafficLogFile,
229440266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
229540266059SGregory Neil Shapiro 							     "%05d === CONNECT %s\n",
229640266059SGregory Neil Shapiro 							     (int) CurrentPid,
229740266059SGregory Neil Shapiro 							     hostbuf);
2298c2aa98e2SPeter Wemm 				break;
2299c2aa98e2SPeter Wemm 			}
2300c2aa98e2SPeter Wemm 			else
2301c2aa98e2SPeter Wemm 			{
2302e92d3f3fSGregory Neil Shapiro 				/* Try FallbackSmartHost? */
2303e92d3f3fSGregory Neil Shapiro 				if (should_try_fbsh(e, &tried_fallbacksmarthost,
2304d0cef73dSGregory Neil Shapiro 						    hostbuf, sizeof(hostbuf), i))
2305e92d3f3fSGregory Neil Shapiro 					goto one_last_try;
2306e92d3f3fSGregory Neil Shapiro 
2307c2aa98e2SPeter Wemm 				if (tTd(11, 1))
230840266059SGregory Neil Shapiro 					sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
2309c2aa98e2SPeter Wemm 						   i, errno);
2310c2aa98e2SPeter Wemm 				if (i == EX_TEMPFAIL)
231140266059SGregory Neil Shapiro 					goodmxfound = true;
2312c2aa98e2SPeter Wemm 				mci_unlock_host(mci);
2313c2aa98e2SPeter Wemm 			}
2314c2aa98e2SPeter Wemm 
2315c2aa98e2SPeter Wemm 			/* enter status of this host */
2316c2aa98e2SPeter Wemm 			setstat(i);
2317c2aa98e2SPeter Wemm 
2318c2aa98e2SPeter Wemm 			/* should print some message here for -v mode */
2319c2aa98e2SPeter Wemm 		}
2320c2aa98e2SPeter Wemm 		if (mci == NULL)
2321c2aa98e2SPeter Wemm 		{
2322c2aa98e2SPeter Wemm 			syserr("deliver: no host name");
2323c2aa98e2SPeter Wemm 			rcode = EX_SOFTWARE;
2324c2aa98e2SPeter Wemm 			goto give_up;
2325c2aa98e2SPeter Wemm 		}
2326c2aa98e2SPeter Wemm 		mci->mci_pid = 0;
2327c2aa98e2SPeter Wemm 	}
2328c2aa98e2SPeter Wemm 	else
2329c2aa98e2SPeter Wemm 	{
2330c2aa98e2SPeter Wemm 		/* flush any expired connections */
2331c2aa98e2SPeter Wemm 		(void) mci_scan(NULL);
2332c2aa98e2SPeter Wemm 		mci = NULL;
2333c2aa98e2SPeter Wemm 
2334c2aa98e2SPeter Wemm 		if (bitnset(M_LMTP, m->m_flags))
2335c2aa98e2SPeter Wemm 		{
2336c2aa98e2SPeter Wemm 			/* try to get a cached connection */
2337c2aa98e2SPeter Wemm 			mci = mci_get(m->m_name, m);
2338c2aa98e2SPeter Wemm 			if (mci->mci_host == NULL)
2339c2aa98e2SPeter Wemm 				mci->mci_host = m->m_name;
2340c2aa98e2SPeter Wemm 			CurHostName = mci->mci_host;
2341c2aa98e2SPeter Wemm 			if (mci->mci_state != MCIS_CLOSED)
2342c2aa98e2SPeter Wemm 			{
2343c2aa98e2SPeter Wemm 				message("Using cached LMTP connection for %s...",
2344c2aa98e2SPeter Wemm 					m->m_name);
234506f25ae9SGregory Neil Shapiro 				mci->mci_deliveries++;
2346c2aa98e2SPeter Wemm 				goto do_transfer;
2347c2aa98e2SPeter Wemm 			}
2348c2aa98e2SPeter Wemm 		}
2349c2aa98e2SPeter Wemm 
2350c2aa98e2SPeter Wemm 		/* announce the connection to verbose listeners */
2351c2aa98e2SPeter Wemm 		if (host == NULL || host[0] == '\0')
2352c2aa98e2SPeter Wemm 			message("Connecting to %s...", m->m_name);
2353c2aa98e2SPeter Wemm 		else
2354c2aa98e2SPeter Wemm 			message("Connecting to %s via %s...", host, m->m_name);
2355c2aa98e2SPeter Wemm 		if (TrafficLogFile != NULL)
2356c2aa98e2SPeter Wemm 		{
2357c2aa98e2SPeter Wemm 			char **av;
2358c2aa98e2SPeter Wemm 
235940266059SGregory Neil Shapiro 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
236040266059SGregory Neil Shapiro 					     "%05d === EXEC", (int) CurrentPid);
2361c2aa98e2SPeter Wemm 			for (av = pv; *av != NULL; av++)
236240266059SGregory Neil Shapiro 				(void) sm_io_fprintf(TrafficLogFile,
236340266059SGregory Neil Shapiro 						     SM_TIME_DEFAULT, " %s",
236440266059SGregory Neil Shapiro 						     *av);
236540266059SGregory Neil Shapiro 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
236640266059SGregory Neil Shapiro 					     "\n");
2367c2aa98e2SPeter Wemm 		}
2368c2aa98e2SPeter Wemm 
2369c2aa98e2SPeter Wemm #if XDEBUG
2370c2aa98e2SPeter Wemm 		checkfd012("before creating mail pipe");
2371*5b0945b5SGregory Neil Shapiro #endif
2372c2aa98e2SPeter Wemm 
2373c2aa98e2SPeter Wemm 		/* create a pipe to shove the mail through */
2374c2aa98e2SPeter Wemm 		if (pipe(mpvect) < 0)
2375c2aa98e2SPeter Wemm 		{
2376c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): pipe (to mailer)",
2377c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2378c2aa98e2SPeter Wemm 			if (tTd(11, 1))
237940266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2380c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2381c2aa98e2SPeter Wemm 			goto give_up;
2382c2aa98e2SPeter Wemm 		}
2383c2aa98e2SPeter Wemm 
2384c2aa98e2SPeter Wemm #if XDEBUG
2385c2aa98e2SPeter Wemm 		/* make sure we didn't get one of the standard I/O files */
2386c2aa98e2SPeter Wemm 		if (mpvect[0] < 3 || mpvect[1] < 3)
2387c2aa98e2SPeter Wemm 		{
2388c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): bogus mpvect %d %d",
2389c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
2390c2aa98e2SPeter Wemm 			       mpvect[0], mpvect[1]);
239140266059SGregory Neil Shapiro 			printopenfds(true);
2392c2aa98e2SPeter Wemm 			if (tTd(11, 1))
239340266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2394c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2395c2aa98e2SPeter Wemm 			goto give_up;
2396c2aa98e2SPeter Wemm 		}
2397c2aa98e2SPeter Wemm 
2398c2aa98e2SPeter Wemm 		/* make sure system call isn't dead meat */
2399c2aa98e2SPeter Wemm 		checkfdopen(mpvect[0], "mpvect[0]");
2400c2aa98e2SPeter Wemm 		checkfdopen(mpvect[1], "mpvect[1]");
2401c2aa98e2SPeter Wemm 		if (mpvect[0] == mpvect[1] ||
2402c2aa98e2SPeter Wemm 		    (e->e_lockfp != NULL &&
240340266059SGregory Neil Shapiro 		     (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
240440266059SGregory Neil Shapiro 						 NULL) ||
240540266059SGregory Neil Shapiro 		      mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
240640266059SGregory Neil Shapiro 						 NULL))))
2407c2aa98e2SPeter Wemm 		{
2408c2aa98e2SPeter Wemm 			if (e->e_lockfp == NULL)
2409c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): overlapping mpvect %d %d",
2410c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2411c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0], mpvect[1]);
2412c2aa98e2SPeter Wemm 			else
2413c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
2414c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2415c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0], mpvect[1],
241640266059SGregory Neil Shapiro 				       sm_io_getinfo(e->e_lockfp,
241740266059SGregory Neil Shapiro 						     SM_IO_WHAT_FD, NULL));
2418c2aa98e2SPeter Wemm 		}
241906f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
2420c2aa98e2SPeter Wemm 
242106f25ae9SGregory Neil Shapiro 		/* create a return pipe */
2422c2aa98e2SPeter Wemm 		if (pipe(rpvect) < 0)
2423c2aa98e2SPeter Wemm 		{
2424c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): pipe (from mailer)",
2425c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR),
2426c2aa98e2SPeter Wemm 			       m->m_name);
2427c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2428c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2429c2aa98e2SPeter Wemm 			if (tTd(11, 1))
243040266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2431c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2432c2aa98e2SPeter Wemm 			goto give_up;
2433c2aa98e2SPeter Wemm 		}
2434c2aa98e2SPeter Wemm #if XDEBUG
2435c2aa98e2SPeter Wemm 		checkfdopen(rpvect[0], "rpvect[0]");
2436c2aa98e2SPeter Wemm 		checkfdopen(rpvect[1], "rpvect[1]");
2437*5b0945b5SGregory Neil Shapiro #endif
2438c2aa98e2SPeter Wemm 
2439c2aa98e2SPeter Wemm 		/*
2440c2aa98e2SPeter Wemm 		**  Actually fork the mailer process.
2441c2aa98e2SPeter Wemm 		**	DOFORK is clever about retrying.
2442c2aa98e2SPeter Wemm 		**
2443c2aa98e2SPeter Wemm 		**	Dispose of SIGCHLD signal catchers that may be laying
244406f25ae9SGregory Neil Shapiro 		**	around so that endmailer will get it.
2445c2aa98e2SPeter Wemm 		*/
2446c2aa98e2SPeter Wemm 
244740266059SGregory Neil Shapiro 		if (e->e_xfp != NULL)	/* for debugging */
244840266059SGregory Neil Shapiro 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
244940266059SGregory Neil Shapiro 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
245040266059SGregory Neil Shapiro 		(void) sm_signal(SIGCHLD, SIG_DFL);
245106f25ae9SGregory Neil Shapiro 
245206f25ae9SGregory Neil Shapiro 
2453c2aa98e2SPeter Wemm 		DOFORK(FORK);
2454c2aa98e2SPeter Wemm 		/* pid is set by DOFORK */
245506f25ae9SGregory Neil Shapiro 
2456c2aa98e2SPeter Wemm 		if (pid < 0)
2457c2aa98e2SPeter Wemm 		{
2458c2aa98e2SPeter Wemm 			/* failure */
2459c2aa98e2SPeter Wemm 			syserr("%s... openmailer(%s): cannot fork",
2460c2aa98e2SPeter Wemm 			       shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
2461c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2462c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2463c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2464c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
2465c2aa98e2SPeter Wemm 			if (tTd(11, 1))
246640266059SGregory Neil Shapiro 				sm_dprintf("openmailer: NULL\n");
2467c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2468c2aa98e2SPeter Wemm 			goto give_up;
2469c2aa98e2SPeter Wemm 		}
2470c2aa98e2SPeter Wemm 		else if (pid == 0)
2471c2aa98e2SPeter Wemm 		{
247206f25ae9SGregory Neil Shapiro 			int save_errno;
247340266059SGregory Neil Shapiro 			int sff;
2474c2aa98e2SPeter Wemm 			int new_euid = NO_UID;
2475c2aa98e2SPeter Wemm 			int new_ruid = NO_UID;
2476c2aa98e2SPeter Wemm 			int new_gid = NO_GID;
247740266059SGregory Neil Shapiro 			char *user = NULL;
2478c2aa98e2SPeter Wemm 			struct stat stb;
2479c2aa98e2SPeter Wemm 			extern int DtableSize;
2480c2aa98e2SPeter Wemm 
248140266059SGregory Neil Shapiro 			CurrentPid = getpid();
248240266059SGregory Neil Shapiro 
248313058a91SGregory Neil Shapiro 			/* clear the events to turn off SIGALRMs */
248440266059SGregory Neil Shapiro 			sm_clear_events();
248513058a91SGregory Neil Shapiro 
24868774250cSGregory Neil Shapiro 			/* Reset global flags */
24878774250cSGregory Neil Shapiro 			RestartRequest = NULL;
248840266059SGregory Neil Shapiro 			RestartWorkGroup = false;
24898774250cSGregory Neil Shapiro 			ShutdownRequest = NULL;
24908774250cSGregory Neil Shapiro 			PendingSignal = 0;
24918774250cSGregory Neil Shapiro 
2492c2aa98e2SPeter Wemm 			if (e->e_lockfp != NULL)
249340266059SGregory Neil Shapiro 				(void) close(sm_io_getinfo(e->e_lockfp,
249440266059SGregory Neil Shapiro 							   SM_IO_WHAT_FD,
249540266059SGregory Neil Shapiro 							   NULL));
2496c2aa98e2SPeter Wemm 
2497c2aa98e2SPeter Wemm 			/* child -- set up input & exec mailer */
249840266059SGregory Neil Shapiro 			(void) sm_signal(SIGALRM, sm_signal_noop);
249940266059SGregory Neil Shapiro 			(void) sm_signal(SIGCHLD, SIG_DFL);
250040266059SGregory Neil Shapiro 			(void) sm_signal(SIGHUP, SIG_IGN);
250140266059SGregory Neil Shapiro 			(void) sm_signal(SIGINT, SIG_IGN);
250240266059SGregory Neil Shapiro 			(void) sm_signal(SIGTERM, SIG_DFL);
250313058a91SGregory Neil Shapiro #ifdef SIGUSR1
250440266059SGregory Neil Shapiro 			(void) sm_signal(SIGUSR1, sm_signal_noop);
2505*5b0945b5SGregory Neil Shapiro #endif
2506c2aa98e2SPeter Wemm 
2507c2aa98e2SPeter Wemm 			if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
2508c2aa98e2SPeter Wemm 				stb.st_mode = 0;
2509c2aa98e2SPeter Wemm 
2510c2aa98e2SPeter Wemm #if HASSETUSERCONTEXT
2511c2aa98e2SPeter Wemm 			/*
2512c2aa98e2SPeter Wemm 			**  Set user resources.
2513c2aa98e2SPeter Wemm 			*/
2514c2aa98e2SPeter Wemm 
2515c2aa98e2SPeter Wemm 			if (contextaddr != NULL)
2516c2aa98e2SPeter Wemm 			{
251713bd1963SGregory Neil Shapiro 				int sucflags;
2518c2aa98e2SPeter Wemm 				struct passwd *pwd;
2519c2aa98e2SPeter Wemm 
2520c2aa98e2SPeter Wemm 				if (contextaddr->q_ruser != NULL)
2521c2aa98e2SPeter Wemm 					pwd = sm_getpwnam(contextaddr->q_ruser);
2522c2aa98e2SPeter Wemm 				else
2523c2aa98e2SPeter Wemm 					pwd = sm_getpwnam(contextaddr->q_user);
252413bd1963SGregory Neil Shapiro 				sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
2525906a940eSEdward Tomasz Napierala # ifdef LOGIN_SETCPUMASK
2526906a940eSEdward Tomasz Napierala 				sucflags |= LOGIN_SETCPUMASK;
2527*5b0945b5SGregory Neil Shapiro # endif
2528906a940eSEdward Tomasz Napierala # ifdef LOGIN_SETLOGINCLASS
2529906a940eSEdward Tomasz Napierala 				sucflags |= LOGIN_SETLOGINCLASS;
2530*5b0945b5SGregory Neil Shapiro # endif
253113bd1963SGregory Neil Shapiro # ifdef LOGIN_SETMAC
253213bd1963SGregory Neil Shapiro 				sucflags |= LOGIN_SETMAC;
2533*5b0945b5SGregory Neil Shapiro # endif
2534959366dcSGregory Neil Shapiro 				if (pwd != NULL &&
2535959366dcSGregory Neil Shapiro 				    setusercontext(NULL, pwd, pwd->pw_uid,
253613bd1963SGregory Neil Shapiro 						   sucflags) == -1 &&
2537959366dcSGregory Neil Shapiro 				    suidwarn)
2538959366dcSGregory Neil Shapiro 				{
2539959366dcSGregory Neil Shapiro 					syserr("openmailer: setusercontext() failed");
2540959366dcSGregory Neil Shapiro 					exit(EX_TEMPFAIL);
2541959366dcSGregory Neil Shapiro 				}
2542c2aa98e2SPeter Wemm 			}
254306f25ae9SGregory Neil Shapiro #endif /* HASSETUSERCONTEXT */
2544c2aa98e2SPeter Wemm 
254540266059SGregory Neil Shapiro #if HASNICE
2546c2aa98e2SPeter Wemm 			/* tweak niceness */
2547c2aa98e2SPeter Wemm 			if (m->m_nice != 0)
254806f25ae9SGregory Neil Shapiro 				(void) nice(m->m_nice);
254940266059SGregory Neil Shapiro #endif /* HASNICE */
2550c2aa98e2SPeter Wemm 
2551c2aa98e2SPeter Wemm 			/* reset group id */
2552c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
2553e92d3f3fSGregory Neil Shapiro 			{
2554e92d3f3fSGregory Neil Shapiro 				if (m->m_gid == NO_GID)
2555e92d3f3fSGregory Neil Shapiro 					new_gid = RunAsGid;
2556e92d3f3fSGregory Neil Shapiro 				else
2557c2aa98e2SPeter Wemm 					new_gid = m->m_gid;
2558e92d3f3fSGregory Neil Shapiro 			}
2559c2aa98e2SPeter Wemm 			else if (bitset(S_ISGID, stb.st_mode))
2560c2aa98e2SPeter Wemm 				new_gid = stb.st_gid;
2561c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_gid != 0)
2562c2aa98e2SPeter Wemm 			{
2563c2aa98e2SPeter Wemm 				if (!DontInitGroups)
2564c2aa98e2SPeter Wemm 				{
256540266059SGregory Neil Shapiro 					user = ctladdr->q_ruser;
256640266059SGregory Neil Shapiro 					if (user == NULL)
256740266059SGregory Neil Shapiro 						user = ctladdr->q_user;
2568c2aa98e2SPeter Wemm 
256940266059SGregory Neil Shapiro 					if (initgroups(user,
257040266059SGregory Neil Shapiro 						       ctladdr->q_gid) == -1
257140266059SGregory Neil Shapiro 					    && suidwarn)
257206f25ae9SGregory Neil Shapiro 					{
2573da7d7b9cSGregory Neil Shapiro 						syserr("openmailer: initgroups(%s, %ld) failed",
2574da7d7b9cSGregory Neil Shapiro 							user, (long) ctladdr->q_gid);
257506f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
257606f25ae9SGregory Neil Shapiro 					}
2577c2aa98e2SPeter Wemm 				}
2578c2aa98e2SPeter Wemm 				else
2579c2aa98e2SPeter Wemm 				{
2580c2aa98e2SPeter Wemm 					GIDSET_T gidset[1];
2581c2aa98e2SPeter Wemm 
2582c2aa98e2SPeter Wemm 					gidset[0] = ctladdr->q_gid;
258340266059SGregory Neil Shapiro 					if (setgroups(1, gidset) == -1
258440266059SGregory Neil Shapiro 					    && suidwarn)
258506f25ae9SGregory Neil Shapiro 					{
2586c2aa98e2SPeter Wemm 						syserr("openmailer: setgroups() failed");
258706f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
258806f25ae9SGregory Neil Shapiro 					}
2589c2aa98e2SPeter Wemm 				}
2590c2aa98e2SPeter Wemm 				new_gid = ctladdr->q_gid;
2591c2aa98e2SPeter Wemm 			}
2592c2aa98e2SPeter Wemm 			else
2593c2aa98e2SPeter Wemm 			{
2594c2aa98e2SPeter Wemm 				if (!DontInitGroups)
2595c2aa98e2SPeter Wemm 				{
259640266059SGregory Neil Shapiro 					user = DefUser;
259740266059SGregory Neil Shapiro 					if (initgroups(DefUser, DefGid) == -1 &&
259840266059SGregory Neil Shapiro 					    suidwarn)
259906f25ae9SGregory Neil Shapiro 					{
2600da7d7b9cSGregory Neil Shapiro 						syserr("openmailer: initgroups(%s, %ld) failed",
2601da7d7b9cSGregory Neil Shapiro 						       DefUser, (long) DefGid);
260206f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
260306f25ae9SGregory Neil Shapiro 					}
2604c2aa98e2SPeter Wemm 				}
2605c2aa98e2SPeter Wemm 				else
2606c2aa98e2SPeter Wemm 				{
2607c2aa98e2SPeter Wemm 					GIDSET_T gidset[1];
2608c2aa98e2SPeter Wemm 
2609c2aa98e2SPeter Wemm 					gidset[0] = DefGid;
261040266059SGregory Neil Shapiro 					if (setgroups(1, gidset) == -1
261140266059SGregory Neil Shapiro 					    && suidwarn)
261206f25ae9SGregory Neil Shapiro 					{
2613c2aa98e2SPeter Wemm 						syserr("openmailer: setgroups() failed");
261406f25ae9SGregory Neil Shapiro 						exit(EX_TEMPFAIL);
261506f25ae9SGregory Neil Shapiro 					}
2616c2aa98e2SPeter Wemm 				}
2617e92d3f3fSGregory Neil Shapiro 				if (m->m_gid == NO_GID)
2618c2aa98e2SPeter Wemm 					new_gid = DefGid;
2619c2aa98e2SPeter Wemm 				else
2620c2aa98e2SPeter Wemm 					new_gid = m->m_gid;
2621c2aa98e2SPeter Wemm 			}
262206f25ae9SGregory Neil Shapiro 			if (new_gid != NO_GID)
262306f25ae9SGregory Neil Shapiro 			{
262406f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 &&
262506f25ae9SGregory Neil Shapiro 				    bitnset(M_SPECIFIC_UID, m->m_flags) &&
262606f25ae9SGregory Neil Shapiro 				    new_gid != getgid() &&
262706f25ae9SGregory Neil Shapiro 				    new_gid != getegid())
262806f25ae9SGregory Neil Shapiro 				{
262906f25ae9SGregory Neil Shapiro 					/* Only root can change the gid */
2630da7d7b9cSGregory Neil Shapiro 					syserr("openmailer: insufficient privileges to change gid, RunAsUid=%ld, new_gid=%ld, gid=%ld, egid=%ld",
2631da7d7b9cSGregory Neil Shapiro 					       (long) RunAsUid, (long) new_gid,
2632da7d7b9cSGregory Neil Shapiro 					       (long) getgid(), (long) getegid());
263306f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
263406f25ae9SGregory Neil Shapiro 				}
263506f25ae9SGregory Neil Shapiro 
263606f25ae9SGregory Neil Shapiro 				if (setgid(new_gid) < 0 && suidwarn)
263706f25ae9SGregory Neil Shapiro 				{
2638c2aa98e2SPeter Wemm 					syserr("openmailer: setgid(%ld) failed",
2639c2aa98e2SPeter Wemm 					       (long) new_gid);
264006f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
264106f25ae9SGregory Neil Shapiro 				}
264206f25ae9SGregory Neil Shapiro 			}
264306f25ae9SGregory Neil Shapiro 
264406f25ae9SGregory Neil Shapiro 			/* change root to some "safe" directory */
264506f25ae9SGregory Neil Shapiro 			if (m->m_rootdir != NULL)
264606f25ae9SGregory Neil Shapiro 			{
2647d0cef73dSGregory Neil Shapiro 				expand(m->m_rootdir, cbuf, sizeof(cbuf), e);
264806f25ae9SGregory Neil Shapiro 				if (tTd(11, 20))
264940266059SGregory Neil Shapiro 					sm_dprintf("openmailer: chroot %s\n",
265094c01205SGregory Neil Shapiro 						   cbuf);
265194c01205SGregory Neil Shapiro 				if (chroot(cbuf) < 0)
265206f25ae9SGregory Neil Shapiro 				{
265306f25ae9SGregory Neil Shapiro 					syserr("openmailer: Cannot chroot(%s)",
265494c01205SGregory Neil Shapiro 					       cbuf);
265506f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
265606f25ae9SGregory Neil Shapiro 				}
265706f25ae9SGregory Neil Shapiro 				if (chdir("/") < 0)
265806f25ae9SGregory Neil Shapiro 				{
265906f25ae9SGregory Neil Shapiro 					syserr("openmailer: cannot chdir(/)");
266006f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
266106f25ae9SGregory Neil Shapiro 				}
266206f25ae9SGregory Neil Shapiro 			}
2663c2aa98e2SPeter Wemm 
2664c2aa98e2SPeter Wemm 			/* reset user id */
2665c2aa98e2SPeter Wemm 			endpwent();
266640266059SGregory Neil Shapiro 			sm_mbdb_terminate();
2667c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, m->m_flags))
266813058a91SGregory Neil Shapiro 			{
2669e92d3f3fSGregory Neil Shapiro 				if (m->m_uid == NO_UID)
2670e92d3f3fSGregory Neil Shapiro 					new_euid = RunAsUid;
2671e92d3f3fSGregory Neil Shapiro 				else
2672c2aa98e2SPeter Wemm 					new_euid = m->m_uid;
267313058a91SGregory Neil Shapiro 
267413058a91SGregory Neil Shapiro 				/*
267513058a91SGregory Neil Shapiro 				**  Undo the effects of the uid change in main
267613058a91SGregory Neil Shapiro 				**  for signal handling.  The real uid may
267713058a91SGregory Neil Shapiro 				**  be used by mailer in adding a "From "
267813058a91SGregory Neil Shapiro 				**  line.
267913058a91SGregory Neil Shapiro 				*/
268013058a91SGregory Neil Shapiro 
268113058a91SGregory Neil Shapiro 				if (RealUid != 0 && RealUid != getuid())
268240266059SGregory Neil Shapiro 				{
268340266059SGregory Neil Shapiro #if MAILER_SETUID_METHOD == USE_SETEUID
268440266059SGregory Neil Shapiro # if HASSETREUID
268540266059SGregory Neil Shapiro 					if (setreuid(RealUid, geteuid()) < 0)
268640266059SGregory Neil Shapiro 					{
268740266059SGregory Neil Shapiro 						syserr("openmailer: setreuid(%d, %d) failed",
268840266059SGregory Neil Shapiro 						       (int) RealUid, (int) geteuid());
268940266059SGregory Neil Shapiro 						exit(EX_OSERR);
269040266059SGregory Neil Shapiro 					}
269140266059SGregory Neil Shapiro # endif /* HASSETREUID */
269240266059SGregory Neil Shapiro #endif /* MAILER_SETUID_METHOD == USE_SETEUID */
269340266059SGregory Neil Shapiro #if MAILER_SETUID_METHOD == USE_SETREUID
269413058a91SGregory Neil Shapiro 					new_ruid = RealUid;
2695*5b0945b5SGregory Neil Shapiro #endif
269640266059SGregory Neil Shapiro 				}
269713058a91SGregory Neil Shapiro 			}
2698c2aa98e2SPeter Wemm 			else if (bitset(S_ISUID, stb.st_mode))
2699c2aa98e2SPeter Wemm 				new_ruid = stb.st_uid;
2700c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
2701c2aa98e2SPeter Wemm 				new_ruid = ctladdr->q_uid;
2702e92d3f3fSGregory Neil Shapiro 			else if (m->m_uid != NO_UID)
2703c2aa98e2SPeter Wemm 				new_ruid = m->m_uid;
2704c2aa98e2SPeter Wemm 			else
2705c2aa98e2SPeter Wemm 				new_ruid = DefUid;
2706605302a5SGregory Neil Shapiro 
2707605302a5SGregory Neil Shapiro #if _FFR_USE_SETLOGIN
2708605302a5SGregory Neil Shapiro 			/* run disconnected from terminal and set login name */
2709605302a5SGregory Neil Shapiro 			if (setsid() >= 0 &&
2710605302a5SGregory Neil Shapiro 			    ctladdr != NULL && ctladdr->q_uid != 0 &&
2711605302a5SGregory Neil Shapiro 			    new_euid == ctladdr->q_uid)
2712605302a5SGregory Neil Shapiro 			{
2713605302a5SGregory Neil Shapiro 				struct passwd *pwd;
2714605302a5SGregory Neil Shapiro 
2715605302a5SGregory Neil Shapiro 				pwd = sm_getpwuid(ctladdr->q_uid);
2716605302a5SGregory Neil Shapiro 				if (pwd != NULL && suidwarn)
2717605302a5SGregory Neil Shapiro 					(void) setlogin(pwd->pw_name);
2718605302a5SGregory Neil Shapiro 				endpwent();
2719605302a5SGregory Neil Shapiro 			}
2720605302a5SGregory Neil Shapiro #endif /* _FFR_USE_SETLOGIN */
2721605302a5SGregory Neil Shapiro 
2722c2aa98e2SPeter Wemm 			if (new_euid != NO_UID)
2723c2aa98e2SPeter Wemm 			{
272406f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 && new_euid != RunAsUid)
272506f25ae9SGregory Neil Shapiro 				{
272606f25ae9SGregory Neil Shapiro 					/* Only root can change the uid */
2727da7d7b9cSGregory Neil Shapiro 					syserr("openmailer: insufficient privileges to change uid, new_euid=%ld, RunAsUid=%ld",
2728da7d7b9cSGregory Neil Shapiro 					       (long) new_euid, (long) RunAsUid);
272906f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
273006f25ae9SGregory Neil Shapiro 				}
273106f25ae9SGregory Neil Shapiro 
2732c2aa98e2SPeter Wemm 				vendor_set_uid(new_euid);
273306f25ae9SGregory Neil Shapiro #if MAILER_SETUID_METHOD == USE_SETEUID
2734c2aa98e2SPeter Wemm 				if (seteuid(new_euid) < 0 && suidwarn)
273506f25ae9SGregory Neil Shapiro 				{
2736c2aa98e2SPeter Wemm 					syserr("openmailer: seteuid(%ld) failed",
2737c2aa98e2SPeter Wemm 					       (long) new_euid);
273806f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
273906f25ae9SGregory Neil Shapiro 				}
274006f25ae9SGregory Neil Shapiro #endif /* MAILER_SETUID_METHOD == USE_SETEUID */
274106f25ae9SGregory Neil Shapiro #if MAILER_SETUID_METHOD == USE_SETREUID
2742c2aa98e2SPeter Wemm 				if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
274306f25ae9SGregory Neil Shapiro 				{
2744c2aa98e2SPeter Wemm 					syserr("openmailer: setreuid(%ld, %ld) failed",
2745c2aa98e2SPeter Wemm 					       (long) new_ruid, (long) new_euid);
274606f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
274706f25ae9SGregory Neil Shapiro 				}
274806f25ae9SGregory Neil Shapiro #endif /* MAILER_SETUID_METHOD == USE_SETREUID */
274906f25ae9SGregory Neil Shapiro #if MAILER_SETUID_METHOD == USE_SETUID
2750c2aa98e2SPeter Wemm 				if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
275106f25ae9SGregory Neil Shapiro 				{
2752c2aa98e2SPeter Wemm 					syserr("openmailer: setuid(%ld) failed",
2753c2aa98e2SPeter Wemm 					       (long) new_euid);
275406f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
275506f25ae9SGregory Neil Shapiro 				}
275606f25ae9SGregory Neil Shapiro #endif /* MAILER_SETUID_METHOD == USE_SETUID */
2757c2aa98e2SPeter Wemm 			}
2758c2aa98e2SPeter Wemm 			else if (new_ruid != NO_UID)
2759c2aa98e2SPeter Wemm 			{
2760c2aa98e2SPeter Wemm 				vendor_set_uid(new_ruid);
2761c2aa98e2SPeter Wemm 				if (setuid(new_ruid) < 0 && suidwarn)
276206f25ae9SGregory Neil Shapiro 				{
2763c2aa98e2SPeter Wemm 					syserr("openmailer: setuid(%ld) failed",
2764c2aa98e2SPeter Wemm 					       (long) new_ruid);
276506f25ae9SGregory Neil Shapiro 					exit(EX_TEMPFAIL);
276606f25ae9SGregory Neil Shapiro 				}
2767c2aa98e2SPeter Wemm 			}
2768c2aa98e2SPeter Wemm 
2769c2aa98e2SPeter Wemm 			if (tTd(11, 2))
2770da7d7b9cSGregory Neil Shapiro 				sm_dprintf("openmailer: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
2771da7d7b9cSGregory Neil Shapiro 					   (long) getuid(), (long) geteuid(),
2772da7d7b9cSGregory Neil Shapiro 					   (long) getgid(), (long) getegid());
2773c2aa98e2SPeter Wemm 
2774c2aa98e2SPeter Wemm 			/* move into some "safe" directory */
2775c2aa98e2SPeter Wemm 			if (m->m_execdir != NULL)
2776c2aa98e2SPeter Wemm 			{
2777c2aa98e2SPeter Wemm 				char *q;
2778c2aa98e2SPeter Wemm 
2779c2aa98e2SPeter Wemm 				for (p = m->m_execdir; p != NULL; p = q)
2780c2aa98e2SPeter Wemm 				{
2781c2aa98e2SPeter Wemm 					q = strchr(p, ':');
2782c2aa98e2SPeter Wemm 					if (q != NULL)
2783c2aa98e2SPeter Wemm 						*q = '\0';
2784d0cef73dSGregory Neil Shapiro 					expand(p, cbuf, sizeof(cbuf), e);
2785c2aa98e2SPeter Wemm 					if (q != NULL)
2786c2aa98e2SPeter Wemm 						*q++ = ':';
2787c2aa98e2SPeter Wemm 					if (tTd(11, 20))
278840266059SGregory Neil Shapiro 						sm_dprintf("openmailer: trydir %s\n",
278994c01205SGregory Neil Shapiro 							   cbuf);
279094c01205SGregory Neil Shapiro 					if (cbuf[0] != '\0' &&
279194c01205SGregory Neil Shapiro 					    chdir(cbuf) >= 0)
2792c2aa98e2SPeter Wemm 						break;
2793c2aa98e2SPeter Wemm 				}
2794c2aa98e2SPeter Wemm 			}
2795c2aa98e2SPeter Wemm 
279640266059SGregory Neil Shapiro 			/* Check safety of program to be run */
279740266059SGregory Neil Shapiro 			sff = SFF_ROOTOK|SFF_EXECOK;
279840266059SGregory Neil Shapiro 			if (!bitnset(DBS_RUNWRITABLEPROGRAM,
279940266059SGregory Neil Shapiro 				     DontBlameSendmail))
280040266059SGregory Neil Shapiro 				sff |= SFF_NOGWFILES|SFF_NOWWFILES;
280140266059SGregory Neil Shapiro 			if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
280240266059SGregory Neil Shapiro 				    DontBlameSendmail))
280340266059SGregory Neil Shapiro 				sff |= SFF_NOPATHCHECK;
280440266059SGregory Neil Shapiro 			else
280540266059SGregory Neil Shapiro 				sff |= SFF_SAFEDIRPATH;
280640266059SGregory Neil Shapiro 			ret = safefile(m->m_mailer, getuid(), getgid(),
280740266059SGregory Neil Shapiro 				       user, sff, 0, NULL);
280840266059SGregory Neil Shapiro 			if (ret != 0)
280940266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
281040266059SGregory Neil Shapiro 					  "Warning: program %s unsafe: %s",
281140266059SGregory Neil Shapiro 					  m->m_mailer, sm_errstring(ret));
281240266059SGregory Neil Shapiro 
2813c2aa98e2SPeter Wemm 			/* arrange to filter std & diag output of command */
2814c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2815c2aa98e2SPeter Wemm 			if (dup2(rpvect[1], STDOUT_FILENO) < 0)
2816c2aa98e2SPeter Wemm 			{
2817c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
2818c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2819c2aa98e2SPeter Wemm 				       m->m_name, rpvect[1]);
2820c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2821c2aa98e2SPeter Wemm 			}
2822c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
282306f25ae9SGregory Neil Shapiro 
2824c2aa98e2SPeter Wemm 			if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
2825c2aa98e2SPeter Wemm 			{
2826c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup stdout for stderr",
2827c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2828c2aa98e2SPeter Wemm 				       m->m_name);
2829c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2830c2aa98e2SPeter Wemm 			}
2831c2aa98e2SPeter Wemm 
2832c2aa98e2SPeter Wemm 			/* arrange to get standard input */
2833c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2834c2aa98e2SPeter Wemm 			if (dup2(mpvect[0], STDIN_FILENO) < 0)
2835c2aa98e2SPeter Wemm 			{
2836c2aa98e2SPeter Wemm 				syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
2837c2aa98e2SPeter Wemm 				       shortenstring(e->e_to, MAXSHORTSTR),
2838c2aa98e2SPeter Wemm 				       m->m_name, mpvect[0]);
2839c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2840c2aa98e2SPeter Wemm 			}
2841c2aa98e2SPeter Wemm 			(void) close(mpvect[0]);
2842c2aa98e2SPeter Wemm 
2843c2aa98e2SPeter Wemm 			/* arrange for all the files to be closed */
2844e92d3f3fSGregory Neil Shapiro 			sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2845c2aa98e2SPeter Wemm 
2846605302a5SGregory Neil Shapiro #if !_FFR_USE_SETLOGIN
2847c2aa98e2SPeter Wemm 			/* run disconnected from terminal */
2848c2aa98e2SPeter Wemm 			(void) setsid();
2849*5b0945b5SGregory Neil Shapiro #endif
2850c2aa98e2SPeter Wemm 
2851c2aa98e2SPeter Wemm 			/* try to execute the mailer */
285206f25ae9SGregory Neil Shapiro 			(void) execve(m->m_mailer, (ARGV_T) pv,
285306f25ae9SGregory Neil Shapiro 				      (ARGV_T) UserEnviron);
285406f25ae9SGregory Neil Shapiro 			save_errno = errno;
2855c2aa98e2SPeter Wemm 			syserr("Cannot exec %s", m->m_mailer);
2856c2aa98e2SPeter Wemm 			if (bitnset(M_LOCALMAILER, m->m_flags) ||
285706f25ae9SGregory Neil Shapiro 			    transienterror(save_errno))
2858c2aa98e2SPeter Wemm 				_exit(EX_OSERR);
2859c2aa98e2SPeter Wemm 			_exit(EX_UNAVAILABLE);
2860c2aa98e2SPeter Wemm 		}
2861c2aa98e2SPeter Wemm 
2862c2aa98e2SPeter Wemm 		/*
2863c2aa98e2SPeter Wemm 		**  Set up return value.
2864c2aa98e2SPeter Wemm 		*/
2865c2aa98e2SPeter Wemm 
2866c2aa98e2SPeter Wemm 		if (mci == NULL)
2867c2aa98e2SPeter Wemm 		{
286840266059SGregory Neil Shapiro 			if (clever)
286940266059SGregory Neil Shapiro 			{
287040266059SGregory Neil Shapiro 				/*
287140266059SGregory Neil Shapiro 				**  Allocate from general heap, not
287240266059SGregory Neil Shapiro 				**  envelope rpool, because this mci
287340266059SGregory Neil Shapiro 				**  is going to be cached.
287440266059SGregory Neil Shapiro 				*/
287540266059SGregory Neil Shapiro 
287640266059SGregory Neil Shapiro 				mci = mci_new(NULL);
287740266059SGregory Neil Shapiro 			}
287840266059SGregory Neil Shapiro 			else
287940266059SGregory Neil Shapiro 			{
288040266059SGregory Neil Shapiro 				/*
288140266059SGregory Neil Shapiro 				**  Prevent a storage leak by allocating
288240266059SGregory Neil Shapiro 				**  this from the envelope rpool.
288340266059SGregory Neil Shapiro 				*/
288440266059SGregory Neil Shapiro 
288540266059SGregory Neil Shapiro 				mci = mci_new(e->e_rpool);
288640266059SGregory Neil Shapiro 			}
2887c2aa98e2SPeter Wemm 		}
2888c2aa98e2SPeter Wemm 		mci->mci_mailer = m;
2889c2aa98e2SPeter Wemm 		if (clever)
2890c2aa98e2SPeter Wemm 		{
2891c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPENING;
2892c2aa98e2SPeter Wemm 			mci_cache(mci);
2893c2aa98e2SPeter Wemm 		}
2894c2aa98e2SPeter Wemm 		else
2895c2aa98e2SPeter Wemm 		{
2896c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPEN;
2897c2aa98e2SPeter Wemm 		}
2898c2aa98e2SPeter Wemm 		mci->mci_pid = pid;
2899c2aa98e2SPeter Wemm 		(void) close(mpvect[0]);
290040266059SGregory Neil Shapiro 		mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2901e92d3f3fSGregory Neil Shapiro 					  (void *) &(mpvect[1]), SM_IO_WRONLY_B,
290240266059SGregory Neil Shapiro 					  NULL);
2903c2aa98e2SPeter Wemm 		if (mci->mci_out == NULL)
2904c2aa98e2SPeter Wemm 		{
2905c2aa98e2SPeter Wemm 			syserr("deliver: cannot create mailer output channel, fd=%d",
2906c2aa98e2SPeter Wemm 			       mpvect[1]);
2907c2aa98e2SPeter Wemm 			(void) close(mpvect[1]);
2908c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
2909c2aa98e2SPeter Wemm 			(void) close(rpvect[1]);
2910c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2911c2aa98e2SPeter Wemm 			goto give_up;
2912c2aa98e2SPeter Wemm 		}
291306f25ae9SGregory Neil Shapiro 
2914c2aa98e2SPeter Wemm 		(void) close(rpvect[1]);
291540266059SGregory Neil Shapiro 		mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
2916e92d3f3fSGregory Neil Shapiro 					 (void *) &(rpvect[0]), SM_IO_RDONLY_B,
291740266059SGregory Neil Shapiro 					 NULL);
2918c2aa98e2SPeter Wemm 		if (mci->mci_in == NULL)
2919c2aa98e2SPeter Wemm 		{
2920c2aa98e2SPeter Wemm 			syserr("deliver: cannot create mailer input channel, fd=%d",
2921c2aa98e2SPeter Wemm 			       mpvect[1]);
2922c2aa98e2SPeter Wemm 			(void) close(rpvect[0]);
292340266059SGregory Neil Shapiro 			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
2924c2aa98e2SPeter Wemm 			mci->mci_out = NULL;
2925c2aa98e2SPeter Wemm 			rcode = EX_OSERR;
2926c2aa98e2SPeter Wemm 			goto give_up;
2927c2aa98e2SPeter Wemm 		}
2928c2aa98e2SPeter Wemm 	}
2929c2aa98e2SPeter Wemm 
2930c2aa98e2SPeter Wemm 	/*
2931c2aa98e2SPeter Wemm 	**  If we are in SMTP opening state, send initial protocol.
2932c2aa98e2SPeter Wemm 	*/
2933c2aa98e2SPeter Wemm 
2934c2aa98e2SPeter Wemm 	if (bitnset(M_7BITS, m->m_flags) &&
2935c2aa98e2SPeter Wemm 	    (!clever || mci->mci_state == MCIS_OPENING))
2936c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_7BIT;
2937c2aa98e2SPeter Wemm 	if (clever && mci->mci_state != MCIS_CLOSED)
2938c2aa98e2SPeter Wemm 	{
293940266059SGregory Neil Shapiro #if STARTTLS || SASL
294040266059SGregory Neil Shapiro 		char *srvname;
294140266059SGregory Neil Shapiro 		extern SOCKADDR CurHostAddr;
294240266059SGregory Neil Shapiro #endif /* STARTTLS || SASL */
294340266059SGregory Neil Shapiro 
294440266059SGregory Neil Shapiro #if SASL
2945193538b7SGregory Neil Shapiro # define DONE_AUTH(f)		bitset(MCIF_AUTHACT, f)
2946*5b0945b5SGregory Neil Shapiro #endif
294706f25ae9SGregory Neil Shapiro #if STARTTLS
2948193538b7SGregory Neil Shapiro # define DONE_STARTTLS(f)	bitset(MCIF_TLSACT, f)
2949*5b0945b5SGregory Neil Shapiro #endif
2950193538b7SGregory Neil Shapiro #define ONLY_HELO(f)		bitset(MCIF_ONLY_EHLO, f)
2951193538b7SGregory Neil Shapiro #define SET_HELO(f)		f |= MCIF_ONLY_EHLO
2952193538b7SGregory Neil Shapiro #define CLR_HELO(f)		f &= ~MCIF_ONLY_EHLO
2953c2aa98e2SPeter Wemm 
295440266059SGregory Neil Shapiro #if STARTTLS || SASL
295540266059SGregory Neil Shapiro 		/* don't use CurHostName, it is changed in many places */
2956602a2b1bSGregory Neil Shapiro 		if (mci->mci_host != NULL)
2957602a2b1bSGregory Neil Shapiro 		{
2958602a2b1bSGregory Neil Shapiro 			srvname = mci->mci_host;
2959*5b0945b5SGregory Neil Shapiro 			RM_TRAIL_DOT(srvname);
2960602a2b1bSGregory Neil Shapiro 		}
296140266059SGregory Neil Shapiro 		else if (mci->mci_mailer != NULL)
2962602a2b1bSGregory Neil Shapiro 		{
296340266059SGregory Neil Shapiro 			srvname = mci->mci_mailer->m_name;
2964602a2b1bSGregory Neil Shapiro 			dotpos = -1;
2965602a2b1bSGregory Neil Shapiro 		}
296606f25ae9SGregory Neil Shapiro 		else
296706f25ae9SGregory Neil Shapiro 		{
296840266059SGregory Neil Shapiro 			srvname = "local";
296940266059SGregory Neil Shapiro 			dotpos = -1;
2970193538b7SGregory Neil Shapiro 		}
297106f25ae9SGregory Neil Shapiro 
297240266059SGregory Neil Shapiro 		/* don't set {server_name} to NULL or "": see getauth() */
297340266059SGregory Neil Shapiro 		macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
297440266059SGregory Neil Shapiro 			  srvname);
297540266059SGregory Neil Shapiro 
297640266059SGregory Neil Shapiro 		/* CurHostAddr is set by makeconnection() and mci_get() */
297740266059SGregory Neil Shapiro 		if (CurHostAddr.sa.sa_family != 0)
297840266059SGregory Neil Shapiro 		{
297940266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_TEMP,
298040266059SGregory Neil Shapiro 				  macid("{server_addr}"),
298140266059SGregory Neil Shapiro 				  anynet_ntoa(&CurHostAddr));
298240266059SGregory Neil Shapiro 		}
298340266059SGregory Neil Shapiro 		else if (mci->mci_mailer != NULL)
298440266059SGregory Neil Shapiro 		{
298540266059SGregory Neil Shapiro 			/* mailer name is unique, use it as address */
298640266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_PERM,
298740266059SGregory Neil Shapiro 				  macid("{server_addr}"),
298840266059SGregory Neil Shapiro 				  mci->mci_mailer->m_name);
298940266059SGregory Neil Shapiro 		}
299040266059SGregory Neil Shapiro 		else
299140266059SGregory Neil Shapiro 		{
299240266059SGregory Neil Shapiro 			/* don't set it to NULL or "": see getauth() */
299340266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_PERM,
299440266059SGregory Neil Shapiro 				  macid("{server_addr}"), "0");
299540266059SGregory Neil Shapiro 		}
299640266059SGregory Neil Shapiro 
2997*5b0945b5SGregory Neil Shapiro # if DANE
2998*5b0945b5SGregory Neil Shapiro 		SM_FREE(dane_vrfy_ctx.dane_vrfy_host);
2999*5b0945b5SGregory Neil Shapiro 		SM_FREE(dane_vrfy_ctx.dane_vrfy_sni);
3000*5b0945b5SGregory Neil Shapiro 		dane_vrfy_ctx.dane_vrfy_fp[0] = '\0';
3001*5b0945b5SGregory Neil Shapiro 		if (ste != NULL && ste->s_tlsa != NULL &&
3002*5b0945b5SGregory Neil Shapiro 		    ste->s_tlsa->dane_tlsa_sni != NULL)
3003*5b0945b5SGregory Neil Shapiro 			dane_vrfy_ctx.dane_vrfy_sni = sm_strdup(ste->s_tlsa->dane_tlsa_sni);
3004*5b0945b5SGregory Neil Shapiro 		dane_vrfy_ctx.dane_vrfy_host = sm_strdup(srvname);
3005*5b0945b5SGregory Neil Shapiro # endif
3006*5b0945b5SGregory Neil Shapiro 
300740266059SGregory Neil Shapiro 		/* undo change of srvname (mci->mci_host) */
3008*5b0945b5SGregory Neil Shapiro 		FIX_TRAIL_DOT(srvname);
300940266059SGregory Neil Shapiro 
301040266059SGregory Neil Shapiro reconnect:	/* after switching to an encrypted connection */
3011*5b0945b5SGregory Neil Shapiro # if DANE
3012*5b0945b5SGregory Neil Shapiro 		if (DONE_STARTTLS(mci->mci_flags))
3013*5b0945b5SGregory Neil Shapiro 		{
3014*5b0945b5SGregory Neil Shapiro 			/* use a "reset" function? */
3015*5b0945b5SGregory Neil Shapiro 			SM_FREE(dane_vrfy_ctx.dane_vrfy_host);
3016*5b0945b5SGregory Neil Shapiro 			SM_FREE(dane_vrfy_ctx.dane_vrfy_sni);
3017*5b0945b5SGregory Neil Shapiro 			dane_vrfy_ctx.dane_vrfy_fp[0] = '\0';
3018*5b0945b5SGregory Neil Shapiro 			dane_vrfy_ctx.dane_vrfy_res = 0;
3019*5b0945b5SGregory Neil Shapiro 		}
3020*5b0945b5SGregory Neil Shapiro # endif
3021*5b0945b5SGregory Neil Shapiro 
302240266059SGregory Neil Shapiro #endif /* STARTTLS || SASL */
302340266059SGregory Neil Shapiro 
302440266059SGregory Neil Shapiro 		/* set the current connection information */
302540266059SGregory Neil Shapiro 		e->e_mci = mci;
302640266059SGregory Neil Shapiro #if SASL
302740266059SGregory Neil Shapiro 		mci->mci_saslcap = NULL;
3028*5b0945b5SGregory Neil Shapiro #endif
302940266059SGregory Neil Shapiro 		smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
303040266059SGregory Neil Shapiro 		CLR_HELO(mci->mci_flags);
303140266059SGregory Neil Shapiro 
303240266059SGregory Neil Shapiro 		if (IS_DLVR_RETURN(e))
303340266059SGregory Neil Shapiro 		{
303440266059SGregory Neil Shapiro 			/*
303540266059SGregory Neil Shapiro 			**  Check whether other side can deliver e-mail
303640266059SGregory Neil Shapiro 			**  fast enough
303740266059SGregory Neil Shapiro 			*/
303840266059SGregory Neil Shapiro 
303940266059SGregory Neil Shapiro 			if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
304040266059SGregory Neil Shapiro 			{
304140266059SGregory Neil Shapiro 				e->e_status = "5.4.7";
304240266059SGregory Neil Shapiro 				usrerrenh(e->e_status,
304340266059SGregory Neil Shapiro 					  "554 Server does not support Deliver By");
304440266059SGregory Neil Shapiro 				rcode = EX_UNAVAILABLE;
304540266059SGregory Neil Shapiro 				goto give_up;
304640266059SGregory Neil Shapiro 			}
304740266059SGregory Neil Shapiro 			if (e->e_deliver_by > 0 &&
304840266059SGregory Neil Shapiro 			    e->e_deliver_by - (curtime() - e->e_ctime) <
304940266059SGregory Neil Shapiro 			    mci->mci_min_by)
305040266059SGregory Neil Shapiro 			{
305140266059SGregory Neil Shapiro 				e->e_status = "5.4.7";
305240266059SGregory Neil Shapiro 				usrerrenh(e->e_status,
305340266059SGregory Neil Shapiro 					  "554 Message can't be delivered in time; %ld < %ld",
3054*5b0945b5SGregory Neil Shapiro 					  e->e_deliver_by - (long) (curtime() -
3055*5b0945b5SGregory Neil Shapiro 								e->e_ctime),
305640266059SGregory Neil Shapiro 					  mci->mci_min_by);
305740266059SGregory Neil Shapiro 				rcode = EX_UNAVAILABLE;
305840266059SGregory Neil Shapiro 				goto give_up;
305940266059SGregory Neil Shapiro 			}
306040266059SGregory Neil Shapiro 		}
306140266059SGregory Neil Shapiro 
306240266059SGregory Neil Shapiro #if STARTTLS
306340266059SGregory Neil Shapiro 		/* first TLS then AUTH to provide a security layer */
306440266059SGregory Neil Shapiro 		if (mci->mci_state != MCIS_CLOSED &&
306540266059SGregory Neil Shapiro 		    !DONE_STARTTLS(mci->mci_flags))
306640266059SGregory Neil Shapiro 		{
306740266059SGregory Neil Shapiro 			int olderrors;
306840266059SGregory Neil Shapiro 			bool usetls;
306940266059SGregory Neil Shapiro 			bool saveQuickAbort = QuickAbort;
307040266059SGregory Neil Shapiro 			bool saveSuprErrs = SuprErrs;
307140266059SGregory Neil Shapiro 			char *host = NULL;
307240266059SGregory Neil Shapiro 
307340266059SGregory Neil Shapiro 			rcode = EX_OK;
307440266059SGregory Neil Shapiro 			usetls = bitset(MCIF_TLS, mci->mci_flags);
307540266059SGregory Neil Shapiro 			if (usetls)
307640266059SGregory Neil Shapiro 				usetls = !iscltflgset(e, D_NOTLS);
3077*5b0945b5SGregory Neil Shapiro 			if (usetls)
3078*5b0945b5SGregory Neil Shapiro 				usetls = tlsstate == 0;
307940266059SGregory Neil Shapiro 
3080d0cef73dSGregory Neil Shapiro 			host = macvalue(macid("{server_name}"), e);
308140266059SGregory Neil Shapiro 			if (usetls)
308240266059SGregory Neil Shapiro 			{
308340266059SGregory Neil Shapiro 				olderrors = Errors;
308440266059SGregory Neil Shapiro 				QuickAbort = false;
308540266059SGregory Neil Shapiro 				SuprErrs = true;
3086959366dcSGregory Neil Shapiro 				if (rscheck("try_tls", host, NULL, e,
3087da7d7b9cSGregory Neil Shapiro 					    RSF_RMCOMM, 7, host, NOQID, NULL,
3088da7d7b9cSGregory Neil Shapiro 					    NULL) != EX_OK
308940266059SGregory Neil Shapiro 				    || Errors > olderrors)
3090d0cef73dSGregory Neil Shapiro 				{
309140266059SGregory Neil Shapiro 					usetls = false;
3092d0cef73dSGregory Neil Shapiro 				}
309340266059SGregory Neil Shapiro 				SuprErrs = saveSuprErrs;
309440266059SGregory Neil Shapiro 				QuickAbort = saveQuickAbort;
309540266059SGregory Neil Shapiro 			}
309640266059SGregory Neil Shapiro 
309706f25ae9SGregory Neil Shapiro 			if (usetls)
309806f25ae9SGregory Neil Shapiro 			{
3099*5b0945b5SGregory Neil Shapiro 				if ((rcode = starttls(m, mci, e
3100*5b0945b5SGregory Neil Shapiro # if DANE
3101*5b0945b5SGregory Neil Shapiro 							, &dane_vrfy_ctx
3102*5b0945b5SGregory Neil Shapiro # endif
3103*5b0945b5SGregory Neil Shapiro 					)) == EX_OK)
310406f25ae9SGregory Neil Shapiro 				{
310506f25ae9SGregory Neil Shapiro 					/* start again without STARTTLS */
310606f25ae9SGregory Neil Shapiro 					mci->mci_flags |= MCIF_TLSACT;
310706f25ae9SGregory Neil Shapiro 				}
310806f25ae9SGregory Neil Shapiro 				else
310906f25ae9SGregory Neil Shapiro 				{
311006f25ae9SGregory Neil Shapiro 					char *s;
311106f25ae9SGregory Neil Shapiro 
311206f25ae9SGregory Neil Shapiro 					/*
31139bd497b8SGregory Neil Shapiro 					**  TLS negotiation failed, what to do?
311406f25ae9SGregory Neil Shapiro 					**  fall back to unencrypted connection
311506f25ae9SGregory Neil Shapiro 					**  or abort? How to decide?
311606f25ae9SGregory Neil Shapiro 					**  set a macro and call a ruleset.
311706f25ae9SGregory Neil Shapiro 					*/
311840266059SGregory Neil Shapiro 
311906f25ae9SGregory Neil Shapiro 					mci->mci_flags &= ~MCIF_TLS;
312006f25ae9SGregory Neil Shapiro 					switch (rcode)
312106f25ae9SGregory Neil Shapiro 					{
312206f25ae9SGregory Neil Shapiro 					  case EX_TEMPFAIL:
312306f25ae9SGregory Neil Shapiro 						s = "TEMP";
312406f25ae9SGregory Neil Shapiro 						break;
312506f25ae9SGregory Neil Shapiro 					  case EX_USAGE:
312606f25ae9SGregory Neil Shapiro 						s = "USAGE";
312706f25ae9SGregory Neil Shapiro 						break;
312806f25ae9SGregory Neil Shapiro 					  case EX_PROTOCOL:
312906f25ae9SGregory Neil Shapiro 						s = "PROTOCOL";
313006f25ae9SGregory Neil Shapiro 						break;
313106f25ae9SGregory Neil Shapiro 					  case EX_SOFTWARE:
313206f25ae9SGregory Neil Shapiro 						s = "SOFTWARE";
313306f25ae9SGregory Neil Shapiro 						break;
31344e4196cbSGregory Neil Shapiro 					  case EX_UNAVAILABLE:
31354e4196cbSGregory Neil Shapiro 						s = "NONE";
31364e4196cbSGregory Neil Shapiro 						break;
313706f25ae9SGregory Neil Shapiro 
313806f25ae9SGregory Neil Shapiro 					  /* everything else is a failure */
313906f25ae9SGregory Neil Shapiro 					  default:
314006f25ae9SGregory Neil Shapiro 						s = "FAILURE";
314106f25ae9SGregory Neil Shapiro 						rcode = EX_TEMPFAIL;
314206f25ae9SGregory Neil Shapiro 					}
314340266059SGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
314440266059SGregory Neil Shapiro 						  macid("{verify}"), s);
314506f25ae9SGregory Neil Shapiro 				}
314606f25ae9SGregory Neil Shapiro 			}
314706f25ae9SGregory Neil Shapiro 			else
3148*5b0945b5SGregory Neil Shapiro 			{
3149*5b0945b5SGregory Neil Shapiro 				p = tlsstate == 0 ? "NONE": "CLEAR";
3150*5b0945b5SGregory Neil Shapiro # if DANE
3151*5b0945b5SGregory Neil Shapiro 				/*
3152*5b0945b5SGregory Neil Shapiro 				**  TLSA found but STARTTLS not offered?
3153*5b0945b5SGregory Neil Shapiro 				**  What is the best way to "fail"?
3154*5b0945b5SGregory Neil Shapiro 				**  XXX: check expiration!
3155*5b0945b5SGregory Neil Shapiro 				*/
3156*5b0945b5SGregory Neil Shapiro 
3157*5b0945b5SGregory Neil Shapiro 				if (!bitset(MCIF_TLS, mci->mci_flags) &&
3158*5b0945b5SGregory Neil Shapiro 				    ste != NULL &&
3159*5b0945b5SGregory Neil Shapiro 				    ste->s_tlsa != NULL &&
3160*5b0945b5SGregory Neil Shapiro 				    ste->s_tlsa->dane_tlsa_n > 0)
3161*5b0945b5SGregory Neil Shapiro 				{
3162*5b0945b5SGregory Neil Shapiro 					if (LogLevel > 8)
3163*5b0945b5SGregory Neil Shapiro 						sm_syslog(LOG_NOTICE, NOQID,
3164*5b0945b5SGregory Neil Shapiro 							"STARTTLS=client, relay=%.100s, warning=DANE configured in DNS but no STARTTLS available",
3165*5b0945b5SGregory Neil Shapiro 							host);
3166*5b0945b5SGregory Neil Shapiro 					/* XXX include TLSA RR from DNS? */
3167*5b0945b5SGregory Neil Shapiro 
3168*5b0945b5SGregory Neil Shapiro 					p = "DANE_FAIL";
3169*5b0945b5SGregory Neil Shapiro 				}
3170*5b0945b5SGregory Neil Shapiro # endif /* DANE */
317140266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
3172*5b0945b5SGregory Neil Shapiro 					  macid("{verify}"), p);
3173*5b0945b5SGregory Neil Shapiro 			}
317406f25ae9SGregory Neil Shapiro 			olderrors = Errors;
317540266059SGregory Neil Shapiro 			QuickAbort = false;
317640266059SGregory Neil Shapiro 			SuprErrs = true;
317706f25ae9SGregory Neil Shapiro 
317806f25ae9SGregory Neil Shapiro 			/*
317906f25ae9SGregory Neil Shapiro 			**  rcode == EX_SOFTWARE is special:
31809bd497b8SGregory Neil Shapiro 			**  the TLS negotiation failed
318106f25ae9SGregory Neil Shapiro 			**  we have to drop the connection no matter what
318206f25ae9SGregory Neil Shapiro 			**  However, we call tls_server to give it the chance
318306f25ae9SGregory Neil Shapiro 			**  to log the problem and return an appropriate
318406f25ae9SGregory Neil Shapiro 			**  error code.
318506f25ae9SGregory Neil Shapiro 			*/
318640266059SGregory Neil Shapiro 
318706f25ae9SGregory Neil Shapiro 			if (rscheck("tls_server",
318840266059SGregory Neil Shapiro 				    macvalue(macid("{verify}"), e),
3189959366dcSGregory Neil Shapiro 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
3190da7d7b9cSGregory Neil Shapiro 				    host, NOQID, NULL, NULL) != EX_OK ||
319106f25ae9SGregory Neil Shapiro 			    Errors > olderrors ||
319206f25ae9SGregory Neil Shapiro 			    rcode == EX_SOFTWARE)
319306f25ae9SGregory Neil Shapiro 			{
319406f25ae9SGregory Neil Shapiro 				char enhsc[ENHSCLEN];
319506f25ae9SGregory Neil Shapiro 				extern char MsgBuf[];
319606f25ae9SGregory Neil Shapiro 
319706f25ae9SGregory Neil Shapiro 				if (ISSMTPCODE(MsgBuf) &&
319806f25ae9SGregory Neil Shapiro 				    extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
319906f25ae9SGregory Neil Shapiro 				{
320040266059SGregory Neil Shapiro 					p = sm_rpool_strdup_x(e->e_rpool,
320140266059SGregory Neil Shapiro 							      MsgBuf);
320206f25ae9SGregory Neil Shapiro 				}
320306f25ae9SGregory Neil Shapiro 				else
320406f25ae9SGregory Neil Shapiro 				{
320506f25ae9SGregory Neil Shapiro 					p = "403 4.7.0 server not authenticated.";
320640266059SGregory Neil Shapiro 					(void) sm_strlcpy(enhsc, "4.7.0",
3207d0cef73dSGregory Neil Shapiro 							  sizeof(enhsc));
320806f25ae9SGregory Neil Shapiro 				}
320906f25ae9SGregory Neil Shapiro 				SuprErrs = saveSuprErrs;
321006f25ae9SGregory Neil Shapiro 				QuickAbort = saveQuickAbort;
321106f25ae9SGregory Neil Shapiro 
321206f25ae9SGregory Neil Shapiro 				if (rcode == EX_SOFTWARE)
321306f25ae9SGregory Neil Shapiro 				{
321406f25ae9SGregory Neil Shapiro 					/* drop the connection */
321506f25ae9SGregory Neil Shapiro 					mci->mci_state = MCIS_QUITING;
321606f25ae9SGregory Neil Shapiro 					if (mci->mci_in != NULL)
321706f25ae9SGregory Neil Shapiro 					{
321840266059SGregory Neil Shapiro 						(void) sm_io_close(mci->mci_in,
321940266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT);
322006f25ae9SGregory Neil Shapiro 						mci->mci_in = NULL;
322106f25ae9SGregory Neil Shapiro 					}
322206f25ae9SGregory Neil Shapiro 					mci->mci_flags &= ~MCIF_TLSACT;
322306f25ae9SGregory Neil Shapiro 					(void) endmailer(mci, e, pv);
3224*5b0945b5SGregory Neil Shapiro 
3225*5b0945b5SGregory Neil Shapiro 					if ((TLSFallbacktoClear ||
3226*5b0945b5SGregory Neil Shapiro 					     SM_TLSI_IS(&(mci->mci_tlsi),
3227*5b0945b5SGregory Neil Shapiro 							TLSI_FL_FB2CLR)) &&
3228*5b0945b5SGregory Neil Shapiro 					    !SM_TLSI_IS(&(mci->mci_tlsi),
3229*5b0945b5SGregory Neil Shapiro 							TLSI_FL_NOFB2CLR)
3230*5b0945b5SGregory Neil Shapiro # if DANE
3231*5b0945b5SGregory Neil Shapiro 					     && dane_vrfy_ctx.dane_vrfy_chk !=
3232*5b0945b5SGregory Neil Shapiro 						DANE_SECURE
3233*5b0945b5SGregory Neil Shapiro # endif
3234*5b0945b5SGregory Neil Shapiro 					    )
3235*5b0945b5SGregory Neil Shapiro 					{
3236*5b0945b5SGregory Neil Shapiro 						++tlsstate;
3237*5b0945b5SGregory Neil Shapiro 					}
323806f25ae9SGregory Neil Shapiro 				}
323906f25ae9SGregory Neil Shapiro 				else
324006f25ae9SGregory Neil Shapiro 				{
324106f25ae9SGregory Neil Shapiro 					/* abort transfer */
324206f25ae9SGregory Neil Shapiro 					smtpquit(m, mci, e);
324306f25ae9SGregory Neil Shapiro 				}
324406f25ae9SGregory Neil Shapiro 
3245193538b7SGregory Neil Shapiro 				/* avoid bogus error msg */
3246193538b7SGregory Neil Shapiro 				mci->mci_errno = 0;
3247193538b7SGregory Neil Shapiro 
324806f25ae9SGregory Neil Shapiro 				/* temp or permanent failure? */
324906f25ae9SGregory Neil Shapiro 				rcode = (*p == '4') ? EX_TEMPFAIL
325006f25ae9SGregory Neil Shapiro 						    : EX_UNAVAILABLE;
325140266059SGregory Neil Shapiro 				mci_setstat(mci, rcode, enhsc, p);
325206f25ae9SGregory Neil Shapiro 
325306f25ae9SGregory Neil Shapiro 				/*
325406f25ae9SGregory Neil Shapiro 				**  hack to get the error message into
325506f25ae9SGregory Neil Shapiro 				**  the envelope (done in giveresponse())
325606f25ae9SGregory Neil Shapiro 				*/
325740266059SGregory Neil Shapiro 
325840266059SGregory Neil Shapiro 				(void) sm_strlcpy(SmtpError, p,
3259d0cef73dSGregory Neil Shapiro 						  sizeof(SmtpError));
326006f25ae9SGregory Neil Shapiro 			}
3261d0cef73dSGregory Neil Shapiro 			else if (mci->mci_state == MCIS_CLOSED)
3262d0cef73dSGregory Neil Shapiro 			{
3263d0cef73dSGregory Neil Shapiro 				/* connection close caused by 421 */
3264d0cef73dSGregory Neil Shapiro 				mci->mci_errno = 0;
3265d0cef73dSGregory Neil Shapiro 				rcode = EX_TEMPFAIL;
3266d0cef73dSGregory Neil Shapiro 				mci_setstat(mci, rcode, NULL, "421");
3267d0cef73dSGregory Neil Shapiro 			}
3268d0cef73dSGregory Neil Shapiro 			else
3269d0cef73dSGregory Neil Shapiro 				rcode = 0;
3270d0cef73dSGregory Neil Shapiro 
327106f25ae9SGregory Neil Shapiro 			QuickAbort = saveQuickAbort;
327206f25ae9SGregory Neil Shapiro 			SuprErrs = saveSuprErrs;
3273193538b7SGregory Neil Shapiro 			if (DONE_STARTTLS(mci->mci_flags) &&
3274193538b7SGregory Neil Shapiro 			    mci->mci_state != MCIS_CLOSED)
327506f25ae9SGregory Neil Shapiro 			{
3276193538b7SGregory Neil Shapiro 				SET_HELO(mci->mci_flags);
32776f9c8e5bSGregory Neil Shapiro 				mci_clr_extensions(mci);
327806f25ae9SGregory Neil Shapiro 				goto reconnect;
327906f25ae9SGregory Neil Shapiro 			}
3280*5b0945b5SGregory Neil Shapiro 			if (tlsstate == 1)
3281*5b0945b5SGregory Neil Shapiro 			{
3282*5b0945b5SGregory Neil Shapiro 				if (tTd(11, 1))
3283*5b0945b5SGregory Neil Shapiro 				{
3284*5b0945b5SGregory Neil Shapiro 					sm_syslog(LOG_DEBUG, NOQID,
3285*5b0945b5SGregory Neil Shapiro 						"STARTTLS=client, relay=%.100s, tlsstate=%d, status=trying_again",
3286*5b0945b5SGregory Neil Shapiro 						mci->mci_host, tlsstate);
3287*5b0945b5SGregory Neil Shapiro 					mci_dump(NULL, mci, true);
3288*5b0945b5SGregory Neil Shapiro 				}
3289*5b0945b5SGregory Neil Shapiro 				++tlsstate;
3290*5b0945b5SGregory Neil Shapiro 
3291*5b0945b5SGregory Neil Shapiro 				/*
3292*5b0945b5SGregory Neil Shapiro 				**  Fake the status so a new connection is
3293*5b0945b5SGregory Neil Shapiro 				**  tried, otherwise the TLS error will
3294*5b0945b5SGregory Neil Shapiro 				**  "persist" during this delivery attempt.
3295*5b0945b5SGregory Neil Shapiro 				*/
3296*5b0945b5SGregory Neil Shapiro 
3297*5b0945b5SGregory Neil Shapiro 				mci->mci_errno = 0;
3298*5b0945b5SGregory Neil Shapiro 				rcode = EX_OK;
3299*5b0945b5SGregory Neil Shapiro 				mci_setstat(mci, rcode, NULL, NULL);
3300*5b0945b5SGregory Neil Shapiro 				goto one_last_try;
3301*5b0945b5SGregory Neil Shapiro }
330206f25ae9SGregory Neil Shapiro 		}
330306f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
330406f25ae9SGregory Neil Shapiro #if SASL
330506f25ae9SGregory Neil Shapiro 		/* if other server supports authentication let's authenticate */
330606f25ae9SGregory Neil Shapiro 		if (mci->mci_state != MCIS_CLOSED &&
330706f25ae9SGregory Neil Shapiro 		    mci->mci_saslcap != NULL &&
330840266059SGregory Neil Shapiro 		    !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
330906f25ae9SGregory Neil Shapiro 		{
331040266059SGregory Neil Shapiro 			/* Should we require some minimum authentication? */
331140266059SGregory Neil Shapiro 			if ((ret = smtpauth(m, mci, e)) == EX_OK)
331206f25ae9SGregory Neil Shapiro 			{
331306f25ae9SGregory Neil Shapiro 				int result;
331440266059SGregory Neil Shapiro 				sasl_ssf_t *ssf = NULL;
331506f25ae9SGregory Neil Shapiro 
331640266059SGregory Neil Shapiro 				/* Get security strength (features) */
331706f25ae9SGregory Neil Shapiro 				result = sasl_getprop(mci->mci_conn, SASL_SSF,
331894c01205SGregory Neil Shapiro # if SASL >= 20000
331994c01205SGregory Neil Shapiro 						      (const void **) &ssf);
3320*5b0945b5SGregory Neil Shapiro # else
332106f25ae9SGregory Neil Shapiro 						      (void **) &ssf);
3322*5b0945b5SGregory Neil Shapiro # endif
332340266059SGregory Neil Shapiro 
332440266059SGregory Neil Shapiro 				/* XXX authid? */
332506f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
332606f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, NOQID,
332740266059SGregory Neil Shapiro 						  "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
332806f25ae9SGregory Neil Shapiro 						  mci->mci_host,
332940266059SGregory Neil Shapiro 						  macvalue(macid("{auth_type}"), e),
333040266059SGregory Neil Shapiro 						  result == SASL_OK ? *ssf : 0);
33318774250cSGregory Neil Shapiro 
333206f25ae9SGregory Neil Shapiro 				/*
333340266059SGregory Neil Shapiro 				**  Only switch to encrypted connection
333406f25ae9SGregory Neil Shapiro 				**  if a security layer has been negotiated
333506f25ae9SGregory Neil Shapiro 				*/
333640266059SGregory Neil Shapiro 
333706f25ae9SGregory Neil Shapiro 				if (result == SASL_OK && *ssf > 0)
333806f25ae9SGregory Neil Shapiro 				{
3339af9557fdSGregory Neil Shapiro 					int tmo;
3340af9557fdSGregory Neil Shapiro 
334106f25ae9SGregory Neil Shapiro 					/*
334240266059SGregory Neil Shapiro 					**  Convert I/O layer to use SASL.
334340266059SGregory Neil Shapiro 					**  If the call fails, the connection
334440266059SGregory Neil Shapiro 					**  is aborted.
334506f25ae9SGregory Neil Shapiro 					*/
334640266059SGregory Neil Shapiro 
3347af9557fdSGregory Neil Shapiro 					tmo = DATA_PROGRESS_TIMEOUT * 1000;
334840266059SGregory Neil Shapiro 					if (sfdcsasl(&mci->mci_in,
334940266059SGregory Neil Shapiro 						     &mci->mci_out,
3350af9557fdSGregory Neil Shapiro 						     mci->mci_conn, tmo) == 0)
335106f25ae9SGregory Neil Shapiro 					{
33526f9c8e5bSGregory Neil Shapiro 						mci_clr_extensions(mci);
335340266059SGregory Neil Shapiro 						mci->mci_flags |= MCIF_AUTHACT|
335440266059SGregory Neil Shapiro 								  MCIF_ONLY_EHLO;
335506f25ae9SGregory Neil Shapiro 						goto reconnect;
335606f25ae9SGregory Neil Shapiro 					}
335740266059SGregory Neil Shapiro 					syserr("AUTH TLS switch failed in client");
335806f25ae9SGregory Neil Shapiro 				}
335906f25ae9SGregory Neil Shapiro 				/* else? XXX */
336006f25ae9SGregory Neil Shapiro 				mci->mci_flags |= MCIF_AUTHACT;
336106f25ae9SGregory Neil Shapiro 
336206f25ae9SGregory Neil Shapiro 			}
336340266059SGregory Neil Shapiro 			else if (ret == EX_TEMPFAIL)
336440266059SGregory Neil Shapiro 			{
336540266059SGregory Neil Shapiro 				if (LogLevel > 8)
336640266059SGregory Neil Shapiro 					sm_syslog(LOG_ERR, NOQID,
336740266059SGregory Neil Shapiro 						  "AUTH=client, relay=%.100s, temporary failure, connection abort",
336840266059SGregory Neil Shapiro 						  mci->mci_host);
336940266059SGregory Neil Shapiro 				smtpquit(m, mci, e);
337040266059SGregory Neil Shapiro 
337140266059SGregory Neil Shapiro 				/* avoid bogus error msg */
337240266059SGregory Neil Shapiro 				mci->mci_errno = 0;
337340266059SGregory Neil Shapiro 				rcode = EX_TEMPFAIL;
3374e92d3f3fSGregory Neil Shapiro 				mci_setstat(mci, rcode, "4.3.0", p);
337540266059SGregory Neil Shapiro 
337640266059SGregory Neil Shapiro 				/*
337740266059SGregory Neil Shapiro 				**  hack to get the error message into
337840266059SGregory Neil Shapiro 				**  the envelope (done in giveresponse())
337940266059SGregory Neil Shapiro 				*/
338040266059SGregory Neil Shapiro 
338140266059SGregory Neil Shapiro 				(void) sm_strlcpy(SmtpError,
338240266059SGregory Neil Shapiro 						  "Temporary AUTH failure",
3383d0cef73dSGregory Neil Shapiro 						  sizeof(SmtpError));
338440266059SGregory Neil Shapiro 			}
338506f25ae9SGregory Neil Shapiro 		}
338606f25ae9SGregory Neil Shapiro #endif /* SASL */
338706f25ae9SGregory Neil Shapiro 	}
338806f25ae9SGregory Neil Shapiro 
3389c2aa98e2SPeter Wemm do_transfer:
3390c2aa98e2SPeter Wemm 	/* clear out per-message flags from connection structure */
3391c2aa98e2SPeter Wemm 	mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
3392c2aa98e2SPeter Wemm 
3393c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
3394c2aa98e2SPeter Wemm 	    !bitset(EF_DONT_MIME, e->e_flags) &&
3395c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, m->m_flags))
3396c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_CVT8TO7;
3397c2aa98e2SPeter Wemm 
3398c2aa98e2SPeter Wemm #if MIME7TO8
3399c2aa98e2SPeter Wemm 	if (bitnset(M_MAKE8BIT, m->m_flags) &&
3400c2aa98e2SPeter Wemm 	    !bitset(MCIF_7BIT, mci->mci_flags) &&
3401c2aa98e2SPeter Wemm 	    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
340240266059SGregory Neil Shapiro 	     (sm_strcasecmp(p, "quoted-printable") == 0 ||
340340266059SGregory Neil Shapiro 	      sm_strcasecmp(p, "base64") == 0) &&
3404c2aa98e2SPeter Wemm 	    (p = hvalue("Content-Type", e->e_header)) != NULL)
3405c2aa98e2SPeter Wemm 	{
3406c2aa98e2SPeter Wemm 		/* may want to convert 7 -> 8 */
3407c2aa98e2SPeter Wemm 		/* XXX should really parse it here -- and use a class XXX */
340840266059SGregory Neil Shapiro 		if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
3409c2aa98e2SPeter Wemm 		    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
3410c2aa98e2SPeter Wemm 			mci->mci_flags |= MCIF_CVT7TO8;
3411c2aa98e2SPeter Wemm 	}
341206f25ae9SGregory Neil Shapiro #endif /* MIME7TO8 */
3413c2aa98e2SPeter Wemm 
3414c2aa98e2SPeter Wemm 	if (tTd(11, 1))
3415c2aa98e2SPeter Wemm 	{
341640266059SGregory Neil Shapiro 		sm_dprintf("openmailer: ");
3417e92d3f3fSGregory Neil Shapiro 		mci_dump(sm_debug_file(), mci, false);
3418c2aa98e2SPeter Wemm 	}
3419c2aa98e2SPeter Wemm 
342040266059SGregory Neil Shapiro #if _FFR_CLIENT_SIZE
342140266059SGregory Neil Shapiro 	/*
342240266059SGregory Neil Shapiro 	**  See if we know the maximum size and
342340266059SGregory Neil Shapiro 	**  abort if the message is too big.
342440266059SGregory Neil Shapiro 	**
342540266059SGregory Neil Shapiro 	**  NOTE: _FFR_CLIENT_SIZE is untested.
342640266059SGregory Neil Shapiro 	*/
342740266059SGregory Neil Shapiro 
342840266059SGregory Neil Shapiro 	if (bitset(MCIF_SIZE, mci->mci_flags) &&
342940266059SGregory Neil Shapiro 	    mci->mci_maxsize > 0 &&
343040266059SGregory Neil Shapiro 	    e->e_msgsize > mci->mci_maxsize)
343140266059SGregory Neil Shapiro 	{
343240266059SGregory Neil Shapiro 		e->e_flags |= EF_NO_BODY_RETN;
343340266059SGregory Neil Shapiro 		if (bitnset(M_LOCALMAILER, m->m_flags))
343440266059SGregory Neil Shapiro 			e->e_status = "5.2.3";
343540266059SGregory Neil Shapiro 		else
343640266059SGregory Neil Shapiro 			e->e_status = "5.3.4";
343740266059SGregory Neil Shapiro 
343840266059SGregory Neil Shapiro 		usrerrenh(e->e_status,
343940266059SGregory Neil Shapiro 			  "552 Message is too large; %ld bytes max",
344040266059SGregory Neil Shapiro 			  mci->mci_maxsize);
344140266059SGregory Neil Shapiro 		rcode = EX_DATAERR;
344240266059SGregory Neil Shapiro 
344340266059SGregory Neil Shapiro 		/* Need an e_message for error */
3444d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(SmtpError, sizeof(SmtpError),
344540266059SGregory Neil Shapiro 				   "Message is too large; %ld bytes max",
344640266059SGregory Neil Shapiro 				   mci->mci_maxsize);
344740266059SGregory Neil Shapiro 		goto give_up;
344840266059SGregory Neil Shapiro 	}
344940266059SGregory Neil Shapiro #endif /* _FFR_CLIENT_SIZE */
345040266059SGregory Neil Shapiro 
3451c2aa98e2SPeter Wemm 	if (mci->mci_state != MCIS_OPEN)
3452c2aa98e2SPeter Wemm 	{
3453c2aa98e2SPeter Wemm 		/* couldn't open the mailer */
3454c2aa98e2SPeter Wemm 		rcode = mci->mci_exitstat;
3455c2aa98e2SPeter Wemm 		errno = mci->mci_errno;
3456602a2b1bSGregory Neil Shapiro 		SM_SET_H_ERRNO(mci->mci_herrno);
3457c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
3458c2aa98e2SPeter Wemm 		{
3459c2aa98e2SPeter Wemm 			/* shouldn't happen */
346006f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
346140266059SGregory Neil Shapiro 			       (unsigned long) mci, rcode, errno,
346240266059SGregory Neil Shapiro 			       mci->mci_state, firstsig);
3463e92d3f3fSGregory Neil Shapiro 			mci_dump_all(smioout, true);
3464c2aa98e2SPeter Wemm 			rcode = EX_SOFTWARE;
3465c2aa98e2SPeter Wemm 		}
346606f25ae9SGregory Neil Shapiro 		else if (nummxhosts > hostnum)
3467c2aa98e2SPeter Wemm 		{
3468c2aa98e2SPeter Wemm 			/* try next MX site */
3469c2aa98e2SPeter Wemm 			goto tryhost;
3470c2aa98e2SPeter Wemm 		}
3471c2aa98e2SPeter Wemm 	}
3472c2aa98e2SPeter Wemm 	else if (!clever)
3473c2aa98e2SPeter Wemm 	{
34744e4196cbSGregory Neil Shapiro 		bool ok;
34754e4196cbSGregory Neil Shapiro 
3476c2aa98e2SPeter Wemm 		/*
3477c2aa98e2SPeter Wemm 		**  Format and send message.
3478c2aa98e2SPeter Wemm 		*/
3479c2aa98e2SPeter Wemm 
34804e4196cbSGregory Neil Shapiro 		rcode = EX_OK;
34814e4196cbSGregory Neil Shapiro 		errno = 0;
34824e4196cbSGregory Neil Shapiro 		ok = putfromline(mci, e);
34834e4196cbSGregory Neil Shapiro 		if (ok)
34844e4196cbSGregory Neil Shapiro 			ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
34854e4196cbSGregory Neil Shapiro 		if (ok)
34864e4196cbSGregory Neil Shapiro 			ok = (*e->e_putbody)(mci, e, NULL);
3487ffb83623SGregory Neil Shapiro 		if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags))
3488ffb83623SGregory Neil Shapiro 			ok = putline("", mci);
3489c2aa98e2SPeter Wemm 
34904e4196cbSGregory Neil Shapiro 		/*
34914e4196cbSGregory Neil Shapiro 		**  Ignore an I/O error that was caused by EPIPE.
34924e4196cbSGregory Neil Shapiro 		**  Some broken mailers don't read the entire body
34934e4196cbSGregory Neil Shapiro 		**  but just exit() thus causing an I/O error.
34944e4196cbSGregory Neil Shapiro 		*/
34954e4196cbSGregory Neil Shapiro 
34964e4196cbSGregory Neil Shapiro 		if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
34974e4196cbSGregory Neil Shapiro 			ok = true;
34984e4196cbSGregory Neil Shapiro 
34994e4196cbSGregory Neil Shapiro 		/* (always) get the exit status */
3500c2aa98e2SPeter Wemm 		rcode = endmailer(mci, e, pv);
35014e4196cbSGregory Neil Shapiro 		if (!ok)
35024e4196cbSGregory Neil Shapiro 			rcode = EX_TEMPFAIL;
350340266059SGregory Neil Shapiro 		if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
3504602a2b1bSGregory Neil Shapiro 		{
3505602a2b1bSGregory Neil Shapiro 			/*
3506602a2b1bSGregory Neil Shapiro 			**  Need an e_message for mailq display.
3507602a2b1bSGregory Neil Shapiro 			**  We set SmtpError as
3508602a2b1bSGregory Neil Shapiro 			*/
3509602a2b1bSGregory Neil Shapiro 
3510d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(SmtpError, sizeof(SmtpError),
3511602a2b1bSGregory Neil Shapiro 					   "%s mailer (%s) exited with EX_TEMPFAIL",
3512602a2b1bSGregory Neil Shapiro 					   m->m_name, m->m_mailer);
3513602a2b1bSGregory Neil Shapiro 		}
3514c2aa98e2SPeter Wemm 	}
3515c2aa98e2SPeter Wemm 	else
3516c2aa98e2SPeter Wemm 	{
3517c2aa98e2SPeter Wemm 		/*
3518c2aa98e2SPeter Wemm 		**  Send the MAIL FROM: protocol
3519c2aa98e2SPeter Wemm 		*/
3520c2aa98e2SPeter Wemm 
352140266059SGregory Neil Shapiro 		/* XXX this isn't pipelined... */
3522c2aa98e2SPeter Wemm 		rcode = smtpmailfrom(m, mci, e);
3523c2aa98e2SPeter Wemm 		if (rcode == EX_OK)
3524c2aa98e2SPeter Wemm 		{
3525c2aa98e2SPeter Wemm 			register int i;
352640266059SGregory Neil Shapiro #if PIPELINING
352740266059SGregory Neil Shapiro 			ADDRESS *volatile pchain;
3528*5b0945b5SGregory Neil Shapiro #endif
3529c2aa98e2SPeter Wemm 
3530c2aa98e2SPeter Wemm 			/* send the recipient list */
3531c2aa98e2SPeter Wemm 			tobuf[0] = '\0';
353240266059SGregory Neil Shapiro 			mci->mci_retryrcpt = false;
353340266059SGregory Neil Shapiro 			mci->mci_tolist = tobuf;
353440266059SGregory Neil Shapiro #if PIPELINING
353540266059SGregory Neil Shapiro 			pchain = NULL;
353640266059SGregory Neil Shapiro 			mci->mci_nextaddr = NULL;
3537*5b0945b5SGregory Neil Shapiro #endif
353806f25ae9SGregory Neil Shapiro 
3539c2aa98e2SPeter Wemm 			for (to = tochain; to != NULL; to = to->q_tchain)
3540c2aa98e2SPeter Wemm 			{
354140266059SGregory Neil Shapiro 				if (!QS_IS_UNMARKED(to->q_state))
3542c2aa98e2SPeter Wemm 					continue;
354306f25ae9SGregory Neil Shapiro 
354440266059SGregory Neil Shapiro 				/* mark recipient state as "ok so far" */
354540266059SGregory Neil Shapiro 				to->q_state = QS_OK;
354640266059SGregory Neil Shapiro 				e->e_to = to->q_paddr;
354706f25ae9SGregory Neil Shapiro #if STARTTLS
354806f25ae9SGregory Neil Shapiro 				i = rscheck("tls_rcpt", to->q_user, NULL, e,
3549959366dcSGregory Neil Shapiro 					    RSF_RMCOMM|RSF_COUNT, 3,
3550da7d7b9cSGregory Neil Shapiro 					    mci->mci_host, e->e_id, NULL, NULL);
355106f25ae9SGregory Neil Shapiro 				if (i != EX_OK)
3552c2aa98e2SPeter Wemm 				{
355340266059SGregory Neil Shapiro 					markfailure(e, to, mci, i, false);
355440266059SGregory Neil Shapiro 					giveresponse(i, to->q_status,  m, mci,
355540266059SGregory Neil Shapiro 						     ctladdr, xstart, e, to);
355640266059SGregory Neil Shapiro 					if (i == EX_TEMPFAIL)
355740266059SGregory Neil Shapiro 					{
355840266059SGregory Neil Shapiro 						mci->mci_retryrcpt = true;
355940266059SGregory Neil Shapiro 						to->q_state = QS_RETRY;
356040266059SGregory Neil Shapiro 					}
356106f25ae9SGregory Neil Shapiro 					continue;
356206f25ae9SGregory Neil Shapiro 				}
356306f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
356406f25ae9SGregory Neil Shapiro 
356540266059SGregory Neil Shapiro 				i = smtprcpt(to, m, mci, e, ctladdr, xstart);
356640266059SGregory Neil Shapiro #if PIPELINING
356740266059SGregory Neil Shapiro 				if (i == EX_OK &&
356840266059SGregory Neil Shapiro 				    bitset(MCIF_PIPELINED, mci->mci_flags))
356906f25ae9SGregory Neil Shapiro 				{
357040266059SGregory Neil Shapiro 					/*
357140266059SGregory Neil Shapiro 					**  Add new element to list of
357240266059SGregory Neil Shapiro 					**  recipients for pipelining.
357340266059SGregory Neil Shapiro 					*/
357440266059SGregory Neil Shapiro 
357540266059SGregory Neil Shapiro 					to->q_pchain = NULL;
357640266059SGregory Neil Shapiro 					if (mci->mci_nextaddr == NULL)
357740266059SGregory Neil Shapiro 						mci->mci_nextaddr = to;
357840266059SGregory Neil Shapiro 					if (pchain == NULL)
357940266059SGregory Neil Shapiro 						pchain = to;
3580c2aa98e2SPeter Wemm 					else
3581c2aa98e2SPeter Wemm 					{
358240266059SGregory Neil Shapiro 						pchain->q_pchain = to;
358340266059SGregory Neil Shapiro 						pchain = pchain->q_pchain;
358440266059SGregory Neil Shapiro 					}
358540266059SGregory Neil Shapiro 				}
358640266059SGregory Neil Shapiro #endif /* PIPELINING */
358740266059SGregory Neil Shapiro 				if (i != EX_OK)
358840266059SGregory Neil Shapiro 				{
358940266059SGregory Neil Shapiro 					markfailure(e, to, mci, i, false);
359040266059SGregory Neil Shapiro 					giveresponse(i, to->q_status, m, mci,
359140266059SGregory Neil Shapiro 						     ctladdr, xstart, e, to);
359240266059SGregory Neil Shapiro 					if (i == EX_TEMPFAIL)
359340266059SGregory Neil Shapiro 						to->q_state = QS_RETRY;
3594c2aa98e2SPeter Wemm 				}
3595c2aa98e2SPeter Wemm 			}
3596c2aa98e2SPeter Wemm 
359740266059SGregory Neil Shapiro 			/* No recipients in list and no missing responses? */
359840266059SGregory Neil Shapiro 			if (tobuf[0] == '\0'
359940266059SGregory Neil Shapiro #if PIPELINING
3600ffb83623SGregory Neil Shapiro 			    && bitset(MCIF_PIPELINED, mci->mci_flags)
360140266059SGregory Neil Shapiro 			    && mci->mci_nextaddr == NULL
3602*5b0945b5SGregory Neil Shapiro #endif
360340266059SGregory Neil Shapiro 			   )
3604c2aa98e2SPeter Wemm 			{
3605c2aa98e2SPeter Wemm 				rcode = EX_OK;
3606c2aa98e2SPeter Wemm 				e->e_to = NULL;
3607c2aa98e2SPeter Wemm 				if (bitset(MCIF_CACHED, mci->mci_flags))
3608c2aa98e2SPeter Wemm 					smtprset(m, mci, e);
3609c2aa98e2SPeter Wemm 			}
3610c2aa98e2SPeter Wemm 			else
3611c2aa98e2SPeter Wemm 			{
3612c2aa98e2SPeter Wemm 				e->e_to = tobuf + 1;
361340266059SGregory Neil Shapiro 				rcode = smtpdata(m, mci, e, ctladdr, xstart);
3614c2aa98e2SPeter Wemm 			}
3615c2aa98e2SPeter Wemm 		}
361606f25ae9SGregory Neil Shapiro 		if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
3617c2aa98e2SPeter Wemm 		{
3618c2aa98e2SPeter Wemm 			/* try next MX site */
3619c2aa98e2SPeter Wemm 			goto tryhost;
3620c2aa98e2SPeter Wemm 		}
3621c2aa98e2SPeter Wemm 	}
3622c2aa98e2SPeter Wemm #if NAMED_BIND
3623c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
3624c2aa98e2SPeter Wemm 		_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
3625*5b0945b5SGregory Neil Shapiro #endif
3626c2aa98e2SPeter Wemm 
3627c2aa98e2SPeter Wemm 	if (tTd(62, 1))
3628c2aa98e2SPeter Wemm 		checkfds("after delivery");
3629c2aa98e2SPeter Wemm 
3630c2aa98e2SPeter Wemm 	/*
3631c2aa98e2SPeter Wemm 	**  Do final status disposal.
3632c2aa98e2SPeter Wemm 	**	We check for something in tobuf for the SMTP case.
3633c2aa98e2SPeter Wemm 	**	If we got a temporary failure, arrange to queue the
3634c2aa98e2SPeter Wemm 	**		addressees.
3635c2aa98e2SPeter Wemm 	*/
3636c2aa98e2SPeter Wemm 
3637c2aa98e2SPeter Wemm   give_up:
3638c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
3639c2aa98e2SPeter Wemm 	{
3640c2aa98e2SPeter Wemm 		lmtp_rcode = rcode;
3641c2aa98e2SPeter Wemm 		tobuf[0] = '\0';
364240266059SGregory Neil Shapiro 		anyok = false;
364340266059SGregory Neil Shapiro 		strsize = 0;
3644c2aa98e2SPeter Wemm 	}
3645c2aa98e2SPeter Wemm 	else
3646c2aa98e2SPeter Wemm 		anyok = rcode == EX_OK;
3647c2aa98e2SPeter Wemm 
3648c2aa98e2SPeter Wemm 	for (to = tochain; to != NULL; to = to->q_tchain)
3649c2aa98e2SPeter Wemm 	{
3650c2aa98e2SPeter Wemm 		/* see if address already marked */
365106f25ae9SGregory Neil Shapiro 		if (!QS_IS_OK(to->q_state))
3652c2aa98e2SPeter Wemm 			continue;
3653c2aa98e2SPeter Wemm 
3654c2aa98e2SPeter Wemm 		/* if running LMTP, get the status for each address */
3655c2aa98e2SPeter Wemm 		if (bitnset(M_LMTP, m->m_flags))
3656c2aa98e2SPeter Wemm 		{
3657c2aa98e2SPeter Wemm 			if (lmtp_rcode == EX_OK)
3658c2aa98e2SPeter Wemm 				rcode = smtpgetstat(m, mci, e);
3659c2aa98e2SPeter Wemm 			if (rcode == EX_OK)
3660c2aa98e2SPeter Wemm 			{
366140266059SGregory Neil Shapiro 				strsize += sm_strlcat2(tobuf + strsize, ",",
366240266059SGregory Neil Shapiro 						to->q_paddr,
366340266059SGregory Neil Shapiro 						tobufsize - strsize);
366440266059SGregory Neil Shapiro 				SM_ASSERT(strsize < tobufsize);
366540266059SGregory Neil Shapiro 				anyok = true;
3666c2aa98e2SPeter Wemm 			}
3667c2aa98e2SPeter Wemm 			else
3668c2aa98e2SPeter Wemm 			{
3669c2aa98e2SPeter Wemm 				e->e_to = to->q_paddr;
367040266059SGregory Neil Shapiro 				markfailure(e, to, mci, rcode, true);
367106f25ae9SGregory Neil Shapiro 				giveresponse(rcode, to->q_status, m, mci,
367240266059SGregory Neil Shapiro 					     ctladdr, xstart, e, to);
3673c2aa98e2SPeter Wemm 				e->e_to = tobuf + 1;
3674c2aa98e2SPeter Wemm 				continue;
3675c2aa98e2SPeter Wemm 			}
3676c2aa98e2SPeter Wemm 		}
3677c2aa98e2SPeter Wemm 		else
3678c2aa98e2SPeter Wemm 		{
3679c2aa98e2SPeter Wemm 			/* mark bad addresses */
3680c2aa98e2SPeter Wemm 			if (rcode != EX_OK)
3681c2aa98e2SPeter Wemm 			{
3682c2aa98e2SPeter Wemm 				if (goodmxfound && rcode == EX_NOHOST)
3683c2aa98e2SPeter Wemm 					rcode = EX_TEMPFAIL;
368440266059SGregory Neil Shapiro 				markfailure(e, to, mci, rcode, true);
3685c2aa98e2SPeter Wemm 				continue;
3686c2aa98e2SPeter Wemm 			}
3687c2aa98e2SPeter Wemm 		}
3688c2aa98e2SPeter Wemm 
3689c2aa98e2SPeter Wemm 		/* successful delivery */
369006f25ae9SGregory Neil Shapiro 		to->q_state = QS_SENT;
3691c2aa98e2SPeter Wemm 		to->q_statdate = curtime();
3692c2aa98e2SPeter Wemm 		e->e_nsent++;
369306f25ae9SGregory Neil Shapiro 
369406f25ae9SGregory Neil Shapiro 		/*
369506f25ae9SGregory Neil Shapiro 		**  Checkpoint the send list every few addresses
369606f25ae9SGregory Neil Shapiro 		*/
369706f25ae9SGregory Neil Shapiro 
369842e5d165SGregory Neil Shapiro 		if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
369906f25ae9SGregory Neil Shapiro 		{
370040266059SGregory Neil Shapiro 			queueup(e, false, false);
370106f25ae9SGregory Neil Shapiro 			e->e_nsent = 0;
370206f25ae9SGregory Neil Shapiro 		}
370306f25ae9SGregory Neil Shapiro 
3704c2aa98e2SPeter Wemm 		if (bitnset(M_LOCALMAILER, m->m_flags) &&
3705c2aa98e2SPeter Wemm 		    bitset(QPINGONSUCCESS, to->q_flags))
3706c2aa98e2SPeter Wemm 		{
3707c2aa98e2SPeter Wemm 			to->q_flags |= QDELIVERED;
3708c2aa98e2SPeter Wemm 			to->q_status = "2.1.5";
370940266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
371040266059SGregory Neil Shapiro 					     "%s... Successfully delivered\n",
3711c2aa98e2SPeter Wemm 					     to->q_paddr);
3712c2aa98e2SPeter Wemm 		}
3713c2aa98e2SPeter Wemm 		else if (bitset(QPINGONSUCCESS, to->q_flags) &&
3714c2aa98e2SPeter Wemm 			 bitset(QPRIMARY, to->q_flags) &&
3715c2aa98e2SPeter Wemm 			 !bitset(MCIF_DSN, mci->mci_flags))
3716c2aa98e2SPeter Wemm 		{
3717c2aa98e2SPeter Wemm 			to->q_flags |= QRELAYED;
371840266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
371940266059SGregory Neil Shapiro 					     "%s... relayed; expect no further notifications\n",
372040266059SGregory Neil Shapiro 					     to->q_paddr);
372140266059SGregory Neil Shapiro 		}
372240266059SGregory Neil Shapiro 		else if (IS_DLVR_NOTIFY(e) &&
372340266059SGregory Neil Shapiro 			 !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
372440266059SGregory Neil Shapiro 			 bitset(QPRIMARY, to->q_flags) &&
372540266059SGregory Neil Shapiro 			 (!bitset(QHASNOTIFY, to->q_flags) ||
372640266059SGregory Neil Shapiro 			  bitset(QPINGONSUCCESS, to->q_flags) ||
372740266059SGregory Neil Shapiro 			  bitset(QPINGONFAILURE, to->q_flags) ||
372840266059SGregory Neil Shapiro 			  bitset(QPINGONDELAY, to->q_flags)))
372940266059SGregory Neil Shapiro 		{
373040266059SGregory Neil Shapiro 			/* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
373140266059SGregory Neil Shapiro 			to->q_flags |= QBYNRELAY;
373240266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
373340266059SGregory Neil Shapiro 					     "%s... Deliver-by notify: relayed\n",
373440266059SGregory Neil Shapiro 					     to->q_paddr);
373540266059SGregory Neil Shapiro 		}
373640266059SGregory Neil Shapiro 		else if (IS_DLVR_TRACE(e) &&
373740266059SGregory Neil Shapiro 			 (!bitset(QHASNOTIFY, to->q_flags) ||
373840266059SGregory Neil Shapiro 			  bitset(QPINGONSUCCESS, to->q_flags) ||
373940266059SGregory Neil Shapiro 			  bitset(QPINGONFAILURE, to->q_flags) ||
374040266059SGregory Neil Shapiro 			  bitset(QPINGONDELAY, to->q_flags)) &&
374140266059SGregory Neil Shapiro 			 bitset(QPRIMARY, to->q_flags))
374240266059SGregory Neil Shapiro 		{
374340266059SGregory Neil Shapiro 			/* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
374440266059SGregory Neil Shapiro 			to->q_flags |= QBYTRACE;
374540266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
374640266059SGregory Neil Shapiro 					     "%s... Deliver-By trace: relayed\n",
3747c2aa98e2SPeter Wemm 					     to->q_paddr);
3748c2aa98e2SPeter Wemm 		}
3749c2aa98e2SPeter Wemm 	}
3750c2aa98e2SPeter Wemm 
3751c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
3752c2aa98e2SPeter Wemm 	{
3753c2aa98e2SPeter Wemm 		/*
3754c2aa98e2SPeter Wemm 		**  Global information applies to the last recipient only;
3755c2aa98e2SPeter Wemm 		**  clear it out to avoid bogus errors.
3756c2aa98e2SPeter Wemm 		*/
3757c2aa98e2SPeter Wemm 
3758c2aa98e2SPeter Wemm 		rcode = EX_OK;
3759c2aa98e2SPeter Wemm 		e->e_statmsg = NULL;
3760c2aa98e2SPeter Wemm 
3761c2aa98e2SPeter Wemm 		/* reset the mci state for the next transaction */
376240266059SGregory Neil Shapiro 		if (mci != NULL &&
376340266059SGregory Neil Shapiro 		    (mci->mci_state == MCIS_MAIL ||
376440266059SGregory Neil Shapiro 		     mci->mci_state == MCIS_RCPT ||
376540266059SGregory Neil Shapiro 		     mci->mci_state == MCIS_DATA))
3766323f6dcbSGregory Neil Shapiro 		{
3767c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_OPEN;
3768323f6dcbSGregory Neil Shapiro 			SmtpPhase = mci->mci_phase = "idle";
3769323f6dcbSGregory Neil Shapiro 			sm_setproctitle(true, e, "%s: %s", CurHostName,
3770323f6dcbSGregory Neil Shapiro 					mci->mci_phase);
3771323f6dcbSGregory Neil Shapiro 		}
3772c2aa98e2SPeter Wemm 	}
3773c2aa98e2SPeter Wemm 
3774c2aa98e2SPeter Wemm 	if (tobuf[0] != '\0')
377540266059SGregory Neil Shapiro 	{
3776da7d7b9cSGregory Neil Shapiro 		giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL);
377740266059SGregory Neil Shapiro #if 0
377840266059SGregory Neil Shapiro 		/*
377940266059SGregory Neil Shapiro 		**  This code is disabled for now because I am not
378040266059SGregory Neil Shapiro 		**  sure that copying status from the first recipient
378140266059SGregory Neil Shapiro 		**  to all non-status'ed recipients is a good idea.
378240266059SGregory Neil Shapiro 		*/
378340266059SGregory Neil Shapiro 
378440266059SGregory Neil Shapiro 		if (tochain->q_message != NULL &&
378540266059SGregory Neil Shapiro 		    !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
378640266059SGregory Neil Shapiro 		{
378740266059SGregory Neil Shapiro 			for (to = tochain->q_tchain; to != NULL;
378840266059SGregory Neil Shapiro 			     to = to->q_tchain)
378940266059SGregory Neil Shapiro 			{
379040266059SGregory Neil Shapiro 				/* see if address already marked */
379140266059SGregory Neil Shapiro 				if (QS_IS_QUEUEUP(to->q_state) &&
379240266059SGregory Neil Shapiro 				    to->q_message == NULL)
379340266059SGregory Neil Shapiro 					to->q_message = sm_rpool_strdup_x(e->e_rpool,
379440266059SGregory Neil Shapiro 							tochain->q_message);
379540266059SGregory Neil Shapiro 			}
379640266059SGregory Neil Shapiro 		}
379740266059SGregory Neil Shapiro #endif /* 0 */
379840266059SGregory Neil Shapiro 	}
3799c2aa98e2SPeter Wemm 	if (anyok)
380040266059SGregory Neil Shapiro 		markstats(e, tochain, STATS_NORMAL);
3801c2aa98e2SPeter Wemm 	mci_store_persistent(mci);
3802c2aa98e2SPeter Wemm 
3803*5b0945b5SGregory Neil Shapiro #if _FFR_OCC
3804*5b0945b5SGregory Neil Shapiro 	/*
3805*5b0945b5SGregory Neil Shapiro 	**  HACK: this is NOT the right place to "close" a connection!
3806*5b0945b5SGregory Neil Shapiro 	**  use smtpquit?
3807*5b0945b5SGregory Neil Shapiro 	**  add a flag to mci to indicate that rate/conc. was increased?
3808*5b0945b5SGregory Neil Shapiro 	*/
3809*5b0945b5SGregory Neil Shapiro 
3810*5b0945b5SGregory Neil Shapiro 	if (clever)
3811*5b0945b5SGregory Neil Shapiro 	{
3812*5b0945b5SGregory Neil Shapiro 		extern SOCKADDR CurHostAddr;
3813*5b0945b5SGregory Neil Shapiro 
3814*5b0945b5SGregory Neil Shapiro 		/* check family... {} */
3815*5b0945b5SGregory Neil Shapiro 		/* r = anynet_pton(AF_INET, p, dst); */
3816*5b0945b5SGregory Neil Shapiro 		occ_close(e, mci, host, &CurHostAddr);
3817*5b0945b5SGregory Neil Shapiro 	}
3818*5b0945b5SGregory Neil Shapiro #endif /* _FFR_OCC */
3819*5b0945b5SGregory Neil Shapiro 
382040266059SGregory Neil Shapiro 	/* Some recipients were tempfailed, try them on the next host */
382140266059SGregory Neil Shapiro 	if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
382240266059SGregory Neil Shapiro 	{
382340266059SGregory Neil Shapiro 		/* try next MX site */
382440266059SGregory Neil Shapiro 		goto tryhost;
382540266059SGregory Neil Shapiro 	}
382640266059SGregory Neil Shapiro 
3827c2aa98e2SPeter Wemm 	/* now close the connection */
3828c2aa98e2SPeter Wemm 	if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
3829c2aa98e2SPeter Wemm 	    !bitset(MCIF_CACHED, mci->mci_flags))
3830c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
3831c2aa98e2SPeter Wemm 
383240266059SGregory Neil Shapiro cleanup: ;
383340266059SGregory Neil Shapiro 	}
383440266059SGregory Neil Shapiro 	SM_FINALLY
383540266059SGregory Neil Shapiro 	{
3836c2aa98e2SPeter Wemm 		/*
3837c2aa98e2SPeter Wemm 		**  Restore state and return.
3838c2aa98e2SPeter Wemm 		*/
3839c2aa98e2SPeter Wemm #if XDEBUG
3840c2aa98e2SPeter Wemm 		char wbuf[MAXLINE];
3841c2aa98e2SPeter Wemm 
3842c2aa98e2SPeter Wemm 		/* make absolutely certain 0, 1, and 2 are in use */
3843d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(wbuf, sizeof(wbuf),
384440266059SGregory Neil Shapiro 				   "%s... end of deliver(%s)",
3845c2aa98e2SPeter Wemm 				   e->e_to == NULL ? "NO-TO-LIST"
384640266059SGregory Neil Shapiro 						   : shortenstring(e->e_to,
384740266059SGregory Neil Shapiro 								   MAXSHORTSTR),
3848c2aa98e2SPeter Wemm 				  m->m_name);
3849c2aa98e2SPeter Wemm 		checkfd012(wbuf);
385006f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
3851c2aa98e2SPeter Wemm 
3852c2aa98e2SPeter Wemm 		errno = 0;
385340266059SGregory Neil Shapiro 
385440266059SGregory Neil Shapiro 		/*
385540266059SGregory Neil Shapiro 		**  It was originally necessary to set macro 'g' to NULL
385640266059SGregory Neil Shapiro 		**  because it previously pointed to an auto buffer.
385740266059SGregory Neil Shapiro 		**  We don't do this any more, so this may be unnecessary.
385840266059SGregory Neil Shapiro 		*/
385940266059SGregory Neil Shapiro 
386040266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
386106f25ae9SGregory Neil Shapiro 		e->e_to = NULL;
386240266059SGregory Neil Shapiro 	}
386340266059SGregory Neil Shapiro 	SM_END_TRY
386406f25ae9SGregory Neil Shapiro 	return rcode;
3865c2aa98e2SPeter Wemm }
386606f25ae9SGregory Neil Shapiro 
386740266059SGregory Neil Shapiro /*
3868c2aa98e2SPeter Wemm **  MARKFAILURE -- mark a failure on a specific address.
3869c2aa98e2SPeter Wemm **
3870c2aa98e2SPeter Wemm **	Parameters:
3871c2aa98e2SPeter Wemm **		e -- the envelope we are sending.
3872c2aa98e2SPeter Wemm **		q -- the address to mark.
3873c2aa98e2SPeter Wemm **		mci -- mailer connection information.
3874c2aa98e2SPeter Wemm **		rcode -- the code signifying the particular failure.
387506f25ae9SGregory Neil Shapiro **		ovr -- override an existing code?
3876c2aa98e2SPeter Wemm **
3877c2aa98e2SPeter Wemm **	Returns:
3878c2aa98e2SPeter Wemm **		none.
3879c2aa98e2SPeter Wemm **
3880c2aa98e2SPeter Wemm **	Side Effects:
3881c2aa98e2SPeter Wemm **		marks the address (and possibly the envelope) with the
3882c2aa98e2SPeter Wemm **			failure so that an error will be returned or
3883c2aa98e2SPeter Wemm **			the message will be queued, as appropriate.
3884c2aa98e2SPeter Wemm */
3885c2aa98e2SPeter Wemm 
388640266059SGregory Neil Shapiro void
388706f25ae9SGregory Neil Shapiro markfailure(e, q, mci, rcode, ovr)
3888c2aa98e2SPeter Wemm 	register ENVELOPE *e;
3889c2aa98e2SPeter Wemm 	register ADDRESS *q;
3890c2aa98e2SPeter Wemm 	register MCI *mci;
3891c2aa98e2SPeter Wemm 	int rcode;
389206f25ae9SGregory Neil Shapiro 	bool ovr;
3893c2aa98e2SPeter Wemm {
389440266059SGregory Neil Shapiro 	int save_errno = errno;
389506f25ae9SGregory Neil Shapiro 	char *status = NULL;
389606f25ae9SGregory Neil Shapiro 	char *rstatus = NULL;
3897c2aa98e2SPeter Wemm 
3898c2aa98e2SPeter Wemm 	switch (rcode)
3899c2aa98e2SPeter Wemm 	{
3900c2aa98e2SPeter Wemm 	  case EX_OK:
3901c2aa98e2SPeter Wemm 		break;
3902c2aa98e2SPeter Wemm 
3903c2aa98e2SPeter Wemm 	  case EX_TEMPFAIL:
3904c2aa98e2SPeter Wemm 	  case EX_IOERR:
3905c2aa98e2SPeter Wemm 	  case EX_OSERR:
390606f25ae9SGregory Neil Shapiro 		q->q_state = QS_QUEUEUP;
3907c2aa98e2SPeter Wemm 		break;
3908c2aa98e2SPeter Wemm 
3909c2aa98e2SPeter Wemm 	  default:
391006f25ae9SGregory Neil Shapiro 		q->q_state = QS_BADADDR;
3911c2aa98e2SPeter Wemm 		break;
3912c2aa98e2SPeter Wemm 	}
3913c2aa98e2SPeter Wemm 
3914c2aa98e2SPeter Wemm 	/* find most specific error code possible */
3915c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_status != NULL)
3916c2aa98e2SPeter Wemm 	{
391740266059SGregory Neil Shapiro 		status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
3918c2aa98e2SPeter Wemm 		if (mci->mci_rstatus != NULL)
391940266059SGregory Neil Shapiro 			rstatus = sm_rpool_strdup_x(e->e_rpool,
392040266059SGregory Neil Shapiro 						    mci->mci_rstatus);
3921c2aa98e2SPeter Wemm 	}
3922c2aa98e2SPeter Wemm 	else if (e->e_status != NULL)
3923c2aa98e2SPeter Wemm 	{
392406f25ae9SGregory Neil Shapiro 		status = e->e_status;
3925c2aa98e2SPeter Wemm 	}
3926c2aa98e2SPeter Wemm 	else
3927c2aa98e2SPeter Wemm 	{
3928c2aa98e2SPeter Wemm 		switch (rcode)
3929c2aa98e2SPeter Wemm 		{
3930c2aa98e2SPeter Wemm 		  case EX_USAGE:
393106f25ae9SGregory Neil Shapiro 			status = "5.5.4";
3932c2aa98e2SPeter Wemm 			break;
3933c2aa98e2SPeter Wemm 
3934c2aa98e2SPeter Wemm 		  case EX_DATAERR:
393506f25ae9SGregory Neil Shapiro 			status = "5.5.2";
3936c2aa98e2SPeter Wemm 			break;
3937c2aa98e2SPeter Wemm 
3938c2aa98e2SPeter Wemm 		  case EX_NOUSER:
393906f25ae9SGregory Neil Shapiro 			status = "5.1.1";
3940c2aa98e2SPeter Wemm 			break;
3941c2aa98e2SPeter Wemm 
3942c2aa98e2SPeter Wemm 		  case EX_NOHOST:
394306f25ae9SGregory Neil Shapiro 			status = "5.1.2";
3944c2aa98e2SPeter Wemm 			break;
3945c2aa98e2SPeter Wemm 
3946c2aa98e2SPeter Wemm 		  case EX_NOINPUT:
3947c2aa98e2SPeter Wemm 		  case EX_CANTCREAT:
3948c2aa98e2SPeter Wemm 		  case EX_NOPERM:
394906f25ae9SGregory Neil Shapiro 			status = "5.3.0";
3950c2aa98e2SPeter Wemm 			break;
3951c2aa98e2SPeter Wemm 
3952c2aa98e2SPeter Wemm 		  case EX_UNAVAILABLE:
3953c2aa98e2SPeter Wemm 		  case EX_SOFTWARE:
3954c2aa98e2SPeter Wemm 		  case EX_OSFILE:
3955c2aa98e2SPeter Wemm 		  case EX_PROTOCOL:
3956c2aa98e2SPeter Wemm 		  case EX_CONFIG:
395706f25ae9SGregory Neil Shapiro 			status = "5.5.0";
3958c2aa98e2SPeter Wemm 			break;
3959c2aa98e2SPeter Wemm 
3960c2aa98e2SPeter Wemm 		  case EX_OSERR:
3961c2aa98e2SPeter Wemm 		  case EX_IOERR:
396206f25ae9SGregory Neil Shapiro 			status = "4.5.0";
3963c2aa98e2SPeter Wemm 			break;
3964c2aa98e2SPeter Wemm 
3965c2aa98e2SPeter Wemm 		  case EX_TEMPFAIL:
396606f25ae9SGregory Neil Shapiro 			status = "4.2.0";
3967c2aa98e2SPeter Wemm 			break;
3968c2aa98e2SPeter Wemm 		}
3969c2aa98e2SPeter Wemm 	}
3970c2aa98e2SPeter Wemm 
397106f25ae9SGregory Neil Shapiro 	/* new status? */
397206f25ae9SGregory Neil Shapiro 	if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL ||
397306f25ae9SGregory Neil Shapiro 	    *q->q_status == '\0' || *q->q_status < *status))
397406f25ae9SGregory Neil Shapiro 	{
397506f25ae9SGregory Neil Shapiro 		q->q_status = status;
397606f25ae9SGregory Neil Shapiro 		q->q_rstatus = rstatus;
397706f25ae9SGregory Neil Shapiro 	}
3978c2aa98e2SPeter Wemm 	if (rcode != EX_OK && q->q_rstatus == NULL &&
3979c2aa98e2SPeter Wemm 	    q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
398040266059SGregory Neil Shapiro 	    sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
3981c2aa98e2SPeter Wemm 	{
398206f25ae9SGregory Neil Shapiro 		char buf[16];
3983c2aa98e2SPeter Wemm 
3984d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof(buf), "%d", rcode);
398540266059SGregory Neil Shapiro 		q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
3986c2aa98e2SPeter Wemm 	}
398706f25ae9SGregory Neil Shapiro 
398806f25ae9SGregory Neil Shapiro 	q->q_statdate = curtime();
398906f25ae9SGregory Neil Shapiro 	if (CurHostName != NULL && CurHostName[0] != '\0' &&
399006f25ae9SGregory Neil Shapiro 	    mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
399140266059SGregory Neil Shapiro 		q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
399240266059SGregory Neil Shapiro 
399340266059SGregory Neil Shapiro 	/* restore errno */
399440266059SGregory Neil Shapiro 	errno = save_errno;
3995c2aa98e2SPeter Wemm }
399640266059SGregory Neil Shapiro /*
3997c2aa98e2SPeter Wemm **  ENDMAILER -- Wait for mailer to terminate.
3998c2aa98e2SPeter Wemm **
3999c2aa98e2SPeter Wemm **	We should never get fatal errors (e.g., segmentation
4000c2aa98e2SPeter Wemm **	violation), so we report those specially.  For other
4001c2aa98e2SPeter Wemm **	errors, we choose a status message (into statmsg),
4002c2aa98e2SPeter Wemm **	and if it represents an error, we print it.
4003c2aa98e2SPeter Wemm **
4004c2aa98e2SPeter Wemm **	Parameters:
400513058a91SGregory Neil Shapiro **		mci -- the mailer connection info.
4006c2aa98e2SPeter Wemm **		e -- the current envelope.
4007c2aa98e2SPeter Wemm **		pv -- the parameter vector that invoked the mailer
4008c2aa98e2SPeter Wemm **			(for error messages).
4009c2aa98e2SPeter Wemm **
4010c2aa98e2SPeter Wemm **	Returns:
4011c2aa98e2SPeter Wemm **		exit code of mailer.
4012c2aa98e2SPeter Wemm **
4013c2aa98e2SPeter Wemm **	Side Effects:
4014c2aa98e2SPeter Wemm **		none.
4015c2aa98e2SPeter Wemm */
4016c2aa98e2SPeter Wemm 
401706f25ae9SGregory Neil Shapiro static jmp_buf	EndWaitTimeout;
401806f25ae9SGregory Neil Shapiro 
401906f25ae9SGregory Neil Shapiro static void
4020b6bacd31SGregory Neil Shapiro endwaittimeout(ignore)
4021b6bacd31SGregory Neil Shapiro 	int ignore;
402206f25ae9SGregory Neil Shapiro {
40238774250cSGregory Neil Shapiro 	/*
40248774250cSGregory Neil Shapiro 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
40258774250cSGregory Neil Shapiro 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
40268774250cSGregory Neil Shapiro 	**	DOING.
40278774250cSGregory Neil Shapiro 	*/
40288774250cSGregory Neil Shapiro 
402906f25ae9SGregory Neil Shapiro 	errno = ETIMEDOUT;
403006f25ae9SGregory Neil Shapiro 	longjmp(EndWaitTimeout, 1);
403106f25ae9SGregory Neil Shapiro }
403206f25ae9SGregory Neil Shapiro 
4033c2aa98e2SPeter Wemm int
4034c2aa98e2SPeter Wemm endmailer(mci, e, pv)
4035c2aa98e2SPeter Wemm 	register MCI *mci;
4036c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4037c2aa98e2SPeter Wemm 	char **pv;
4038c2aa98e2SPeter Wemm {
4039c2aa98e2SPeter Wemm 	int st;
404006f25ae9SGregory Neil Shapiro 	int save_errno = errno;
404106f25ae9SGregory Neil Shapiro 	char buf[MAXLINE];
404240266059SGregory Neil Shapiro 	SM_EVENT *ev = NULL;
404306f25ae9SGregory Neil Shapiro 
4044c2aa98e2SPeter Wemm 
4045c2aa98e2SPeter Wemm 	mci_unlock_host(mci);
4046c2aa98e2SPeter Wemm 
40478774250cSGregory Neil Shapiro 	/* close output to mailer */
40488774250cSGregory Neil Shapiro 	if (mci->mci_out != NULL)
4049b6bacd31SGregory Neil Shapiro 	{
405040266059SGregory Neil Shapiro 		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
4051b6bacd31SGregory Neil Shapiro 		mci->mci_out = NULL;
4052b6bacd31SGregory Neil Shapiro 	}
40538774250cSGregory Neil Shapiro 
40548774250cSGregory Neil Shapiro 	/* copy any remaining input to transcript */
40558774250cSGregory Neil Shapiro 	if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR &&
40568774250cSGregory Neil Shapiro 	    e->e_xfp != NULL)
40578774250cSGregory Neil Shapiro 	{
4058d0cef73dSGregory Neil Shapiro 		while (sfgets(buf, sizeof(buf), mci->mci_in,
40598774250cSGregory Neil Shapiro 			      TimeOuts.to_quit, "Draining Input") != NULL)
406040266059SGregory Neil Shapiro 			(void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
40618774250cSGregory Neil Shapiro 	}
40628774250cSGregory Neil Shapiro 
406306f25ae9SGregory Neil Shapiro #if SASL
406440266059SGregory Neil Shapiro 	/* close SASL connection */
406506f25ae9SGregory Neil Shapiro 	if (bitset(MCIF_AUTHACT, mci->mci_flags))
406606f25ae9SGregory Neil Shapiro 	{
406706f25ae9SGregory Neil Shapiro 		sasl_dispose(&mci->mci_conn);
406806f25ae9SGregory Neil Shapiro 		mci->mci_flags &= ~MCIF_AUTHACT;
406906f25ae9SGregory Neil Shapiro 	}
407006f25ae9SGregory Neil Shapiro #endif /* SASL */
407106f25ae9SGregory Neil Shapiro 
407206f25ae9SGregory Neil Shapiro #if STARTTLS
407306f25ae9SGregory Neil Shapiro 	/* shutdown TLS */
407406f25ae9SGregory Neil Shapiro 	(void) endtlsclt(mci);
4075*5b0945b5SGregory Neil Shapiro #endif
407606f25ae9SGregory Neil Shapiro 
407706f25ae9SGregory Neil Shapiro 	/* now close the input */
407806f25ae9SGregory Neil Shapiro 	if (mci->mci_in != NULL)
4079b6bacd31SGregory Neil Shapiro 	{
408040266059SGregory Neil Shapiro 		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
4081b6bacd31SGregory Neil Shapiro 		mci->mci_in = NULL;
4082b6bacd31SGregory Neil Shapiro 	}
4083c2aa98e2SPeter Wemm 	mci->mci_state = MCIS_CLOSED;
4084c2aa98e2SPeter Wemm 
408506f25ae9SGregory Neil Shapiro 	errno = save_errno;
408606f25ae9SGregory Neil Shapiro 
4087c2aa98e2SPeter Wemm 	/* in the IPC case there is nothing to wait for */
4088c2aa98e2SPeter Wemm 	if (mci->mci_pid == 0)
408906f25ae9SGregory Neil Shapiro 		return EX_OK;
4090c2aa98e2SPeter Wemm 
409106f25ae9SGregory Neil Shapiro 	/* put a timeout around the wait */
409206f25ae9SGregory Neil Shapiro 	if (mci->mci_mailer->m_wait > 0)
409306f25ae9SGregory Neil Shapiro 	{
409406f25ae9SGregory Neil Shapiro 		if (setjmp(EndWaitTimeout) == 0)
409540266059SGregory Neil Shapiro 			ev = sm_setevent(mci->mci_mailer->m_wait,
409606f25ae9SGregory Neil Shapiro 					 endwaittimeout, 0);
409706f25ae9SGregory Neil Shapiro 		else
409806f25ae9SGregory Neil Shapiro 		{
409942e5d165SGregory Neil Shapiro 			syserr("endmailer %s: wait timeout (%ld)",
410006f25ae9SGregory Neil Shapiro 			       mci->mci_mailer->m_name,
410142e5d165SGregory Neil Shapiro 			       (long) mci->mci_mailer->m_wait);
410206f25ae9SGregory Neil Shapiro 			return EX_TEMPFAIL;
410306f25ae9SGregory Neil Shapiro 		}
410406f25ae9SGregory Neil Shapiro 	}
4105c2aa98e2SPeter Wemm 
410606f25ae9SGregory Neil Shapiro 	/* wait for the mailer process, collect status */
4107c2aa98e2SPeter Wemm 	st = waitfor(mci->mci_pid);
410806f25ae9SGregory Neil Shapiro 	save_errno = errno;
410906f25ae9SGregory Neil Shapiro 	if (ev != NULL)
411040266059SGregory Neil Shapiro 		sm_clrevent(ev);
411106f25ae9SGregory Neil Shapiro 	errno = save_errno;
411206f25ae9SGregory Neil Shapiro 
4113c2aa98e2SPeter Wemm 	if (st == -1)
4114c2aa98e2SPeter Wemm 	{
4115c2aa98e2SPeter Wemm 		syserr("endmailer %s: wait", mci->mci_mailer->m_name);
411606f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
4117c2aa98e2SPeter Wemm 	}
4118c2aa98e2SPeter Wemm 
4119c2aa98e2SPeter Wemm 	if (WIFEXITED(st))
4120c2aa98e2SPeter Wemm 	{
4121c2aa98e2SPeter Wemm 		/* normal death -- return status */
4122c2aa98e2SPeter Wemm 		return (WEXITSTATUS(st));
4123c2aa98e2SPeter Wemm 	}
4124c2aa98e2SPeter Wemm 
4125c2aa98e2SPeter Wemm 	/* it died a horrid death */
412606f25ae9SGregory Neil Shapiro 	syserr("451 4.3.0 mailer %s died with signal %d%s",
412706f25ae9SGregory Neil Shapiro 		mci->mci_mailer->m_name, WTERMSIG(st),
412806f25ae9SGregory Neil Shapiro 		WCOREDUMP(st) ? " (core dumped)" :
412906f25ae9SGregory Neil Shapiro 		(WIFSTOPPED(st) ? " (stopped)" : ""));
4130c2aa98e2SPeter Wemm 
4131c2aa98e2SPeter Wemm 	/* log the arguments */
4132c2aa98e2SPeter Wemm 	if (pv != NULL && e->e_xfp != NULL)
4133c2aa98e2SPeter Wemm 	{
4134c2aa98e2SPeter Wemm 		register char **av;
4135c2aa98e2SPeter Wemm 
413640266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
4137c2aa98e2SPeter Wemm 		for (av = pv; *av != NULL; av++)
413840266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
413940266059SGregory Neil Shapiro 					     *av);
414040266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
4141c2aa98e2SPeter Wemm 	}
4142c2aa98e2SPeter Wemm 
4143c2aa98e2SPeter Wemm 	ExitStat = EX_TEMPFAIL;
414406f25ae9SGregory Neil Shapiro 	return EX_TEMPFAIL;
4145c2aa98e2SPeter Wemm }
414640266059SGregory Neil Shapiro /*
4147c2aa98e2SPeter Wemm **  GIVERESPONSE -- Interpret an error response from a mailer
4148c2aa98e2SPeter Wemm **
4149c2aa98e2SPeter Wemm **	Parameters:
415006f25ae9SGregory Neil Shapiro **		status -- the status code from the mailer (high byte
4151c2aa98e2SPeter Wemm **			only; core dumps must have been taken care of
4152c2aa98e2SPeter Wemm **			already).
415306f25ae9SGregory Neil Shapiro **		dsn -- the DSN associated with the address, if any.
4154c2aa98e2SPeter Wemm **		m -- the mailer info for this mailer.
4155c2aa98e2SPeter Wemm **		mci -- the mailer connection info -- can be NULL if the
4156c2aa98e2SPeter Wemm **			response is given before the connection is made.
4157c2aa98e2SPeter Wemm **		ctladdr -- the controlling address for the recipient
4158c2aa98e2SPeter Wemm **			address(es).
4159c2aa98e2SPeter Wemm **		xstart -- the transaction start time, for computing
4160c2aa98e2SPeter Wemm **			transaction delays.
4161c2aa98e2SPeter Wemm **		e -- the current envelope.
416240266059SGregory Neil Shapiro **		to -- the current recipient (NULL if none).
4163c2aa98e2SPeter Wemm **
4164c2aa98e2SPeter Wemm **	Returns:
4165c2aa98e2SPeter Wemm **		none.
4166c2aa98e2SPeter Wemm **
4167c2aa98e2SPeter Wemm **	Side Effects:
4168c2aa98e2SPeter Wemm **		Errors may be incremented.
4169c2aa98e2SPeter Wemm **		ExitStat may be set.
4170c2aa98e2SPeter Wemm */
4171c2aa98e2SPeter Wemm 
4172c2aa98e2SPeter Wemm void
417340266059SGregory Neil Shapiro giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
417406f25ae9SGregory Neil Shapiro 	int status;
417506f25ae9SGregory Neil Shapiro 	char *dsn;
4176c2aa98e2SPeter Wemm 	register MAILER *m;
4177c2aa98e2SPeter Wemm 	register MCI *mci;
4178c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
4179c2aa98e2SPeter Wemm 	time_t xstart;
4180c2aa98e2SPeter Wemm 	ENVELOPE *e;
418140266059SGregory Neil Shapiro 	ADDRESS *to;
4182c2aa98e2SPeter Wemm {
4183c2aa98e2SPeter Wemm 	register const char *statmsg;
418406f25ae9SGregory Neil Shapiro 	int errnum = errno;
418506f25ae9SGregory Neil Shapiro 	int off = 4;
418640266059SGregory Neil Shapiro 	bool usestat = false;
418706f25ae9SGregory Neil Shapiro 	char dsnbuf[ENHSCLEN];
4188c2aa98e2SPeter Wemm 	char buf[MAXLINE];
418940266059SGregory Neil Shapiro 	char *exmsg;
4190c2aa98e2SPeter Wemm 
4191c2aa98e2SPeter Wemm 	if (e == NULL)
4192af9557fdSGregory Neil Shapiro 	{
4193c2aa98e2SPeter Wemm 		syserr("giveresponse: null envelope");
4194af9557fdSGregory Neil Shapiro 		/* NOTREACHED */
4195af9557fdSGregory Neil Shapiro 		SM_ASSERT(0);
4196af9557fdSGregory Neil Shapiro 	}
4197c2aa98e2SPeter Wemm 
4198c2aa98e2SPeter Wemm 	/*
4199c2aa98e2SPeter Wemm 	**  Compute status message from code.
4200c2aa98e2SPeter Wemm 	*/
4201c2aa98e2SPeter Wemm 
420240266059SGregory Neil Shapiro 	exmsg = sm_sysexmsg(status);
420306f25ae9SGregory Neil Shapiro 	if (status == 0)
4204c2aa98e2SPeter Wemm 	{
420506f25ae9SGregory Neil Shapiro 		statmsg = "250 2.0.0 Sent";
4206c2aa98e2SPeter Wemm 		if (e->e_statmsg != NULL)
4207c2aa98e2SPeter Wemm 		{
4208d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
420906f25ae9SGregory Neil Shapiro 					   statmsg,
421006f25ae9SGregory Neil Shapiro 					   shortenstring(e->e_statmsg, 403));
4211c2aa98e2SPeter Wemm 			statmsg = buf;
4212c2aa98e2SPeter Wemm 		}
4213c2aa98e2SPeter Wemm 	}
421440266059SGregory Neil Shapiro 	else if (exmsg == NULL)
4215c2aa98e2SPeter Wemm 	{
4216d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof(buf),
421706f25ae9SGregory Neil Shapiro 				   "554 5.3.0 unknown mailer error %d",
421806f25ae9SGregory Neil Shapiro 				   status);
421906f25ae9SGregory Neil Shapiro 		status = EX_UNAVAILABLE;
4220c2aa98e2SPeter Wemm 		statmsg = buf;
422140266059SGregory Neil Shapiro 		usestat = true;
4222c2aa98e2SPeter Wemm 	}
422306f25ae9SGregory Neil Shapiro 	else if (status == EX_TEMPFAIL)
4224c2aa98e2SPeter Wemm 	{
4225c2aa98e2SPeter Wemm 		char *bp = buf;
4226c2aa98e2SPeter Wemm 
422740266059SGregory Neil Shapiro 		(void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
4228c2aa98e2SPeter Wemm 		bp += strlen(bp);
4229c2aa98e2SPeter Wemm #if NAMED_BIND
4230c2aa98e2SPeter Wemm 		if (h_errno == TRY_AGAIN)
423140266059SGregory Neil Shapiro 			statmsg = sm_errstring(h_errno + E_DNSBASE);
4232c2aa98e2SPeter Wemm 		else
423306f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
4234c2aa98e2SPeter Wemm 		{
423506f25ae9SGregory Neil Shapiro 			if (errnum != 0)
423640266059SGregory Neil Shapiro 				statmsg = sm_errstring(errnum);
4237c2aa98e2SPeter Wemm 			else
4238c2aa98e2SPeter Wemm 				statmsg = SmtpError;
4239c2aa98e2SPeter Wemm 		}
4240c2aa98e2SPeter Wemm 		if (statmsg != NULL && statmsg[0] != '\0')
424106f25ae9SGregory Neil Shapiro 		{
424206f25ae9SGregory Neil Shapiro 			switch (errnum)
424306f25ae9SGregory Neil Shapiro 			{
424406f25ae9SGregory Neil Shapiro #ifdef ENETDOWN
424506f25ae9SGregory Neil Shapiro 			  case ENETDOWN:	/* Network is down */
4246*5b0945b5SGregory Neil Shapiro #endif
424706f25ae9SGregory Neil Shapiro #ifdef ENETUNREACH
424806f25ae9SGregory Neil Shapiro 			  case ENETUNREACH:	/* Network is unreachable */
4249*5b0945b5SGregory Neil Shapiro #endif
425006f25ae9SGregory Neil Shapiro #ifdef ENETRESET
425106f25ae9SGregory Neil Shapiro 			  case ENETRESET:	/* Network dropped connection on reset */
4252*5b0945b5SGregory Neil Shapiro #endif
425306f25ae9SGregory Neil Shapiro #ifdef ECONNABORTED
425406f25ae9SGregory Neil Shapiro 			  case ECONNABORTED:	/* Software caused connection abort */
4255*5b0945b5SGregory Neil Shapiro #endif
425606f25ae9SGregory Neil Shapiro #ifdef EHOSTDOWN
425706f25ae9SGregory Neil Shapiro 			  case EHOSTDOWN:	/* Host is down */
4258*5b0945b5SGregory Neil Shapiro #endif
425906f25ae9SGregory Neil Shapiro #ifdef EHOSTUNREACH
426006f25ae9SGregory Neil Shapiro 			  case EHOSTUNREACH:	/* No route to host */
4261*5b0945b5SGregory Neil Shapiro #endif
4262b6bacd31SGregory Neil Shapiro 				if (mci != NULL && mci->mci_host != NULL)
426306f25ae9SGregory Neil Shapiro 				{
426440266059SGregory Neil Shapiro 					(void) sm_strlcpyn(bp,
426540266059SGregory Neil Shapiro 							   SPACELEFT(buf, bp),
426640266059SGregory Neil Shapiro 							   2, ": ",
426740266059SGregory Neil Shapiro 							   mci->mci_host);
426806f25ae9SGregory Neil Shapiro 					bp += strlen(bp);
426906f25ae9SGregory Neil Shapiro 				}
427006f25ae9SGregory Neil Shapiro 				break;
427106f25ae9SGregory Neil Shapiro 			}
427240266059SGregory Neil Shapiro 			(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
427340266059SGregory Neil Shapiro 					   statmsg);
4274*5b0945b5SGregory Neil Shapiro #if DANE
4275*5b0945b5SGregory Neil Shapiro 			if (errnum == 0 && SmtpError[0] != '\0' &&
4276*5b0945b5SGregory Neil Shapiro 			    h_errno == TRY_AGAIN &&
4277*5b0945b5SGregory Neil Shapiro 			    mci->mci_exitstat == EX_TEMPFAIL)
4278*5b0945b5SGregory Neil Shapiro 			{
4279*5b0945b5SGregory Neil Shapiro 				(void) sm_strlcat(bp, SmtpError,
4280*5b0945b5SGregory Neil Shapiro 					SPACELEFT(buf, bp));
4281*5b0945b5SGregory Neil Shapiro 				bp += strlen(bp);
4282*5b0945b5SGregory Neil Shapiro 			}
4283*5b0945b5SGregory Neil Shapiro #endif /* DANE */
428440266059SGregory Neil Shapiro 			usestat = true;
428506f25ae9SGregory Neil Shapiro 		}
4286c2aa98e2SPeter Wemm 		statmsg = buf;
4287c2aa98e2SPeter Wemm 	}
4288c2aa98e2SPeter Wemm #if NAMED_BIND
428906f25ae9SGregory Neil Shapiro 	else if (status == EX_NOHOST && h_errno != 0)
4290c2aa98e2SPeter Wemm 	{
429140266059SGregory Neil Shapiro 		statmsg = sm_errstring(h_errno + E_DNSBASE);
4292d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1,
429340266059SGregory Neil Shapiro 				   statmsg);
4294c2aa98e2SPeter Wemm 		statmsg = buf;
429540266059SGregory Neil Shapiro 		usestat = true;
4296c2aa98e2SPeter Wemm 	}
429706f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
4298c2aa98e2SPeter Wemm 	else
4299c2aa98e2SPeter Wemm 	{
430040266059SGregory Neil Shapiro 		statmsg = exmsg;
430106f25ae9SGregory Neil Shapiro 		if (*statmsg++ == ':' && errnum != 0)
4302c2aa98e2SPeter Wemm 		{
4303d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg,
430440266059SGregory Neil Shapiro 					   sm_errstring(errnum));
4305c2aa98e2SPeter Wemm 			statmsg = buf;
430640266059SGregory Neil Shapiro 			usestat = true;
4307c2aa98e2SPeter Wemm 		}
4308605302a5SGregory Neil Shapiro 		else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
4309605302a5SGregory Neil Shapiro 		{
4310d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg,
4311605302a5SGregory Neil Shapiro 					   shortenstring(e->e_statmsg, 403));
4312605302a5SGregory Neil Shapiro 			statmsg = buf;
4313605302a5SGregory Neil Shapiro 			usestat = true;
4314605302a5SGregory Neil Shapiro 		}
4315c2aa98e2SPeter Wemm 	}
4316c2aa98e2SPeter Wemm 
4317c2aa98e2SPeter Wemm 	/*
4318c2aa98e2SPeter Wemm 	**  Print the message as appropriate
4319c2aa98e2SPeter Wemm 	*/
4320c2aa98e2SPeter Wemm 
432106f25ae9SGregory Neil Shapiro 	if (status == EX_OK || status == EX_TEMPFAIL)
4322c2aa98e2SPeter Wemm 	{
4323c2aa98e2SPeter Wemm 		extern char MsgBuf[];
4324c2aa98e2SPeter Wemm 
432506f25ae9SGregory Neil Shapiro 		if ((off = isenhsc(statmsg + 4, ' ')) > 0)
432606f25ae9SGregory Neil Shapiro 		{
432706f25ae9SGregory Neil Shapiro 			if (dsn == NULL)
432806f25ae9SGregory Neil Shapiro 			{
4329d0cef73dSGregory Neil Shapiro 				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
433006f25ae9SGregory Neil Shapiro 						   "%.*s", off, statmsg + 4);
433106f25ae9SGregory Neil Shapiro 				dsn = dsnbuf;
433206f25ae9SGregory Neil Shapiro 			}
433306f25ae9SGregory Neil Shapiro 			off += 5;
433406f25ae9SGregory Neil Shapiro 		}
433506f25ae9SGregory Neil Shapiro 		else
433606f25ae9SGregory Neil Shapiro 		{
433706f25ae9SGregory Neil Shapiro 			off = 4;
433806f25ae9SGregory Neil Shapiro 		}
433906f25ae9SGregory Neil Shapiro 		message("%s", statmsg + off);
434006f25ae9SGregory Neil Shapiro 		if (status == EX_TEMPFAIL && e->e_xfp != NULL)
434140266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
434240266059SGregory Neil Shapiro 					     &MsgBuf[4]);
4343c2aa98e2SPeter Wemm 	}
4344c2aa98e2SPeter Wemm 	else
4345c2aa98e2SPeter Wemm 	{
434606f25ae9SGregory Neil Shapiro 		char mbuf[ENHSCLEN + 4];
4347c2aa98e2SPeter Wemm 
4348c2aa98e2SPeter Wemm 		Errors++;
434906f25ae9SGregory Neil Shapiro 		if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
4350d0cef73dSGregory Neil Shapiro 		    off < sizeof(mbuf) - 4)
435106f25ae9SGregory Neil Shapiro 		{
435206f25ae9SGregory Neil Shapiro 			if (dsn == NULL)
435306f25ae9SGregory Neil Shapiro 			{
4354d0cef73dSGregory Neil Shapiro 				(void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
435506f25ae9SGregory Neil Shapiro 						   "%.*s", off, statmsg + 4);
435606f25ae9SGregory Neil Shapiro 				dsn = dsnbuf;
435706f25ae9SGregory Neil Shapiro 			}
435806f25ae9SGregory Neil Shapiro 			off += 5;
435940266059SGregory Neil Shapiro 
436040266059SGregory Neil Shapiro 			/* copy only part of statmsg to mbuf */
436140266059SGregory Neil Shapiro 			(void) sm_strlcpy(mbuf, statmsg, off);
4362d0cef73dSGregory Neil Shapiro 			(void) sm_strlcat(mbuf, " %s", sizeof(mbuf));
436306f25ae9SGregory Neil Shapiro 		}
436406f25ae9SGregory Neil Shapiro 		else
436506f25ae9SGregory Neil Shapiro 		{
436606f25ae9SGregory Neil Shapiro 			dsnbuf[0] = '\0';
4367d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s",
436840266059SGregory Neil Shapiro 					   statmsg);
436906f25ae9SGregory Neil Shapiro 			off = 4;
437006f25ae9SGregory Neil Shapiro 		}
437106f25ae9SGregory Neil Shapiro 		usrerr(mbuf, &statmsg[off]);
4372c2aa98e2SPeter Wemm 	}
4373c2aa98e2SPeter Wemm 
4374c2aa98e2SPeter Wemm 	/*
4375c2aa98e2SPeter Wemm 	**  Final cleanup.
4376da7d7b9cSGregory Neil Shapiro 	**	Log a record of the transaction.  Compute the new ExitStat
4377da7d7b9cSGregory Neil Shapiro 	**	-- if we already had an error, stick with that.
4378c2aa98e2SPeter Wemm 	*/
4379c2aa98e2SPeter Wemm 
4380c2aa98e2SPeter Wemm 	if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
438106f25ae9SGregory Neil Shapiro 	    LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
4382da7d7b9cSGregory Neil Shapiro 		logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status);
4383c2aa98e2SPeter Wemm 
4384c2aa98e2SPeter Wemm 	if (tTd(11, 2))
438540266059SGregory Neil Shapiro 		sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
438606f25ae9SGregory Neil Shapiro 			   status,
438706f25ae9SGregory Neil Shapiro 			   dsn == NULL ? "<NULL>" : dsn,
438840266059SGregory Neil Shapiro 			   e->e_message == NULL ? "<NULL>" : e->e_message,
438940266059SGregory Neil Shapiro 			   errnum);
4390c2aa98e2SPeter Wemm 
439106f25ae9SGregory Neil Shapiro 	if (status != EX_TEMPFAIL)
439206f25ae9SGregory Neil Shapiro 		setstat(status);
439306f25ae9SGregory Neil Shapiro 	if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
439440266059SGregory Neil Shapiro 		e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
439540266059SGregory Neil Shapiro 	if (status != EX_OK && to != NULL && to->q_message == NULL)
4396c2aa98e2SPeter Wemm 	{
439740266059SGregory Neil Shapiro 		if (!usestat && e->e_message != NULL)
439840266059SGregory Neil Shapiro 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
439940266059SGregory Neil Shapiro 							  e->e_message);
440040266059SGregory Neil Shapiro 		else
440140266059SGregory Neil Shapiro 			to->q_message = sm_rpool_strdup_x(e->e_rpool,
440240266059SGregory Neil Shapiro 							  statmsg + off);
4403c2aa98e2SPeter Wemm 	}
4404c2aa98e2SPeter Wemm 	errno = 0;
4405602a2b1bSGregory Neil Shapiro 	SM_SET_H_ERRNO(0);
4406c2aa98e2SPeter Wemm }
440740266059SGregory Neil Shapiro /*
4408c2aa98e2SPeter Wemm **  LOGDELIVERY -- log the delivery in the system log
4409c2aa98e2SPeter Wemm **
4410c2aa98e2SPeter Wemm **	Care is taken to avoid logging lines that are too long, because
4411c2aa98e2SPeter Wemm **	some versions of syslog have an unfortunate proclivity for core
4412c2aa98e2SPeter Wemm **	dumping.  This is a hack, to be sure, that is at best empirical.
4413c2aa98e2SPeter Wemm **
4414c2aa98e2SPeter Wemm **	Parameters:
4415c2aa98e2SPeter Wemm **		m -- the mailer info.  Can be NULL for initial queue.
4416c2aa98e2SPeter Wemm **		mci -- the mailer connection info -- can be NULL if the
441706f25ae9SGregory Neil Shapiro **			log is occurring when no connection is active.
441806f25ae9SGregory Neil Shapiro **		dsn -- the DSN attached to the status.
441906f25ae9SGregory Neil Shapiro **		status -- the message to print for the status.
4420c2aa98e2SPeter Wemm **		ctladdr -- the controlling address for the to list.
4421c2aa98e2SPeter Wemm **		xstart -- the transaction start time, used for
4422c2aa98e2SPeter Wemm **			computing transaction delay.
4423c2aa98e2SPeter Wemm **		e -- the current envelope.
4424da7d7b9cSGregory Neil Shapiro **		to -- the current recipient (NULL if none).
4425da7d7b9cSGregory Neil Shapiro **		rcode -- status code
4426c2aa98e2SPeter Wemm **
4427c2aa98e2SPeter Wemm **	Returns:
4428c2aa98e2SPeter Wemm **		none
4429c2aa98e2SPeter Wemm **
4430c2aa98e2SPeter Wemm **	Side Effects:
4431c2aa98e2SPeter Wemm **		none
4432c2aa98e2SPeter Wemm */
4433c2aa98e2SPeter Wemm 
4434c2aa98e2SPeter Wemm void
4435da7d7b9cSGregory Neil Shapiro logdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode)
4436c2aa98e2SPeter Wemm 	MAILER *m;
4437c2aa98e2SPeter Wemm 	register MCI *mci;
443806f25ae9SGregory Neil Shapiro 	char *dsn;
443906f25ae9SGregory Neil Shapiro 	const char *status;
4440c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
4441c2aa98e2SPeter Wemm 	time_t xstart;
4442c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4443da7d7b9cSGregory Neil Shapiro 	ADDRESS *to;
4444da7d7b9cSGregory Neil Shapiro 	int rcode;
4445c2aa98e2SPeter Wemm {
4446c2aa98e2SPeter Wemm 	register char *bp;
4447c2aa98e2SPeter Wemm 	register char *p;
4448c2aa98e2SPeter Wemm 	int l;
444940266059SGregory Neil Shapiro 	time_t now = curtime();
4450c2aa98e2SPeter Wemm 	char buf[1024];
4451c2aa98e2SPeter Wemm 
4452c2aa98e2SPeter Wemm #if (SYSLOG_BUFSIZE) >= 256
4453c2aa98e2SPeter Wemm 	/* ctladdr: max 106 bytes */
4454c2aa98e2SPeter Wemm 	bp = buf;
4455c2aa98e2SPeter Wemm 	if (ctladdr != NULL)
4456c2aa98e2SPeter Wemm 	{
445740266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
4458c2aa98e2SPeter Wemm 				   shortenstring(ctladdr->q_paddr, 83));
4459c2aa98e2SPeter Wemm 		bp += strlen(bp);
4460c2aa98e2SPeter Wemm 		if (bitset(QGOODUID, ctladdr->q_flags))
4461c2aa98e2SPeter Wemm 		{
446240266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
446306f25ae9SGregory Neil Shapiro 					   (int) ctladdr->q_uid,
446406f25ae9SGregory Neil Shapiro 					   (int) ctladdr->q_gid);
4465c2aa98e2SPeter Wemm 			bp += strlen(bp);
4466c2aa98e2SPeter Wemm 		}
4467c2aa98e2SPeter Wemm 	}
4468c2aa98e2SPeter Wemm 
4469c2aa98e2SPeter Wemm 	/* delay & xdelay: max 41 bytes */
447040266059SGregory Neil Shapiro 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
447140266059SGregory Neil Shapiro 			   pintvl(now - e->e_ctime, true));
4472c2aa98e2SPeter Wemm 	bp += strlen(bp);
4473c2aa98e2SPeter Wemm 
4474c2aa98e2SPeter Wemm 	if (xstart != (time_t) 0)
4475c2aa98e2SPeter Wemm 	{
447640266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
447740266059SGregory Neil Shapiro 				   pintvl(now - xstart, true));
4478c2aa98e2SPeter Wemm 		bp += strlen(bp);
4479c2aa98e2SPeter Wemm 	}
4480c2aa98e2SPeter Wemm 
4481c2aa98e2SPeter Wemm 	/* mailer: assume about 19 bytes (max 10 byte mailer name) */
4482c2aa98e2SPeter Wemm 	if (m != NULL)
4483c2aa98e2SPeter Wemm 	{
448440266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
448540266059SGregory Neil Shapiro 				   m->m_name);
4486c2aa98e2SPeter Wemm 		bp += strlen(bp);
4487c2aa98e2SPeter Wemm 	}
4488c2aa98e2SPeter Wemm 
4489da7d7b9cSGregory Neil Shapiro # if _FFR_LOG_MORE2
4490*5b0945b5SGregory Neil Shapiro 	LOG_MORE(buf, bp);
4491*5b0945b5SGregory Neil Shapiro # endif
4492da7d7b9cSGregory Neil Shapiro 
449306f25ae9SGregory Neil Shapiro 	/* pri: changes with each delivery attempt */
449440266059SGregory Neil Shapiro 	(void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
4495ba00ec3dSGregory Neil Shapiro 		PRT_NONNEGL(e->e_msgpriority));
449606f25ae9SGregory Neil Shapiro 	bp += strlen(bp);
449706f25ae9SGregory Neil Shapiro 
4498c2aa98e2SPeter Wemm 	/* relay: max 66 bytes for IPv4 addresses */
4499c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_host != NULL)
4500c2aa98e2SPeter Wemm 	{
4501c2aa98e2SPeter Wemm 		extern SOCKADDR CurHostAddr;
4502c2aa98e2SPeter Wemm 
450340266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
4504c2aa98e2SPeter Wemm 				   shortenstring(mci->mci_host, 40));
4505c2aa98e2SPeter Wemm 		bp += strlen(bp);
4506c2aa98e2SPeter Wemm 
4507c2aa98e2SPeter Wemm 		if (CurHostAddr.sa.sa_family != 0)
4508c2aa98e2SPeter Wemm 		{
450940266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
4510c2aa98e2SPeter Wemm 					   anynet_ntoa(&CurHostAddr));
4511c2aa98e2SPeter Wemm 		}
4512c2aa98e2SPeter Wemm 	}
451340266059SGregory Neil Shapiro 	else if (strcmp(status, "quarantined") == 0)
451440266059SGregory Neil Shapiro 	{
451540266059SGregory Neil Shapiro 		if (e->e_quarmsg != NULL)
451640266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
451740266059SGregory Neil Shapiro 					   ", quarantine=%s",
451840266059SGregory Neil Shapiro 					   shortenstring(e->e_quarmsg, 40));
451940266059SGregory Neil Shapiro 	}
452006f25ae9SGregory Neil Shapiro 	else if (strcmp(status, "queued") != 0)
4521c2aa98e2SPeter Wemm 	{
4522c2aa98e2SPeter Wemm 		p = macvalue('h', e);
4523c2aa98e2SPeter Wemm 		if (p != NULL && p[0] != '\0')
4524c2aa98e2SPeter Wemm 		{
452540266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
452640266059SGregory Neil Shapiro 					   ", relay=%s", shortenstring(p, 40));
4527c2aa98e2SPeter Wemm 		}
4528c2aa98e2SPeter Wemm 	}
4529c2aa98e2SPeter Wemm 	bp += strlen(bp);
4530c2aa98e2SPeter Wemm 
453106f25ae9SGregory Neil Shapiro 	/* dsn */
453206f25ae9SGregory Neil Shapiro 	if (dsn != NULL && *dsn != '\0')
453306f25ae9SGregory Neil Shapiro 	{
453440266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
453506f25ae9SGregory Neil Shapiro 				   shortenstring(dsn, ENHSCLEN));
453606f25ae9SGregory Neil Shapiro 		bp += strlen(bp);
453706f25ae9SGregory Neil Shapiro 	}
453806f25ae9SGregory Neil Shapiro 
453913d88268SGregory Neil Shapiro # if _FFR_LOG_NTRIES
454013d88268SGregory Neil Shapiro 	/* ntries */
454113d88268SGregory Neil Shapiro 	if (e->e_ntries >= 0)
454213d88268SGregory Neil Shapiro 	{
454313d88268SGregory Neil Shapiro 		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
454413d88268SGregory Neil Shapiro 				   ", ntries=%d", e->e_ntries + 1);
454513d88268SGregory Neil Shapiro 		bp += strlen(bp);
454613d88268SGregory Neil Shapiro 	}
454713d88268SGregory Neil Shapiro # endif /* _FFR_LOG_NTRIES */
454813d88268SGregory Neil Shapiro 
4549c2aa98e2SPeter Wemm # define STATLEN		(((SYSLOG_BUFSIZE) - 100) / 4)
4550c2aa98e2SPeter Wemm # if (STATLEN) < 63
4551c2aa98e2SPeter Wemm #  undef STATLEN
4552c2aa98e2SPeter Wemm #  define STATLEN	63
455306f25ae9SGregory Neil Shapiro # endif /* (STATLEN) < 63 */
4554c2aa98e2SPeter Wemm # if (STATLEN) > 203
4555c2aa98e2SPeter Wemm #  undef STATLEN
4556c2aa98e2SPeter Wemm #  define STATLEN	203
455706f25ae9SGregory Neil Shapiro # endif /* (STATLEN) > 203 */
4558c2aa98e2SPeter Wemm 
4559da7d7b9cSGregory Neil Shapiro 	/*
4560da7d7b9cSGregory Neil Shapiro 	**  Notes:
4561da7d7b9cSGregory Neil Shapiro 	**  per-rcpt status: to->q_rstatus
4562da7d7b9cSGregory Neil Shapiro 	**  global status: e->e_text
4563da7d7b9cSGregory Neil Shapiro 	**
4564da7d7b9cSGregory Neil Shapiro 	**  We (re)use STATLEN here, is that a good choice?
4565da7d7b9cSGregory Neil Shapiro 	**
4566da7d7b9cSGregory Neil Shapiro 	**  stat=Deferred: ...
4567da7d7b9cSGregory Neil Shapiro 	**  has sometimes the same text?
4568da7d7b9cSGregory Neil Shapiro 	**
4569da7d7b9cSGregory Neil Shapiro 	**  Note: this doesn't show the stage at which the error happened.
4570da7d7b9cSGregory Neil Shapiro 	**  can/should we log that?
4571da7d7b9cSGregory Neil Shapiro 	**  XS_* in reply() basically encodes the state.
4572*5b0945b5SGregory Neil Shapiro 	**
4573*5b0945b5SGregory Neil Shapiro 	**  Note: in some case the normal logging might show the same server
4574*5b0945b5SGregory Neil Shapiro 	**  reply - how to avoid that?
4575da7d7b9cSGregory Neil Shapiro 	*/
4576da7d7b9cSGregory Neil Shapiro 
4577da7d7b9cSGregory Neil Shapiro 	/* only show errors */
4578da7d7b9cSGregory Neil Shapiro 	if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL &&
4579da7d7b9cSGregory Neil Shapiro 	    *to->q_rstatus != '\0')
4580da7d7b9cSGregory Neil Shapiro 	{
4581da7d7b9cSGregory Neil Shapiro 		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4582da7d7b9cSGregory Neil Shapiro 			", reply=%s",
4583da7d7b9cSGregory Neil Shapiro 			shortenstring(to->q_rstatus, STATLEN));
4584da7d7b9cSGregory Neil Shapiro 		bp += strlen(bp);
4585da7d7b9cSGregory Neil Shapiro 	}
4586da7d7b9cSGregory Neil Shapiro 	else if (rcode != EX_OK && e->e_text != NULL)
4587da7d7b9cSGregory Neil Shapiro 	{
4588da7d7b9cSGregory Neil Shapiro 		(void) sm_snprintf(bp, SPACELEFT(buf, bp),
4589da7d7b9cSGregory Neil Shapiro 			", reply=%d %s%s%s",
4590da7d7b9cSGregory Neil Shapiro 			e->e_rcode,
4591da7d7b9cSGregory Neil Shapiro 			e->e_renhsc,
4592da7d7b9cSGregory Neil Shapiro 			(e->e_renhsc[0] != '\0') ? " " : "",
4593da7d7b9cSGregory Neil Shapiro 			shortenstring(e->e_text, STATLEN));
4594da7d7b9cSGregory Neil Shapiro 		bp += strlen(bp);
4595da7d7b9cSGregory Neil Shapiro 	}
4596da7d7b9cSGregory Neil Shapiro 
4597c2aa98e2SPeter Wemm 	/* stat: max 210 bytes */
4598d0cef73dSGregory Neil Shapiro 	if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20)))
4599c2aa98e2SPeter Wemm 	{
4600c2aa98e2SPeter Wemm 		/* desperation move -- truncate data */
4601d0cef73dSGregory Neil Shapiro 		bp = buf + sizeof(buf) - ((STATLEN) + 17);
460240266059SGregory Neil Shapiro 		(void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
4603c2aa98e2SPeter Wemm 		bp += 3;
4604c2aa98e2SPeter Wemm 	}
4605c2aa98e2SPeter Wemm 
460640266059SGregory Neil Shapiro 	(void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
4607c2aa98e2SPeter Wemm 	bp += strlen(bp);
4608c2aa98e2SPeter Wemm 
460940266059SGregory Neil Shapiro 	(void) sm_strlcpy(bp, shortenstring(status, STATLEN),
461040266059SGregory Neil Shapiro 			  SPACELEFT(buf, bp));
4611c2aa98e2SPeter Wemm 
4612c2aa98e2SPeter Wemm 	/* id, to: max 13 + TOBUFSIZE bytes */
4613c2aa98e2SPeter Wemm 	l = SYSLOG_BUFSIZE - 100 - strlen(buf);
461440266059SGregory Neil Shapiro 	if (l < 0)
461540266059SGregory Neil Shapiro 		l = 0;
461606f25ae9SGregory Neil Shapiro 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
461740266059SGregory Neil Shapiro 	while (strlen(p) >= l)
4618c2aa98e2SPeter Wemm 	{
461906f25ae9SGregory Neil Shapiro 		register char *q;
4620c2aa98e2SPeter Wemm 
462106f25ae9SGregory Neil Shapiro 		for (q = p + l; q > p; q--)
462206f25ae9SGregory Neil Shapiro 		{
4623da7d7b9cSGregory Neil Shapiro 			/* XXX a comma in an address will break this! */
462406f25ae9SGregory Neil Shapiro 			if (*q == ',')
462506f25ae9SGregory Neil Shapiro 				break;
462606f25ae9SGregory Neil Shapiro 		}
462706f25ae9SGregory Neil Shapiro 		if (p == q)
462806f25ae9SGregory Neil Shapiro 			break;
462940266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
463042e5d165SGregory Neil Shapiro 			  (int) (++q - p), p, buf);
4631c2aa98e2SPeter Wemm 		p = q;
4632c2aa98e2SPeter Wemm 	}
463306f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
4634c2aa98e2SPeter Wemm 
463506f25ae9SGregory Neil Shapiro #else /* (SYSLOG_BUFSIZE) >= 256 */
4636c2aa98e2SPeter Wemm 
4637c2aa98e2SPeter Wemm 	l = SYSLOG_BUFSIZE - 85;
463840266059SGregory Neil Shapiro 	if (l < 0)
463940266059SGregory Neil Shapiro 		l = 0;
464006f25ae9SGregory Neil Shapiro 	p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
464140266059SGregory Neil Shapiro 	while (strlen(p) >= l)
4642c2aa98e2SPeter Wemm 	{
464306f25ae9SGregory Neil Shapiro 		register char *q;
4644c2aa98e2SPeter Wemm 
464506f25ae9SGregory Neil Shapiro 		for (q = p + l; q > p; q--)
464606f25ae9SGregory Neil Shapiro 		{
464706f25ae9SGregory Neil Shapiro 			if (*q == ',')
464806f25ae9SGregory Neil Shapiro 				break;
464906f25ae9SGregory Neil Shapiro 		}
465006f25ae9SGregory Neil Shapiro 		if (p == q)
465106f25ae9SGregory Neil Shapiro 			break;
465206f25ae9SGregory Neil Shapiro 
465340266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
465442e5d165SGregory Neil Shapiro 			  (int) (++q - p), p);
4655c2aa98e2SPeter Wemm 		p = q;
4656c2aa98e2SPeter Wemm 	}
465706f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
4658c2aa98e2SPeter Wemm 
4659c2aa98e2SPeter Wemm 	if (ctladdr != NULL)
4660c2aa98e2SPeter Wemm 	{
4661c2aa98e2SPeter Wemm 		bp = buf;
466240266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
4663c2aa98e2SPeter Wemm 				   shortenstring(ctladdr->q_paddr, 83));
4664c2aa98e2SPeter Wemm 		bp += strlen(bp);
4665c2aa98e2SPeter Wemm 		if (bitset(QGOODUID, ctladdr->q_flags))
4666c2aa98e2SPeter Wemm 		{
466740266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
4668c2aa98e2SPeter Wemm 					   ctladdr->q_uid, ctladdr->q_gid);
4669c2aa98e2SPeter Wemm 			bp += strlen(bp);
4670c2aa98e2SPeter Wemm 		}
4671c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id, "%s", buf);
4672c2aa98e2SPeter Wemm 	}
4673c2aa98e2SPeter Wemm 	bp = buf;
467440266059SGregory Neil Shapiro 	(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
467540266059SGregory Neil Shapiro 			   pintvl(now - e->e_ctime, true));
4676c2aa98e2SPeter Wemm 	bp += strlen(bp);
4677c2aa98e2SPeter Wemm 	if (xstart != (time_t) 0)
4678c2aa98e2SPeter Wemm 	{
467940266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
468040266059SGregory Neil Shapiro 				   pintvl(now - xstart, true));
4681c2aa98e2SPeter Wemm 		bp += strlen(bp);
4682c2aa98e2SPeter Wemm 	}
4683c2aa98e2SPeter Wemm 
4684c2aa98e2SPeter Wemm 	if (m != NULL)
4685c2aa98e2SPeter Wemm 	{
468640266059SGregory Neil Shapiro 		(void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
468740266059SGregory Neil Shapiro 				   m->m_name);
4688c2aa98e2SPeter Wemm 		bp += strlen(bp);
4689c2aa98e2SPeter Wemm 	}
4690c2aa98e2SPeter Wemm 	sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4691c2aa98e2SPeter Wemm 
4692c2aa98e2SPeter Wemm 	buf[0] = '\0';
4693c2aa98e2SPeter Wemm 	bp = buf;
4694c2aa98e2SPeter Wemm 	if (mci != NULL && mci->mci_host != NULL)
4695c2aa98e2SPeter Wemm 	{
4696c2aa98e2SPeter Wemm 		extern SOCKADDR CurHostAddr;
4697c2aa98e2SPeter Wemm 
469840266059SGregory Neil Shapiro 		(void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
469940266059SGregory Neil Shapiro 				   mci->mci_host);
4700c2aa98e2SPeter Wemm 		bp += strlen(bp);
4701c2aa98e2SPeter Wemm 
4702c2aa98e2SPeter Wemm 		if (CurHostAddr.sa.sa_family != 0)
470340266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
470440266059SGregory Neil Shapiro 					   " [%.100s]",
4705c2aa98e2SPeter Wemm 					   anynet_ntoa(&CurHostAddr));
4706c2aa98e2SPeter Wemm 	}
470740266059SGregory Neil Shapiro 	else if (strcmp(status, "quarantined") == 0)
470840266059SGregory Neil Shapiro 	{
470940266059SGregory Neil Shapiro 		if (e->e_quarmsg != NULL)
471040266059SGregory Neil Shapiro 			(void) sm_snprintf(bp, SPACELEFT(buf, bp),
471140266059SGregory Neil Shapiro 					   ", quarantine=%.100s",
471240266059SGregory Neil Shapiro 					   e->e_quarmsg);
471340266059SGregory Neil Shapiro 	}
471406f25ae9SGregory Neil Shapiro 	else if (strcmp(status, "queued") != 0)
4715c2aa98e2SPeter Wemm 	{
4716c2aa98e2SPeter Wemm 		p = macvalue('h', e);
4717c2aa98e2SPeter Wemm 		if (p != NULL && p[0] != '\0')
4718d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p);
4719c2aa98e2SPeter Wemm 	}
4720c2aa98e2SPeter Wemm 	if (buf[0] != '\0')
4721c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
4722c2aa98e2SPeter Wemm 
472306f25ae9SGregory Neil Shapiro 	sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
472406f25ae9SGregory Neil Shapiro #endif /* (SYSLOG_BUFSIZE) >= 256 */
4725c2aa98e2SPeter Wemm }
472640266059SGregory Neil Shapiro /*
4727c2aa98e2SPeter Wemm **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
4728c2aa98e2SPeter Wemm **
4729c2aa98e2SPeter Wemm **	This can be made an arbitrary message separator by changing $l
4730c2aa98e2SPeter Wemm **
4731c2aa98e2SPeter Wemm **	One of the ugliest hacks seen by human eyes is contained herein:
4732c2aa98e2SPeter Wemm **	UUCP wants those stupid "remote from <host>" lines.  Why oh why
4733c2aa98e2SPeter Wemm **	does a well-meaning programmer such as myself have to deal with
4734c2aa98e2SPeter Wemm **	this kind of antique garbage????
4735c2aa98e2SPeter Wemm **
4736c2aa98e2SPeter Wemm **	Parameters:
4737c2aa98e2SPeter Wemm **		mci -- the connection information.
4738c2aa98e2SPeter Wemm **		e -- the envelope.
4739c2aa98e2SPeter Wemm **
4740c2aa98e2SPeter Wemm **	Returns:
47414e4196cbSGregory Neil Shapiro **		true iff line was written successfully
4742c2aa98e2SPeter Wemm **
4743c2aa98e2SPeter Wemm **	Side Effects:
4744c2aa98e2SPeter Wemm **		outputs some text to fp.
4745c2aa98e2SPeter Wemm */
4746c2aa98e2SPeter Wemm 
47474e4196cbSGregory Neil Shapiro bool
4748c2aa98e2SPeter Wemm putfromline(mci, e)
4749c2aa98e2SPeter Wemm 	register MCI *mci;
4750c2aa98e2SPeter Wemm 	ENVELOPE *e;
4751c2aa98e2SPeter Wemm {
4752c2aa98e2SPeter Wemm 	char *template = UnixFromLine;
4753c2aa98e2SPeter Wemm 	char buf[MAXLINE];
4754c2aa98e2SPeter Wemm 	char xbuf[MAXLINE];
4755c2aa98e2SPeter Wemm 
4756c2aa98e2SPeter Wemm 	if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
47574e4196cbSGregory Neil Shapiro 		return true;
4758c2aa98e2SPeter Wemm 
4759c2aa98e2SPeter Wemm 	mci->mci_flags |= MCIF_INHEADER;
4760c2aa98e2SPeter Wemm 
4761c2aa98e2SPeter Wemm 	if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
4762c2aa98e2SPeter Wemm 	{
4763c2aa98e2SPeter Wemm 		char *bang;
4764c2aa98e2SPeter Wemm 
4765d0cef73dSGregory Neil Shapiro 		expand("\201g", buf, sizeof(buf), e);
4766c2aa98e2SPeter Wemm 		bang = strchr(buf, '!');
4767c2aa98e2SPeter Wemm 		if (bang == NULL)
4768c2aa98e2SPeter Wemm 		{
4769c2aa98e2SPeter Wemm 			char *at;
4770c2aa98e2SPeter Wemm 			char hname[MAXNAME];
4771c2aa98e2SPeter Wemm 
4772c2aa98e2SPeter Wemm 			/*
4773c2aa98e2SPeter Wemm 			**  If we can construct a UUCP path, do so
4774c2aa98e2SPeter Wemm 			*/
4775c2aa98e2SPeter Wemm 
4776c2aa98e2SPeter Wemm 			at = strrchr(buf, '@');
4777c2aa98e2SPeter Wemm 			if (at == NULL)
4778c2aa98e2SPeter Wemm 			{
4779d0cef73dSGregory Neil Shapiro 				expand("\201k", hname, sizeof(hname), e);
4780c2aa98e2SPeter Wemm 				at = hname;
4781c2aa98e2SPeter Wemm 			}
4782c2aa98e2SPeter Wemm 			else
4783c2aa98e2SPeter Wemm 				*at++ = '\0';
4784d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(xbuf, sizeof(xbuf),
4785c2aa98e2SPeter Wemm 					   "From %.800s  \201d remote from %.100s\n",
4786c2aa98e2SPeter Wemm 					   buf, at);
4787c2aa98e2SPeter Wemm 		}
4788c2aa98e2SPeter Wemm 		else
4789c2aa98e2SPeter Wemm 		{
4790c2aa98e2SPeter Wemm 			*bang++ = '\0';
4791d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(xbuf, sizeof(xbuf),
4792c2aa98e2SPeter Wemm 					   "From %.800s  \201d remote from %.100s\n",
4793c2aa98e2SPeter Wemm 					   bang, buf);
4794c2aa98e2SPeter Wemm 			template = xbuf;
4795c2aa98e2SPeter Wemm 		}
4796c2aa98e2SPeter Wemm 	}
4797d0cef73dSGregory Neil Shapiro 	expand(template, buf, sizeof(buf), e);
47984e4196cbSGregory Neil Shapiro 	return putxline(buf, strlen(buf), mci, PXLF_HEADER);
4799c2aa98e2SPeter Wemm }
48004e4196cbSGregory Neil Shapiro 
480140266059SGregory Neil Shapiro /*
4802c2aa98e2SPeter Wemm **  PUTBODY -- put the body of a message.
4803c2aa98e2SPeter Wemm **
4804c2aa98e2SPeter Wemm **	Parameters:
4805c2aa98e2SPeter Wemm **		mci -- the connection information.
4806c2aa98e2SPeter Wemm **		e -- the envelope to put out.
4807c2aa98e2SPeter Wemm **		separator -- if non-NULL, a message separator that must
4808c2aa98e2SPeter Wemm **			not be permitted in the resulting message.
4809c2aa98e2SPeter Wemm **
4810c2aa98e2SPeter Wemm **	Returns:
48114e4196cbSGregory Neil Shapiro **		true iff message was written successfully
4812c2aa98e2SPeter Wemm **
4813c2aa98e2SPeter Wemm **	Side Effects:
4814c2aa98e2SPeter Wemm **		The message is written onto fp.
4815c2aa98e2SPeter Wemm */
4816c2aa98e2SPeter Wemm 
4817c2aa98e2SPeter Wemm /* values for output state variable */
48184e4196cbSGregory Neil Shapiro #define OSTATE_HEAD	0	/* at beginning of line */
48194e4196cbSGregory Neil Shapiro #define OSTATE_CR	1	/* read a carriage return */
48204e4196cbSGregory Neil Shapiro #define OSTATE_INLINE	2	/* putting rest of line */
4821c2aa98e2SPeter Wemm 
48224e4196cbSGregory Neil Shapiro bool
4823c2aa98e2SPeter Wemm putbody(mci, e, separator)
4824c2aa98e2SPeter Wemm 	register MCI *mci;
4825c2aa98e2SPeter Wemm 	register ENVELOPE *e;
4826c2aa98e2SPeter Wemm 	char *separator;
4827c2aa98e2SPeter Wemm {
482840266059SGregory Neil Shapiro 	bool dead = false;
48294e4196cbSGregory Neil Shapiro 	bool ioerr = false;
48304e4196cbSGregory Neil Shapiro 	int save_errno;
4831c2aa98e2SPeter Wemm 	char buf[MAXLINE];
483240266059SGregory Neil Shapiro #if MIME8TO7
4833065a643dSPeter Wemm 	char *boundaries[MAXMIMENESTING + 1];
4834*5b0945b5SGregory Neil Shapiro #endif
4835c2aa98e2SPeter Wemm 
4836c2aa98e2SPeter Wemm 	/*
4837c2aa98e2SPeter Wemm 	**  Output the body of the message
4838c2aa98e2SPeter Wemm 	*/
4839c2aa98e2SPeter Wemm 
4840c2aa98e2SPeter Wemm 	if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
4841c2aa98e2SPeter Wemm 	{
484240266059SGregory Neil Shapiro 		char *df = queuename(e, DATAFL_LETTER);
4843c2aa98e2SPeter Wemm 
484440266059SGregory Neil Shapiro 		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
4845a7ec597cSGregory Neil Shapiro 				      SM_IO_RDONLY_B, NULL);
4846c2aa98e2SPeter Wemm 		if (e->e_dfp == NULL)
484706f25ae9SGregory Neil Shapiro 		{
484806f25ae9SGregory Neil Shapiro 			char *msg = "!putbody: Cannot open %s for %s from %s";
484906f25ae9SGregory Neil Shapiro 
485006f25ae9SGregory Neil Shapiro 			if (errno == ENOENT)
485106f25ae9SGregory Neil Shapiro 				msg++;
485206f25ae9SGregory Neil Shapiro 			syserr(msg, df, e->e_to, e->e_from.q_paddr);
485306f25ae9SGregory Neil Shapiro 		}
485440266059SGregory Neil Shapiro 
4855c2aa98e2SPeter Wemm 	}
4856c2aa98e2SPeter Wemm 	if (e->e_dfp == NULL)
4857c2aa98e2SPeter Wemm 	{
4858c2aa98e2SPeter Wemm 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4859c2aa98e2SPeter Wemm 		{
48604e4196cbSGregory Neil Shapiro 			if (!putline("", mci))
48614e4196cbSGregory Neil Shapiro 				goto writeerr;
4862c2aa98e2SPeter Wemm 			mci->mci_flags &= ~MCIF_INHEADER;
4863c2aa98e2SPeter Wemm 		}
48644e4196cbSGregory Neil Shapiro 		if (!putline("<<< No Message Collected >>>", mci))
48654e4196cbSGregory Neil Shapiro 			goto writeerr;
4866c2aa98e2SPeter Wemm 		goto endofmessage;
4867c2aa98e2SPeter Wemm 	}
486806f25ae9SGregory Neil Shapiro 
4869c2aa98e2SPeter Wemm 	if (e->e_dfino == (ino_t) 0)
4870c2aa98e2SPeter Wemm 	{
4871c2aa98e2SPeter Wemm 		struct stat stbuf;
4872c2aa98e2SPeter Wemm 
487340266059SGregory Neil Shapiro 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
487440266059SGregory Neil Shapiro 		    < 0)
4875c2aa98e2SPeter Wemm 			e->e_dfino = -1;
4876c2aa98e2SPeter Wemm 		else
4877c2aa98e2SPeter Wemm 		{
4878c2aa98e2SPeter Wemm 			e->e_dfdev = stbuf.st_dev;
4879c2aa98e2SPeter Wemm 			e->e_dfino = stbuf.st_ino;
4880c2aa98e2SPeter Wemm 		}
4881c2aa98e2SPeter Wemm 	}
488206f25ae9SGregory Neil Shapiro 
488340266059SGregory Neil Shapiro 	/* paranoia: the data file should always be in a rewound state */
488406f25ae9SGregory Neil Shapiro 	(void) bfrewind(e->e_dfp);
4885c2aa98e2SPeter Wemm 
48864e4196cbSGregory Neil Shapiro 	/* simulate an I/O timeout when used as source */
48874e4196cbSGregory Neil Shapiro 	if (tTd(84, 101))
48884e4196cbSGregory Neil Shapiro 		sleep(319);
48894e4196cbSGregory Neil Shapiro 
4890c2aa98e2SPeter Wemm #if MIME8TO7
4891c2aa98e2SPeter Wemm 	if (bitset(MCIF_CVT8TO7, mci->mci_flags))
4892c2aa98e2SPeter Wemm 	{
4893c2aa98e2SPeter Wemm 		/*
4894c2aa98e2SPeter Wemm 		**  Do 8 to 7 bit MIME conversion.
4895c2aa98e2SPeter Wemm 		*/
4896c2aa98e2SPeter Wemm 
4897c2aa98e2SPeter Wemm 		/* make sure it looks like a MIME message */
48984e4196cbSGregory Neil Shapiro 		if (hvalue("MIME-Version", e->e_header) == NULL &&
48994e4196cbSGregory Neil Shapiro 		    !putline("MIME-Version: 1.0", mci))
49004e4196cbSGregory Neil Shapiro 			goto writeerr;
4901c2aa98e2SPeter Wemm 
4902c2aa98e2SPeter Wemm 		if (hvalue("Content-Type", e->e_header) == NULL)
4903c2aa98e2SPeter Wemm 		{
4904d0cef73dSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof(buf),
4905c2aa98e2SPeter Wemm 					   "Content-Type: text/plain; charset=%s",
4906c2aa98e2SPeter Wemm 					   defcharset(e));
49074e4196cbSGregory Neil Shapiro 			if (!putline(buf, mci))
49084e4196cbSGregory Neil Shapiro 				goto writeerr;
4909c2aa98e2SPeter Wemm 		}
4910c2aa98e2SPeter Wemm 
4911c2aa98e2SPeter Wemm 		/* now do the hard work */
4912c2aa98e2SPeter Wemm 		boundaries[0] = NULL;
4913c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_INHEADER;
4914af9557fdSGregory Neil Shapiro 		if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) ==
49154e4196cbSGregory Neil Shapiro 								SM_IO_EOF)
49164e4196cbSGregory Neil Shapiro 			goto writeerr;
4917c2aa98e2SPeter Wemm 	}
4918c2aa98e2SPeter Wemm # if MIME7TO8
4919c2aa98e2SPeter Wemm 	else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
4920c2aa98e2SPeter Wemm 	{
49214e4196cbSGregory Neil Shapiro 		if (!mime7to8(mci, e->e_header, e))
49224e4196cbSGregory Neil Shapiro 			goto writeerr;
4923c2aa98e2SPeter Wemm 	}
492406f25ae9SGregory Neil Shapiro # endif /* MIME7TO8 */
4925065a643dSPeter Wemm 	else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
4926065a643dSPeter Wemm 	{
492706f25ae9SGregory Neil Shapiro 		bool oldsuprerrs = SuprErrs;
492806f25ae9SGregory Neil Shapiro 
4929065a643dSPeter Wemm 		/* Use mime8to7 to check multipart for MIME header overflows */
4930065a643dSPeter Wemm 		boundaries[0] = NULL;
4931065a643dSPeter Wemm 		mci->mci_flags |= MCIF_INHEADER;
493206f25ae9SGregory Neil Shapiro 
493306f25ae9SGregory Neil Shapiro 		/*
493406f25ae9SGregory Neil Shapiro 		**  If EF_DONT_MIME is set, we have a broken MIME message
493506f25ae9SGregory Neil Shapiro 		**  and don't want to generate a new bounce message whose
493606f25ae9SGregory Neil Shapiro 		**  body propagates the broken MIME.  We can't just not call
493706f25ae9SGregory Neil Shapiro 		**  mime8to7() as is done above since we need the security
493806f25ae9SGregory Neil Shapiro 		**  checks.  The best we can do is suppress the errors.
493906f25ae9SGregory Neil Shapiro 		*/
494006f25ae9SGregory Neil Shapiro 
494106f25ae9SGregory Neil Shapiro 		if (bitset(EF_DONT_MIME, e->e_flags))
494240266059SGregory Neil Shapiro 			SuprErrs = true;
494306f25ae9SGregory Neil Shapiro 
49444e4196cbSGregory Neil Shapiro 		if (mime8to7(mci, e->e_header, e, boundaries,
4945af9557fdSGregory Neil Shapiro 				M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF)
49464e4196cbSGregory Neil Shapiro 			goto writeerr;
494706f25ae9SGregory Neil Shapiro 
494806f25ae9SGregory Neil Shapiro 		/* restore SuprErrs */
494906f25ae9SGregory Neil Shapiro 		SuprErrs = oldsuprerrs;
4950065a643dSPeter Wemm 	}
4951c2aa98e2SPeter Wemm 	else
495206f25ae9SGregory Neil Shapiro #endif /* MIME8TO7 */
4953c2aa98e2SPeter Wemm 	{
4954c2aa98e2SPeter Wemm 		int ostate;
4955c2aa98e2SPeter Wemm 		register char *bp;
4956c2aa98e2SPeter Wemm 		register char *pbp;
4957c2aa98e2SPeter Wemm 		register int c;
4958c2aa98e2SPeter Wemm 		register char *xp;
4959c2aa98e2SPeter Wemm 		int padc;
4960c2aa98e2SPeter Wemm 		char *buflim;
4961c2aa98e2SPeter Wemm 		int pos = 0;
496206f25ae9SGregory Neil Shapiro 		char peekbuf[12];
4963c2aa98e2SPeter Wemm 
4964c2aa98e2SPeter Wemm 		if (bitset(MCIF_INHEADER, mci->mci_flags))
4965c2aa98e2SPeter Wemm 		{
49664e4196cbSGregory Neil Shapiro 			if (!putline("", mci))
49674e4196cbSGregory Neil Shapiro 				goto writeerr;
4968c2aa98e2SPeter Wemm 			mci->mci_flags &= ~MCIF_INHEADER;
4969c2aa98e2SPeter Wemm 		}
4970c2aa98e2SPeter Wemm 
4971c2aa98e2SPeter Wemm 		/* determine end of buffer; allow for short mailer lines */
4972d0cef73dSGregory Neil Shapiro 		buflim = &buf[sizeof(buf) - 1];
4973c2aa98e2SPeter Wemm 		if (mci->mci_mailer->m_linelimit > 0 &&
4974d0cef73dSGregory Neil Shapiro 		    mci->mci_mailer->m_linelimit < sizeof(buf) - 1)
4975c2aa98e2SPeter Wemm 			buflim = &buf[mci->mci_mailer->m_linelimit - 1];
4976c2aa98e2SPeter Wemm 
4977c2aa98e2SPeter Wemm 		/* copy temp file to output with mapping */
49784e4196cbSGregory Neil Shapiro 		ostate = OSTATE_HEAD;
4979c2aa98e2SPeter Wemm 		bp = buf;
4980c2aa98e2SPeter Wemm 		pbp = peekbuf;
498140266059SGregory Neil Shapiro 		while (!sm_io_error(mci->mci_out) && !dead)
4982c2aa98e2SPeter Wemm 		{
4983c2aa98e2SPeter Wemm 			if (pbp > peekbuf)
4984c2aa98e2SPeter Wemm 				c = *--pbp;
498540266059SGregory Neil Shapiro 			else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
498640266059SGregory Neil Shapiro 				 == SM_IO_EOF)
4987c2aa98e2SPeter Wemm 				break;
4988c2aa98e2SPeter Wemm 			if (bitset(MCIF_7BIT, mci->mci_flags))
4989c2aa98e2SPeter Wemm 				c &= 0x7f;
4990c2aa98e2SPeter Wemm 			switch (ostate)
4991c2aa98e2SPeter Wemm 			{
49924e4196cbSGregory Neil Shapiro 			  case OSTATE_HEAD:
4993c2aa98e2SPeter Wemm 				if (c == '\0' &&
499440266059SGregory Neil Shapiro 				    bitnset(M_NONULLS,
499540266059SGregory Neil Shapiro 					    mci->mci_mailer->m_flags))
4996c2aa98e2SPeter Wemm 					break;
4997c2aa98e2SPeter Wemm 				if (c != '\r' && c != '\n' && bp < buflim)
4998c2aa98e2SPeter Wemm 				{
4999c2aa98e2SPeter Wemm 					*bp++ = c;
5000c2aa98e2SPeter Wemm 					break;
5001c2aa98e2SPeter Wemm 				}
5002c2aa98e2SPeter Wemm 
5003c2aa98e2SPeter Wemm 				/* check beginning of line for special cases */
5004c2aa98e2SPeter Wemm 				*bp = '\0';
5005c2aa98e2SPeter Wemm 				pos = 0;
500640266059SGregory Neil Shapiro 				padc = SM_IO_EOF;
5007c2aa98e2SPeter Wemm 				if (buf[0] == 'F' &&
500840266059SGregory Neil Shapiro 				    bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
500940266059SGregory Neil Shapiro 				    && strncmp(buf, "From ", 5) == 0)
5010c2aa98e2SPeter Wemm 				{
5011c2aa98e2SPeter Wemm 					padc = '>';
5012c2aa98e2SPeter Wemm 				}
5013c2aa98e2SPeter Wemm 				if (buf[0] == '-' && buf[1] == '-' &&
5014c2aa98e2SPeter Wemm 				    separator != NULL)
5015c2aa98e2SPeter Wemm 				{
5016c2aa98e2SPeter Wemm 					/* possible separator */
5017c2aa98e2SPeter Wemm 					int sl = strlen(separator);
5018c2aa98e2SPeter Wemm 
501940266059SGregory Neil Shapiro 					if (strncmp(&buf[2], separator, sl)
502040266059SGregory Neil Shapiro 					    == 0)
5021c2aa98e2SPeter Wemm 						padc = ' ';
5022c2aa98e2SPeter Wemm 				}
5023c2aa98e2SPeter Wemm 				if (buf[0] == '.' &&
5024c2aa98e2SPeter Wemm 				    bitnset(M_XDOT, mci->mci_mailer->m_flags))
5025c2aa98e2SPeter Wemm 				{
5026c2aa98e2SPeter Wemm 					padc = '.';
5027c2aa98e2SPeter Wemm 				}
5028c2aa98e2SPeter Wemm 
5029c2aa98e2SPeter Wemm 				/* now copy out saved line */
5030c2aa98e2SPeter Wemm 				if (TrafficLogFile != NULL)
5031c2aa98e2SPeter Wemm 				{
503240266059SGregory Neil Shapiro 					(void) sm_io_fprintf(TrafficLogFile,
503340266059SGregory Neil Shapiro 							     SM_TIME_DEFAULT,
503440266059SGregory Neil Shapiro 							     "%05d >>> ",
503540266059SGregory Neil Shapiro 							     (int) CurrentPid);
503640266059SGregory Neil Shapiro 					if (padc != SM_IO_EOF)
503740266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
503840266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
503940266059SGregory Neil Shapiro 								  padc);
5040c2aa98e2SPeter Wemm 					for (xp = buf; xp < bp; xp++)
504140266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
504240266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
504340266059SGregory Neil Shapiro 								  (unsigned char) *xp);
5044c2aa98e2SPeter Wemm 					if (c == '\n')
504540266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
504640266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
504740266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
5048c2aa98e2SPeter Wemm 				}
504940266059SGregory Neil Shapiro 				if (padc != SM_IO_EOF)
5050c2aa98e2SPeter Wemm 				{
505140266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
505240266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT, padc)
505340266059SGregory Neil Shapiro 					    == SM_IO_EOF)
505406f25ae9SGregory Neil Shapiro 					{
505540266059SGregory Neil Shapiro 						dead = true;
505606f25ae9SGregory Neil Shapiro 						continue;
505706f25ae9SGregory Neil Shapiro 					}
5058c2aa98e2SPeter Wemm 					pos++;
5059c2aa98e2SPeter Wemm 				}
5060c2aa98e2SPeter Wemm 				for (xp = buf; xp < bp; xp++)
5061c2aa98e2SPeter Wemm 				{
506240266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
506340266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT,
506440266059SGregory Neil Shapiro 						       (unsigned char) *xp)
506540266059SGregory Neil Shapiro 					    == SM_IO_EOF)
506606f25ae9SGregory Neil Shapiro 					{
506740266059SGregory Neil Shapiro 						dead = true;
506806f25ae9SGregory Neil Shapiro 						break;
5069c2aa98e2SPeter Wemm 					}
5070193538b7SGregory Neil Shapiro 				}
507106f25ae9SGregory Neil Shapiro 				if (dead)
507206f25ae9SGregory Neil Shapiro 					continue;
5073c2aa98e2SPeter Wemm 				if (c == '\n')
5074c2aa98e2SPeter Wemm 				{
507540266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
507640266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
507740266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
507840266059SGregory Neil Shapiro 							== SM_IO_EOF)
507906f25ae9SGregory Neil Shapiro 						break;
5080c2aa98e2SPeter Wemm 					pos = 0;
5081c2aa98e2SPeter Wemm 				}
5082c2aa98e2SPeter Wemm 				else
5083c2aa98e2SPeter Wemm 				{
5084c2aa98e2SPeter Wemm 					pos += bp - buf;
5085c2aa98e2SPeter Wemm 					if (c != '\r')
50865ef517c0SGregory Neil Shapiro 					{
50875ef517c0SGregory Neil Shapiro 						SM_ASSERT(pbp < peekbuf +
50885ef517c0SGregory Neil Shapiro 								sizeof(peekbuf));
5089c2aa98e2SPeter Wemm 						*pbp++ = c;
5090c2aa98e2SPeter Wemm 					}
50915ef517c0SGregory Neil Shapiro 				}
509206f25ae9SGregory Neil Shapiro 
5093c2aa98e2SPeter Wemm 				bp = buf;
5094c2aa98e2SPeter Wemm 
5095c2aa98e2SPeter Wemm 				/* determine next state */
5096c2aa98e2SPeter Wemm 				if (c == '\n')
50974e4196cbSGregory Neil Shapiro 					ostate = OSTATE_HEAD;
5098c2aa98e2SPeter Wemm 				else if (c == '\r')
50994e4196cbSGregory Neil Shapiro 					ostate = OSTATE_CR;
5100c2aa98e2SPeter Wemm 				else
51014e4196cbSGregory Neil Shapiro 					ostate = OSTATE_INLINE;
5102c2aa98e2SPeter Wemm 				continue;
5103c2aa98e2SPeter Wemm 
51044e4196cbSGregory Neil Shapiro 			  case OSTATE_CR:
5105c2aa98e2SPeter Wemm 				if (c == '\n')
5106c2aa98e2SPeter Wemm 				{
5107c2aa98e2SPeter Wemm 					/* got CRLF */
510840266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
510940266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
511040266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
511140266059SGregory Neil Shapiro 							== SM_IO_EOF)
511206f25ae9SGregory Neil Shapiro 						continue;
511306f25ae9SGregory Neil Shapiro 
5114c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
5115c2aa98e2SPeter Wemm 					{
511640266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
511740266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
511840266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
5119c2aa98e2SPeter Wemm 					}
5120d0cef73dSGregory Neil Shapiro 					pos = 0;
51214e4196cbSGregory Neil Shapiro 					ostate = OSTATE_HEAD;
5122c2aa98e2SPeter Wemm 					continue;
5123c2aa98e2SPeter Wemm 				}
5124c2aa98e2SPeter Wemm 
5125c2aa98e2SPeter Wemm 				/* had a naked carriage return */
51265ef517c0SGregory Neil Shapiro 				SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
5127c2aa98e2SPeter Wemm 				*pbp++ = c;
5128c2aa98e2SPeter Wemm 				c = '\r';
51294e4196cbSGregory Neil Shapiro 				ostate = OSTATE_INLINE;
5130c2aa98e2SPeter Wemm 				goto putch;
5131c2aa98e2SPeter Wemm 
51324e4196cbSGregory Neil Shapiro 			  case OSTATE_INLINE:
5133c2aa98e2SPeter Wemm 				if (c == '\r')
5134c2aa98e2SPeter Wemm 				{
51354e4196cbSGregory Neil Shapiro 					ostate = OSTATE_CR;
5136c2aa98e2SPeter Wemm 					continue;
5137c2aa98e2SPeter Wemm 				}
5138c2aa98e2SPeter Wemm 				if (c == '\0' &&
513940266059SGregory Neil Shapiro 				    bitnset(M_NONULLS,
514040266059SGregory Neil Shapiro 					    mci->mci_mailer->m_flags))
5141c2aa98e2SPeter Wemm 					break;
5142c2aa98e2SPeter Wemm putch:
5143c2aa98e2SPeter Wemm 				if (mci->mci_mailer->m_linelimit > 0 &&
514406f25ae9SGregory Neil Shapiro 				    pos >= mci->mci_mailer->m_linelimit - 1 &&
5145c2aa98e2SPeter Wemm 				    c != '\n')
5146c2aa98e2SPeter Wemm 				{
514706f25ae9SGregory Neil Shapiro 					int d;
514806f25ae9SGregory Neil Shapiro 
514906f25ae9SGregory Neil Shapiro 					/* check next character for EOL */
515006f25ae9SGregory Neil Shapiro 					if (pbp > peekbuf)
515106f25ae9SGregory Neil Shapiro 						d = *(pbp - 1);
515240266059SGregory Neil Shapiro 					else if ((d = sm_io_getc(e->e_dfp,
515340266059SGregory Neil Shapiro 								 SM_TIME_DEFAULT))
515440266059SGregory Neil Shapiro 						 != SM_IO_EOF)
51555ef517c0SGregory Neil Shapiro 					{
51565ef517c0SGregory Neil Shapiro 						SM_ASSERT(pbp < peekbuf +
51575ef517c0SGregory Neil Shapiro 								sizeof(peekbuf));
515806f25ae9SGregory Neil Shapiro 						*pbp++ = d;
51595ef517c0SGregory Neil Shapiro 					}
516006f25ae9SGregory Neil Shapiro 
516140266059SGregory Neil Shapiro 					if (d == '\n' || d == SM_IO_EOF)
516206f25ae9SGregory Neil Shapiro 					{
516306f25ae9SGregory Neil Shapiro 						if (TrafficLogFile != NULL)
516440266059SGregory Neil Shapiro 							(void) sm_io_putc(TrafficLogFile,
516540266059SGregory Neil Shapiro 									  SM_TIME_DEFAULT,
516640266059SGregory Neil Shapiro 									  (unsigned char) c);
516740266059SGregory Neil Shapiro 						if (sm_io_putc(mci->mci_out,
516840266059SGregory Neil Shapiro 							       SM_TIME_DEFAULT,
516940266059SGregory Neil Shapiro 							       (unsigned char) c)
517040266059SGregory Neil Shapiro 							       == SM_IO_EOF)
517106f25ae9SGregory Neil Shapiro 						{
517240266059SGregory Neil Shapiro 							dead = true;
517306f25ae9SGregory Neil Shapiro 							continue;
517406f25ae9SGregory Neil Shapiro 						}
517506f25ae9SGregory Neil Shapiro 						pos++;
517606f25ae9SGregory Neil Shapiro 						continue;
517706f25ae9SGregory Neil Shapiro 					}
517806f25ae9SGregory Neil Shapiro 
517940266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
518040266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT, '!')
518140266059SGregory Neil Shapiro 					    == SM_IO_EOF ||
518240266059SGregory Neil Shapiro 					    sm_io_fputs(mci->mci_out,
518340266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
518440266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
518540266059SGregory Neil Shapiro 					    == SM_IO_EOF)
518606f25ae9SGregory Neil Shapiro 					{
518740266059SGregory Neil Shapiro 						dead = true;
518806f25ae9SGregory Neil Shapiro 						continue;
518906f25ae9SGregory Neil Shapiro 					}
519006f25ae9SGregory Neil Shapiro 
5191c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
5192c2aa98e2SPeter Wemm 					{
519340266059SGregory Neil Shapiro 						(void) sm_io_fprintf(TrafficLogFile,
519440266059SGregory Neil Shapiro 								     SM_TIME_DEFAULT,
519540266059SGregory Neil Shapiro 								     "!%s",
5196c2aa98e2SPeter Wemm 								     mci->mci_mailer->m_eol);
5197c2aa98e2SPeter Wemm 					}
51984e4196cbSGregory Neil Shapiro 					ostate = OSTATE_HEAD;
51995ef517c0SGregory Neil Shapiro 					SM_ASSERT(pbp < peekbuf +
52005ef517c0SGregory Neil Shapiro 							sizeof(peekbuf));
5201c2aa98e2SPeter Wemm 					*pbp++ = c;
5202c2aa98e2SPeter Wemm 					continue;
5203c2aa98e2SPeter Wemm 				}
5204c2aa98e2SPeter Wemm 				if (c == '\n')
5205c2aa98e2SPeter Wemm 				{
5206c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
520740266059SGregory Neil Shapiro 						(void) sm_io_fputs(TrafficLogFile,
520840266059SGregory Neil Shapiro 								   SM_TIME_DEFAULT,
520940266059SGregory Neil Shapiro 								   mci->mci_mailer->m_eol);
521040266059SGregory Neil Shapiro 					if (sm_io_fputs(mci->mci_out,
521140266059SGregory Neil Shapiro 							SM_TIME_DEFAULT,
521240266059SGregory Neil Shapiro 							mci->mci_mailer->m_eol)
521340266059SGregory Neil Shapiro 							== SM_IO_EOF)
521406f25ae9SGregory Neil Shapiro 						continue;
5215c2aa98e2SPeter Wemm 					pos = 0;
52164e4196cbSGregory Neil Shapiro 					ostate = OSTATE_HEAD;
5217c2aa98e2SPeter Wemm 				}
5218c2aa98e2SPeter Wemm 				else
5219c2aa98e2SPeter Wemm 				{
5220c2aa98e2SPeter Wemm 					if (TrafficLogFile != NULL)
522140266059SGregory Neil Shapiro 						(void) sm_io_putc(TrafficLogFile,
522240266059SGregory Neil Shapiro 								  SM_TIME_DEFAULT,
522340266059SGregory Neil Shapiro 								  (unsigned char) c);
522440266059SGregory Neil Shapiro 					if (sm_io_putc(mci->mci_out,
522540266059SGregory Neil Shapiro 						       SM_TIME_DEFAULT,
522640266059SGregory Neil Shapiro 						       (unsigned char) c)
522740266059SGregory Neil Shapiro 					    == SM_IO_EOF)
522806f25ae9SGregory Neil Shapiro 					{
522940266059SGregory Neil Shapiro 						dead = true;
523006f25ae9SGregory Neil Shapiro 						continue;
523106f25ae9SGregory Neil Shapiro 					}
5232c2aa98e2SPeter Wemm 					pos++;
52334e4196cbSGregory Neil Shapiro 					ostate = OSTATE_INLINE;
5234c2aa98e2SPeter Wemm 				}
5235c2aa98e2SPeter Wemm 				break;
5236c2aa98e2SPeter Wemm 			}
5237c2aa98e2SPeter Wemm 		}
5238c2aa98e2SPeter Wemm 
5239c2aa98e2SPeter Wemm 		/* make sure we are at the beginning of a line */
5240c2aa98e2SPeter Wemm 		if (bp > buf)
5241c2aa98e2SPeter Wemm 		{
5242c2aa98e2SPeter Wemm 			if (TrafficLogFile != NULL)
5243c2aa98e2SPeter Wemm 			{
5244c2aa98e2SPeter Wemm 				for (xp = buf; xp < bp; xp++)
524540266059SGregory Neil Shapiro 					(void) sm_io_putc(TrafficLogFile,
524640266059SGregory Neil Shapiro 							  SM_TIME_DEFAULT,
524740266059SGregory Neil Shapiro 							  (unsigned char) *xp);
5248c2aa98e2SPeter Wemm 			}
5249c2aa98e2SPeter Wemm 			for (xp = buf; xp < bp; xp++)
5250c2aa98e2SPeter Wemm 			{
525140266059SGregory Neil Shapiro 				if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
525240266059SGregory Neil Shapiro 					       (unsigned char) *xp)
525340266059SGregory Neil Shapiro 				    == SM_IO_EOF)
525406f25ae9SGregory Neil Shapiro 				{
525540266059SGregory Neil Shapiro 					dead = true;
525606f25ae9SGregory Neil Shapiro 					break;
525706f25ae9SGregory Neil Shapiro 				}
5258193538b7SGregory Neil Shapiro 			}
5259c2aa98e2SPeter Wemm 			pos += bp - buf;
5260c2aa98e2SPeter Wemm 		}
526106f25ae9SGregory Neil Shapiro 		if (!dead && pos > 0)
5262c2aa98e2SPeter Wemm 		{
5263c2aa98e2SPeter Wemm 			if (TrafficLogFile != NULL)
526440266059SGregory Neil Shapiro 				(void) sm_io_fputs(TrafficLogFile,
526540266059SGregory Neil Shapiro 						   SM_TIME_DEFAULT,
526640266059SGregory Neil Shapiro 						   mci->mci_mailer->m_eol);
52674e4196cbSGregory Neil Shapiro 			if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
52684e4196cbSGregory Neil Shapiro 					   mci->mci_mailer->m_eol) == SM_IO_EOF)
52694e4196cbSGregory Neil Shapiro 				goto writeerr;
5270c2aa98e2SPeter Wemm 		}
5271c2aa98e2SPeter Wemm 	}
5272c2aa98e2SPeter Wemm 
527340266059SGregory Neil Shapiro 	if (sm_io_error(e->e_dfp))
5274c2aa98e2SPeter Wemm 	{
527540266059SGregory Neil Shapiro 		syserr("putbody: %s/%cf%s: read error",
527640266059SGregory Neil Shapiro 		       qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
527740266059SGregory Neil Shapiro 		       DATAFL_LETTER, e->e_id);
5278c2aa98e2SPeter Wemm 		ExitStat = EX_IOERR;
52794e4196cbSGregory Neil Shapiro 		ioerr = true;
5280c2aa98e2SPeter Wemm 	}
5281c2aa98e2SPeter Wemm 
5282c2aa98e2SPeter Wemm endofmessage:
528306f25ae9SGregory Neil Shapiro 	/*
528406f25ae9SGregory Neil Shapiro 	**  Since mailfile() uses e_dfp in a child process,
528506f25ae9SGregory Neil Shapiro 	**  the file offset in the stdio library for the
528606f25ae9SGregory Neil Shapiro 	**  parent process will not agree with the in-kernel
528706f25ae9SGregory Neil Shapiro 	**  file offset since the file descriptor is shared
528806f25ae9SGregory Neil Shapiro 	**  between the processes.  Therefore, it is vital
528906f25ae9SGregory Neil Shapiro 	**  that the file always be rewound.  This forces the
529006f25ae9SGregory Neil Shapiro 	**  kernel offset (lseek) and stdio library (ftell)
529106f25ae9SGregory Neil Shapiro 	**  offset to match.
529206f25ae9SGregory Neil Shapiro 	*/
529306f25ae9SGregory Neil Shapiro 
52944e4196cbSGregory Neil Shapiro 	save_errno = errno;
529506f25ae9SGregory Neil Shapiro 	if (e->e_dfp != NULL)
529606f25ae9SGregory Neil Shapiro 		(void) bfrewind(e->e_dfp);
529706f25ae9SGregory Neil Shapiro 
5298c2aa98e2SPeter Wemm 	/* some mailers want extra blank line at end of message */
529906f25ae9SGregory Neil Shapiro 	if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
5300c2aa98e2SPeter Wemm 	    buf[0] != '\0' && buf[0] != '\n')
5301c2aa98e2SPeter Wemm 	{
53024e4196cbSGregory Neil Shapiro 		if (!putline("", mci))
53034e4196cbSGregory Neil Shapiro 			goto writeerr;
53044e4196cbSGregory Neil Shapiro 	}
53054e4196cbSGregory Neil Shapiro 
53064e4196cbSGregory Neil Shapiro 	if (!dead &&
53074e4196cbSGregory Neil Shapiro 	    (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
53084e4196cbSGregory Neil Shapiro 	     (sm_io_error(mci->mci_out) && errno != EPIPE)))
53094e4196cbSGregory Neil Shapiro 	{
53104e4196cbSGregory Neil Shapiro 		save_errno = errno;
5311c2aa98e2SPeter Wemm 		syserr("putbody: write error");
5312c2aa98e2SPeter Wemm 		ExitStat = EX_IOERR;
53134e4196cbSGregory Neil Shapiro 		ioerr = true;
5314c2aa98e2SPeter Wemm 	}
531506f25ae9SGregory Neil Shapiro 
53164e4196cbSGregory Neil Shapiro 	errno = save_errno;
53174e4196cbSGregory Neil Shapiro 	return !dead && !ioerr;
53184e4196cbSGregory Neil Shapiro 
53194e4196cbSGregory Neil Shapiro   writeerr:
53204e4196cbSGregory Neil Shapiro 	return false;
5321c2aa98e2SPeter Wemm }
53224e4196cbSGregory Neil Shapiro 
532340266059SGregory Neil Shapiro /*
5324c2aa98e2SPeter Wemm **  MAILFILE -- Send a message to a file.
5325c2aa98e2SPeter Wemm **
532640266059SGregory Neil Shapiro **	If the file has the set-user-ID/set-group-ID bits set, but NO
532740266059SGregory Neil Shapiro **	execute bits, sendmail will try to become the owner of that file
5328c2aa98e2SPeter Wemm **	rather than the real user.  Obviously, this only works if
5329c2aa98e2SPeter Wemm **	sendmail runs as root.
5330c2aa98e2SPeter Wemm **
5331c2aa98e2SPeter Wemm **	This could be done as a subordinate mailer, except that it
5332c2aa98e2SPeter Wemm **	is used implicitly to save messages in ~/dead.letter.  We
5333c2aa98e2SPeter Wemm **	view this as being sufficiently important as to include it
5334c2aa98e2SPeter Wemm **	here.  For example, if the system is dying, we shouldn't have
5335c2aa98e2SPeter Wemm **	to create another process plus some pipes to save the message.
5336c2aa98e2SPeter Wemm **
5337c2aa98e2SPeter Wemm **	Parameters:
5338c2aa98e2SPeter Wemm **		filename -- the name of the file to send to.
5339c2aa98e2SPeter Wemm **		mailer -- mailer definition for recipient -- if NULL,
5340c2aa98e2SPeter Wemm **			use FileMailer.
5341c2aa98e2SPeter Wemm **		ctladdr -- the controlling address header -- includes
5342c2aa98e2SPeter Wemm **			the userid/groupid to be when sending.
5343c2aa98e2SPeter Wemm **		sfflags -- flags for opening.
5344c2aa98e2SPeter Wemm **		e -- the current envelope.
5345c2aa98e2SPeter Wemm **
5346c2aa98e2SPeter Wemm **	Returns:
5347c2aa98e2SPeter Wemm **		The exit code associated with the operation.
5348c2aa98e2SPeter Wemm **
5349c2aa98e2SPeter Wemm **	Side Effects:
5350c2aa98e2SPeter Wemm **		none.
5351c2aa98e2SPeter Wemm */
5352c2aa98e2SPeter Wemm 
535340266059SGregory Neil Shapiro # define RETURN(st)			exit(st);
535440266059SGregory Neil Shapiro 
5355c2aa98e2SPeter Wemm static jmp_buf	CtxMailfileTimeout;
5356c2aa98e2SPeter Wemm 
5357c2aa98e2SPeter Wemm int
5358c2aa98e2SPeter Wemm mailfile(filename, mailer, ctladdr, sfflags, e)
5359c2aa98e2SPeter Wemm 	char *volatile filename;
5360c2aa98e2SPeter Wemm 	MAILER *volatile mailer;
5361c2aa98e2SPeter Wemm 	ADDRESS *ctladdr;
536206f25ae9SGregory Neil Shapiro 	volatile long sfflags;
5363c2aa98e2SPeter Wemm 	register ENVELOPE *e;
5364c2aa98e2SPeter Wemm {
536540266059SGregory Neil Shapiro 	register SM_FILE_T *f;
5366c2aa98e2SPeter Wemm 	register pid_t pid = -1;
536706f25ae9SGregory Neil Shapiro 	volatile int mode;
536806f25ae9SGregory Neil Shapiro 	int len;
536906f25ae9SGregory Neil Shapiro 	off_t curoff;
5370c2aa98e2SPeter Wemm 	bool suidwarn = geteuid() == 0;
5371c2aa98e2SPeter Wemm 	char *p;
537206f25ae9SGregory Neil Shapiro 	char *volatile realfile;
537340266059SGregory Neil Shapiro 	SM_EVENT *ev;
537494c01205SGregory Neil Shapiro 	char buf[MAXPATHLEN];
537594c01205SGregory Neil Shapiro 	char targetfile[MAXPATHLEN];
5376c2aa98e2SPeter Wemm 
5377c2aa98e2SPeter Wemm 	if (tTd(11, 1))
5378c2aa98e2SPeter Wemm 	{
537940266059SGregory Neil Shapiro 		sm_dprintf("mailfile %s\n  ctladdr=", filename);
5380e92d3f3fSGregory Neil Shapiro 		printaddr(sm_debug_file(), ctladdr, false);
5381c2aa98e2SPeter Wemm 	}
5382c2aa98e2SPeter Wemm 
5383c2aa98e2SPeter Wemm 	if (mailer == NULL)
5384c2aa98e2SPeter Wemm 		mailer = FileMailer;
5385c2aa98e2SPeter Wemm 
5386c2aa98e2SPeter Wemm 	if (e->e_xfp != NULL)
538740266059SGregory Neil Shapiro 		(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
5388c2aa98e2SPeter Wemm 
5389c2aa98e2SPeter Wemm 	/*
5390c2aa98e2SPeter Wemm 	**  Special case /dev/null.  This allows us to restrict file
5391c2aa98e2SPeter Wemm 	**  delivery to regular files only.
5392c2aa98e2SPeter Wemm 	*/
5393c2aa98e2SPeter Wemm 
539440266059SGregory Neil Shapiro 	if (sm_path_isdevnull(filename))
5395c2aa98e2SPeter Wemm 		return EX_OK;
5396c2aa98e2SPeter Wemm 
5397c2aa98e2SPeter Wemm 	/* check for 8-bit available */
5398c2aa98e2SPeter Wemm 	if (bitset(EF_HAS8BIT, e->e_flags) &&
5399c2aa98e2SPeter Wemm 	    bitnset(M_7BITS, mailer->m_flags) &&
5400c2aa98e2SPeter Wemm 	    (bitset(EF_DONT_MIME, e->e_flags) ||
5401c2aa98e2SPeter Wemm 	     !(bitset(MM_MIME8BIT, MimeMode) ||
5402c2aa98e2SPeter Wemm 	       (bitset(EF_IS_MIME, e->e_flags) &&
5403c2aa98e2SPeter Wemm 		bitset(MM_CVTMIME, MimeMode)))))
5404c2aa98e2SPeter Wemm 	{
5405c2aa98e2SPeter Wemm 		e->e_status = "5.6.3";
540606f25ae9SGregory Neil Shapiro 		usrerrenh(e->e_status,
540706f25ae9SGregory Neil Shapiro 			  "554 Cannot send 8-bit data to 7-bit destination");
540840266059SGregory Neil Shapiro 		errno = 0;
540906f25ae9SGregory Neil Shapiro 		return EX_DATAERR;
541006f25ae9SGregory Neil Shapiro 	}
541106f25ae9SGregory Neil Shapiro 
541206f25ae9SGregory Neil Shapiro 	/* Find the actual file */
541306f25ae9SGregory Neil Shapiro 	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
541406f25ae9SGregory Neil Shapiro 	{
541506f25ae9SGregory Neil Shapiro 		len = strlen(SafeFileEnv);
541606f25ae9SGregory Neil Shapiro 
541706f25ae9SGregory Neil Shapiro 		if (strncmp(SafeFileEnv, filename, len) == 0)
541806f25ae9SGregory Neil Shapiro 			filename += len;
541906f25ae9SGregory Neil Shapiro 
5420d0cef73dSGregory Neil Shapiro 		if (len + strlen(filename) + 1 >= sizeof(targetfile))
542106f25ae9SGregory Neil Shapiro 		{
542206f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s/%s)",
542306f25ae9SGregory Neil Shapiro 			       SafeFileEnv, filename);
542406f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
542506f25ae9SGregory Neil Shapiro 		}
5426d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile));
542706f25ae9SGregory Neil Shapiro 		realfile = targetfile + len;
542806f25ae9SGregory Neil Shapiro 		if (*filename == '/')
542906f25ae9SGregory Neil Shapiro 			filename++;
5430605302a5SGregory Neil Shapiro 		if (*filename != '\0')
5431605302a5SGregory Neil Shapiro 		{
5432605302a5SGregory Neil Shapiro 			/* paranoia: trailing / should be removed in readcf */
5433605302a5SGregory Neil Shapiro 			if (targetfile[len - 1] != '/')
5434605302a5SGregory Neil Shapiro 				(void) sm_strlcat(targetfile,
5435d0cef73dSGregory Neil Shapiro 						  "/", sizeof(targetfile));
5436605302a5SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename,
5437d0cef73dSGregory Neil Shapiro 					  sizeof(targetfile));
5438605302a5SGregory Neil Shapiro 		}
543906f25ae9SGregory Neil Shapiro 	}
544006f25ae9SGregory Neil Shapiro 	else if (mailer->m_rootdir != NULL)
544106f25ae9SGregory Neil Shapiro 	{
5442d0cef73dSGregory Neil Shapiro 		expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e);
544306f25ae9SGregory Neil Shapiro 		len = strlen(targetfile);
544406f25ae9SGregory Neil Shapiro 
544506f25ae9SGregory Neil Shapiro 		if (strncmp(targetfile, filename, len) == 0)
544606f25ae9SGregory Neil Shapiro 			filename += len;
544706f25ae9SGregory Neil Shapiro 
5448d0cef73dSGregory Neil Shapiro 		if (len + strlen(filename) + 1 >= sizeof(targetfile))
544906f25ae9SGregory Neil Shapiro 		{
545006f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s/%s)",
545106f25ae9SGregory Neil Shapiro 			       targetfile, filename);
545206f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
545306f25ae9SGregory Neil Shapiro 		}
545406f25ae9SGregory Neil Shapiro 		realfile = targetfile + len;
545506f25ae9SGregory Neil Shapiro 		if (targetfile[len - 1] != '/')
5456d0cef73dSGregory Neil Shapiro 			(void) sm_strlcat(targetfile, "/", sizeof(targetfile));
545706f25ae9SGregory Neil Shapiro 		if (*filename == '/')
545840266059SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename + 1,
5459d0cef73dSGregory Neil Shapiro 					  sizeof(targetfile));
546006f25ae9SGregory Neil Shapiro 		else
546140266059SGregory Neil Shapiro 			(void) sm_strlcat(targetfile, filename,
5462d0cef73dSGregory Neil Shapiro 					  sizeof(targetfile));
546306f25ae9SGregory Neil Shapiro 	}
546406f25ae9SGregory Neil Shapiro 	else
546506f25ae9SGregory Neil Shapiro 	{
5466d0cef73dSGregory Neil Shapiro 		if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >=
5467d0cef73dSGregory Neil Shapiro 		    sizeof(targetfile))
546806f25ae9SGregory Neil Shapiro 		{
546906f25ae9SGregory Neil Shapiro 			syserr("mailfile: filename too long (%s)", filename);
547006f25ae9SGregory Neil Shapiro 			return EX_CANTCREAT;
547106f25ae9SGregory Neil Shapiro 		}
547206f25ae9SGregory Neil Shapiro 		realfile = targetfile;
5473c2aa98e2SPeter Wemm 	}
5474c2aa98e2SPeter Wemm 
5475c2aa98e2SPeter Wemm 	/*
5476c2aa98e2SPeter Wemm 	**  Fork so we can change permissions here.
5477c2aa98e2SPeter Wemm 	**	Note that we MUST use fork, not vfork, because of
5478c2aa98e2SPeter Wemm 	**	the complications of calling subroutines, etc.
5479c2aa98e2SPeter Wemm 	*/
5480c2aa98e2SPeter Wemm 
5481605302a5SGregory Neil Shapiro 
5482605302a5SGregory Neil Shapiro 	/*
5483605302a5SGregory Neil Shapiro 	**  Dispose of SIGCHLD signal catchers that may be laying
5484605302a5SGregory Neil Shapiro 	**  around so that the waitfor() below will get it.
5485605302a5SGregory Neil Shapiro 	*/
5486605302a5SGregory Neil Shapiro 
5487605302a5SGregory Neil Shapiro 	(void) sm_signal(SIGCHLD, SIG_DFL);
5488605302a5SGregory Neil Shapiro 
5489c2aa98e2SPeter Wemm 	DOFORK(fork);
5490c2aa98e2SPeter Wemm 
5491c2aa98e2SPeter Wemm 	if (pid < 0)
549206f25ae9SGregory Neil Shapiro 		return EX_OSERR;
5493c2aa98e2SPeter Wemm 	else if (pid == 0)
5494c2aa98e2SPeter Wemm 	{
5495c2aa98e2SPeter Wemm 		/* child -- actually write to file */
5496c2aa98e2SPeter Wemm 		struct stat stb;
5497c2aa98e2SPeter Wemm 		MCI mcibuf;
5498065a643dSPeter Wemm 		int err;
5499c2aa98e2SPeter Wemm 		volatile int oflags = O_WRONLY|O_APPEND;
5500c2aa98e2SPeter Wemm 
55018774250cSGregory Neil Shapiro 		/* Reset global flags */
55028774250cSGregory Neil Shapiro 		RestartRequest = NULL;
550340266059SGregory Neil Shapiro 		RestartWorkGroup = false;
55048774250cSGregory Neil Shapiro 		ShutdownRequest = NULL;
55058774250cSGregory Neil Shapiro 		PendingSignal = 0;
550640266059SGregory Neil Shapiro 		CurrentPid = getpid();
55078774250cSGregory Neil Shapiro 
5508c2aa98e2SPeter Wemm 		if (e->e_lockfp != NULL)
5509af9557fdSGregory Neil Shapiro 		{
5510af9557fdSGregory Neil Shapiro 			int fd;
5511af9557fdSGregory Neil Shapiro 
5512af9557fdSGregory Neil Shapiro 			fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
5513af9557fdSGregory Neil Shapiro 			/* SM_ASSERT(fd >= 0); */
5514af9557fdSGregory Neil Shapiro 			if (fd >= 0)
5515af9557fdSGregory Neil Shapiro 				(void) close(fd);
5516af9557fdSGregory Neil Shapiro 		}
5517c2aa98e2SPeter Wemm 
551840266059SGregory Neil Shapiro 		(void) sm_signal(SIGINT, SIG_DFL);
551940266059SGregory Neil Shapiro 		(void) sm_signal(SIGHUP, SIG_DFL);
552040266059SGregory Neil Shapiro 		(void) sm_signal(SIGTERM, SIG_DFL);
5521c2aa98e2SPeter Wemm 		(void) umask(OldUmask);
5522c2aa98e2SPeter Wemm 		e->e_to = filename;
5523c2aa98e2SPeter Wemm 		ExitStat = EX_OK;
5524c2aa98e2SPeter Wemm 
5525c2aa98e2SPeter Wemm 		if (setjmp(CtxMailfileTimeout) != 0)
5526c2aa98e2SPeter Wemm 		{
552740266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
5528c2aa98e2SPeter Wemm 		}
5529c2aa98e2SPeter Wemm 
5530c2aa98e2SPeter Wemm 		if (TimeOuts.to_fileopen > 0)
553140266059SGregory Neil Shapiro 			ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
553240266059SGregory Neil Shapiro 					 0);
5533c2aa98e2SPeter Wemm 		else
5534c2aa98e2SPeter Wemm 			ev = NULL;
5535c2aa98e2SPeter Wemm 
553640266059SGregory Neil Shapiro 		/* check file mode to see if set-user-ID */
553706f25ae9SGregory Neil Shapiro 		if (stat(targetfile, &stb) < 0)
5538c2aa98e2SPeter Wemm 			mode = FileMode;
553906f25ae9SGregory Neil Shapiro 		else
5540c2aa98e2SPeter Wemm 			mode = stb.st_mode;
5541c2aa98e2SPeter Wemm 
5542c2aa98e2SPeter Wemm 		/* limit the errors to those actually caused in the child */
5543c2aa98e2SPeter Wemm 		errno = 0;
5544c2aa98e2SPeter Wemm 		ExitStat = EX_OK;
5545c2aa98e2SPeter Wemm 
554606f25ae9SGregory Neil Shapiro 		/* Allow alias expansions to use the S_IS{U,G}ID bits */
554706f25ae9SGregory Neil Shapiro 		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||
554806f25ae9SGregory Neil Shapiro 		    bitset(SFF_RUNASREALUID, sfflags))
5549c2aa98e2SPeter Wemm 		{
555040266059SGregory Neil Shapiro 			/* ignore set-user-ID and set-group-ID bits */
5551c2aa98e2SPeter Wemm 			mode &= ~(S_ISGID|S_ISUID);
555206f25ae9SGregory Neil Shapiro 			if (tTd(11, 20))
555340266059SGregory Neil Shapiro 				sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
5554c2aa98e2SPeter Wemm 		}
5555c2aa98e2SPeter Wemm 
555640266059SGregory Neil Shapiro 		/* we have to open the data file BEFORE setuid() */
5557c2aa98e2SPeter Wemm 		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
5558c2aa98e2SPeter Wemm 		{
555940266059SGregory Neil Shapiro 			char *df = queuename(e, DATAFL_LETTER);
5560c2aa98e2SPeter Wemm 
556140266059SGregory Neil Shapiro 			e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
5562a7ec597cSGregory Neil Shapiro 					      SM_IO_RDONLY_B, NULL);
5563c2aa98e2SPeter Wemm 			if (e->e_dfp == NULL)
5564c2aa98e2SPeter Wemm 			{
5565c2aa98e2SPeter Wemm 				syserr("mailfile: Cannot open %s for %s from %s",
5566c2aa98e2SPeter Wemm 					df, e->e_to, e->e_from.q_paddr);
5567c2aa98e2SPeter Wemm 			}
5568c2aa98e2SPeter Wemm 		}
5569c2aa98e2SPeter Wemm 
5570c2aa98e2SPeter Wemm 		/* select a new user to run as */
5571c2aa98e2SPeter Wemm 		if (!bitset(SFF_RUNASREALUID, sfflags))
5572c2aa98e2SPeter Wemm 		{
5573c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
5574c2aa98e2SPeter Wemm 			{
5575c2aa98e2SPeter Wemm 				RealUserName = NULL;
5576e92d3f3fSGregory Neil Shapiro 				if (mailer->m_uid == NO_UID)
5577e92d3f3fSGregory Neil Shapiro 					RealUid = RunAsUid;
5578e92d3f3fSGregory Neil Shapiro 				else
5579c2aa98e2SPeter Wemm 					RealUid = mailer->m_uid;
558006f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 && RealUid != RunAsUid)
558106f25ae9SGregory Neil Shapiro 				{
558206f25ae9SGregory Neil Shapiro 					/* Only root can change the uid */
5583da7d7b9cSGregory Neil Shapiro 					syserr("mailfile: insufficient privileges to change uid, RunAsUid=%ld, RealUid=%ld",
5584da7d7b9cSGregory Neil Shapiro 						(long) RunAsUid, (long) RealUid);
558540266059SGregory Neil Shapiro 					RETURN(EX_TEMPFAIL);
558606f25ae9SGregory Neil Shapiro 				}
5587c2aa98e2SPeter Wemm 			}
5588c2aa98e2SPeter Wemm 			else if (bitset(S_ISUID, mode))
5589c2aa98e2SPeter Wemm 			{
5590c2aa98e2SPeter Wemm 				RealUserName = NULL;
5591c2aa98e2SPeter Wemm 				RealUid = stb.st_uid;
5592c2aa98e2SPeter Wemm 			}
5593c2aa98e2SPeter Wemm 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5594c2aa98e2SPeter Wemm 			{
5595c2aa98e2SPeter Wemm 				if (ctladdr->q_ruser != NULL)
5596c2aa98e2SPeter Wemm 					RealUserName = ctladdr->q_ruser;
5597c2aa98e2SPeter Wemm 				else
5598c2aa98e2SPeter Wemm 					RealUserName = ctladdr->q_user;
5599c2aa98e2SPeter Wemm 				RealUid = ctladdr->q_uid;
5600c2aa98e2SPeter Wemm 			}
5601e92d3f3fSGregory Neil Shapiro 			else if (mailer != NULL && mailer->m_uid != NO_UID)
5602c2aa98e2SPeter Wemm 			{
5603c2aa98e2SPeter Wemm 				RealUserName = DefUser;
5604c2aa98e2SPeter Wemm 				RealUid = mailer->m_uid;
5605c2aa98e2SPeter Wemm 			}
5606c2aa98e2SPeter Wemm 			else
5607c2aa98e2SPeter Wemm 			{
5608c2aa98e2SPeter Wemm 				RealUserName = DefUser;
5609c2aa98e2SPeter Wemm 				RealUid = DefUid;
5610c2aa98e2SPeter Wemm 			}
5611c2aa98e2SPeter Wemm 
5612c2aa98e2SPeter Wemm 			/* select a new group to run as */
5613c2aa98e2SPeter Wemm 			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
561406f25ae9SGregory Neil Shapiro 			{
5615e92d3f3fSGregory Neil Shapiro 				if (mailer->m_gid == NO_GID)
5616e92d3f3fSGregory Neil Shapiro 					RealGid = RunAsGid;
5617e92d3f3fSGregory Neil Shapiro 				else
5618c2aa98e2SPeter Wemm 					RealGid = mailer->m_gid;
561906f25ae9SGregory Neil Shapiro 				if (RunAsUid != 0 &&
562006f25ae9SGregory Neil Shapiro 				    (RealGid != getgid() ||
562106f25ae9SGregory Neil Shapiro 				     RealGid != getegid()))
562206f25ae9SGregory Neil Shapiro 				{
562306f25ae9SGregory Neil Shapiro 					/* Only root can change the gid */
5624da7d7b9cSGregory Neil Shapiro 					syserr("mailfile: insufficient privileges to change gid, RealGid=%ld, RunAsUid=%ld, gid=%ld, egid=%ld",
5625da7d7b9cSGregory Neil Shapiro 					       (long) RealGid, (long) RunAsUid,
5626da7d7b9cSGregory Neil Shapiro 					       (long) getgid(), (long) getegid());
562740266059SGregory Neil Shapiro 					RETURN(EX_TEMPFAIL);
562806f25ae9SGregory Neil Shapiro 				}
562906f25ae9SGregory Neil Shapiro 			}
5630c2aa98e2SPeter Wemm 			else if (bitset(S_ISGID, mode))
5631c2aa98e2SPeter Wemm 				RealGid = stb.st_gid;
563206f25ae9SGregory Neil Shapiro 			else if (ctladdr != NULL &&
563306f25ae9SGregory Neil Shapiro 				 ctladdr->q_uid == DefUid &&
563406f25ae9SGregory Neil Shapiro 				 ctladdr->q_gid == 0)
5635193538b7SGregory Neil Shapiro 			{
5636193538b7SGregory Neil Shapiro 				/*
5637193538b7SGregory Neil Shapiro 				**  Special case:  This means it is an
5638193538b7SGregory Neil Shapiro 				**  alias and we should act as DefaultUser.
5639193538b7SGregory Neil Shapiro 				**  See alias()'s comments.
5640193538b7SGregory Neil Shapiro 				*/
5641193538b7SGregory Neil Shapiro 
564206f25ae9SGregory Neil Shapiro 				RealGid = DefGid;
5643193538b7SGregory Neil Shapiro 				RealUserName = DefUser;
5644193538b7SGregory Neil Shapiro 			}
5645193538b7SGregory Neil Shapiro 			else if (ctladdr != NULL && ctladdr->q_uid != 0)
5646193538b7SGregory Neil Shapiro 				RealGid = ctladdr->q_gid;
5647e92d3f3fSGregory Neil Shapiro 			else if (mailer != NULL && mailer->m_gid != NO_GID)
5648c2aa98e2SPeter Wemm 				RealGid = mailer->m_gid;
5649c2aa98e2SPeter Wemm 			else
5650c2aa98e2SPeter Wemm 				RealGid = DefGid;
5651c2aa98e2SPeter Wemm 		}
5652c2aa98e2SPeter Wemm 
5653c2aa98e2SPeter Wemm 		/* last ditch */
5654c2aa98e2SPeter Wemm 		if (!bitset(SFF_ROOTOK, sfflags))
5655c2aa98e2SPeter Wemm 		{
5656c2aa98e2SPeter Wemm 			if (RealUid == 0)
5657c2aa98e2SPeter Wemm 				RealUid = DefUid;
5658c2aa98e2SPeter Wemm 			if (RealGid == 0)
5659c2aa98e2SPeter Wemm 				RealGid = DefGid;
5660c2aa98e2SPeter Wemm 		}
5661c2aa98e2SPeter Wemm 
5662c2aa98e2SPeter Wemm 		/* set group id list (needs /etc/group access) */
5663c2aa98e2SPeter Wemm 		if (RealUserName != NULL && !DontInitGroups)
5664c2aa98e2SPeter Wemm 		{
5665c2aa98e2SPeter Wemm 			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
566606f25ae9SGregory Neil Shapiro 			{
5667da7d7b9cSGregory Neil Shapiro 				syserr("mailfile: initgroups(%s, %ld) failed",
5668da7d7b9cSGregory Neil Shapiro 					RealUserName, (long) RealGid);
566940266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
567006f25ae9SGregory Neil Shapiro 			}
5671c2aa98e2SPeter Wemm 		}
5672c2aa98e2SPeter Wemm 		else
5673c2aa98e2SPeter Wemm 		{
5674c2aa98e2SPeter Wemm 			GIDSET_T gidset[1];
5675c2aa98e2SPeter Wemm 
5676c2aa98e2SPeter Wemm 			gidset[0] = RealGid;
5677c2aa98e2SPeter Wemm 			if (setgroups(1, gidset) == -1 && suidwarn)
567806f25ae9SGregory Neil Shapiro 			{
5679c2aa98e2SPeter Wemm 				syserr("mailfile: setgroups() failed");
568040266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
568106f25ae9SGregory Neil Shapiro 			}
5682c2aa98e2SPeter Wemm 		}
5683c2aa98e2SPeter Wemm 
568406f25ae9SGregory Neil Shapiro 		/*
568506f25ae9SGregory Neil Shapiro 		**  If you have a safe environment, go into it.
568606f25ae9SGregory Neil Shapiro 		*/
5687c2aa98e2SPeter Wemm 
568806f25ae9SGregory Neil Shapiro 		if (realfile != targetfile)
568906f25ae9SGregory Neil Shapiro 		{
5690605302a5SGregory Neil Shapiro 			char save;
5691605302a5SGregory Neil Shapiro 
5692605302a5SGregory Neil Shapiro 			save = *realfile;
569306f25ae9SGregory Neil Shapiro 			*realfile = '\0';
569406f25ae9SGregory Neil Shapiro 			if (tTd(11, 20))
569540266059SGregory Neil Shapiro 				sm_dprintf("mailfile: chroot %s\n", targetfile);
569606f25ae9SGregory Neil Shapiro 			if (chroot(targetfile) < 0)
5697c2aa98e2SPeter Wemm 			{
5698c2aa98e2SPeter Wemm 				syserr("mailfile: Cannot chroot(%s)",
569906f25ae9SGregory Neil Shapiro 				       targetfile);
570040266059SGregory Neil Shapiro 				RETURN(EX_CANTCREAT);
5701c2aa98e2SPeter Wemm 			}
5702605302a5SGregory Neil Shapiro 			*realfile = save;
5703c2aa98e2SPeter Wemm 		}
570406f25ae9SGregory Neil Shapiro 
570506f25ae9SGregory Neil Shapiro 		if (tTd(11, 40))
570640266059SGregory Neil Shapiro 			sm_dprintf("mailfile: deliver to %s\n", realfile);
570706f25ae9SGregory Neil Shapiro 
5708c2aa98e2SPeter Wemm 		if (chdir("/") < 0)
570906f25ae9SGregory Neil Shapiro 		{
5710c2aa98e2SPeter Wemm 			syserr("mailfile: cannot chdir(/)");
571140266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
571206f25ae9SGregory Neil Shapiro 		}
5713c2aa98e2SPeter Wemm 
5714c2aa98e2SPeter Wemm 		/* now reset the group and user ids */
5715c2aa98e2SPeter Wemm 		endpwent();
571640266059SGregory Neil Shapiro 		sm_mbdb_terminate();
5717c2aa98e2SPeter Wemm 		if (setgid(RealGid) < 0 && suidwarn)
571806f25ae9SGregory Neil Shapiro 		{
5719c2aa98e2SPeter Wemm 			syserr("mailfile: setgid(%ld) failed", (long) RealGid);
572040266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
572106f25ae9SGregory Neil Shapiro 		}
5722c2aa98e2SPeter Wemm 		vendor_set_uid(RealUid);
5723c2aa98e2SPeter Wemm 		if (setuid(RealUid) < 0 && suidwarn)
572406f25ae9SGregory Neil Shapiro 		{
5725c2aa98e2SPeter Wemm 			syserr("mailfile: setuid(%ld) failed", (long) RealUid);
572640266059SGregory Neil Shapiro 			RETURN(EX_TEMPFAIL);
572706f25ae9SGregory Neil Shapiro 		}
572806f25ae9SGregory Neil Shapiro 
572906f25ae9SGregory Neil Shapiro 		if (tTd(11, 2))
5730da7d7b9cSGregory Neil Shapiro 			sm_dprintf("mailfile: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n",
5731da7d7b9cSGregory Neil Shapiro 				(long) getuid(), (long) geteuid(),
5732da7d7b9cSGregory Neil Shapiro 				(long) getgid(), (long) getegid());
573306f25ae9SGregory Neil Shapiro 
5734c2aa98e2SPeter Wemm 
5735c2aa98e2SPeter Wemm 		/* move into some "safe" directory */
5736c2aa98e2SPeter Wemm 		if (mailer->m_execdir != NULL)
5737c2aa98e2SPeter Wemm 		{
5738c2aa98e2SPeter Wemm 			char *q;
5739c2aa98e2SPeter Wemm 
5740c2aa98e2SPeter Wemm 			for (p = mailer->m_execdir; p != NULL; p = q)
5741c2aa98e2SPeter Wemm 			{
5742c2aa98e2SPeter Wemm 				q = strchr(p, ':');
5743c2aa98e2SPeter Wemm 				if (q != NULL)
5744c2aa98e2SPeter Wemm 					*q = '\0';
5745d0cef73dSGregory Neil Shapiro 				expand(p, buf, sizeof(buf), e);
5746c2aa98e2SPeter Wemm 				if (q != NULL)
5747c2aa98e2SPeter Wemm 					*q++ = ':';
5748c2aa98e2SPeter Wemm 				if (tTd(11, 20))
574940266059SGregory Neil Shapiro 					sm_dprintf("mailfile: trydir %s\n",
575040266059SGregory Neil Shapiro 						   buf);
5751c2aa98e2SPeter Wemm 				if (buf[0] != '\0' && chdir(buf) >= 0)
5752c2aa98e2SPeter Wemm 					break;
5753c2aa98e2SPeter Wemm 			}
5754c2aa98e2SPeter Wemm 		}
5755c2aa98e2SPeter Wemm 
575606f25ae9SGregory Neil Shapiro 		/*
575706f25ae9SGregory Neil Shapiro 		**  Recheck the file after we have assumed the ID of the
575806f25ae9SGregory Neil Shapiro 		**  delivery user to make sure we can deliver to it as
575906f25ae9SGregory Neil Shapiro 		**  that user.  This is necessary if sendmail is running
576006f25ae9SGregory Neil Shapiro 		**  as root and the file is on an NFS mount which treats
576106f25ae9SGregory Neil Shapiro 		**  root as nobody.
576206f25ae9SGregory Neil Shapiro 		*/
576306f25ae9SGregory Neil Shapiro 
576406f25ae9SGregory Neil Shapiro #if HASLSTAT
576506f25ae9SGregory Neil Shapiro 		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
576606f25ae9SGregory Neil Shapiro 			err = stat(realfile, &stb);
576706f25ae9SGregory Neil Shapiro 		else
576806f25ae9SGregory Neil Shapiro 			err = lstat(realfile, &stb);
576906f25ae9SGregory Neil Shapiro #else /* HASLSTAT */
577006f25ae9SGregory Neil Shapiro 		err = stat(realfile, &stb);
577106f25ae9SGregory Neil Shapiro #endif /* HASLSTAT */
577206f25ae9SGregory Neil Shapiro 
577306f25ae9SGregory Neil Shapiro 		if (err < 0)
577406f25ae9SGregory Neil Shapiro 		{
577506f25ae9SGregory Neil Shapiro 			stb.st_mode = ST_MODE_NOFILE;
577606f25ae9SGregory Neil Shapiro 			mode = FileMode;
577706f25ae9SGregory Neil Shapiro 			oflags |= O_CREAT|O_EXCL;
577806f25ae9SGregory Neil Shapiro 		}
577906f25ae9SGregory Neil Shapiro 		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||
578006f25ae9SGregory Neil Shapiro 			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,
578106f25ae9SGregory Neil Shapiro 				   DontBlameSendmail) &&
578206f25ae9SGregory Neil Shapiro 			  stb.st_nlink != 1) ||
578306f25ae9SGregory Neil Shapiro 			 (realfile != targetfile && !S_ISREG(mode)))
578406f25ae9SGregory Neil Shapiro 			exit(EX_CANTCREAT);
578506f25ae9SGregory Neil Shapiro 		else
578606f25ae9SGregory Neil Shapiro 			mode = stb.st_mode;
578706f25ae9SGregory Neil Shapiro 
578806f25ae9SGregory Neil Shapiro 		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
5789c2aa98e2SPeter Wemm 			sfflags |= SFF_NOSLINK;
579006f25ae9SGregory Neil Shapiro 		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
5791c2aa98e2SPeter Wemm 			sfflags |= SFF_NOHLINK;
5792c2aa98e2SPeter Wemm 		sfflags &= ~SFF_OPENASROOT;
579306f25ae9SGregory Neil Shapiro 		f = safefopen(realfile, oflags, mode, sfflags);
5794c2aa98e2SPeter Wemm 		if (f == NULL)
5795c2aa98e2SPeter Wemm 		{
579606f25ae9SGregory Neil Shapiro 			if (transienterror(errno))
579706f25ae9SGregory Neil Shapiro 			{
579806f25ae9SGregory Neil Shapiro 				usrerr("454 4.3.0 cannot open %s: %s",
579906f25ae9SGregory Neil Shapiro 				       shortenstring(realfile, MAXSHORTSTR),
580040266059SGregory Neil Shapiro 				       sm_errstring(errno));
580140266059SGregory Neil Shapiro 				RETURN(EX_TEMPFAIL);
580206f25ae9SGregory Neil Shapiro 			}
580306f25ae9SGregory Neil Shapiro 			else
580406f25ae9SGregory Neil Shapiro 			{
580506f25ae9SGregory Neil Shapiro 				usrerr("554 5.3.0 cannot open %s: %s",
580606f25ae9SGregory Neil Shapiro 				       shortenstring(realfile, MAXSHORTSTR),
580740266059SGregory Neil Shapiro 				       sm_errstring(errno));
580840266059SGregory Neil Shapiro 				RETURN(EX_CANTCREAT);
5809c2aa98e2SPeter Wemm 			}
581006f25ae9SGregory Neil Shapiro 		}
581140266059SGregory Neil Shapiro 		if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
581240266059SGregory Neil Shapiro 		    &stb))
5813c2aa98e2SPeter Wemm 		{
581406f25ae9SGregory Neil Shapiro 			syserr("554 5.3.0 file changed after open");
581540266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
5816c2aa98e2SPeter Wemm 		}
581740266059SGregory Neil Shapiro 		if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
5818c2aa98e2SPeter Wemm 		{
581940266059SGregory Neil Shapiro 			syserr("554 5.3.0 cannot fstat %s",
582040266059SGregory Neil Shapiro 				sm_errstring(errno));
582140266059SGregory Neil Shapiro 			RETURN(EX_CANTCREAT);
5822c2aa98e2SPeter Wemm 		}
5823c2aa98e2SPeter Wemm 
582406f25ae9SGregory Neil Shapiro 		curoff = stb.st_size;
582506f25ae9SGregory Neil Shapiro 
5826c2aa98e2SPeter Wemm 		if (ev != NULL)
582740266059SGregory Neil Shapiro 			sm_clrevent(ev);
5828c2aa98e2SPeter Wemm 
5829d0cef73dSGregory Neil Shapiro 		memset(&mcibuf, '\0', sizeof(mcibuf));
5830c2aa98e2SPeter Wemm 		mcibuf.mci_mailer = mailer;
5831c2aa98e2SPeter Wemm 		mcibuf.mci_out = f;
5832c2aa98e2SPeter Wemm 		if (bitnset(M_7BITS, mailer->m_flags))
5833c2aa98e2SPeter Wemm 			mcibuf.mci_flags |= MCIF_7BIT;
5834c2aa98e2SPeter Wemm 
5835c2aa98e2SPeter Wemm 		/* clear out per-message flags from connection structure */
5836c2aa98e2SPeter Wemm 		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
5837c2aa98e2SPeter Wemm 
5838c2aa98e2SPeter Wemm 		if (bitset(EF_HAS8BIT, e->e_flags) &&
5839c2aa98e2SPeter Wemm 		    !bitset(EF_DONT_MIME, e->e_flags) &&
5840c2aa98e2SPeter Wemm 		    bitnset(M_7BITS, mailer->m_flags))
5841c2aa98e2SPeter Wemm 			mcibuf.mci_flags |= MCIF_CVT8TO7;
5842c2aa98e2SPeter Wemm 
5843c2aa98e2SPeter Wemm #if MIME7TO8
5844c2aa98e2SPeter Wemm 		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
5845c2aa98e2SPeter Wemm 		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
5846c2aa98e2SPeter Wemm 		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
584740266059SGregory Neil Shapiro 		    (sm_strcasecmp(p, "quoted-printable") == 0 ||
584840266059SGregory Neil Shapiro 		     sm_strcasecmp(p, "base64") == 0) &&
5849c2aa98e2SPeter Wemm 		    (p = hvalue("Content-Type", e->e_header)) != NULL)
5850c2aa98e2SPeter Wemm 		{
5851c2aa98e2SPeter Wemm 			/* may want to convert 7 -> 8 */
5852c2aa98e2SPeter Wemm 			/* XXX should really parse it here -- and use a class XXX */
585340266059SGregory Neil Shapiro 			if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
5854c2aa98e2SPeter Wemm 			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
5855c2aa98e2SPeter Wemm 				mcibuf.mci_flags |= MCIF_CVT7TO8;
5856c2aa98e2SPeter Wemm 		}
585706f25ae9SGregory Neil Shapiro #endif /* MIME7TO8 */
5858c2aa98e2SPeter Wemm 
58594e4196cbSGregory Neil Shapiro 		if (!putfromline(&mcibuf, e) ||
58604e4196cbSGregory Neil Shapiro 		    !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
58614e4196cbSGregory Neil Shapiro 		    !(*e->e_putbody)(&mcibuf, e, NULL) ||
58624e4196cbSGregory Neil Shapiro 		    !putline("\n", &mcibuf) ||
58634e4196cbSGregory Neil Shapiro 		    (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
586440266059SGregory Neil Shapiro 		    (SuperSafe != SAFE_NO &&
586540266059SGregory Neil Shapiro 		     fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
58664e4196cbSGregory Neil Shapiro 		    sm_io_error(f)))
5867c2aa98e2SPeter Wemm 		{
5868c2aa98e2SPeter Wemm 			setstat(EX_IOERR);
586906f25ae9SGregory Neil Shapiro #if !NOFTRUNCATE
587040266059SGregory Neil Shapiro 			(void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
587140266059SGregory Neil Shapiro 					 curoff);
5872*5b0945b5SGregory Neil Shapiro #endif
5873c2aa98e2SPeter Wemm 		}
5874c2aa98e2SPeter Wemm 
5875c2aa98e2SPeter Wemm 		/* reset ISUID & ISGID bits for paranoid systems */
5876c2aa98e2SPeter Wemm #if HASFCHMOD
587740266059SGregory Neil Shapiro 		(void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
587840266059SGregory Neil Shapiro 			      (MODE_T) mode);
587906f25ae9SGregory Neil Shapiro #else /* HASFCHMOD */
588006f25ae9SGregory Neil Shapiro 		(void) chmod(filename, (MODE_T) mode);
588106f25ae9SGregory Neil Shapiro #endif /* HASFCHMOD */
588240266059SGregory Neil Shapiro 		if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
588306f25ae9SGregory Neil Shapiro 			setstat(EX_IOERR);
588440266059SGregory Neil Shapiro 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
588506f25ae9SGregory Neil Shapiro 		(void) setuid(RealUid);
5886c2aa98e2SPeter Wemm 		exit(ExitStat);
5887c2aa98e2SPeter Wemm 		/* NOTREACHED */
5888c2aa98e2SPeter Wemm 	}
5889c2aa98e2SPeter Wemm 	else
5890c2aa98e2SPeter Wemm 	{
5891c2aa98e2SPeter Wemm 		/* parent -- wait for exit status */
5892c2aa98e2SPeter Wemm 		int st;
5893c2aa98e2SPeter Wemm 
5894c2aa98e2SPeter Wemm 		st = waitfor(pid);
5895c2aa98e2SPeter Wemm 		if (st == -1)
5896c2aa98e2SPeter Wemm 		{
5897c2aa98e2SPeter Wemm 			syserr("mailfile: %s: wait", mailer->m_name);
589806f25ae9SGregory Neil Shapiro 			return EX_SOFTWARE;
5899c2aa98e2SPeter Wemm 		}
5900c2aa98e2SPeter Wemm 		if (WIFEXITED(st))
590140266059SGregory Neil Shapiro 		{
590240266059SGregory Neil Shapiro 			errno = 0;
5903c2aa98e2SPeter Wemm 			return (WEXITSTATUS(st));
590440266059SGregory Neil Shapiro 		}
5905c2aa98e2SPeter Wemm 		else
5906c2aa98e2SPeter Wemm 		{
5907c2aa98e2SPeter Wemm 			syserr("mailfile: %s: child died on signal %d",
5908c2aa98e2SPeter Wemm 			       mailer->m_name, st);
590906f25ae9SGregory Neil Shapiro 			return EX_UNAVAILABLE;
5910c2aa98e2SPeter Wemm 		}
5911c2aa98e2SPeter Wemm 		/* NOTREACHED */
5912c2aa98e2SPeter Wemm 	}
5913c2aa98e2SPeter Wemm 	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */
5914c2aa98e2SPeter Wemm }
5915c2aa98e2SPeter Wemm 
5916c2aa98e2SPeter Wemm static void
5917b6bacd31SGregory Neil Shapiro mailfiletimeout(ignore)
5918b6bacd31SGregory Neil Shapiro 	int ignore;
5919c2aa98e2SPeter Wemm {
59208774250cSGregory Neil Shapiro 	/*
59218774250cSGregory Neil Shapiro 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
59228774250cSGregory Neil Shapiro 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
59238774250cSGregory Neil Shapiro 	**	DOING.
59248774250cSGregory Neil Shapiro 	*/
59258774250cSGregory Neil Shapiro 
59268774250cSGregory Neil Shapiro 	errno = ETIMEDOUT;
5927c2aa98e2SPeter Wemm 	longjmp(CtxMailfileTimeout, 1);
5928c2aa98e2SPeter Wemm }
5929*5b0945b5SGregory Neil Shapiro 
5930*5b0945b5SGregory Neil Shapiro #if DANE
5931*5b0945b5SGregory Neil Shapiro 
5932*5b0945b5SGregory Neil Shapiro /*
5933*5b0945b5SGregory Neil Shapiro **  GETMPORT -- return the port of a mailer
5934*5b0945b5SGregory Neil Shapiro **
5935*5b0945b5SGregory Neil Shapiro **	Parameters:
5936*5b0945b5SGregory Neil Shapiro **		m -- the mailer describing this host.
5937*5b0945b5SGregory Neil Shapiro **
5938*5b0945b5SGregory Neil Shapiro **	Returns:
5939*5b0945b5SGregory Neil Shapiro **		the port of the mailer if defined.
5940*5b0945b5SGregory Neil Shapiro **		0 otherwise
5941*5b0945b5SGregory Neil Shapiro **		<0 error
5942*5b0945b5SGregory Neil Shapiro */
5943*5b0945b5SGregory Neil Shapiro 
5944*5b0945b5SGregory Neil Shapiro static int getmport __P((MAILER *));
5945*5b0945b5SGregory Neil Shapiro 
5946*5b0945b5SGregory Neil Shapiro static int
5947*5b0945b5SGregory Neil Shapiro getmport(m)
5948*5b0945b5SGregory Neil Shapiro 	MAILER *m;
5949*5b0945b5SGregory Neil Shapiro {
5950*5b0945b5SGregory Neil Shapiro 	unsigned long ulval;
5951*5b0945b5SGregory Neil Shapiro 	char *buf, *ep;
5952*5b0945b5SGregory Neil Shapiro 
5953*5b0945b5SGregory Neil Shapiro 	if (m->m_port > 0)
5954*5b0945b5SGregory Neil Shapiro 		return m->m_port;
5955*5b0945b5SGregory Neil Shapiro 
5956*5b0945b5SGregory Neil Shapiro 	if (NULL == m->m_argv[0] ||NULL == m->m_argv[1])
5957*5b0945b5SGregory Neil Shapiro 		return -1;
5958*5b0945b5SGregory Neil Shapiro 	buf = m->m_argv[2];
5959*5b0945b5SGregory Neil Shapiro 	if (NULL == buf)
5960*5b0945b5SGregory Neil Shapiro 		return 0;
5961*5b0945b5SGregory Neil Shapiro 
5962*5b0945b5SGregory Neil Shapiro 	errno = 0;
5963*5b0945b5SGregory Neil Shapiro 	ulval = strtoul(buf, &ep, 0);
5964*5b0945b5SGregory Neil Shapiro 	if (buf[0] == '\0' || *ep != '\0')
5965*5b0945b5SGregory Neil Shapiro 		return -1;
5966*5b0945b5SGregory Neil Shapiro 	if (errno == ERANGE && ulval == ULONG_MAX)
5967*5b0945b5SGregory Neil Shapiro 		return -1;
5968*5b0945b5SGregory Neil Shapiro 	if (ulval > USHRT_MAX)
5969*5b0945b5SGregory Neil Shapiro 		return -1;
5970*5b0945b5SGregory Neil Shapiro 	m->m_port = (unsigned short) ulval;
5971*5b0945b5SGregory Neil Shapiro 	if (tTd(17, 30))
5972*5b0945b5SGregory Neil Shapiro 		sm_dprintf("getmport: mailer=%s, port=%d\n", m->m_name,
5973*5b0945b5SGregory Neil Shapiro 			m->m_port);
5974*5b0945b5SGregory Neil Shapiro 	return m->m_port;
5975*5b0945b5SGregory Neil Shapiro }
5976*5b0945b5SGregory Neil Shapiro # define GETMPORT(m) getmport(m)
5977*5b0945b5SGregory Neil Shapiro #else /* DANE */
5978*5b0945b5SGregory Neil Shapiro # define GETMPORT(m)	25
5979*5b0945b5SGregory Neil Shapiro #endif /* DANE */
5980*5b0945b5SGregory Neil Shapiro 
598140266059SGregory Neil Shapiro /*
5982c2aa98e2SPeter Wemm **  HOSTSIGNATURE -- return the "signature" for a host.
5983c2aa98e2SPeter Wemm **
5984c2aa98e2SPeter Wemm **	The signature describes how we are going to send this -- it
5985c2aa98e2SPeter Wemm **	can be just the hostname (for non-Internet hosts) or can be
5986c2aa98e2SPeter Wemm **	an ordered list of MX hosts.
5987c2aa98e2SPeter Wemm **
5988c2aa98e2SPeter Wemm **	Parameters:
5989c2aa98e2SPeter Wemm **		m -- the mailer describing this host.
5990c2aa98e2SPeter Wemm **		host -- the host name.
5991*5b0945b5SGregory Neil Shapiro **		ad -- DNSSEC: ad
5992c2aa98e2SPeter Wemm **
5993c2aa98e2SPeter Wemm **	Returns:
5994c2aa98e2SPeter Wemm **		The signature for this host.
5995c2aa98e2SPeter Wemm **
5996c2aa98e2SPeter Wemm **	Side Effects:
5997c2aa98e2SPeter Wemm **		Can tweak the symbol table.
5998c2aa98e2SPeter Wemm */
599940266059SGregory Neil Shapiro 
600006f25ae9SGregory Neil Shapiro #define MAXHOSTSIGNATURE	8192	/* max len of hostsignature */
6001c2aa98e2SPeter Wemm 
600240266059SGregory Neil Shapiro char *
6003*5b0945b5SGregory Neil Shapiro hostsignature(m, host, ad)
6004c2aa98e2SPeter Wemm 	register MAILER *m;
6005c2aa98e2SPeter Wemm 	char *host;
6006*5b0945b5SGregory Neil Shapiro 	bool ad;
6007c2aa98e2SPeter Wemm {
6008c2aa98e2SPeter Wemm 	register char *p;
6009c2aa98e2SPeter Wemm 	register STAB *s;
601040266059SGregory Neil Shapiro 	time_t now;
601106f25ae9SGregory Neil Shapiro #if NAMED_BIND
601206f25ae9SGregory Neil Shapiro 	char sep = ':';
601306f25ae9SGregory Neil Shapiro 	char prevsep = ':';
6014c2aa98e2SPeter Wemm 	int i;
6015c2aa98e2SPeter Wemm 	int len;
6016c2aa98e2SPeter Wemm 	int nmx;
601706f25ae9SGregory Neil Shapiro 	int hl;
6018c2aa98e2SPeter Wemm 	char *hp;
6019c2aa98e2SPeter Wemm 	char *endp;
6020c2aa98e2SPeter Wemm 	int oldoptions = _res.options;
6021c2aa98e2SPeter Wemm 	char *mxhosts[MAXMXHOSTS + 1];
602240266059SGregory Neil Shapiro 	unsigned short mxprefs[MAXMXHOSTS + 1];
602306f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
602406f25ae9SGregory Neil Shapiro 
602506f25ae9SGregory Neil Shapiro 	if (tTd(17, 3))
6026*5b0945b5SGregory Neil Shapiro 		sm_dprintf("hostsignature(%s), ad=%d\n", host, ad);
602706f25ae9SGregory Neil Shapiro 
602806f25ae9SGregory Neil Shapiro 	/*
60298774250cSGregory Neil Shapiro 	**  If local delivery (and not remote), just return a constant.
603006f25ae9SGregory Neil Shapiro 	*/
603106f25ae9SGregory Neil Shapiro 
60328774250cSGregory Neil Shapiro 	if (bitnset(M_LOCALMAILER, m->m_flags) &&
603340266059SGregory Neil Shapiro 	    strcmp(m->m_mailer, "[IPC]") != 0 &&
603440266059SGregory Neil Shapiro 	    !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
603506f25ae9SGregory Neil Shapiro 		return "localhost";
6036c2aa98e2SPeter Wemm 
603713d88268SGregory Neil Shapiro 	/* an empty host does not have MX records */
603813d88268SGregory Neil Shapiro 	if (*host == '\0')
603913d88268SGregory Neil Shapiro 		return "_empty_";
604013d88268SGregory Neil Shapiro 
6041c2aa98e2SPeter Wemm 	/*
6042c2aa98e2SPeter Wemm 	**  Check to see if this uses IPC -- if not, it can't have MX records.
6043c2aa98e2SPeter Wemm 	*/
6044c2aa98e2SPeter Wemm 
604540266059SGregory Neil Shapiro 	if (strcmp(m->m_mailer, "[IPC]") != 0 ||
604640266059SGregory Neil Shapiro 	    CurEnv->e_sendmode == SM_DEFER)
6047c2aa98e2SPeter Wemm 	{
604840266059SGregory Neil Shapiro 		/* just an ordinary mailer or deferred mode */
6049c2aa98e2SPeter Wemm 		return host;
6050c2aa98e2SPeter Wemm 	}
605106f25ae9SGregory Neil Shapiro #if NETUNIX
605206f25ae9SGregory Neil Shapiro 	else if (m->m_argv[0] != NULL &&
605306f25ae9SGregory Neil Shapiro 		 strcmp(m->m_argv[0], "FILE") == 0)
605406f25ae9SGregory Neil Shapiro 	{
605506f25ae9SGregory Neil Shapiro 		/* rendezvous in the file system, no MX records */
605606f25ae9SGregory Neil Shapiro 		return host;
605706f25ae9SGregory Neil Shapiro 	}
605806f25ae9SGregory Neil Shapiro #endif /* NETUNIX */
6059c2aa98e2SPeter Wemm 
6060c2aa98e2SPeter Wemm 	/*
6061c2aa98e2SPeter Wemm 	**  Look it up in the symbol table.
6062c2aa98e2SPeter Wemm 	*/
6063c2aa98e2SPeter Wemm 
606440266059SGregory Neil Shapiro 	now = curtime();
6065c2aa98e2SPeter Wemm 	s = stab(host, ST_HOSTSIG, ST_ENTER);
606640266059SGregory Neil Shapiro 	if (s->s_hostsig.hs_sig != NULL)
606740266059SGregory Neil Shapiro 	{
606840266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_exp >= now)
606906f25ae9SGregory Neil Shapiro 		{
607006f25ae9SGregory Neil Shapiro 			if (tTd(17, 3))
607140266059SGregory Neil Shapiro 				sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
607240266059SGregory Neil Shapiro 					   s->s_hostsig.hs_sig);
607340266059SGregory Neil Shapiro 			return s->s_hostsig.hs_sig;
607406f25ae9SGregory Neil Shapiro 		}
6075c2aa98e2SPeter Wemm 
607640266059SGregory Neil Shapiro 		/* signature is expired: clear it */
607740266059SGregory Neil Shapiro 		sm_free(s->s_hostsig.hs_sig);
607840266059SGregory Neil Shapiro 		s->s_hostsig.hs_sig = NULL;
607940266059SGregory Neil Shapiro 	}
608040266059SGregory Neil Shapiro 
608140266059SGregory Neil Shapiro 	/* set default TTL */
608240266059SGregory Neil Shapiro 	s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
608340266059SGregory Neil Shapiro 
6084c2aa98e2SPeter Wemm 	/*
608540266059SGregory Neil Shapiro 	**  Not already there or expired -- create a signature.
6086c2aa98e2SPeter Wemm 	*/
6087c2aa98e2SPeter Wemm 
6088c2aa98e2SPeter Wemm #if NAMED_BIND
6089c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
6090c2aa98e2SPeter Wemm 		_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
6091c2aa98e2SPeter Wemm 
6092c2aa98e2SPeter Wemm 	for (hp = host; hp != NULL; hp = endp)
6093c2aa98e2SPeter Wemm 	{
609406f25ae9SGregory Neil Shapiro # if NETINET6
609506f25ae9SGregory Neil Shapiro 		if (*hp == '[')
609606f25ae9SGregory Neil Shapiro 		{
609706f25ae9SGregory Neil Shapiro 			endp = strchr(hp + 1, ']');
6098c2aa98e2SPeter Wemm 			if (endp != NULL)
609906f25ae9SGregory Neil Shapiro 				endp = strpbrk(endp + 1, ":,");
610006f25ae9SGregory Neil Shapiro 		}
610106f25ae9SGregory Neil Shapiro 		else
610206f25ae9SGregory Neil Shapiro 			endp = strpbrk(hp, ":,");
610306f25ae9SGregory Neil Shapiro # else /* NETINET6 */
610406f25ae9SGregory Neil Shapiro 		endp = strpbrk(hp, ":,");
610506f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
610606f25ae9SGregory Neil Shapiro 		if (endp != NULL)
610706f25ae9SGregory Neil Shapiro 		{
610806f25ae9SGregory Neil Shapiro 			sep = *endp;
6109c2aa98e2SPeter Wemm 			*endp = '\0';
611006f25ae9SGregory Neil Shapiro 		}
6111c2aa98e2SPeter Wemm 
6112c2aa98e2SPeter Wemm 		if (bitnset(M_NOMX, m->m_flags))
6113c2aa98e2SPeter Wemm 		{
6114c2aa98e2SPeter Wemm 			/* skip MX lookups */
6115c2aa98e2SPeter Wemm 			nmx = 1;
6116c2aa98e2SPeter Wemm 			mxhosts[0] = hp;
6117c2aa98e2SPeter Wemm 		}
6118c2aa98e2SPeter Wemm 		else
6119c2aa98e2SPeter Wemm 		{
6120c2aa98e2SPeter Wemm 			auto int rcode;
612140266059SGregory Neil Shapiro 			int ttl;
6122c2aa98e2SPeter Wemm 
6123*5b0945b5SGregory Neil Shapiro 			GETMPORT(m);
6124*5b0945b5SGregory Neil Shapiro 			nmx = getmxrr(hp, mxhosts, mxprefs,
6125*5b0945b5SGregory Neil Shapiro 				      DROPLOCALHOST|TRYFALLBACK|(ad ? ISAD :0),
6126*5b0945b5SGregory Neil Shapiro 				      &rcode, &ttl, M_PORT(m));
6127c2aa98e2SPeter Wemm 			if (nmx <= 0)
6128c2aa98e2SPeter Wemm 			{
612913058a91SGregory Neil Shapiro 				int save_errno;
6130c2aa98e2SPeter Wemm 				register MCI *mci;
6131c2aa98e2SPeter Wemm 
6132c2aa98e2SPeter Wemm 				/* update the connection info for this host */
613313058a91SGregory Neil Shapiro 				save_errno = errno;
6134c2aa98e2SPeter Wemm 				mci = mci_get(hp, m);
613513058a91SGregory Neil Shapiro 				mci->mci_errno = save_errno;
6136c2aa98e2SPeter Wemm 				mci->mci_herrno = h_errno;
6137193538b7SGregory Neil Shapiro 				mci->mci_lastuse = now;
6138*5b0945b5SGregory Neil Shapiro 				if (nmx == NULLMX)
6139*5b0945b5SGregory Neil Shapiro 					mci_setstat(mci, rcode, "5.7.27",
6140*5b0945b5SGregory Neil Shapiro 						    "550 Host does not accept mail");
6141*5b0945b5SGregory Neil Shapiro 				else if (rcode == EX_NOHOST)
614206f25ae9SGregory Neil Shapiro 					mci_setstat(mci, rcode, "5.1.2",
614306f25ae9SGregory Neil Shapiro 						    "550 Host unknown");
614406f25ae9SGregory Neil Shapiro 				else
6145c2aa98e2SPeter Wemm 					mci_setstat(mci, rcode, NULL, NULL);
6146c2aa98e2SPeter Wemm 
6147c2aa98e2SPeter Wemm 				/* use the original host name as signature */
6148c2aa98e2SPeter Wemm 				nmx = 1;
6149c2aa98e2SPeter Wemm 				mxhosts[0] = hp;
6150c2aa98e2SPeter Wemm 			}
615106f25ae9SGregory Neil Shapiro 			if (tTd(17, 3))
615240266059SGregory Neil Shapiro 				sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
615306f25ae9SGregory Neil Shapiro 					   nmx, mxhosts[0]);
615440266059SGregory Neil Shapiro 
615540266059SGregory Neil Shapiro 			/*
615640266059SGregory Neil Shapiro 			**  Set new TTL: we use only one!
615740266059SGregory Neil Shapiro 			**	We could try to use the minimum instead.
615840266059SGregory Neil Shapiro 			*/
615940266059SGregory Neil Shapiro 
616040266059SGregory Neil Shapiro 			s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
6161c2aa98e2SPeter Wemm 		}
6162c2aa98e2SPeter Wemm 
6163c2aa98e2SPeter Wemm 		len = 0;
6164c2aa98e2SPeter Wemm 		for (i = 0; i < nmx; i++)
6165c2aa98e2SPeter Wemm 			len += strlen(mxhosts[i]) + 1;
616640266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_sig != NULL)
616740266059SGregory Neil Shapiro 			len += strlen(s->s_hostsig.hs_sig) + 1;
616840266059SGregory Neil Shapiro 		if (len < 0 || len >= MAXHOSTSIGNATURE)
616906f25ae9SGregory Neil Shapiro 		{
617006f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
617106f25ae9SGregory Neil Shapiro 				  host, MAXHOSTSIGNATURE, len);
617206f25ae9SGregory Neil Shapiro 			len = MAXHOSTSIGNATURE;
617306f25ae9SGregory Neil Shapiro 		}
617440266059SGregory Neil Shapiro 		p = sm_pmalloc_x(len);
617540266059SGregory Neil Shapiro 		if (s->s_hostsig.hs_sig != NULL)
6176c2aa98e2SPeter Wemm 		{
617740266059SGregory Neil Shapiro 			(void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
617840266059SGregory Neil Shapiro 			sm_free(s->s_hostsig.hs_sig); /* XXX */
617940266059SGregory Neil Shapiro 			s->s_hostsig.hs_sig = p;
618006f25ae9SGregory Neil Shapiro 			hl = strlen(p);
618106f25ae9SGregory Neil Shapiro 			p += hl;
618206f25ae9SGregory Neil Shapiro 			*p++ = prevsep;
618306f25ae9SGregory Neil Shapiro 			len -= hl + 1;
6184c2aa98e2SPeter Wemm 		}
6185c2aa98e2SPeter Wemm 		else
618640266059SGregory Neil Shapiro 			s->s_hostsig.hs_sig = p;
6187c2aa98e2SPeter Wemm 		for (i = 0; i < nmx; i++)
6188c2aa98e2SPeter Wemm 		{
618906f25ae9SGregory Neil Shapiro 			hl = strlen(mxhosts[i]);
619006f25ae9SGregory Neil Shapiro 			if (len - 1 < hl || len <= 1)
619106f25ae9SGregory Neil Shapiro 			{
619206f25ae9SGregory Neil Shapiro 				/* force to drop out of outer loop */
619306f25ae9SGregory Neil Shapiro 				len = -1;
619406f25ae9SGregory Neil Shapiro 				break;
6195c2aa98e2SPeter Wemm 			}
619606f25ae9SGregory Neil Shapiro 			if (i != 0)
619706f25ae9SGregory Neil Shapiro 			{
619806f25ae9SGregory Neil Shapiro 				if (mxprefs[i] == mxprefs[i - 1])
619906f25ae9SGregory Neil Shapiro 					*p++ = ',';
620006f25ae9SGregory Neil Shapiro 				else
620106f25ae9SGregory Neil Shapiro 					*p++ = ':';
620206f25ae9SGregory Neil Shapiro 				len--;
620306f25ae9SGregory Neil Shapiro 			}
620440266059SGregory Neil Shapiro 			(void) sm_strlcpy(p, mxhosts[i], len);
620506f25ae9SGregory Neil Shapiro 			p += hl;
620606f25ae9SGregory Neil Shapiro 			len -= hl;
620706f25ae9SGregory Neil Shapiro 		}
620806f25ae9SGregory Neil Shapiro 
620906f25ae9SGregory Neil Shapiro 		/*
621006f25ae9SGregory Neil Shapiro 		**  break out of loop if len exceeded MAXHOSTSIGNATURE
621106f25ae9SGregory Neil Shapiro 		**  because we won't have more space for further hosts
621206f25ae9SGregory Neil Shapiro 		**  anyway (separated by : in the .cf file).
621306f25ae9SGregory Neil Shapiro 		*/
621406f25ae9SGregory Neil Shapiro 
621506f25ae9SGregory Neil Shapiro 		if (len < 0)
621606f25ae9SGregory Neil Shapiro 			break;
6217c2aa98e2SPeter Wemm 		if (endp != NULL)
621806f25ae9SGregory Neil Shapiro 			*endp++ = sep;
621906f25ae9SGregory Neil Shapiro 		prevsep = sep;
6220c2aa98e2SPeter Wemm 	}
622140266059SGregory Neil Shapiro 	makelower(s->s_hostsig.hs_sig);
6222c2aa98e2SPeter Wemm 	if (ConfigLevel < 2)
6223c2aa98e2SPeter Wemm 		_res.options = oldoptions;
622406f25ae9SGregory Neil Shapiro #else /* NAMED_BIND */
6225c2aa98e2SPeter Wemm 	/* not using BIND -- the signature is just the host name */
622640266059SGregory Neil Shapiro 	/*
622740266059SGregory Neil Shapiro 	**  'host' points to storage that will be freed after we are
622840266059SGregory Neil Shapiro 	**  done processing the current envelope, so we copy it.
622940266059SGregory Neil Shapiro 	*/
623040266059SGregory Neil Shapiro 	s->s_hostsig.hs_sig = sm_pstrdup_x(host);
623106f25ae9SGregory Neil Shapiro #endif /* NAMED_BIND */
6232c2aa98e2SPeter Wemm 	if (tTd(17, 1))
623340266059SGregory Neil Shapiro 		sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
623440266059SGregory Neil Shapiro 	return s->s_hostsig.hs_sig;
6235c2aa98e2SPeter Wemm }
623640266059SGregory Neil Shapiro /*
623706f25ae9SGregory Neil Shapiro **  PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
623806f25ae9SGregory Neil Shapiro **
623906f25ae9SGregory Neil Shapiro **	The signature describes how we are going to send this -- it
624006f25ae9SGregory Neil Shapiro **	can be just the hostname (for non-Internet hosts) or can be
624106f25ae9SGregory Neil Shapiro **	an ordered list of MX hosts which must be randomized for equal
624206f25ae9SGregory Neil Shapiro **	MX preference values.
624306f25ae9SGregory Neil Shapiro **
624406f25ae9SGregory Neil Shapiro **	Parameters:
624506f25ae9SGregory Neil Shapiro **		sig -- the host signature.
624606f25ae9SGregory Neil Shapiro **		mxhosts -- array to populate.
624740266059SGregory Neil Shapiro **		mailer -- mailer.
624806f25ae9SGregory Neil Shapiro **
624906f25ae9SGregory Neil Shapiro **	Returns:
625006f25ae9SGregory Neil Shapiro **		The number of hosts inserted into mxhosts array.
625106f25ae9SGregory Neil Shapiro **
625206f25ae9SGregory Neil Shapiro **	Side Effects:
625306f25ae9SGregory Neil Shapiro **		Randomizes equal MX preference hosts in mxhosts.
625406f25ae9SGregory Neil Shapiro */
625506f25ae9SGregory Neil Shapiro 
625606f25ae9SGregory Neil Shapiro static int
625706f25ae9SGregory Neil Shapiro parse_hostsignature(sig, mxhosts, mailer)
625806f25ae9SGregory Neil Shapiro 	char *sig;
625906f25ae9SGregory Neil Shapiro 	char **mxhosts;
626006f25ae9SGregory Neil Shapiro 	MAILER *mailer;
626106f25ae9SGregory Neil Shapiro {
626240266059SGregory Neil Shapiro 	unsigned short curpref = 0;
626340266059SGregory Neil Shapiro 	int nmx = 0, i, j;	/* NOTE: i, j, and nmx must have same type */
626406f25ae9SGregory Neil Shapiro 	char *hp, *endp;
626540266059SGregory Neil Shapiro 	unsigned short prefer[MAXMXHOSTS];
626606f25ae9SGregory Neil Shapiro 	long rndm[MAXMXHOSTS];
626706f25ae9SGregory Neil Shapiro 
626806f25ae9SGregory Neil Shapiro 	for (hp = sig; hp != NULL; hp = endp)
626906f25ae9SGregory Neil Shapiro 	{
627006f25ae9SGregory Neil Shapiro 		char sep = ':';
627106f25ae9SGregory Neil Shapiro 
627206f25ae9SGregory Neil Shapiro #if NETINET6
627306f25ae9SGregory Neil Shapiro 		if (*hp == '[')
627406f25ae9SGregory Neil Shapiro 		{
627506f25ae9SGregory Neil Shapiro 			endp = strchr(hp + 1, ']');
627606f25ae9SGregory Neil Shapiro 			if (endp != NULL)
627706f25ae9SGregory Neil Shapiro 				endp = strpbrk(endp + 1, ":,");
627806f25ae9SGregory Neil Shapiro 		}
627906f25ae9SGregory Neil Shapiro 		else
628006f25ae9SGregory Neil Shapiro 			endp = strpbrk(hp, ":,");
628106f25ae9SGregory Neil Shapiro #else /* NETINET6 */
628206f25ae9SGregory Neil Shapiro 		endp = strpbrk(hp, ":,");
628306f25ae9SGregory Neil Shapiro #endif /* NETINET6 */
628406f25ae9SGregory Neil Shapiro 		if (endp != NULL)
628506f25ae9SGregory Neil Shapiro 		{
628606f25ae9SGregory Neil Shapiro 			sep = *endp;
628706f25ae9SGregory Neil Shapiro 			*endp = '\0';
628806f25ae9SGregory Neil Shapiro 		}
628906f25ae9SGregory Neil Shapiro 
629006f25ae9SGregory Neil Shapiro 		mxhosts[nmx] = hp;
629106f25ae9SGregory Neil Shapiro 		prefer[nmx] = curpref;
629206f25ae9SGregory Neil Shapiro 		if (mci_match(hp, mailer))
629306f25ae9SGregory Neil Shapiro 			rndm[nmx] = 0;
629406f25ae9SGregory Neil Shapiro 		else
629506f25ae9SGregory Neil Shapiro 			rndm[nmx] = get_random();
629606f25ae9SGregory Neil Shapiro 
629706f25ae9SGregory Neil Shapiro 		if (endp != NULL)
629806f25ae9SGregory Neil Shapiro 		{
629906f25ae9SGregory Neil Shapiro 			/*
630006f25ae9SGregory Neil Shapiro 			**  Since we don't have the original MX prefs,
630106f25ae9SGregory Neil Shapiro 			**  make our own.  If the separator is a ':', that
630206f25ae9SGregory Neil Shapiro 			**  means the preference for the next host will be
630306f25ae9SGregory Neil Shapiro 			**  higher than this one, so simply increment curpref.
630406f25ae9SGregory Neil Shapiro 			*/
630506f25ae9SGregory Neil Shapiro 
630606f25ae9SGregory Neil Shapiro 			if (sep == ':')
630706f25ae9SGregory Neil Shapiro 				curpref++;
630806f25ae9SGregory Neil Shapiro 
630906f25ae9SGregory Neil Shapiro 			*endp++ = sep;
631006f25ae9SGregory Neil Shapiro 		}
631106f25ae9SGregory Neil Shapiro 		if (++nmx >= MAXMXHOSTS)
631206f25ae9SGregory Neil Shapiro 			break;
631306f25ae9SGregory Neil Shapiro 	}
631406f25ae9SGregory Neil Shapiro 
631506f25ae9SGregory Neil Shapiro 	/* sort the records using the random factor for equal preferences */
631606f25ae9SGregory Neil Shapiro 	for (i = 0; i < nmx; i++)
631706f25ae9SGregory Neil Shapiro 	{
631806f25ae9SGregory Neil Shapiro 		for (j = i + 1; j < nmx; j++)
631906f25ae9SGregory Neil Shapiro 		{
632006f25ae9SGregory Neil Shapiro 			/*
632106f25ae9SGregory Neil Shapiro 			**  List is already sorted by MX preference, only
632206f25ae9SGregory Neil Shapiro 			**  need to look for equal preference MX records
632306f25ae9SGregory Neil Shapiro 			*/
632406f25ae9SGregory Neil Shapiro 
632506f25ae9SGregory Neil Shapiro 			if (prefer[i] < prefer[j])
632606f25ae9SGregory Neil Shapiro 				break;
632706f25ae9SGregory Neil Shapiro 
632806f25ae9SGregory Neil Shapiro 			if (prefer[i] > prefer[j] ||
632906f25ae9SGregory Neil Shapiro 			    (prefer[i] == prefer[j] && rndm[i] > rndm[j]))
633006f25ae9SGregory Neil Shapiro 			{
633140266059SGregory Neil Shapiro 				register unsigned short tempp;
633206f25ae9SGregory Neil Shapiro 				register long tempr;
633306f25ae9SGregory Neil Shapiro 				register char *temp1;
633406f25ae9SGregory Neil Shapiro 
633506f25ae9SGregory Neil Shapiro 				tempp = prefer[i];
633606f25ae9SGregory Neil Shapiro 				prefer[i] = prefer[j];
633706f25ae9SGregory Neil Shapiro 				prefer[j] = tempp;
633806f25ae9SGregory Neil Shapiro 				temp1 = mxhosts[i];
633906f25ae9SGregory Neil Shapiro 				mxhosts[i] = mxhosts[j];
634006f25ae9SGregory Neil Shapiro 				mxhosts[j] = temp1;
634106f25ae9SGregory Neil Shapiro 				tempr = rndm[i];
634206f25ae9SGregory Neil Shapiro 				rndm[i] = rndm[j];
634306f25ae9SGregory Neil Shapiro 				rndm[j] = tempr;
634406f25ae9SGregory Neil Shapiro 			}
634506f25ae9SGregory Neil Shapiro 		}
634606f25ae9SGregory Neil Shapiro 	}
634706f25ae9SGregory Neil Shapiro 	return nmx;
634806f25ae9SGregory Neil Shapiro }
634906f25ae9SGregory Neil Shapiro 
635006f25ae9SGregory Neil Shapiro #if STARTTLS
635106f25ae9SGregory Neil Shapiro static SSL_CTX	*clt_ctx = NULL;
635240266059SGregory Neil Shapiro static bool	tls_ok_clt = true;
635306f25ae9SGregory Neil Shapiro 
635440266059SGregory Neil Shapiro /*
635540266059SGregory Neil Shapiro **  SETCLTTLS -- client side TLS: allow/disallow.
635640266059SGregory Neil Shapiro **
635740266059SGregory Neil Shapiro **	Parameters:
635840266059SGregory Neil Shapiro **		tls_ok -- should tls be done?
635940266059SGregory Neil Shapiro **
636040266059SGregory Neil Shapiro **	Returns:
636140266059SGregory Neil Shapiro **		none.
636240266059SGregory Neil Shapiro **
636340266059SGregory Neil Shapiro **	Side Effects:
636440266059SGregory Neil Shapiro **		sets tls_ok_clt (static variable in this module)
636540266059SGregory Neil Shapiro */
636640266059SGregory Neil Shapiro 
636740266059SGregory Neil Shapiro void
636840266059SGregory Neil Shapiro setclttls(tls_ok)
636940266059SGregory Neil Shapiro 	bool tls_ok;
637040266059SGregory Neil Shapiro {
637140266059SGregory Neil Shapiro 	tls_ok_clt = tls_ok;
637240266059SGregory Neil Shapiro 	return;
637340266059SGregory Neil Shapiro }
637440266059SGregory Neil Shapiro /*
637506f25ae9SGregory Neil Shapiro **  INITCLTTLS -- initialize client side TLS
637606f25ae9SGregory Neil Shapiro **
637706f25ae9SGregory Neil Shapiro **	Parameters:
637840266059SGregory Neil Shapiro **		tls_ok -- should tls initialization be done?
637906f25ae9SGregory Neil Shapiro **
638006f25ae9SGregory Neil Shapiro **	Returns:
638106f25ae9SGregory Neil Shapiro **		succeeded?
638240266059SGregory Neil Shapiro **
638340266059SGregory Neil Shapiro **	Side Effects:
638440266059SGregory Neil Shapiro **		sets tls_ok_clt (static variable in this module)
638506f25ae9SGregory Neil Shapiro */
638606f25ae9SGregory Neil Shapiro 
638706f25ae9SGregory Neil Shapiro bool
638840266059SGregory Neil Shapiro initclttls(tls_ok)
638940266059SGregory Neil Shapiro 	bool tls_ok;
639006f25ae9SGregory Neil Shapiro {
639140266059SGregory Neil Shapiro 	if (!tls_ok_clt)
639240266059SGregory Neil Shapiro 		return false;
639340266059SGregory Neil Shapiro 	tls_ok_clt = tls_ok;
639440266059SGregory Neil Shapiro 	if (!tls_ok_clt)
639540266059SGregory Neil Shapiro 		return false;
639606f25ae9SGregory Neil Shapiro 	if (clt_ctx != NULL)
639740266059SGregory Neil Shapiro 		return true;	/* already done */
63989bd497b8SGregory Neil Shapiro 	tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false,
63999bd497b8SGregory Neil Shapiro 			CltCertFile, CltKeyFile,
6400*5b0945b5SGregory Neil Shapiro # if _FFR_CLIENTCA
6401*5b0945b5SGregory Neil Shapiro 			(CltCACertPath != NULL) ? CltCACertPath :
6402*5b0945b5SGregory Neil Shapiro # endif
6403*5b0945b5SGregory Neil Shapiro 				CACertPath,
6404*5b0945b5SGregory Neil Shapiro # if _FFR_CLIENTCA
6405*5b0945b5SGregory Neil Shapiro 			(CltCACertFile != NULL) ? CltCACertFile :
6406*5b0945b5SGregory Neil Shapiro # endif
6407*5b0945b5SGregory Neil Shapiro 				CACertFile,
6408*5b0945b5SGregory Neil Shapiro 			DHParams);
640940266059SGregory Neil Shapiro 	return tls_ok_clt;
641006f25ae9SGregory Neil Shapiro }
641106f25ae9SGregory Neil Shapiro 
641240266059SGregory Neil Shapiro /*
641306f25ae9SGregory Neil Shapiro **  STARTTLS -- try to start secure connection (client side)
641406f25ae9SGregory Neil Shapiro **
641506f25ae9SGregory Neil Shapiro **	Parameters:
641606f25ae9SGregory Neil Shapiro **		m -- the mailer.
641706f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
641806f25ae9SGregory Neil Shapiro **		e -- the envelope.
641906f25ae9SGregory Neil Shapiro **
642006f25ae9SGregory Neil Shapiro **	Returns:
642106f25ae9SGregory Neil Shapiro **		success?
642206f25ae9SGregory Neil Shapiro **		(maybe this should be some other code than EX_
642306f25ae9SGregory Neil Shapiro **		that denotes which stage failed.)
642406f25ae9SGregory Neil Shapiro */
642506f25ae9SGregory Neil Shapiro 
642606f25ae9SGregory Neil Shapiro static int
6427*5b0945b5SGregory Neil Shapiro starttls(m, mci, e
6428*5b0945b5SGregory Neil Shapiro # if DANE
6429*5b0945b5SGregory Neil Shapiro 	, dane_vrfy_ctx
6430*5b0945b5SGregory Neil Shapiro # endif
6431*5b0945b5SGregory Neil Shapiro 	)
643206f25ae9SGregory Neil Shapiro 	MAILER *m;
643306f25ae9SGregory Neil Shapiro 	MCI *mci;
643406f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
6435*5b0945b5SGregory Neil Shapiro # if DANE
6436*5b0945b5SGregory Neil Shapiro 	dane_vrfy_ctx_P	dane_vrfy_ctx;
6437*5b0945b5SGregory Neil Shapiro # endif
643806f25ae9SGregory Neil Shapiro {
643906f25ae9SGregory Neil Shapiro 	int smtpresult;
644042e5d165SGregory Neil Shapiro 	int result = 0;
644142e5d165SGregory Neil Shapiro 	int rfd, wfd;
644206f25ae9SGregory Neil Shapiro 	SSL *clt_ssl = NULL;
644340266059SGregory Neil Shapiro 	time_t tlsstart;
6444*5b0945b5SGregory Neil Shapiro 	extern int TLSsslidx;
644506f25ae9SGregory Neil Shapiro 
644640266059SGregory Neil Shapiro 	if (clt_ctx == NULL && !initclttls(true))
644742e5d165SGregory Neil Shapiro 		return EX_TEMPFAIL;
64489bd497b8SGregory Neil Shapiro 
6449*5b0945b5SGregory Neil Shapiro 	if (!TLS_set_engine(SSLEngine, false))
64509bd497b8SGregory Neil Shapiro 	{
64519bd497b8SGregory Neil Shapiro 		sm_syslog(LOG_ERR, NOQID,
6452*5b0945b5SGregory Neil Shapiro 			  "STARTTLS=client, engine=%s, TLS_set_engine=failed",
6453*5b0945b5SGregory Neil Shapiro 			  SSLEngine);
64549bd497b8SGregory Neil Shapiro 		return EX_TEMPFAIL;
64559bd497b8SGregory Neil Shapiro 	}
64569bd497b8SGregory Neil Shapiro 
645706f25ae9SGregory Neil Shapiro 	smtpmessage("STARTTLS", m, mci);
645806f25ae9SGregory Neil Shapiro 
645906f25ae9SGregory Neil Shapiro 	/* get the reply */
6460e92d3f3fSGregory Neil Shapiro 	smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
6461e92d3f3fSGregory Neil Shapiro 			XS_STARTTLS);
646206f25ae9SGregory Neil Shapiro 
646306f25ae9SGregory Neil Shapiro 	/* check return code from server */
64644e4196cbSGregory Neil Shapiro 	if (REPLYTYPE(smtpresult) == 4)
646506f25ae9SGregory Neil Shapiro 		return EX_TEMPFAIL;
646606f25ae9SGregory Neil Shapiro 	if (smtpresult == 501)
646706f25ae9SGregory Neil Shapiro 		return EX_USAGE;
646806f25ae9SGregory Neil Shapiro 	if (smtpresult == -1)
646906f25ae9SGregory Neil Shapiro 		return smtpresult;
64704e4196cbSGregory Neil Shapiro 
64714e4196cbSGregory Neil Shapiro 	/* not an expected reply but we have to deal with it */
64724e4196cbSGregory Neil Shapiro 	if (REPLYTYPE(smtpresult) == 5)
64734e4196cbSGregory Neil Shapiro 		return EX_UNAVAILABLE;
647406f25ae9SGregory Neil Shapiro 	if (smtpresult != 220)
647506f25ae9SGregory Neil Shapiro 		return EX_PROTOCOL;
647606f25ae9SGregory Neil Shapiro 
647706f25ae9SGregory Neil Shapiro 	if (LogLevel > 13)
647840266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
647906f25ae9SGregory Neil Shapiro 
648006f25ae9SGregory Neil Shapiro 	/* start connection */
648106f25ae9SGregory Neil Shapiro 	if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
648206f25ae9SGregory Neil Shapiro 	{
648306f25ae9SGregory Neil Shapiro 		if (LogLevel > 5)
648406f25ae9SGregory Neil Shapiro 		{
648540266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
648640266059SGregory Neil Shapiro 				  "STARTTLS=client, error: SSL_new failed");
6487*5b0945b5SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, 9, "client");
648806f25ae9SGregory Neil Shapiro 		}
648906f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
649006f25ae9SGregory Neil Shapiro 	}
6491da7d7b9cSGregory Neil Shapiro 	/* SSL_clear(clt_ssl); ? */
6492da7d7b9cSGregory Neil Shapiro 
6493*5b0945b5SGregory Neil Shapiro 	if (get_tls_se_options(e, clt_ssl, &mci->mci_tlsi, false) != 0)
6494da7d7b9cSGregory Neil Shapiro 	{
6495da7d7b9cSGregory Neil Shapiro 		sm_syslog(LOG_ERR, NOQID,
6496da7d7b9cSGregory Neil Shapiro 			  "STARTTLS=client, get_tls_se_options=fail");
6497da7d7b9cSGregory Neil Shapiro 		return EX_SOFTWARE;
6498da7d7b9cSGregory Neil Shapiro 	}
6499*5b0945b5SGregory Neil Shapiro 	result = SSL_set_ex_data(clt_ssl, TLSsslidx, &mci->mci_tlsi);
6500*5b0945b5SGregory Neil Shapiro 	if (0 == result)
6501*5b0945b5SGregory Neil Shapiro 	{
6502*5b0945b5SGregory Neil Shapiro 		if (LogLevel > 5)
6503*5b0945b5SGregory Neil Shapiro 		{
6504*5b0945b5SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
6505*5b0945b5SGregory Neil Shapiro 				  "STARTTLS=client, error: SSL_set_ex_data failed=%d, idx=%d",
6506*5b0945b5SGregory Neil Shapiro 				  result, TLSsslidx);
6507*5b0945b5SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, 9, "client");
6508*5b0945b5SGregory Neil Shapiro 		}
6509*5b0945b5SGregory Neil Shapiro 		return EX_SOFTWARE;
6510*5b0945b5SGregory Neil Shapiro 	}
6511*5b0945b5SGregory Neil Shapiro # if DANE
6512*5b0945b5SGregory Neil Shapiro 	if (SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE))
6513*5b0945b5SGregory Neil Shapiro 		dane_vrfy_ctx->dane_vrfy_chk = DANE_NEVER;
6514*5b0945b5SGregory Neil Shapiro 	else
6515*5b0945b5SGregory Neil Shapiro 	{
6516*5b0945b5SGregory Neil Shapiro 		int r;
6517*5b0945b5SGregory Neil Shapiro 
6518*5b0945b5SGregory Neil Shapiro #  define SM_IS_EMPTY(s)	(NULL == (s) || '\0' == *(s))
6519*5b0945b5SGregory Neil Shapiro 
6520*5b0945b5SGregory Neil Shapiro 		/* set SNI only if there is a TLSA RR */
6521*5b0945b5SGregory Neil Shapiro 		if (dane_get_tlsa(dane_vrfy_ctx) != NULL &&
6522*5b0945b5SGregory Neil Shapiro 		    !(SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_host) &&
6523*5b0945b5SGregory Neil Shapiro 		      SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)) &&
6524*5b0945b5SGregory Neil Shapiro 		    (r = SSL_set_tlsext_host_name(clt_ssl,
6525*5b0945b5SGregory Neil Shapiro 				(!SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)
6526*5b0945b5SGregory Neil Shapiro 				? dane_vrfy_ctx->dane_vrfy_sni
6527*5b0945b5SGregory Neil Shapiro 				: dane_vrfy_ctx->dane_vrfy_host))) <= 0)
6528*5b0945b5SGregory Neil Shapiro 		{
6529*5b0945b5SGregory Neil Shapiro 			if (LogLevel > 5)
6530*5b0945b5SGregory Neil Shapiro 			{
6531*5b0945b5SGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
6532*5b0945b5SGregory Neil Shapiro 					  "STARTTLS=client, host=%s, SSL_set_tlsext_host_name=%d",
6533*5b0945b5SGregory Neil Shapiro 					  dane_vrfy_ctx->dane_vrfy_host, r);
6534*5b0945b5SGregory Neil Shapiro 			}
6535*5b0945b5SGregory Neil Shapiro 			tlslogerr(LOG_ERR, 5, "client");
6536*5b0945b5SGregory Neil Shapiro 			/* return EX_SOFTWARE; */
6537*5b0945b5SGregory Neil Shapiro 		}
6538*5b0945b5SGregory Neil Shapiro 	}
6539*5b0945b5SGregory Neil Shapiro 	memcpy(&mci->mci_tlsi.tlsi_dvc, dane_vrfy_ctx, sizeof(*dane_vrfy_ctx));
6540*5b0945b5SGregory Neil Shapiro # endif /* DANE */
654106f25ae9SGregory Neil Shapiro 
654240266059SGregory Neil Shapiro 	rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
654340266059SGregory Neil Shapiro 	wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
654442e5d165SGregory Neil Shapiro 
654542e5d165SGregory Neil Shapiro 	if (rfd < 0 || wfd < 0 ||
654640266059SGregory Neil Shapiro 	    (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
654740266059SGregory Neil Shapiro 	    (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
654806f25ae9SGregory Neil Shapiro 	{
654906f25ae9SGregory Neil Shapiro 		if (LogLevel > 5)
655006f25ae9SGregory Neil Shapiro 		{
655140266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
655240266059SGregory Neil Shapiro 				  "STARTTLS=client, error: SSL_set_xfd failed=%d",
655340266059SGregory Neil Shapiro 				  result);
6554*5b0945b5SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, 9, "client");
655506f25ae9SGregory Neil Shapiro 		}
655606f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
655706f25ae9SGregory Neil Shapiro 	}
655806f25ae9SGregory Neil Shapiro 	SSL_set_connect_state(clt_ssl);
655940266059SGregory Neil Shapiro 	tlsstart = curtime();
656040266059SGregory Neil Shapiro 
656140266059SGregory Neil Shapiro ssl_retry:
656206f25ae9SGregory Neil Shapiro 	if ((result = SSL_connect(clt_ssl)) <= 0)
656306f25ae9SGregory Neil Shapiro 	{
65644e4196cbSGregory Neil Shapiro 		int i, ssl_err;
6565da7d7b9cSGregory Neil Shapiro 		int save_errno = errno;
656606f25ae9SGregory Neil Shapiro 
65674e4196cbSGregory Neil Shapiro 		ssl_err = SSL_get_error(clt_ssl, result);
65684e4196cbSGregory Neil Shapiro 		i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
65694e4196cbSGregory Neil Shapiro 			TimeOuts.to_starttls, ssl_err, "client");
65704e4196cbSGregory Neil Shapiro 		if (i > 0)
65714e4196cbSGregory Neil Shapiro 			goto ssl_retry;
657240266059SGregory Neil Shapiro 
657313bd1963SGregory Neil Shapiro 		if (LogLevel > 5)
657413bd1963SGregory Neil Shapiro 		{
6575ba00ec3dSGregory Neil Shapiro 			unsigned long l;
6576ba00ec3dSGregory Neil Shapiro 			const char *sr;
6577ba00ec3dSGregory Neil Shapiro 
6578ba00ec3dSGregory Neil Shapiro 			l = ERR_peek_error();
6579ba00ec3dSGregory Neil Shapiro 			sr = ERR_reason_error_string(l);
65804e4196cbSGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
6581ba00ec3dSGregory Neil Shapiro 				  "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d",
6582ba00ec3dSGregory Neil Shapiro 				  result, sr == NULL ? "unknown" : sr, ssl_err,
6583da7d7b9cSGregory Neil Shapiro 				  save_errno, i);
6584*5b0945b5SGregory Neil Shapiro 			tlslogerr(LOG_WARNING, 9, "client");
658513bd1963SGregory Neil Shapiro 		}
658640266059SGregory Neil Shapiro 
6587*5b0945b5SGregory Neil Shapiro 		SM_SSL_FREE(clt_ssl);
658806f25ae9SGregory Neil Shapiro 		return EX_SOFTWARE;
658906f25ae9SGregory Neil Shapiro 	}
659006f25ae9SGregory Neil Shapiro 	mci->mci_ssl = clt_ssl;
659140266059SGregory Neil Shapiro 	result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
659240266059SGregory Neil Shapiro 			      &mci->mci_macro, true);
659306f25ae9SGregory Neil Shapiro 
659440266059SGregory Neil Shapiro 	/* switch to use TLS... */
659506f25ae9SGregory Neil Shapiro 	if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
659606f25ae9SGregory Neil Shapiro 		return EX_OK;
659706f25ae9SGregory Neil Shapiro 
659806f25ae9SGregory Neil Shapiro 	/* failure */
6599*5b0945b5SGregory Neil Shapiro 	SM_SSL_FREE(clt_ssl);
660006f25ae9SGregory Neil Shapiro 	return EX_SOFTWARE;
660106f25ae9SGregory Neil Shapiro }
660240266059SGregory Neil Shapiro /*
660306f25ae9SGregory Neil Shapiro **  ENDTLSCLT -- shutdown secure connection (client side)
660406f25ae9SGregory Neil Shapiro **
660506f25ae9SGregory Neil Shapiro **	Parameters:
660606f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
660706f25ae9SGregory Neil Shapiro **
660806f25ae9SGregory Neil Shapiro **	Returns:
660906f25ae9SGregory Neil Shapiro **		success?
661006f25ae9SGregory Neil Shapiro */
661140266059SGregory Neil Shapiro 
661240266059SGregory Neil Shapiro static int
661306f25ae9SGregory Neil Shapiro endtlsclt(mci)
661406f25ae9SGregory Neil Shapiro 	MCI *mci;
661506f25ae9SGregory Neil Shapiro {
661606f25ae9SGregory Neil Shapiro 	int r;
661706f25ae9SGregory Neil Shapiro 
661806f25ae9SGregory Neil Shapiro 	if (!bitset(MCIF_TLSACT, mci->mci_flags))
661906f25ae9SGregory Neil Shapiro 		return EX_OK;
6620*5b0945b5SGregory Neil Shapiro 	r = endtls(&mci->mci_ssl, "client");
662106f25ae9SGregory Neil Shapiro 	mci->mci_flags &= ~MCIF_TLSACT;
662206f25ae9SGregory Neil Shapiro 	return r;
662306f25ae9SGregory Neil Shapiro }
662440266059SGregory Neil Shapiro #endif /* STARTTLS */
662540266059SGregory Neil Shapiro #if STARTTLS || SASL
662640266059SGregory Neil Shapiro /*
662740266059SGregory Neil Shapiro **  ISCLTFLGSET -- check whether client flag is set.
662806f25ae9SGregory Neil Shapiro **
662906f25ae9SGregory Neil Shapiro **	Parameters:
663040266059SGregory Neil Shapiro **		e -- envelope.
663140266059SGregory Neil Shapiro **		flag -- flag to check in {client_flags}
663206f25ae9SGregory Neil Shapiro **
663306f25ae9SGregory Neil Shapiro **	Returns:
663440266059SGregory Neil Shapiro **		true iff flag is set.
663506f25ae9SGregory Neil Shapiro */
663606f25ae9SGregory Neil Shapiro 
663740266059SGregory Neil Shapiro static bool
663840266059SGregory Neil Shapiro iscltflgset(e, flag)
663940266059SGregory Neil Shapiro 	ENVELOPE *e;
664040266059SGregory Neil Shapiro 	int flag;
664106f25ae9SGregory Neil Shapiro {
664240266059SGregory Neil Shapiro 	char *p;
6643602a2b1bSGregory Neil Shapiro 
664440266059SGregory Neil Shapiro 	p = macvalue(macid("{client_flags}"), e);
664540266059SGregory Neil Shapiro 	if (p == NULL)
664640266059SGregory Neil Shapiro 		return false;
664740266059SGregory Neil Shapiro 	for (; *p != '\0'; p++)
664806f25ae9SGregory Neil Shapiro 	{
664940266059SGregory Neil Shapiro 		/* look for just this one flag */
665040266059SGregory Neil Shapiro 		if (*p == (char) flag)
665140266059SGregory Neil Shapiro 			return true;
665206f25ae9SGregory Neil Shapiro 	}
665340266059SGregory Neil Shapiro 	return false;
665406f25ae9SGregory Neil Shapiro }
665540266059SGregory Neil Shapiro #endif /* STARTTLS || SASL */
6656