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