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