1/*
2 * Copyright (c) 1998-2005, 2010 Proofpoint, 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 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: mci.c,v 8.225 2013-11-22 20:51:56 ca Exp $")
17
18#if NETINET || NETINET6
19# include <arpa/inet.h>
20#endif
21
22#include <dirent.h>
23#if STARTTLS
24# include <tls.h>
25#endif
26
27static int	mci_generate_persistent_path __P((const char *, char *,
28						  int, bool));
29static bool	mci_load_persistent __P((MCI *));
30static void	mci_uncache __P((MCI **, bool));
31static void	mci_clear __P((MCI *));
32static int	mci_lock_host_statfile __P((MCI *));
33static int	mci_read_persistent __P((SM_FILE_T *, MCI *));
34
35/*
36**  Mail Connection Information (MCI) Caching Module.
37**
38**	There are actually two separate things cached.  The first is
39**	the set of all open connections -- these are stored in a
40**	(small) list.  The second is stored in the symbol table; it
41**	has the overall status for all hosts, whether or not there
42**	is a connection open currently.
43**
44**	There should never be too many connections open (since this
45**	could flood the socket table), nor should a connection be
46**	allowed to sit idly for too long.
47**
48**	MaxMciCache is the maximum number of open connections that
49**	will be supported.
50**
51**	MciCacheTimeout is the time (in seconds) that a connection
52**	is permitted to survive without activity.
53**
54**	We actually try any cached connections by sending a RSET
55**	before we use them; if the RSET fails we close down the
56**	connection and reopen it (see smtpprobe()).
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			(void *)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			(void *)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
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		mci->mci_okrcpts = 0;
253	}
254
255	SM_FREE(mci->mci_status);
256	SM_FREE(mci->mci_rstatus);
257	SM_FREE(mci->mci_heloname);
258	mci_clear(mci);
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/*
296**  MCI_CLR_EXTENSIONS -- clear knowledge about SMTP extensions
297**
298**	Parameters:
299**		mci -- the connection to clear.
300**
301**	Returns:
302**		none.
303*/
304
305void
306mci_clr_extensions(mci)
307	MCI *mci;
308{
309	if (mci == NULL)
310		return;
311
312	mci->mci_flags &= ~MCIF_EXTENS;
313	mci->mci_maxsize = 0;
314	mci->mci_min_by = 0;
315#if SASL
316	mci->mci_saslcap = NULL;
317#endif
318}
319
320/*
321**  MCI_CLEAR -- clear mci
322**
323**	Parameters:
324**		mci -- the connection to clear.
325**
326**	Returns:
327**		none.
328*/
329
330static void
331mci_clear(mci)
332	MCI *mci;
333{
334	if (mci == NULL)
335		return;
336
337	mci->mci_maxsize = 0;
338	mci->mci_min_by = 0;
339	mci->mci_deliveries = 0;
340#if SASL
341	if (bitset(MCIF_AUTHACT, mci->mci_flags))
342		sasl_dispose(&mci->mci_conn);
343#endif
344#if STARTTLS
345	if (bitset(MCIF_TLSACT, mci->mci_flags) && mci->mci_ssl != NULL)
346		SM_SSL_FREE(mci->mci_ssl);
347#endif
348
349	/* which flags to preserve? */
350	mci->mci_flags &= MCIF_CACHED;
351	mactabclear(&mci->mci_macro);
352}
353
354/*
355**  MCI_GET -- get information about a particular host
356**
357**	Parameters:
358**		host -- host to look for.
359**		m -- mailer.
360**
361**	Returns:
362**		mci for this host (might be new).
363*/
364
365MCI *
366mci_get(host, m)
367	char *host;
368	MAILER *m;
369{
370	register MCI *mci;
371	register STAB *s;
372	extern SOCKADDR CurHostAddr;
373
374	/* clear CurHostAddr so we don't get a bogus address with this name */
375	memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
376
377	/* clear out any expired connections */
378	(void) mci_scan(NULL);
379
380	if (m->m_mno < 0)
381		syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
382
383	s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
384	mci = &s->s_mci;
385
386	/* initialize per-message data */
387	mci->mci_retryrcpt = false;
388	mci->mci_tolist = NULL;
389	mci->mci_okrcpts = 0;
390	mci->mci_flags &= ~MCIF_NOTSTICKY;
391
392	if (mci->mci_rpool == NULL)
393		mci->mci_rpool = sm_rpool_new_x(NULL);
394
395	if (mci->mci_macro.mac_rpool == NULL)
396		mci->mci_macro.mac_rpool = mci->mci_rpool;
397
398	/*
399	**  We don't need to load the persistent data if we have data
400	**  already loaded in the cache.
401	*/
402
403	if (mci->mci_host == NULL &&
404	    (mci->mci_host = s->s_name) != NULL &&
405	    !mci_load_persistent(mci))
406	{
407		if (tTd(42, 2))
408			sm_dprintf("mci_get(%s %s): lock failed\n",
409				host, m->m_name);
410		mci->mci_exitstat = EX_TEMPFAIL;
411		mci->mci_state = MCIS_CLOSED;
412		mci->mci_statfile = NULL;
413		return mci;
414	}
415
416	if (tTd(42, 2))
417	{
418		sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
419			host, m->m_name, mci->mci_state, mci->mci_flags,
420			mci->mci_exitstat, mci->mci_errno);
421	}
422
423	if (mci->mci_state == MCIS_OPEN)
424	{
425		/* poke the connection to see if it's still alive */
426		(void) smtpprobe(mci);
427
428		/* reset the stored state in the event of a timeout */
429		if (mci->mci_state != MCIS_OPEN)
430		{
431			mci->mci_errno = 0;
432			mci->mci_exitstat = EX_OK;
433			mci->mci_state = MCIS_CLOSED;
434		}
435		else
436		{
437			/* get peer host address */
438			/* (this should really be in the mci struct) */
439			SOCKADDR_LEN_T socklen = sizeof(CurHostAddr);
440
441			(void) getpeername(sm_io_getinfo(mci->mci_in,
442							 SM_IO_WHAT_FD, NULL),
443				(struct sockaddr *) &CurHostAddr, &socklen);
444		}
445	}
446	if (mci->mci_state == MCIS_CLOSED)
447	{
448		time_t now = curtime();
449
450		/* if this info is stale, ignore it */
451		if (mci->mci_lastuse + MciInfoTimeout <= now)
452		{
453			mci->mci_lastuse = now;
454			mci->mci_errno = 0;
455			mci->mci_exitstat = EX_OK;
456		}
457		mci_clear(mci);
458	}
459
460	return mci;
461}
462
463/*
464**  MCI_CLOSE -- (forcefully) close files used for a connection.
465**	Note: this is a last resort, usually smtpquit() or endmailer()
466**		should be used to close a connection.
467**
468**	Parameters:
469**		mci -- the connection to close.
470**		where -- where has this been called?
471**
472**	Returns:
473**		none.
474*/
475
476void
477mci_close(mci, where)
478	MCI *mci;
479	char *where;
480{
481	bool dumped;
482
483	if (mci == NULL)
484		return;
485	dumped = false;
486	if (mci->mci_out != NULL)
487	{
488		if (tTd(56, 1))
489		{
490			sm_dprintf("mci_close: mci_out!=NULL, where=%s\n",
491				where);
492			mci_dump(sm_debug_file(), mci, false);
493			dumped = true;
494		}
495		(void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
496		mci->mci_out = NULL;
497	}
498	if (mci->mci_in != NULL)
499	{
500		if (tTd(56, 1))
501		{
502			sm_dprintf("mci_close: mci_in!=NULL, where=%s\n",
503				where);
504			if (!dumped)
505				mci_dump(sm_debug_file(), mci, false);
506		}
507		(void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
508		mci->mci_in = NULL;
509	}
510	mci->mci_state = MCIS_CLOSED;
511}
512
513/*
514**  MCI_NEW -- allocate new MCI structure
515**
516**	Parameters:
517**		rpool -- if non-NULL: allocate from that rpool.
518**
519**	Returns:
520**		mci (new).
521*/
522
523MCI *
524mci_new(rpool)
525	SM_RPOOL_T *rpool;
526{
527	register MCI *mci;
528
529	if (rpool == NULL)
530		mci = (MCI *) sm_malloc_x(sizeof(*mci));
531	else
532		mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof(*mci));
533	memset((char *) mci, '\0', sizeof(*mci));
534	mci->mci_rpool = sm_rpool_new_x(NULL);
535	mci->mci_macro.mac_rpool = mci->mci_rpool;
536	return mci;
537}
538/*
539**  MCI_MATCH -- check connection cache for a particular host
540**
541**	Parameters:
542**		host -- host to look for.
543**		m -- mailer.
544**
545**	Returns:
546**		true iff open connection exists.
547*/
548
549bool
550mci_match(host, m)
551	char *host;
552	MAILER *m;
553{
554	register MCI *mci;
555	register STAB *s;
556
557	if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
558		return false;
559	s = stab(host, ST_MCI + m->m_mno, ST_FIND);
560	if (s == NULL)
561		return false;
562
563	mci = &s->s_mci;
564	return mci->mci_state == MCIS_OPEN;
565}
566/*
567**  MCI_SETSTAT -- set status codes in MCI structure.
568**
569**	Parameters:
570**		mci -- the MCI structure to set.
571**		xstat -- the exit status code.
572**		dstat -- the DSN status code.
573**		rstat -- the SMTP status code.
574**
575**	Returns:
576**		none.
577*/
578
579void
580mci_setstat(mci, xstat, dstat, rstat)
581	MCI *mci;
582	int xstat;
583	char *dstat;
584	char *rstat;
585{
586	/* protocol errors should never be interpreted as sticky */
587	if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
588		mci->mci_exitstat = xstat;
589
590	SM_FREE(mci->mci_status);
591	if (dstat != NULL)
592		mci->mci_status = sm_strdup_x(dstat);
593
594	SM_FREE(mci->mci_rstatus);
595	if (rstat != NULL)
596		mci->mci_rstatus = sm_strdup_x(rstat);
597}
598/*
599**  MCI_DUMP -- dump the contents of an MCI structure.
600**
601**	Parameters:
602**		fp -- output file pointer
603**		mci -- the MCI structure to dump.
604**
605**	Returns:
606**		none.
607**
608**	Side Effects:
609**		none.
610*/
611
612struct mcifbits
613{
614	int	mcif_bit;	/* flag bit */
615	char	*mcif_name;	/* flag name */
616};
617static struct mcifbits	MciFlags[] =
618{
619	{ MCIF_OCC_INCR,	"OCC_INCR"	},
620	{ MCIF_CACHED,		"CACHED"	},
621	{ MCIF_ESMTP,		"ESMTP"		},
622	{ MCIF_EXPN,		"EXPN"		},
623	{ MCIF_SIZE,		"SIZE"		},
624	{ MCIF_8BITMIME,	"8BITMIME"	},
625	{ MCIF_7BIT,		"7BIT"		},
626	{ MCIF_INHEADER,	"INHEADER"	},
627	{ MCIF_CVT8TO7,		"CVT8TO7"	},
628	{ MCIF_DSN,		"DSN"		},
629	{ MCIF_8BITOK,		"8BITOK"	},
630	{ MCIF_CVT7TO8,		"CVT7TO8"	},
631	{ MCIF_INMIME,		"INMIME"	},
632	{ MCIF_AUTH,		"AUTH"		},
633	{ MCIF_AUTHACT,		"AUTHACT"	},
634	{ MCIF_ENHSTAT,		"ENHSTAT"	},
635	{ MCIF_PIPELINED,	"PIPELINED"	},
636	{ MCIF_VERB,		"VERB"	},
637#if STARTTLS
638	{ MCIF_TLS,		"TLS"		},
639	{ MCIF_TLSACT,		"TLSACT"	},
640#endif
641	{ MCIF_DLVR_BY,		"DLVR_BY"	},
642#if _FFR_IGNORE_EXT_ON_HELO
643	{ MCIF_HELO,		"HELO"		},
644#endif
645	{ MCIF_INLONGLINE,	"INLONGLINE"	},
646	{ MCIF_AUTH2,		"AUTH2"		},
647	{ MCIF_ONLY_EHLO,	"ONLY_EHLO"	},
648	{ MCIF_NOTSTICKY,	"NOTSTICKY"	},
649#if USE_EAI
650	{ MCIF_EAI,		"EAI"		},
651#endif
652	{ 0,			NULL		}
653};
654
655void
656mci_dump(fp, mci, logit)
657	SM_FILE_T *fp;
658	register MCI *mci;
659	bool logit;
660{
661	register char *p;
662	char *sep;
663	char buf[4000];
664
665	sep = logit ? " " : "\n\t";
666	p = buf;
667	(void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", (void *)mci);
668	p += strlen(p);
669	if (mci == NULL)
670	{
671		(void) sm_snprintf(p, SPACELEFT(buf, p), "NULL");
672		goto printit;
673	}
674	(void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags);
675	p += strlen(p);
676
677	/*
678	**  The following check is just for paranoia.  It protects the
679	**  assignment in the if() clause. If there's not some minimum
680	**  amount of space we can stop right now. The check will not
681	**  trigger as long as sizeof(buf)=4000.
682	*/
683
684	if (p >= buf + sizeof(buf) - 4)
685		goto printit;
686	if (mci->mci_flags != 0)
687	{
688		struct mcifbits *f;
689
690		*p++ = '<';	/* protected above */
691		for (f = MciFlags; f->mcif_bit != 0; f++)
692		{
693			if (!bitset(f->mcif_bit, mci->mci_flags))
694				continue;
695			(void) sm_strlcpyn(p, SPACELEFT(buf, p), 2,
696					   f->mcif_name, ",");
697			p += strlen(p);
698		}
699		p[-1] = '>';
700	}
701
702	/* Note: sm_snprintf() takes care of NULL arguments for %s */
703	(void) sm_snprintf(p, SPACELEFT(buf, p),
704		",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
705		sep, mci->mci_errno, mci->mci_herrno,
706		mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep);
707	p += strlen(p);
708	(void) sm_snprintf(p, SPACELEFT(buf, p),
709		"maxsize=%ld, phase=%s, mailer=%s,%s",
710		mci->mci_maxsize, mci->mci_phase,
711		mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
712		sep);
713	p += strlen(p);
714	(void) sm_snprintf(p, SPACELEFT(buf, p),
715		"status=%s, rstatus=%s,%s",
716		mci->mci_status, mci->mci_rstatus, sep);
717	p += strlen(p);
718	(void) sm_snprintf(p, SPACELEFT(buf, p),
719		"host=%s, lastuse=%s",
720		mci->mci_host, ctime(&mci->mci_lastuse));
721printit:
722	if (logit)
723		sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
724	else
725		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf);
726}
727/*
728**  MCI_DUMP_ALL -- print the entire MCI cache
729**
730**	Parameters:
731**		fp -- output file pointer
732**		logit -- if set, log the result instead of printing
733**			to stdout.
734**
735**	Returns:
736**		none.
737*/
738
739void
740mci_dump_all(fp, logit)
741	SM_FILE_T *fp;
742	bool logit;
743{
744	register int i;
745
746	if (MciCache == NULL)
747		return;
748
749	for (i = 0; i < MaxMciCache; i++)
750		mci_dump(fp, MciCache[i], logit);
751}
752/*
753**  MCI_LOCK_HOST -- Lock host while sending.
754**
755**	If we are contacting a host, we'll need to
756**	update the status information in the host status
757**	file, and if we want to do that, we ought to have
758**	locked it. This has the (according to some)
759**	desirable effect of serializing connectivity with
760**	remote hosts -- i.e.: one connection to a given
761**	host at a time.
762**
763**	Parameters:
764**		mci -- containing the host we want to lock.
765**
766**	Returns:
767**		EX_OK	    -- got the lock.
768**		EX_TEMPFAIL -- didn't get the lock.
769*/
770
771int
772mci_lock_host(mci)
773	MCI *mci;
774{
775	if (mci == NULL)
776	{
777		if (tTd(56, 1))
778			sm_dprintf("mci_lock_host: NULL mci\n");
779		return EX_OK;
780	}
781
782	if (!SingleThreadDelivery)
783		return EX_OK;
784
785	return mci_lock_host_statfile(mci);
786}
787
788static int
789mci_lock_host_statfile(mci)
790	MCI *mci;
791{
792	int save_errno = errno;
793	int retVal = EX_OK;
794	char fname[MAXPATHLEN];
795
796	if (HostStatDir == NULL || mci->mci_host == NULL)
797		return EX_OK;
798
799	if (tTd(56, 2))
800		sm_dprintf("mci_lock_host: attempting to lock %s\n",
801			   mci->mci_host);
802
803	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname),
804					 true) < 0)
805	{
806		/* of course this should never happen */
807		if (tTd(56, 2))
808			sm_dprintf("mci_lock_host: Failed to generate host path for %s\n",
809				   mci->mci_host);
810
811		retVal = EX_TEMPFAIL;
812		goto cleanup;
813	}
814
815	mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
816				      SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
817
818	if (mci->mci_statfile == NULL)
819	{
820		syserr("mci_lock_host: cannot create host lock file %s", fname);
821		goto cleanup;
822	}
823
824	if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
825		      fname, "", LOCK_EX|LOCK_NB))
826	{
827		if (tTd(56, 2))
828			sm_dprintf("mci_lock_host: couldn't get lock on %s\n",
829				fname);
830		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
831		mci->mci_statfile = NULL;
832		retVal = EX_TEMPFAIL;
833		goto cleanup;
834	}
835
836	if (tTd(56, 12) && mci->mci_statfile != NULL)
837		sm_dprintf("mci_lock_host: Sanity check -- lock is good\n");
838
839cleanup:
840	errno = save_errno;
841	return retVal;
842}
843/*
844**  MCI_UNLOCK_HOST -- unlock host
845**
846**	Clean up the lock on a host, close the file, let
847**	someone else use it.
848**
849**	Parameters:
850**		mci -- us.
851**
852**	Returns:
853**		nothing.
854*/
855
856void
857mci_unlock_host(mci)
858	MCI *mci;
859{
860	int save_errno = errno;
861
862	if (mci == NULL)
863	{
864		if (tTd(56, 1))
865			sm_dprintf("mci_unlock_host: NULL mci\n");
866		return;
867	}
868
869	if (HostStatDir == NULL || mci->mci_host == NULL)
870		return;
871
872	if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
873	{
874		if (tTd(56, 1))
875			sm_dprintf("mci_unlock_host: stat file already locked\n");
876	}
877	else
878	{
879		if (tTd(56, 2))
880			sm_dprintf("mci_unlock_host: store prior to unlock\n");
881		mci_store_persistent(mci);
882	}
883
884	if (mci->mci_statfile != NULL)
885	{
886		(void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
887		mci->mci_statfile = NULL;
888	}
889
890	errno = save_errno;
891}
892/*
893**  MCI_LOAD_PERSISTENT -- load persistent host info
894**
895**	Load information about host that is kept
896**	in common for all running sendmails.
897**
898**	Parameters:
899**		mci -- the host/connection to load persistent info for.
900**
901**	Returns:
902**		true -- lock was successful
903**		false -- lock failed
904*/
905
906static bool
907mci_load_persistent(mci)
908	MCI *mci;
909{
910	int save_errno = errno;
911	bool locked = true;
912	SM_FILE_T *fp;
913	char fname[MAXPATHLEN];
914
915	if (mci == NULL)
916	{
917		if (tTd(56, 1))
918			sm_dprintf("mci_load_persistent: NULL mci\n");
919		return true;
920	}
921
922	if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
923		return true;
924
925	/* Already have the persistent information in memory */
926	if (SingleThreadDelivery && mci->mci_statfile != NULL)
927		return true;
928
929	if (tTd(56, 1))
930		sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n",
931			   mci->mci_host);
932
933	if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname),
934					 false) < 0)
935	{
936		/* Not much we can do if the file isn't there... */
937		if (tTd(56, 1))
938			sm_dprintf("mci_load_persistent: Couldn't generate host path\n");
939		goto cleanup;
940	}
941
942	fp = safefopen(fname, O_RDONLY, FileMode,
943		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
944	if (fp == NULL)
945	{
946		/* I can't think of any reason this should ever happen */
947		if (tTd(56, 1))
948			sm_dprintf("mci_load_persistent: open(%s): %s\n",
949				fname, sm_errstring(errno));
950		goto cleanup;
951	}
952
953	FileName = fname;
954	locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "",
955			  LOCK_SH|LOCK_NB);
956	if (locked)
957	{
958		(void) mci_read_persistent(fp, mci);
959		(void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname,
960				"", LOCK_UN);
961	}
962	FileName = NULL;
963	(void) sm_io_close(fp, SM_TIME_DEFAULT);
964
965cleanup:
966	errno = save_errno;
967	return locked;
968}
969/*
970**  MCI_READ_PERSISTENT -- read persistent host status file
971**
972**	Parameters:
973**		fp -- the file pointer to read.
974**		mci -- the pointer to fill in.
975**
976**	Returns:
977**		-1 -- if the file was corrupt.
978**		0 -- otherwise.
979**
980**	Warning:
981**		This code makes the assumption that this data
982**		will be read in an atomic fashion, and that the data
983**		was written in an atomic fashion.  Any other functioning
984**		may lead to some form of insanity.  This should be
985**		perfectly safe due to underlying stdio buffering.
986*/
987
988static int
989mci_read_persistent(fp, mci)
990	SM_FILE_T *fp;
991	register MCI *mci;
992{
993	int ver;
994	register char *p;
995	int saveLineNumber = LineNumber;
996	char buf[MAXLINE];
997
998	if (fp == NULL)
999	{
1000		syserr("mci_read_persistent: NULL fp");
1001		/* NOTREACHED */
1002		return -1;
1003	}
1004	if (mci == NULL)
1005	{
1006		syserr("mci_read_persistent: NULL mci");
1007		/* NOTREACHED */
1008		return -1;
1009	}
1010	if (tTd(56, 93))
1011	{
1012		sm_dprintf("mci_read_persistent: fp=%lx, mci=",
1013			   (unsigned long) fp);
1014	}
1015
1016	SM_FREE(mci->mci_status);
1017	SM_FREE(mci->mci_rstatus);
1018
1019	sm_io_rewind(fp, SM_TIME_DEFAULT);
1020	ver = -1;
1021	LineNumber = 0;
1022	while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
1023	{
1024		LineNumber++;
1025		p = strchr(buf, '\n');
1026		if (p != NULL)
1027			*p = '\0';
1028		switch (buf[0])
1029		{
1030		  case 'V':		/* version stamp */
1031			ver = atoi(&buf[1]);
1032			if (ver < 0 || ver > 0)
1033				syserr("Unknown host status version %d: %d max",
1034					ver, 0);
1035			break;
1036
1037		  case 'E':		/* UNIX error number */
1038			mci->mci_errno = atoi(&buf[1]);
1039			break;
1040
1041		  case 'H':		/* DNS error number */
1042			mci->mci_herrno = atoi(&buf[1]);
1043			break;
1044
1045		  case 'S':		/* UNIX exit status */
1046			mci->mci_exitstat = atoi(&buf[1]);
1047			break;
1048
1049		  case 'D':		/* DSN status */
1050			mci->mci_status = newstr(&buf[1]);
1051			break;
1052
1053		  case 'R':		/* SMTP status */
1054			mci->mci_rstatus = newstr(&buf[1]);
1055			break;
1056
1057		  case 'U':		/* last usage time */
1058			mci->mci_lastuse = atol(&buf[1]);
1059			break;
1060
1061		  case '.':		/* end of file */
1062			if (tTd(56, 93))
1063				mci_dump(sm_debug_file(), mci, false);
1064			return 0;
1065
1066		  default:
1067			sm_syslog(LOG_CRIT, NOQID,
1068				  "%s: line %d: Unknown host status line \"%s\"",
1069				  FileName == NULL ? mci->mci_host : FileName,
1070				  LineNumber, buf);
1071			LineNumber = saveLineNumber;
1072			return -1;
1073		}
1074	}
1075	LineNumber = saveLineNumber;
1076	if (tTd(56, 93))
1077		sm_dprintf("incomplete (missing dot for EOF)\n");
1078	if (ver < 0)
1079		return -1;
1080	return 0;
1081}
1082/*
1083**  MCI_STORE_PERSISTENT -- Store persistent MCI information
1084**
1085**	Store information about host that is kept
1086**	in common for all running sendmails.
1087**
1088**	Parameters:
1089**		mci -- the host/connection to store persistent info for.
1090**
1091**	Returns:
1092**		none.
1093*/
1094
1095void
1096mci_store_persistent(mci)
1097	MCI *mci;
1098{
1099	int save_errno = errno;
1100
1101	if (mci == NULL)
1102	{
1103		if (tTd(56, 1))
1104			sm_dprintf("mci_store_persistent: NULL mci\n");
1105		return;
1106	}
1107
1108	if (HostStatDir == NULL || mci->mci_host == NULL)
1109		return;
1110
1111	if (tTd(56, 1))
1112		sm_dprintf("mci_store_persistent: Storing information for %s\n",
1113			   mci->mci_host);
1114
1115	if (mci->mci_statfile == NULL)
1116	{
1117		if (tTd(56, 1))
1118			sm_dprintf("mci_store_persistent: no statfile\n");
1119		return;
1120	}
1121
1122	sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT);
1123#if !NOFTRUNCATE
1124	(void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
1125			 (off_t) 0);
1126#endif
1127
1128	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n");
1129	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n",
1130			     mci->mci_errno);
1131	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n",
1132			     mci->mci_herrno);
1133	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n",
1134			     mci->mci_exitstat);
1135	if (mci->mci_status != NULL)
1136		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
1137				     "D%.80s\n",
1138				     denlstring(mci->mci_status, true, false));
1139	if (mci->mci_rstatus != NULL)
1140		(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
1141				     "R%.80s\n",
1142				     denlstring(mci->mci_rstatus, true, false));
1143	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n",
1144			     (long)(mci->mci_lastuse));
1145	(void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n");
1146
1147	(void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT);
1148
1149	errno = save_errno;
1150	return;
1151}
1152/*
1153**  MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
1154**
1155**	Recursively find all the mci host files in `pathname'.  Default to
1156**		main host status directory if no path is provided.
1157**	Call (*action)(pathname, host) for each file found.
1158**
1159**	Note: all information is collected in a list before it is processed.
1160**	This may not be the best way to do it, but it seems safest, since
1161**	the file system would be touched while we are attempting to traverse
1162**	the directory tree otherwise (during purges).
1163**
1164**	Parameters:
1165**		action -- function to call on each node.  If returns < 0,
1166**			return immediately.
1167**		pathname -- root of tree.  If null, use main host status
1168**			directory.
1169**
1170**	Returns:
1171**		< 0 -- if any action routine returns a negative value, that
1172**			value is returned.
1173**		0 -- if we successfully went to completion.
1174**		> 0 -- return status from action()
1175*/
1176
1177int
1178mci_traverse_persistent(action, pathname)
1179	int (*action)__P((char *, char *));
1180	char *pathname;
1181{
1182	struct stat statbuf;
1183	DIR *d;
1184	int ret;
1185
1186	if (pathname == NULL)
1187		pathname = HostStatDir;
1188	if (pathname == NULL)
1189		return -1;
1190
1191	if (tTd(56, 1))
1192		sm_dprintf("mci_traverse: pathname is %s\n", pathname);
1193
1194	ret = stat(pathname, &statbuf);
1195	if (ret < 0)
1196	{
1197		if (tTd(56, 2))
1198			sm_dprintf("mci_traverse: Failed to stat %s: %s\n",
1199				pathname, sm_errstring(errno));
1200		return ret;
1201	}
1202	if (S_ISDIR(statbuf.st_mode))
1203	{
1204		bool leftone, removedone;
1205		size_t len;
1206		char *newptr;
1207		struct dirent *e;
1208		char newpath[MAXPATHLEN];
1209#if MAXPATHLEN <= MAXNAMLEN - 3
1210# error "MAXPATHLEN <= MAXNAMLEN - 3"
1211#endif
1212
1213		if ((d = opendir(pathname)) == NULL)
1214		{
1215			if (tTd(56, 2))
1216				sm_dprintf("mci_traverse: opendir %s: %s\n",
1217					pathname, sm_errstring(errno));
1218			return -1;
1219		}
1220
1221		/*
1222		**  Reserve space for trailing '/', at least one
1223		**  character, and '\0'
1224		*/
1225
1226		len = sizeof(newpath) - 3;
1227		if (sm_strlcpy(newpath, pathname, len) >= len)
1228		{
1229			int save_errno = errno;
1230
1231			if (tTd(56, 2))
1232				sm_dprintf("mci_traverse: path \"%s\" too long",
1233					pathname);
1234			(void) closedir(d);
1235			errno = save_errno;
1236			return -1;
1237		}
1238		newptr = newpath + strlen(newpath);
1239		*newptr++ = '/';
1240		len = sizeof(newpath) - (newptr - newpath);
1241
1242		/*
1243		**  repeat until no file has been removed
1244		**  this may become ugly when several files "expire"
1245		**  during these loops, but it's better than doing
1246		**  a rewinddir() inside the inner loop
1247		*/
1248
1249		do
1250		{
1251			leftone = removedone = false;
1252			while ((e = readdir(d)) != NULL)
1253			{
1254				if (e->d_name[0] == '.')
1255					continue;
1256
1257				if (sm_strlcpy(newptr, e->d_name, len) >= len)
1258				{
1259					/* Skip truncated copies */
1260					if (tTd(56, 4))
1261					{
1262						*newptr = '\0';
1263						sm_dprintf("mci_traverse: path \"%s%s\" too long",
1264							   newpath, e->d_name);
1265					}
1266					continue;
1267				}
1268
1269				if (StopRequest)
1270					stop_sendmail();
1271				ret = mci_traverse_persistent(action, newpath);
1272				if (ret < 0)
1273					break;
1274				if (ret == 1)
1275					leftone = true;
1276				if (!removedone && ret == 0 &&
1277				    action == mci_purge_persistent)
1278					removedone = true;
1279			}
1280			if (ret < 0)
1281				break;
1282
1283			/*
1284			**  The following appears to be
1285			**  necessary during purges, since
1286			**  we modify the directory structure
1287			*/
1288
1289			if (removedone)
1290				rewinddir(d);
1291			if (tTd(56, 40))
1292				sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n",
1293					pathname, ret, removedone, leftone);
1294		} while (removedone);
1295
1296		/* purge (or whatever) the directory proper */
1297		if (!leftone)
1298		{
1299			*--newptr = '\0';
1300			ret = (*action)(newpath, NULL);
1301		}
1302		(void) closedir(d);
1303	}
1304	else if (S_ISREG(statbuf.st_mode))
1305	{
1306		char *end = pathname + strlen(pathname) - 1;
1307		char *start;
1308		char *scan;
1309		char host[MAXHOSTNAMELEN];
1310		char *hostptr = host;
1311
1312		/*
1313		**  Reconstruct the host name from the path to the
1314		**  persistent information.
1315		*/
1316
1317		do
1318		{
1319			if (hostptr != host)
1320				*(hostptr++) = '.';
1321			start = end;
1322			while (start > pathname && *(start - 1) != '/')
1323				start--;
1324
1325			if (*end == '.')
1326				end--;
1327
1328			for (scan = start; scan <= end; scan++)
1329				*(hostptr++) = *scan;
1330
1331			end = start - 2;
1332		} while (end > pathname && *end == '.');
1333
1334		*hostptr = '\0';
1335
1336		/*
1337		**  Do something with the file containing the persistent
1338		**  information.
1339		*/
1340
1341		ret = (*action)(pathname, host);
1342	}
1343
1344	return ret;
1345}
1346/*
1347**  MCI_PRINT_PERSISTENT -- print persistent info
1348**
1349**	Dump the persistent information in the file 'pathname'
1350**
1351**	Parameters:
1352**		pathname -- the pathname to the status file.
1353**		hostname -- the corresponding host name.
1354**
1355**	Returns:
1356**		0
1357*/
1358
1359int
1360mci_print_persistent(pathname, hostname)
1361	char *pathname;
1362	char *hostname;
1363{
1364	static bool initflag = false;
1365	SM_FILE_T *fp;
1366	int width = Verbose ? 78 : 25;
1367	bool locked;
1368	MCI mcib;
1369
1370	/* skip directories */
1371	if (hostname == NULL)
1372		return 0;
1373
1374	if (StopRequest)
1375		stop_sendmail();
1376
1377	if (!initflag)
1378	{
1379		initflag = true;
1380		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1381				     " -------------- Hostname --------------- How long ago ---------Results---------\n");
1382	}
1383
1384	fp = safefopen(pathname, O_RDONLY, FileMode,
1385		       SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
1386
1387	if (fp == NULL)
1388	{
1389		if (tTd(56, 1))
1390			sm_dprintf("mci_print_persistent: cannot open %s: %s\n",
1391				pathname, sm_errstring(errno));
1392		return 0;
1393	}
1394
1395	FileName = pathname;
1396	memset(&mcib, '\0', sizeof(mcib));
1397	if (mci_read_persistent(fp, &mcib) < 0)
1398	{
1399		syserr("%s: could not read status file", pathname);
1400		(void) sm_io_close(fp, SM_TIME_DEFAULT);
1401		FileName = NULL;
1402		return 0;
1403	}
1404
1405	locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname,
1406			   "", LOCK_SH|LOCK_NB);
1407	(void) sm_io_close(fp, SM_TIME_DEFAULT);
1408	FileName = NULL;
1409
1410	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ",
1411			     locked ? '*' : ' ', hostname,
1412			     pintvl(curtime() - mcib.mci_lastuse, true));
1413	if (mcib.mci_rstatus != NULL)
1414		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width,
1415				     mcib.mci_rstatus);
1416	else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
1417		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1418				     "Deferred: %.*s\n", width - 10,
1419				     sm_errstring(mcib.mci_errno));
1420	else if (mcib.mci_exitstat != 0)
1421	{
1422		char *exmsg = sm_sysexmsg(mcib.mci_exitstat);
1423
1424		if (exmsg == NULL)
1425		{
1426			char buf[80];
1427
1428			(void) sm_snprintf(buf, sizeof(buf),
1429				"Unknown mailer error %d",
1430				mcib.mci_exitstat);
1431			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1432					     width, buf);
1433		}
1434		else
1435			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
1436					     width, &exmsg[5]);
1437	}
1438	else if (mcib.mci_errno == 0)
1439		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n");
1440	else
1441		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n",
1442				     width - 4, sm_errstring(mcib.mci_errno));
1443
1444	return 0;
1445}
1446/*
1447**  MCI_PURGE_PERSISTENT -- Remove a persistence status file.
1448**
1449**	Parameters:
1450**		pathname -- path to the status file.
1451**		hostname -- name of host corresponding to that file.
1452**			NULL if this is a directory (domain).
1453**
1454**	Returns:
1455**		0 -- ok
1456**		1 -- file not deleted (too young, incorrect format)
1457**		< 0 -- some error occurred
1458*/
1459
1460int
1461mci_purge_persistent(pathname, hostname)
1462	char *pathname;
1463	char *hostname;
1464{
1465	struct stat statbuf;
1466	char *end = pathname + strlen(pathname) - 1;
1467	int ret;
1468
1469	if (tTd(56, 1))
1470		sm_dprintf("mci_purge_persistent: purging %s\n", pathname);
1471
1472	ret = stat(pathname, &statbuf);
1473	if (ret < 0)
1474	{
1475		if (tTd(56, 2))
1476			sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n",
1477				pathname, sm_errstring(errno));
1478		return ret;
1479	}
1480	if (curtime() - statbuf.st_mtime <= MciInfoTimeout)
1481		return 1;
1482	if (hostname != NULL)
1483	{
1484		/* remove the file */
1485		ret = unlink(pathname);
1486		if (ret < 0)
1487		{
1488			if (LogLevel > 8)
1489				sm_syslog(LOG_ERR, NOQID,
1490					  "mci_purge_persistent: failed to unlink %s: %s",
1491					  pathname, sm_errstring(errno));
1492			if (tTd(56, 2))
1493				sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n",
1494					pathname, sm_errstring(errno));
1495			return ret;
1496		}
1497	}
1498	else
1499	{
1500		/* remove the directory */
1501		if (*end != '.')
1502			return 1;
1503
1504		if (tTd(56, 1))
1505			sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname);
1506
1507		ret = rmdir(pathname);
1508		if (ret < 0)
1509		{
1510			if (tTd(56, 2))
1511				sm_dprintf("mci_purge_persistent: rmdir %s: %s\n",
1512					pathname, sm_errstring(errno));
1513			return ret;
1514		}
1515	}
1516
1517	return 0;
1518}
1519/*
1520**  MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
1521**
1522**	Given `host', convert from a.b.c to $HostStatDir/c./b./a,
1523**	putting the result into `path'.  if `createflag' is set, intervening
1524**	directories will be created as needed.
1525**
1526**	Parameters:
1527**		host -- host name to convert from.
1528**		path -- place to store result.
1529**		pathlen -- length of path buffer.
1530**		createflag -- if set, create intervening directories as
1531**			needed.
1532**
1533**	Returns:
1534**		0 -- success
1535**		-1 -- failure
1536*/
1537
1538static int
1539mci_generate_persistent_path(host, path, pathlen, createflag)
1540	const char *host;
1541	char *path;
1542	int pathlen;
1543	bool createflag;
1544{
1545	char *elem, *p, *x, ch;
1546	int ret = 0;
1547	int len;
1548	char t_host[MAXHOSTNAMELEN];
1549#if NETINET6
1550	struct in6_addr in6_addr;
1551#endif
1552
1553	/*
1554	**  Rationality check the arguments.
1555	*/
1556
1557	if (host == NULL)
1558	{
1559		syserr("mci_generate_persistent_path: null host");
1560		return -1;
1561	}
1562	if (path == NULL)
1563	{
1564		syserr("mci_generate_persistent_path: null path");
1565		return -1;
1566	}
1567
1568	if (tTd(56, 80))
1569		sm_dprintf("mci_generate_persistent_path(%s): ", host);
1570
1571	if (*host == '\0' || *host == '.')
1572		return -1;
1573
1574	/* make certain this is not a bracketed host number */
1575	if (strlen(host) > sizeof(t_host) - 1)
1576		return -1;
1577	if (host[0] == '[')
1578		(void) sm_strlcpy(t_host, host + 1, sizeof(t_host));
1579	else
1580		(void) sm_strlcpy(t_host, host, sizeof(t_host));
1581
1582	/*
1583	**  Delete any trailing dots from the hostname.
1584	**  Leave 'elem' pointing at the \0.
1585	*/
1586
1587	elem = t_host + strlen(t_host);
1588	while (elem > t_host &&
1589	       (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
1590		*--elem = '\0';
1591
1592	/* check for bogus bracketed address */
1593	if (host[0] == '[')
1594	{
1595		bool good = false;
1596#if NETINET6
1597		if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1)
1598			good = true;
1599#endif
1600#if NETINET
1601		if (inet_addr(t_host) != INADDR_NONE)
1602			good = true;
1603#endif
1604		if (!good)
1605			return -1;
1606	}
1607
1608	/* check for what will be the final length of the path */
1609	len = strlen(HostStatDir) + 2;
1610	for (p = (char *) t_host; *p != '\0'; p++)
1611	{
1612		if (*p == '.')
1613			len++;
1614		len++;
1615		if (p[0] == '.' && p[1] == '.')
1616			return -1;
1617	}
1618	if (len > pathlen || len < 1)
1619		return -1;
1620	(void) sm_strlcpy(path, HostStatDir, pathlen);
1621	p = path + strlen(path);
1622	while (elem > t_host)
1623	{
1624		if (!path_is_dir(path, createflag))
1625		{
1626			ret = -1;
1627			break;
1628		}
1629		elem--;
1630		while (elem >= t_host && *elem != '.')
1631			elem--;
1632		*p++ = '/';
1633		x = elem + 1;
1634		while ((ch = *x++) != '\0' && ch != '.')
1635		{
1636			if (isascii(ch) && isupper(ch))
1637				ch = tolower(ch);
1638			if (ch == '/')
1639				ch = ':';	/* / -> : */
1640			*p++ = ch;
1641		}
1642		if (elem >= t_host)
1643			*p++ = '.';
1644		*p = '\0';
1645	}
1646	if (tTd(56, 80))
1647	{
1648		if (ret < 0)
1649			sm_dprintf("FAILURE %d\n", ret);
1650		else
1651			sm_dprintf("SUCCESS %s\n", path);
1652	}
1653	return ret;
1654}
1655