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