source: trunk/third/sendmail/sendmail/mci.c @ 19204

Revision 19204, 32.6 KB checked in by zacheiss, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
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
16SM_RCSID("@(#)$Id: mci.c,v 1.1.1.1 2003-04-08 15:08:45 zacheiss Exp $")
17
18#if NETINET || NETINET6
19# include <arpa/inet.h>
20#endif /* NETINET || NETINET6 */
21
22#include <dirent.h>
23
24static int      mci_generate_persistent_path __P((const char *, char *,
25                                                  int, bool));
26static bool     mci_load_persistent __P((MCI *));
27static void     mci_uncache __P((MCI **, bool));
28static int      mci_lock_host_statfile __P((MCI *));
29static 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
62static 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
76void
77mci_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
127MCI **
128mci_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
195static void
196mci_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
277void
278mci_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
304MCI *
305mci_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
411MCI *
412mci_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
437bool
438mci_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
467void
468mci_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
499struct mcifbits
500{
501        int     mcif_bit;       /* flag bit */
502        char    *mcif_name;     /* flag name */
503};
504static 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
531void
532mci_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));
586printit:
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
603void
604mci_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
634int
635mci_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
651static int
652mci_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
702cleanup:
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
719void
720mci_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
769static bool
770mci_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
828cleanup:
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
851static int
852mci_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
950void
951mci_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
1032int
1033mci_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
1192int
1193mci_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
1293int
1294mci_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
1371static int
1372mci_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}
Note: See TracBrowser for help on using the repository browser.