mci.c revision 110563
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 * $FreeBSD: head/contrib/sendmail/src/mci.c 110563 2003-02-08 20:35:51Z gshapiro $
13 *
14 */
15
16#include <sendmail.h>
17
18SM_RCSID("@(#)$Id: mci.c,v 8.205.2.2 2002/11/26 19:15:19 gshapiro Exp $")
19
20#if NETINET || NETINET6
21# include <arpa/inet.h>
22#endif /* NETINET || NETINET6 */
23
24#include <dirent.h>
25
26static int	mci_generate_persistent_path __P((const char *, char *,
27						  int, bool));
28static bool	mci_load_persistent __P((MCI *));
29static void	mci_uncache __P((MCI **, bool));
30static int	mci_lock_host_statfile __P((MCI *));
31static int	mci_read_persistent __P((SM_FILE_T *, MCI *));
32
33/*
34**  Mail Connection Information (MCI) Caching Module.
35**
36**	There are actually two separate things cached.  The first is
37**	the set of all open connections -- these are stored in a
38**	(small) list.  The second is stored in the symbol table; it
39**	has the overall status for all hosts, whether or not there
40**	is a connection open currently.
41**
42**	There should never be too many connections open (since this
43**	could flood the socket table), nor should a connection be
44**	allowed to sit idly for too long.
45**
46**	MaxMciCache is the maximum number of open connections that
47**	will be supported.
48**
49**	MciCacheTimeout is the time (in seconds) that a connection
50**	is permitted to survive without activity.
51**
52**	We actually try any cached connections by sending a NOOP
53**	before we use them; if the NOOP fails we close down the
54**	connection and reopen it.  Note that this means that a
55**	server SMTP that doesn't support NOOP will hose the
56**	algorithm -- but that doesn't seem too likely.
57**
58**	The persistent MCI code is donated by Mark Lovell and Paul
59**	Vixie.  It is based on the long term host status code in KJS
60**	written by Paul but has been adapted by Mark to fit into the
61**	MCI structure.
62*/
63
64static MCI	**MciCache;		/* the open connection cache */
65
66/*
67**  MCI_CACHE -- enter a connection structure into the open connection cache
68**
69**	This may cause something else to be flushed.
70**
71**	Parameters:
72**		mci -- the connection to cache.
73**
74**	Returns:
75**		none.
76*/
77
78void
79mci_cache(mci)
80	register MCI *mci;
81{
82	register MCI **mcislot;
83
84	/*
85	**  Find the best slot.  This may cause expired connections
86	**  to be closed.
87	*/
88
89	mcislot = mci_scan(mci);
90	if (mcislot == NULL)
91	{
92		/* we don't support caching */
93		return;
94	}
95
96	if (mci->mci_host == NULL)
97		return;
98
99	/* if this is already cached, we are done */
100	if (bitset(MCIF_CACHED, mci->mci_flags))
101		return;
102
103	/* otherwise we may have to clear the slot */
104	if (*mcislot != NULL)
105		mci_uncache(mcislot, true);
106
107	if (tTd(42, 5))
108		sm_dprintf("mci_cache: caching %p (%s) in slot %d\n",
109			   mci, mci->mci_host, (int) (mcislot - MciCache));
110	if (tTd(91, 100))
111		sm_syslog(LOG_DEBUG, CurEnv->e_id,
112			  "mci_cache: caching %lx (%.100s) in slot %d",
113			  (unsigned long) mci, mci->mci_host,
114			  (int) (mcislot - MciCache));
115
116	*mcislot = mci;
117	mci->mci_flags |= MCIF_CACHED;
118}
119/*
120**  MCI_SCAN -- scan the cache, flush junk, and return best slot
121**
122**	Parameters:
123**		savemci -- never flush this one.  Can be null.
124**
125**	Returns:
126**		The LRU (or empty) slot.
127*/
128
129MCI **
130mci_scan(savemci)
131	MCI *savemci;
132{
133	time_t now;
134	register MCI **bestmci;
135	register MCI *mci;
136	register int i;
137
138	if (MaxMciCache <= 0)
139	{
140		/* we don't support caching */
141		return NULL;
142	}
143
144	if (MciCache == NULL)
145	{
146		/* first call */
147		MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache);
148		memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache);
149		return &MciCache[0];
150	}
151
152	now = curtime();
153	bestmci = &MciCache[0];
154	for (i = 0; i < MaxMciCache; i++)
155	{
156		mci = MciCache[i];
157		if (mci == NULL || mci->mci_state == MCIS_CLOSED)
158		{
159			bestmci = &MciCache[i];
160			continue;
161		}
162		if ((mci->mci_lastuse + MciCacheTimeout <= now ||
163		     (mci->mci_mailer != NULL &&
164		      mci->mci_mailer->m_maxdeliveries > 0 &&
165		      mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&&
166		    mci != savemci)
167		{
168			/* connection idle too long or too many deliveries */
169			bestmci = &MciCache[i];
170
171			/* close it */
172			mci_uncache(bestmci, true);
173			continue;
174		}
175		if (*bestmci == NULL)
176			continue;
177		if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
178			bestmci = &MciCache[i];
179	}
180	return bestmci;
181}
182/*
183**  MCI_UNCACHE -- remove a connection from a slot.
184**
185**	May close a connection.
186**
187**	Parameters:
188**		mcislot -- the slot to empty.
189**		doquit -- if true, send QUIT protocol on this connection.
190**			  if false, we are assumed to be in a forked child;
191**				all we want to do is close the file(s).
192**
193**	Returns:
194**		none.
195*/
196
197static void
198mci_uncache(mcislot, doquit)
199	register MCI **mcislot;
200	bool doquit;
201{
202	register MCI *mci;
203	extern ENVELOPE BlankEnvelope;
204
205	mci = *mcislot;
206	if (mci == NULL)
207		return;
208	*mcislot = NULL;
209	if (mci->mci_host == NULL)
210		return;
211
212	mci_unlock_host(mci);
213
214	if (tTd(42, 5))
215		sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n",
216			   mci, mci->mci_host, (int) (mcislot - MciCache),
217			   doquit);
218	if (tTd(91, 100))
219		sm_syslog(LOG_DEBUG, CurEnv->e_id,
220			  "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)",
221			  (unsigned long) mci, mci->mci_host,
222			  (int) (mcislot - MciCache), doquit);
223
224	mci->mci_deliveries = 0;
225	if (doquit)
226	{
227		message("Closing connection to %s", mci->mci_host);
228
229		mci->mci_flags &= ~MCIF_CACHED;
230
231		/* only uses the envelope to flush the transcript file */
232		if (mci->mci_state != MCIS_CLOSED)
233			smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
234#if XLA
235		xla_host_end(mci->mci_host);
236#endif /* XLA */
237	}
238	else
239	{
240		if (mci->mci_in != NULL)
241			(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
242		if (mci->mci_out != NULL)
243			(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
244		mci->mci_in = mci->mci_out = NULL;
245		mci->mci_state = MCIS_CLOSED;
246		mci->mci_exitstat = EX_OK;
247		mci->mci_errno = 0;
248		mci->mci_flags = 0;
249
250		mci->mci_retryrcpt = false;
251		mci->mci_tolist = NULL;
252#if PIPELINING
253		mci->mci_okrcpts = 0;
254#endif /* PIPELINING */
255	}
256
257	SM_FREE_CLR(mci->mci_status);
258	SM_FREE_CLR(mci->mci_rstatus);
259	SM_FREE_CLR(mci->mci_heloname);
260	if (mci->mci_rpool != NULL)
261	{
262		sm_rpool_free(mci->mci_rpool);
263		mci->mci_macro.mac_rpool = NULL;
264		mci->mci_rpool = NULL;
265	}
266}
267/*
268**  MCI_FLUSH -- flush the entire cache
269**
270**	Parameters:
271**		doquit -- if true, send QUIT protocol.
272**			  if false, just close the connection.
273**		allbut -- but leave this one open.
274**
275**	Returns:
276**		none.
277*/
278
279void
280mci_flush(doquit, allbut)
281	bool doquit;
282	MCI *allbut;
283{
284	register int i;
285
286	if (MciCache == NULL)
287		return;
288
289	for (i = 0; i < MaxMciCache; i++)
290	{
291		if (allbut != MciCache[i])
292			mci_uncache(&MciCache[i], doquit);
293	}
294}
295/*
296**  MCI_GET -- get information about a particular host
297**
298**	Parameters:
299**		host -- host to look for.
300**		m -- mailer.
301**
302**	Returns:
303**		mci for this host (might be new).
304*/
305
306MCI *
307mci_get(host, m)
308	char *host;
309	MAILER *m;
310{
311	register MCI *mci;
312	register STAB *s;
313	extern SOCKADDR CurHostAddr;
314
315	/* clear CurHostAddr so we don't get a bogus address with this name */
316	memset(&CurHostAddr, '\0', sizeof CurHostAddr);
317
318	/* clear out any expired connections */
319	(void) mci_scan(NULL);
320
321	if (m->m_mno < 0)
322		syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
323
324	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
325	mci = &s->s_mci;
326
327	/* initialize per-message data */
328	mci->mci_retryrcpt = false;
329	mci->mci_tolist = NULL;
330#if PIPELINING
331	mci->mci_okrcpts = 0;
332#endif /* PIPELINING */
333
334	if (mci->mci_rpool == NULL)
335		mci->mci_rpool = sm_rpool_new_x(NULL);
336
337	if (mci->mci_macro.mac_rpool == NULL)
338		mci->mci_macro.mac_rpool = mci->mci_rpool;
339
340	/*
341	**  We don't need to load the persistent data if we have data
342	**  already loaded in the cache.
343	*/
344
345	if (mci->mci_host == NULL &&
346	    (mci->mci_host = s->s_name) != NULL &&
347	    !mci_load_persistent(mci))
348	{
349		if (tTd(42, 2))
350			sm_dprintf("mci_get(%s %s): lock failed\n",
351				host, m->m_name);
352		mci->mci_exitstat = EX_TEMPFAIL;
353		mci->mci_state = MCIS_CLOSED;
354		mci->mci_statfile = NULL;
355		return mci;
356	}
357
358	if (tTd(42, 2))
359	{
360		sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
361			host, m->m_name, mci->mci_state, mci->mci_flags,
362			mci->mci_exitstat, mci->mci_errno);
363	}
364
365	if (mci->mci_state == MCIS_OPEN)
366	{
367		/* poke the connection to see if it's still alive */
368		(void) smtpprobe(mci);
369
370		/* reset the stored state in the event of a timeout */
371		if (mci->mci_state != MCIS_OPEN)
372		{
373			mci->mci_errno = 0;
374			mci->mci_exitstat = EX_OK;
375			mci->mci_state = MCIS_CLOSED;
376		}
377		else
378		{
379			/* get peer host address */
380			/* (this should really be in the mci struct) */
381			SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
382
383			(void) getpeername(sm_io_getinfo(mci->mci_in,
384							 SM_IO_WHAT_FD, NULL),
385				(struct sockaddr *) &CurHostAddr, &socklen);
386		}
387	}
388	if (mci->mci_state == MCIS_CLOSED)
389	{
390		time_t now = curtime();
391
392		/* if this info is stale, ignore it */
393		if (mci->mci_lastuse + MciInfoTimeout <= now)
394		{
395			mci->mci_lastuse = now;
396			mci->mci_errno = 0;
397			mci->mci_exitstat = EX_OK;
398		}
399	}
400
401	return mci;
402}
403/*
404**  MCI_NEW -- allocate new MCI structure
405**
406**	Parameters:
407**		rpool -- if non-NULL: allocate from that rpool.
408**
409**	Returns:
410**		mci (new).
411*/
412
413MCI *
414mci_new(rpool)
415	SM_RPOOL_T *rpool;
416{
417	register MCI *mci;
418
419	if (rpool == NULL)
420		mci = (MCI *) sm_malloc_x(sizeof *mci);
421	else
422		mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci);
423	memset((char *) mci, '\0', sizeof *mci);
424	mci->mci_rpool = sm_rpool_new_x(NULL);
425	mci->mci_macro.mac_rpool = mci->mci_rpool;
426	return mci;
427}
428/*
429**  MCI_MATCH -- check connection cache for a particular host
430**
431**	Parameters:
432**		host -- host to look for.
433**		m -- mailer.
434**
435**	Returns:
436**		true iff open connection exists.
437*/
438
439bool
440mci_match(host, m)
441	char *host;
442	MAILER *m;
443{
444	register MCI *mci;
445	register STAB *s;
446
447	if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
448		return false;
449	s = stab(host, ST_MCI + m->m_mno, ST_FIND);
450	if (s == NULL)
451		return false;
452
453	mci = &s->s_mci;
454	return mci->mci_state == MCIS_OPEN;
455}
456/*
457**  MCI_SETSTAT -- set status codes in MCI structure.
458**
459**	Parameters:
460**		mci -- the MCI structure to set.
461**		xstat -- the exit status code.
462**		dstat -- the DSN status code.
463**		rstat -- the SMTP status code.
464**
465**	Returns:
466**		none.
467*/
468
469void
470mci_setstat(mci, xstat, dstat, rstat)
471	MCI *mci;
472	int xstat;
473	char *dstat;
474	char *rstat;
475{
476	/* protocol errors should never be interpreted as sticky */
477	if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
478		mci->mci_exitstat = xstat;
479
480	SM_FREE_CLR(mci->mci_status);
481	if (dstat != NULL)
482		mci->mci_status = sm_strdup_x(dstat);
483
484	SM_FREE_CLR(mci->mci_rstatus);
485	if (rstat != NULL)
486		mci->mci_rstatus = sm_strdup_x(rstat);
487}
488/*
489**  MCI_DUMP -- dump the contents of an MCI structure.
490**
491**	Parameters:
492**		mci -- the MCI structure to dump.
493**
494**	Returns:
495**		none.
496**
497**	Side Effects:
498**		none.
499*/
500
501struct mcifbits
502{
503	int	mcif_bit;	/* flag bit */
504	char	*mcif_name;	/* flag name */
505};
506static struct mcifbits	MciFlags[] =
507{
508	{ MCIF_VALID,		"VALID"		},
509	{ MCIF_CACHED,		"CACHED"	},
510	{ MCIF_ESMTP,		"ESMTP"		},
511	{ MCIF_EXPN,		"EXPN"		},
512	{ MCIF_SIZE,		"SIZE"		},
513	{ MCIF_8BITMIME,	"8BITMIME"	},
514	{ MCIF_7BIT,		"7BIT"		},
515	{ MCIF_INHEADER,	"INHEADER"	},
516	{ MCIF_CVT8TO7,		"CVT8TO7"	},
517	{ MCIF_DSN,		"DSN"		},
518	{ MCIF_8BITOK,		"8BITOK"	},
519	{ MCIF_CVT7TO8,		"CVT7TO8"	},
520	{ MCIF_INMIME,		"INMIME"	},
521	{ MCIF_AUTH,		"AUTH"		},
522	{ MCIF_AUTHACT,		"AUTHACT"	},
523	{ MCIF_ENHSTAT,		"ENHSTAT"	},
524	{ MCIF_PIPELINED,	"PIPELINED"	},
525#if STARTTLS
526	{ MCIF_TLS,		"TLS"		},
527	{ MCIF_TLSACT,		"TLSACT"	},
528#endif /* STARTTLS */
529	{ MCIF_DLVR_BY,		"DLVR_BY"	},
530	{ 0,			NULL		}
531};
532
533void
534mci_dump(mci, logit)
535	register MCI *mci;
536	bool logit;
537{
538	register char *p;
539	char *sep;
540	char buf[4000];
541
542	sep = logit ? " " : "\n\t";
543	p = buf;
544	(void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci);
545	p += strlen(p);
546	if (mci == NULL)
547	{
548		(void) sm_snprintf(p, SPACELEFT(buf, p), "NULL");
549		goto printit;
550	}
551	(void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags);
552	p += strlen(p);
553	if (mci->mci_flags != 0)
554	{
555		struct mcifbits *f;
556
557		*p++ = '<';
558		for (f = MciFlags; f->mcif_bit != 0; f++)
559		{
560			if (!bitset(f->mcif_bit, mci->mci_flags))
561				continue;
562			(void) sm_strlcpyn(p, SPACELEFT(buf, p), 2,
563					   f->mcif_name, ",");
564			p += strlen(p);
565		}
566		p[-1] = '>';
567	}
568
569	/* Note: sm_snprintf() takes care of NULL arguments for %s */
570	(void) sm_snprintf(p, SPACELEFT(buf, p),
571		",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
572		sep, mci->mci_errno, mci->mci_herrno,
573		mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep);
574	p += strlen(p);
575	(void) sm_snprintf(p, SPACELEFT(buf, p),
576		"maxsize=%ld, phase=%s, mailer=%s,%s",
577		mci->mci_maxsize, mci->mci_phase,
578		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
579		sep);
580	p += strlen(p);
581	(void) sm_snprintf(p, SPACELEFT(buf, p),
582		"status=%s, rstatus=%s,%s",
583		mci->mci_status, mci->mci_rstatus, sep);
584	p += strlen(p);
585	(void) sm_snprintf(p, SPACELEFT(buf, p),
586		"host=%s, lastuse=%s",
587		mci->mci_host, ctime(&mci->mci_lastuse));
588printit:
589	if (logit)
590		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
591	else
592		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf);
593}
594/*
595**  MCI_DUMP_ALL -- print the entire MCI cache
596**
597**	Parameters:
598**		logit -- if set, log the result instead of printing
599**			to stdout.
600**
601**	Returns:
602**		none.
603*/
604
605void
606mci_dump_all(logit)
607	bool logit;
608{
609	register int i;
610
611	if (MciCache == NULL)
612		return;
613
614	for (i = 0; i < MaxMciCache; i++)
615		mci_dump(MciCache[i], logit);
616}
617/*
618**  MCI_LOCK_HOST -- Lock host while sending.
619**
620**	If we are contacting a host, we'll need to
621**	update the status information in the host status
622**	file, and if we want to do that, we ought to have
623**	locked it. This has the (according to some)
624**	desirable effect of serializing connectivity with
625**	remote hosts -- i.e.: one connection to a given
626**	host at a time.
627**
628**	Parameters:
629**		mci -- containing the host we want to lock.
630**
631**	Returns:
632**		EX_OK	    -- got the lock.
633**		EX_TEMPFAIL -- didn't get the lock.
634*/
635
636int
637mci_lock_host(mci)
638	MCI *mci;
639{
640	if (mci == NULL)
641	{
642		if (tTd(56, 1))
643			sm_dprintf("mci_lock_host: NULL mci\n");
644		return EX_OK;
645	}
646
647	if (!SingleThreadDelivery)
648		return EX_OK;
649
650	return mci_lock_host_statfile(mci);
651}
652
653static int
654mci_lock_host_statfile(mci)
655	MCI *mci;
656{
657	int save_errno = errno;
658	int retVal = EX_OK;
659	char fname[MAXPATHLEN];
660
661	if (HostStatDir == NULL || mci->mci_host == NULL)
662		return EX_OK;
663
664	if (tTd(56, 2))
665		sm_dprintf("mci_lock_host: attempting to lock %s\n",
666			   mci->mci_host);
667
668	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname,
669					 true) < 0)
670	{
671		/* of course this should never happen */
672		if (tTd(56, 2))
673			sm_dprintf("mci_lock_host: Failed to generate host path for %s\n",
674				   mci->mci_host);
675
676		retVal = EX_TEMPFAIL;
677		goto cleanup;
678	}
679
680	mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
681				      SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
682
683	if (mci->mci_statfile == NULL)
684	{
685		syserr("mci_lock_host: cannot create host lock file %s", fname);
686		goto cleanup;
687	}
688
689	if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
690		      fname, "", LOCK_EX|LOCK_NB))
691	{
692		if (tTd(56, 2))
693			sm_dprintf("mci_lock_host: couldn't get lock on %s\n",
694				fname);
695		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
696		mci->mci_statfile = NULL;
697		retVal = EX_TEMPFAIL;
698		goto cleanup;
699	}
700
701	if (tTd(56, 12) && mci->mci_statfile != NULL)
702		sm_dprintf("mci_lock_host: Sanity check -- lock is good\n");
703
704cleanup:
705	errno = save_errno;
706	return retVal;
707}
708/*
709**  MCI_UNLOCK_HOST -- unlock host
710**
711**	Clean up the lock on a host, close the file, let
712**	someone else use it.
713**
714**	Parameters:
715**		mci -- us.
716**
717**	Returns:
718**		nothing.
719*/
720
721void
722mci_unlock_host(mci)
723	MCI *mci;
724{
725	int save_errno = errno;
726
727	if (mci == NULL)
728	{
729		if (tTd(56, 1))
730			sm_dprintf("mci_unlock_host: NULL mci\n");
731		return;
732	}
733
734	if (HostStatDir == NULL || mci->mci_host == NULL)
735		return;
736
737	if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
738	{
739		if (tTd(56, 1))
740			sm_dprintf("mci_unlock_host: stat file already locked\n");
741	}
742	else
743	{
744		if (tTd(56, 2))
745			sm_dprintf("mci_unlock_host: store prior to unlock\n");
746		mci_store_persistent(mci);
747	}
748
749	if (mci->mci_statfile != NULL)
750	{
751		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
752		mci->mci_statfile = NULL;
753	}
754
755	errno = save_errno;
756}
757/*
758**  MCI_LOAD_PERSISTENT -- load persistent host info
759**
760**	Load information about host that is kept
761**	in common for all running sendmails.
762**
763**	Parameters:
764**		mci -- the host/connection to load persistent info for.
765**
766**	Returns:
767**		true -- lock was successful
768**		false -- lock failed
769*/
770
771static bool
772mci_load_persistent(mci)
773	MCI *mci;
774{
775	int save_errno = errno;
776	bool locked = true;
777	SM_FILE_T *fp;
778	char fname[MAXPATHLEN];
779
780	if (mci == NULL)
781	{
782		if (tTd(56, 1))
783			sm_dprintf("mci_load_persistent: NULL mci\n");
784		return true;
785	}
786
787	if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
788		return true;
789
790	/* Already have the persistent information in memory */
791	if (SingleThreadDelivery && mci->mci_statfile != NULL)
792		return true;
793
794	if (tTd(56, 1))
795		sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n",
796			   mci->mci_host);
797
798	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname,
799					 false) < 0)
800	{
801		/* Not much we can do if the file isn't there... */
802		if (tTd(56, 1))
803			sm_dprintf("mci_load_persistent: Couldn't generate host path\n");
804		goto cleanup;
805	}
806
807	fp = safefopen(fname, O_RDONLY, FileMode,
808		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
809	if (fp == NULL)
810	{
811		/* I can't think of any reason this should ever happen */
812		if (tTd(56, 1))
813			sm_dprintf("mci_load_persistent: open(%s): %s\n",
814				fname, sm_errstring(errno));
815		goto cleanup;
816	}
817
818	FileName = fname;
819	locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "",
820			  LOCK_SH|LOCK_NB);
821	if (locked)
822	{
823		(void) mci_read_persistent(fp, mci);
824		(void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname,
825				"", LOCK_UN);
826	}
827	FileName = NULL;
828	(void) sm_io_close(fp, SM_TIME_DEFAULT);
829
830cleanup:
831	errno = save_errno;
832	return locked;
833}
834/*
835**  MCI_READ_PERSISTENT -- read persistent host status file
836**
837**	Parameters:
838**		fp -- the file pointer to read.
839**		mci -- the pointer to fill in.
840**
841**	Returns:
842**		-1 -- if the file was corrupt.
843**		0 -- otherwise.
844**
845**	Warning:
846**		This code makes the assumption that this data
847**		will be read in an atomic fashion, and that the data
848**		was written in an atomic fashion.  Any other functioning
849**		may lead to some form of insanity.  This should be
850**		perfectly safe due to underlying stdio buffering.
851*/
852
853static int
854mci_read_persistent(fp, mci)
855	SM_FILE_T *fp;
856	register MCI *mci;
857{
858	int ver;
859	register char *p;
860	int saveLineNumber = LineNumber;
861	char buf[MAXLINE];
862
863	if (fp == NULL)
864		syserr("mci_read_persistent: NULL fp");
865	if (mci == NULL)
866		syserr("mci_read_persistent: NULL mci");
867	if (tTd(56, 93))
868	{
869		sm_dprintf("mci_read_persistent: fp=%lx, mci=",
870			   (unsigned long) fp);
871	}
872
873	SM_FREE_CLR(mci->mci_status);
874	SM_FREE_CLR(mci->mci_rstatus);
875
876	sm_io_rewind(fp, SM_TIME_DEFAULT);
877	ver = -1;
878	LineNumber = 0;
879	while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
880	{
881		LineNumber++;
882		p = strchr(buf, '\n');
883		if (p != NULL)
884			*p = '\0';
885		switch (buf[0])
886		{
887		  case 'V':		/* version stamp */
888			ver = atoi(&buf[1]);
889			if (ver < 0 || ver > 0)
890				syserr("Unknown host status version %d: %d max",
891					ver, 0);
892			break;
893
894		  case 'E':		/* UNIX error number */
895			mci->mci_errno = atoi(&buf[1]);
896			break;
897
898		  case 'H':		/* DNS error number */
899			mci->mci_herrno = atoi(&buf[1]);
900			break;
901
902		  case 'S':		/* UNIX exit status */
903			mci->mci_exitstat = atoi(&buf[1]);
904			break;
905
906		  case 'D':		/* DSN status */
907			mci->mci_status = newstr(&buf[1]);
908			break;
909
910		  case 'R':		/* SMTP status */
911			mci->mci_rstatus = newstr(&buf[1]);
912			break;
913
914		  case 'U':		/* last usage time */
915			mci->mci_lastuse = atol(&buf[1]);
916			break;
917
918		  case '.':		/* end of file */
919			if (tTd(56, 93))
920				mci_dump(mci, false);
921			return 0;
922
923		  default:
924			sm_syslog(LOG_CRIT, NOQID,
925				  "%s: line %d: Unknown host status line \"%s\"",
926				  FileName == NULL ? mci->mci_host : FileName,
927				  LineNumber, buf);
928			LineNumber = saveLineNumber;
929			return -1;
930		}
931	}
932	LineNumber = saveLineNumber;
933	if (tTd(56, 93))
934		sm_dprintf("incomplete (missing dot for EOF)\n");
935	if (ver < 0)
936		return -1;
937	return 0;
938}
939/*
940**  MCI_STORE_PERSISTENT -- Store persistent MCI information
941**
942**	Store information about host that is kept
943**	in common for all running sendmails.
944**
945**	Parameters:
946**		mci -- the host/connection to store persistent info for.
947**
948**	Returns:
949**		none.
950*/
951
952void
953mci_store_persistent(mci)
954	MCI *mci;
955{
956	int save_errno = errno;
957
958	if (mci == NULL)
959	{
960		if (tTd(56, 1))
961			sm_dprintf("mci_store_persistent: NULL mci\n");
962		return;
963	}
964
965	if (HostStatDir == NULL || mci->mci_host == NULL)
966		return;
967
968	if (tTd(56, 1))
969		sm_dprintf("mci_store_persistent: Storing information for %s\n",
970			   mci->mci_host);
971
972	if (mci->mci_statfile == NULL)
973	{
974		if (tTd(56, 1))
975			sm_dprintf("mci_store_persistent: no statfile\n");
976		return;
977	}
978
979	sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT);
980#if !NOFTRUNCATE
981	(void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
982			 (off_t) 0);
983#endif /* !NOFTRUNCATE */
984
985	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n");
986	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n",
987			     mci->mci_errno);
988	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n",
989			     mci->mci_herrno);
990	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n",
991			     mci->mci_exitstat);
992	if (mci->mci_status != NULL)
993		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
994				     "D%.80s\n",
995				     denlstring(mci->mci_status, true, false));
996	if (mci->mci_rstatus != NULL)
997		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
998				     "R%.80s\n",
999				     denlstring(mci->mci_rstatus, true, false));
1000	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n",
1001			     (long)(mci->mci_lastuse));
1002	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n");
1003
1004	(void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT);
1005
1006	errno = save_errno;
1007	return;
1008}
1009/*
1010**  MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
1011**
1012**	Recursively find all the mci host files in `pathname'.  Default to
1013**		main host status directory if no path is provided.
1014**	Call (*action)(pathname, host) for each file found.
1015**
1016**	Note: all information is collected in a list before it is processed.
1017**	This may not be the best way to do it, but it seems safest, since
1018**	the file system would be touched while we are attempting to traverse
1019**	the directory tree otherwise (during purges).
1020**
1021**	Parameters:
1022**		action -- function to call on each node.  If returns < 0,
1023**			return immediately.
1024**		pathname -- root of tree.  If null, use main host status
1025**			directory.
1026**
1027**	Returns:
1028**		< 0 -- if any action routine returns a negative value, that
1029**			value is returned.
1030**		0 -- if we successfully went to completion.
1031**		> 0 -- return status from action()
1032*/
1033
1034int
1035mci_traverse_persistent(action, pathname)
1036	int (*action)();
1037	char *pathname;
1038{
1039	struct stat statbuf;
1040	DIR *d;
1041	int ret;
1042
1043	if (pathname == NULL)
1044		pathname = HostStatDir;
1045	if (pathname == NULL)
1046		return -1;
1047
1048	if (tTd(56, 1))
1049		sm_dprintf("mci_traverse: pathname is %s\n", pathname);
1050
1051	ret = stat(pathname, &statbuf);
1052	if (ret < 0)
1053	{
1054		if (tTd(56, 2))
1055			sm_dprintf("mci_traverse: Failed to stat %s: %s\n",
1056				pathname, sm_errstring(errno));
1057		return ret;
1058	}
1059	if (S_ISDIR(statbuf.st_mode))
1060	{
1061		bool leftone, removedone;
1062		size_t len;
1063		char *newptr;
1064		struct dirent *e;
1065		char newpath[MAXPATHLEN];
1066
1067		if ((d = opendir(pathname)) == NULL)
1068		{
1069			if (tTd(56, 2))
1070				sm_dprintf("mci_traverse: opendir %s: %s\n",
1071					pathname, sm_errstring(errno));
1072			return -1;
1073		}
1074		len = sizeof(newpath) - MAXNAMLEN - 3;
1075		if (sm_strlcpy(newpath, pathname, len) >= len)
1076		{
1077			if (tTd(56, 2))
1078				sm_dprintf("mci_traverse: path \"%s\" too long",
1079					pathname);
1080			return -1;
1081		}
1082		newptr = newpath + strlen(newpath);
1083		*newptr++ = '/';
1084
1085		/*
1086		**  repeat until no file has been removed
1087		**  this may become ugly when several files "expire"
1088		**  during these loops, but it's better than doing
1089		**  a rewinddir() inside the inner loop
1090		*/
1091
1092		do
1093		{
1094			leftone = removedone = false;
1095			while ((e = readdir(d)) != NULL)
1096			{
1097				if (e->d_name[0] == '.')
1098					continue;
1099
1100				(void) sm_strlcpy(newptr, e->d_name,
1101					       sizeof newpath -
1102					       (newptr - newpath));
1103
1104				if (StopRequest)
1105					stop_sendmail();
1106				ret = mci_traverse_persistent(action, newpath);
1107				if (ret < 0)
1108					break;
1109				if (ret == 1)
1110					leftone = true;
1111				if (!removedone && ret == 0 &&
1112				    action == mci_purge_persistent)
1113					removedone = true;
1114			}
1115			if (ret < 0)
1116				break;
1117
1118			/*
1119			**  The following appears to be
1120			**  necessary during purges, since
1121			**  we modify the directory structure
1122			*/
1123
1124			if (removedone)
1125				rewinddir(d);
1126			if (tTd(56, 40))
1127				sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n",
1128					pathname, ret, removedone, leftone);
1129		} while (removedone);
1130
1131		/* purge (or whatever) the directory proper */
1132		if (!leftone)
1133		{
1134			*--newptr = '\0';
1135			ret = (*action)(newpath, NULL);
1136		}
1137		(void) closedir(d);
1138	}
1139	else if (S_ISREG(statbuf.st_mode))
1140	{
1141		char *end = pathname + strlen(pathname) - 1;
1142		char *start;
1143		char *scan;
1144		char host[MAXHOSTNAMELEN];
1145		char *hostptr = host;
1146
1147		/*
1148		**  Reconstruct the host name from the path to the
1149		**  persistent information.
1150		*/
1151
1152		do
1153		{
1154			if (hostptr != host)
1155				*(hostptr++) = '.';
1156			start = end;
1157			while (*(start - 1) != '/')
1158				start--;
1159
1160			if (*end == '.')
1161				end--;
1162
1163			for (scan = start; scan <= end; scan++)
1164				*(hostptr++) = *scan;
1165
1166			end = start - 2;
1167		} while (*end == '.');
1168
1169		*hostptr = '\0';
1170
1171		/*
1172		**  Do something with the file containing the persistent
1173		**  information.
1174		*/
1175
1176		ret = (*action)(pathname, host);
1177	}
1178
1179	return ret;
1180}
1181/*
1182**  MCI_PRINT_PERSISTENT -- print persistent info
1183**
1184**	Dump the persistent information in the file 'pathname'
1185**
1186**	Parameters:
1187**		pathname -- the pathname to the status file.
1188**		hostname -- the corresponding host name.
1189**
1190**	Returns:
1191**		0
1192*/
1193
1194int
1195mci_print_persistent(pathname, hostname)
1196	char *pathname;
1197	char *hostname;
1198{
1199	static bool initflag = false;
1200	SM_FILE_T *fp;
1201	int width = Verbose ? 78 : 25;
1202	bool locked;
1203	MCI mcib;
1204
1205	/* skip directories */
1206	if (hostname == NULL)
1207		return 0;
1208
1209	if (StopRequest)
1210		stop_sendmail();
1211
1212	if (!initflag)
1213	{
1214		initflag = true;
1215		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1216				     " -------------- Hostname --------------- How long ago ---------Results---------\n");
1217	}
1218
1219	fp = safefopen(pathname, O_RDONLY, FileMode,
1220		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
1221
1222	if (fp == NULL)
1223	{
1224		if (tTd(56, 1))
1225			sm_dprintf("mci_print_persistent: cannot open %s: %s\n",
1226				pathname, sm_errstring(errno));
1227		return 0;
1228	}
1229
1230	FileName = pathname;
1231	memset(&mcib, '\0', sizeof mcib);
1232	if (mci_read_persistent(fp, &mcib) < 0)
1233	{
1234		syserr("%s: could not read status file", pathname);
1235		(void) sm_io_close(fp, SM_TIME_DEFAULT);
1236		FileName = NULL;
1237		return 0;
1238	}
1239
1240	locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname,
1241			   "", LOCK_SH|LOCK_NB);
1242	(void) sm_io_close(fp, SM_TIME_DEFAULT);
1243	FileName = NULL;
1244
1245	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ",
1246			     locked ? '*' : ' ', hostname,
1247			     pintvl(curtime() - mcib.mci_lastuse, true));
1248	if (mcib.mci_rstatus != NULL)
1249	{
1250		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width,
1251				     mcib.mci_rstatus);
1252	}
1253	else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
1254		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1255				     "Deferred: %.*s\n", width - 10,
1256				     sm_errstring(mcib.mci_errno));
1257	else if (mcib.mci_exitstat != 0)
1258	{
1259		char *exmsg = sm_sysexmsg(mcib.mci_exitstat);
1260
1261		if (exmsg == NULL)
1262		{
1263			char buf[80];
1264
1265			(void) sm_snprintf(buf, sizeof buf,
1266				"Unknown mailer error %d",
1267				mcib.mci_exitstat);
1268			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1269					     width, buf);
1270		}
1271		else
1272			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1273					     width, &exmsg[5]);
1274	}
1275	else if (mcib.mci_errno == 0)
1276		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n");
1277	else
1278		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n",
1279				     width - 4, sm_errstring(mcib.mci_errno));
1280
1281	return 0;
1282}
1283/*
1284**  MCI_PURGE_PERSISTENT -- Remove a persistence status file.
1285**
1286**	Parameters:
1287**		pathname -- path to the status file.
1288**		hostname -- name of host corresponding to that file.
1289**			NULL if this is a directory (domain).
1290**
1291**	Returns:
1292**		0 -- ok
1293**		1 -- file not deleted (too young, incorrect format)
1294**		< 0 -- some error occurred
1295*/
1296
1297int
1298mci_purge_persistent(pathname, hostname)
1299	char *pathname;
1300	char *hostname;
1301{
1302	struct stat statbuf;
1303	char *end = pathname + strlen(pathname) - 1;
1304	int ret;
1305
1306	if (tTd(56, 1))
1307		sm_dprintf("mci_purge_persistent: purging %s\n", pathname);
1308
1309	ret = stat(pathname, &statbuf);
1310	if (ret < 0)
1311	{
1312		if (tTd(56, 2))
1313			sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n",
1314				pathname, sm_errstring(errno));
1315		return ret;
1316	}
1317	if (curtime() - statbuf.st_mtime <= MciInfoTimeout)
1318		return 1;
1319	if (hostname != NULL)
1320	{
1321		/* remove the file */
1322		ret = unlink(pathname);
1323		if (ret < 0)
1324		{
1325			if (LogLevel > 8)
1326				sm_syslog(LOG_ERR, NOQID,
1327					  "mci_purge_persistent: failed to unlink %s: %s",
1328					  pathname, sm_errstring(errno));
1329			if (tTd(56, 2))
1330				sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n",
1331					pathname, sm_errstring(errno));
1332			return ret;
1333		}
1334	}
1335	else
1336	{
1337		/* remove the directory */
1338		if (*end != '.')
1339			return 1;
1340
1341		if (tTd(56, 1))
1342			sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname);
1343
1344		ret = rmdir(pathname);
1345		if (ret < 0)
1346		{
1347			if (tTd(56, 2))
1348				sm_dprintf("mci_purge_persistent: rmdir %s: %s\n",
1349					pathname, sm_errstring(errno));
1350			return ret;
1351		}
1352	}
1353
1354	return 0;
1355}
1356/*
1357**  MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
1358**
1359**	Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a,
1360**	putting the result into `path'.  if `createflag' is set, intervening
1361**	directories will be created as needed.
1362**
1363**	Parameters:
1364**		host -- host name to convert from.
1365**		path -- place to store result.
1366**		pathlen -- length of path buffer.
1367**		createflag -- if set, create intervening directories as
1368**			needed.
1369**
1370**	Returns:
1371**		0 -- success
1372**		-1 -- failure
1373*/
1374
1375static int
1376mci_generate_persistent_path(host, path, pathlen, createflag)
1377	const char *host;
1378	char *path;
1379	int pathlen;
1380	bool createflag;
1381{
1382	char *elem, *p, *x, ch;
1383	int ret = 0;
1384	int len;
1385	char t_host[MAXHOSTNAMELEN];
1386#if NETINET6
1387	struct in6_addr in6_addr;
1388#endif /* NETINET6 */
1389
1390	/*
1391	**  Rationality check the arguments.
1392	*/
1393
1394	if (host == NULL)
1395	{
1396		syserr("mci_generate_persistent_path: null host");
1397		return -1;
1398	}
1399	if (path == NULL)
1400	{
1401		syserr("mci_generate_persistent_path: null path");
1402		return -1;
1403	}
1404
1405	if (tTd(56, 80))
1406		sm_dprintf("mci_generate_persistent_path(%s): ", host);
1407
1408	if (*host == '\0' || *host == '.')
1409		return -1;
1410
1411	/* make certain this is not a bracketed host number */
1412	if (strlen(host) > sizeof t_host - 1)
1413		return -1;
1414	if (host[0] == '[')
1415		(void) sm_strlcpy(t_host, host + 1, sizeof t_host);
1416	else
1417		(void) sm_strlcpy(t_host, host, sizeof t_host);
1418
1419	/*
1420	**  Delete any trailing dots from the hostname.
1421	**  Leave 'elem' pointing at the \0.
1422	*/
1423
1424	elem = t_host + strlen(t_host);
1425	while (elem > t_host &&
1426	       (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
1427		*--elem = '\0';
1428
1429	/* check for bogus bracketed address */
1430	if (host[0] == '[')
1431	{
1432		bool good = false;
1433# if NETINET6
1434		if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1)
1435			good = true;
1436# endif /* NETINET6 */
1437# if NETINET
1438		if (inet_addr(t_host) != INADDR_NONE)
1439			good = true;
1440# endif /* NETINET */
1441		if (!good)
1442			return -1;
1443	}
1444
1445	/* check for what will be the final length of the path */
1446	len = strlen(HostStatDir) + 2;
1447	for (p = (char *) t_host; *p != '\0'; p++)
1448	{
1449		if (*p == '.')
1450			len++;
1451		len++;
1452		if (p[0] == '.' && p[1] == '.')
1453			return -1;
1454	}
1455	if (len > pathlen || len < 1)
1456		return -1;
1457	(void) sm_strlcpy(path, HostStatDir, pathlen);
1458	p = path + strlen(path);
1459	while (elem > t_host)
1460	{
1461		if (!path_is_dir(path, createflag))
1462		{
1463			ret = -1;
1464			break;
1465		}
1466		elem--;
1467		while (elem >= t_host && *elem != '.')
1468			elem--;
1469		*p++ = '/';
1470		x = elem + 1;
1471		while ((ch = *x++) != '\0' && ch != '.')
1472		{
1473			if (isascii(ch) && isupper(ch))
1474				ch = tolower(ch);
1475			if (ch == '/')
1476				ch = ':';	/* / -> : */
1477			*p++ = ch;
1478		}
1479		if (elem >= t_host)
1480			*p++ = '.';
1481		*p = '\0';
1482	}
1483	if (tTd(56, 80))
1484	{
1485		if (ret < 0)
1486			sm_dprintf("FAILURE %d\n", ret);
1487		else
1488			sm_dprintf("SUCCESS %s\n", path);
1489	}
1490	return ret;
1491}
1492