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