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