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