mci.c revision 141887
138032Speter/* 2111826Sgshapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 12120259Sgshapiro * $FreeBSD: head/contrib/sendmail/src/mci.c 141887 2005-02-14 08:04:08Z gshapiro $ 1338032Speter */ 1438032Speter 1564565Sgshapiro#include <sendmail.h> 1664565Sgshapiro 17141862SgshapiroSM_RCSID("@(#)$Id: mci.c,v 8.212 2004/08/04 21:11:31 ca Exp $") 1864565Sgshapiro 1964565Sgshapiro#if NETINET || NETINET6 2064565Sgshapiro# include <arpa/inet.h> 2164565Sgshapiro#endif /* NETINET || NETINET6 */ 2264565Sgshapiro 2338032Speter#include <dirent.h> 2438032Speter 2564565Sgshapirostatic int mci_generate_persistent_path __P((const char *, char *, 2664565Sgshapiro int, bool)); 2764565Sgshapirostatic bool mci_load_persistent __P((MCI *)); 2864565Sgshapirostatic void mci_uncache __P((MCI **, bool)); 2964565Sgshapirostatic int mci_lock_host_statfile __P((MCI *)); 3090795Sgshapirostatic int mci_read_persistent __P((SM_FILE_T *, MCI *)); 3164565Sgshapiro 3238032Speter/* 3338032Speter** Mail Connection Information (MCI) Caching Module. 3438032Speter** 3538032Speter** There are actually two separate things cached. The first is 3638032Speter** the set of all open connections -- these are stored in a 3738032Speter** (small) list. The second is stored in the symbol table; it 3838032Speter** has the overall status for all hosts, whether or not there 3938032Speter** is a connection open currently. 4038032Speter** 4138032Speter** There should never be too many connections open (since this 4238032Speter** could flood the socket table), nor should a connection be 4338032Speter** allowed to sit idly for too long. 4438032Speter** 4538032Speter** MaxMciCache is the maximum number of open connections that 4638032Speter** will be supported. 4738032Speter** 4838032Speter** MciCacheTimeout is the time (in seconds) that a connection 4938032Speter** is permitted to survive without activity. 5038032Speter** 5138032Speter** We actually try any cached connections by sending a NOOP 5238032Speter** before we use them; if the NOOP fails we close down the 5338032Speter** connection and reopen it. Note that this means that a 5438032Speter** server SMTP that doesn't support NOOP will hose the 5538032Speter** algorithm -- but that doesn't seem too likely. 5638032Speter** 5738032Speter** The persistent MCI code is donated by Mark Lovell and Paul 5838032Speter** Vixie. It is based on the long term host status code in KJS 5938032Speter** written by Paul but has been adapted by Mark to fit into the 6038032Speter** MCI structure. 6138032Speter*/ 6238032Speter 6364565Sgshapirostatic MCI **MciCache; /* the open connection cache */ 6438032Speter 6590795Sgshapiro/* 6638032Speter** MCI_CACHE -- enter a connection structure into the open connection cache 6738032Speter** 6838032Speter** This may cause something else to be flushed. 6938032Speter** 7038032Speter** Parameters: 7138032Speter** mci -- the connection to cache. 7238032Speter** 7338032Speter** Returns: 7438032Speter** none. 7538032Speter*/ 7638032Speter 7738032Spetervoid 7838032Spetermci_cache(mci) 7938032Speter register MCI *mci; 8038032Speter{ 8138032Speter register MCI **mcislot; 8238032Speter 8338032Speter /* 8438032Speter ** Find the best slot. This may cause expired connections 8538032Speter ** to be closed. 8638032Speter */ 8738032Speter 8838032Speter mcislot = mci_scan(mci); 8938032Speter if (mcislot == NULL) 9038032Speter { 9138032Speter /* we don't support caching */ 9238032Speter return; 9338032Speter } 9438032Speter 9538032Speter if (mci->mci_host == NULL) 9638032Speter return; 9738032Speter 9838032Speter /* if this is already cached, we are done */ 9938032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 10038032Speter return; 10138032Speter 10238032Speter /* otherwise we may have to clear the slot */ 10338032Speter if (*mcislot != NULL) 10490795Sgshapiro mci_uncache(mcislot, true); 10538032Speter 10638032Speter if (tTd(42, 5)) 10790795Sgshapiro sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 10890795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache)); 10938032Speter if (tTd(91, 100)) 11038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 11164565Sgshapiro "mci_cache: caching %lx (%.100s) in slot %d", 11290795Sgshapiro (unsigned long) mci, mci->mci_host, 11390795Sgshapiro (int) (mcislot - MciCache)); 11438032Speter 11538032Speter *mcislot = mci; 11638032Speter mci->mci_flags |= MCIF_CACHED; 11738032Speter} 11890795Sgshapiro/* 11938032Speter** MCI_SCAN -- scan the cache, flush junk, and return best slot 12038032Speter** 12138032Speter** Parameters: 12238032Speter** savemci -- never flush this one. Can be null. 12338032Speter** 12438032Speter** Returns: 12538032Speter** The LRU (or empty) slot. 12638032Speter*/ 12738032Speter 12838032SpeterMCI ** 12938032Spetermci_scan(savemci) 13038032Speter MCI *savemci; 13138032Speter{ 13238032Speter time_t now; 13338032Speter register MCI **bestmci; 13438032Speter register MCI *mci; 13538032Speter register int i; 13638032Speter 13738032Speter if (MaxMciCache <= 0) 13838032Speter { 13938032Speter /* we don't support caching */ 14038032Speter return NULL; 14138032Speter } 14238032Speter 14338032Speter if (MciCache == NULL) 14438032Speter { 14538032Speter /* first call */ 14690795Sgshapiro MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); 14764565Sgshapiro memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); 14864565Sgshapiro return &MciCache[0]; 14938032Speter } 15038032Speter 15138032Speter now = curtime(); 15238032Speter bestmci = &MciCache[0]; 15338032Speter for (i = 0; i < MaxMciCache; i++) 15438032Speter { 15538032Speter mci = MciCache[i]; 15638032Speter if (mci == NULL || mci->mci_state == MCIS_CLOSED) 15738032Speter { 15838032Speter bestmci = &MciCache[i]; 15938032Speter continue; 16038032Speter } 16194337Sgshapiro if ((mci->mci_lastuse + MciCacheTimeout <= now || 16264565Sgshapiro (mci->mci_mailer != NULL && 16364565Sgshapiro mci->mci_mailer->m_maxdeliveries > 0 && 16464565Sgshapiro mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 16564565Sgshapiro mci != savemci) 16638032Speter { 16764565Sgshapiro /* connection idle too long or too many deliveries */ 16838032Speter bestmci = &MciCache[i]; 16964565Sgshapiro 17064565Sgshapiro /* close it */ 17190795Sgshapiro mci_uncache(bestmci, true); 17238032Speter continue; 17338032Speter } 17438032Speter if (*bestmci == NULL) 17538032Speter continue; 17638032Speter if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 17738032Speter bestmci = &MciCache[i]; 17838032Speter } 17938032Speter return bestmci; 18038032Speter} 18190795Sgshapiro/* 18238032Speter** MCI_UNCACHE -- remove a connection from a slot. 18338032Speter** 18438032Speter** May close a connection. 18538032Speter** 18638032Speter** Parameters: 18738032Speter** mcislot -- the slot to empty. 18890795Sgshapiro** doquit -- if true, send QUIT protocol on this connection. 18990795Sgshapiro** if false, we are assumed to be in a forked child; 19038032Speter** all we want to do is close the file(s). 19138032Speter** 19238032Speter** Returns: 19338032Speter** none. 19438032Speter*/ 19538032Speter 19664565Sgshapirostatic void 19738032Spetermci_uncache(mcislot, doquit) 19838032Speter register MCI **mcislot; 19938032Speter bool doquit; 20038032Speter{ 20138032Speter register MCI *mci; 20238032Speter extern ENVELOPE BlankEnvelope; 20338032Speter 20438032Speter mci = *mcislot; 20538032Speter if (mci == NULL) 20638032Speter return; 20738032Speter *mcislot = NULL; 20838032Speter if (mci->mci_host == NULL) 20938032Speter return; 21038032Speter 21138032Speter mci_unlock_host(mci); 21238032Speter 21338032Speter if (tTd(42, 5)) 21490795Sgshapiro sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 21590795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache), 21690795Sgshapiro doquit); 21738032Speter if (tTd(91, 100)) 21838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 21964565Sgshapiro "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 22090795Sgshapiro (unsigned long) mci, mci->mci_host, 22190795Sgshapiro (int) (mcislot - MciCache), doquit); 22238032Speter 22364565Sgshapiro mci->mci_deliveries = 0; 22438032Speter if (doquit) 22538032Speter { 22638032Speter message("Closing connection to %s", mci->mci_host); 22738032Speter 22838032Speter mci->mci_flags &= ~MCIF_CACHED; 22938032Speter 23038032Speter /* only uses the envelope to flush the transcript file */ 23138032Speter if (mci->mci_state != MCIS_CLOSED) 23238032Speter smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 23390795Sgshapiro#if XLA 23438032Speter xla_host_end(mci->mci_host); 23590795Sgshapiro#endif /* XLA */ 23638032Speter } 23738032Speter else 23838032Speter { 23938032Speter if (mci->mci_in != NULL) 24090795Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 24138032Speter if (mci->mci_out != NULL) 24290795Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 24338032Speter mci->mci_in = mci->mci_out = NULL; 24438032Speter mci->mci_state = MCIS_CLOSED; 24538032Speter mci->mci_exitstat = EX_OK; 24638032Speter mci->mci_errno = 0; 24738032Speter mci->mci_flags = 0; 24890795Sgshapiro 24990795Sgshapiro mci->mci_retryrcpt = false; 25090795Sgshapiro mci->mci_tolist = NULL; 25190795Sgshapiro#if PIPELINING 25290795Sgshapiro mci->mci_okrcpts = 0; 25390795Sgshapiro#endif /* PIPELINING */ 25438032Speter } 25590795Sgshapiro 25690795Sgshapiro SM_FREE_CLR(mci->mci_status); 25790795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 25890795Sgshapiro SM_FREE_CLR(mci->mci_heloname); 25990795Sgshapiro if (mci->mci_rpool != NULL) 26090795Sgshapiro { 26190795Sgshapiro sm_rpool_free(mci->mci_rpool); 26290795Sgshapiro mci->mci_macro.mac_rpool = NULL; 26390795Sgshapiro mci->mci_rpool = NULL; 26490795Sgshapiro } 26538032Speter} 26690795Sgshapiro/* 26738032Speter** MCI_FLUSH -- flush the entire cache 26838032Speter** 26938032Speter** Parameters: 27090795Sgshapiro** doquit -- if true, send QUIT protocol. 27190795Sgshapiro** if false, just close the connection. 27238032Speter** allbut -- but leave this one open. 27338032Speter** 27438032Speter** Returns: 27538032Speter** none. 27638032Speter*/ 27738032Speter 27838032Spetervoid 27938032Spetermci_flush(doquit, allbut) 28038032Speter bool doquit; 28138032Speter MCI *allbut; 28238032Speter{ 28338032Speter register int i; 28438032Speter 28538032Speter if (MciCache == NULL) 28638032Speter return; 28738032Speter 28838032Speter for (i = 0; i < MaxMciCache; i++) 28971348Sgshapiro { 29038032Speter if (allbut != MciCache[i]) 29138032Speter mci_uncache(&MciCache[i], doquit); 29271348Sgshapiro } 29338032Speter} 29490795Sgshapiro/* 29538032Speter** MCI_GET -- get information about a particular host 29690795Sgshapiro** 29790795Sgshapiro** Parameters: 29890795Sgshapiro** host -- host to look for. 29990795Sgshapiro** m -- mailer. 30090795Sgshapiro** 30190795Sgshapiro** Returns: 30290795Sgshapiro** mci for this host (might be new). 30338032Speter*/ 30438032Speter 30538032SpeterMCI * 30638032Spetermci_get(host, m) 30738032Speter char *host; 30838032Speter MAILER *m; 30938032Speter{ 31038032Speter register MCI *mci; 31138032Speter register STAB *s; 31238032Speter extern SOCKADDR CurHostAddr; 31338032Speter 31438032Speter /* clear CurHostAddr so we don't get a bogus address with this name */ 31564565Sgshapiro memset(&CurHostAddr, '\0', sizeof CurHostAddr); 31638032Speter 31738032Speter /* clear out any expired connections */ 31838032Speter (void) mci_scan(NULL); 31938032Speter 32038032Speter if (m->m_mno < 0) 32171348Sgshapiro syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 32264565Sgshapiro 32338032Speter s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 32438032Speter mci = &s->s_mci; 32538032Speter 32690795Sgshapiro /* initialize per-message data */ 32790795Sgshapiro mci->mci_retryrcpt = false; 32890795Sgshapiro mci->mci_tolist = NULL; 32990795Sgshapiro#if PIPELINING 33090795Sgshapiro mci->mci_okrcpts = 0; 33190795Sgshapiro#endif /* PIPELINING */ 33290795Sgshapiro 33390795Sgshapiro if (mci->mci_rpool == NULL) 33490795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 33590795Sgshapiro 33690795Sgshapiro if (mci->mci_macro.mac_rpool == NULL) 33790795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 33890795Sgshapiro 33964565Sgshapiro /* 34090795Sgshapiro ** We don't need to load the persistent data if we have data 34164565Sgshapiro ** already loaded in the cache. 34264565Sgshapiro */ 34364565Sgshapiro 34464565Sgshapiro if (mci->mci_host == NULL && 34564565Sgshapiro (mci->mci_host = s->s_name) != NULL && 34664565Sgshapiro !mci_load_persistent(mci)) 34738032Speter { 34838032Speter if (tTd(42, 2)) 34990795Sgshapiro sm_dprintf("mci_get(%s %s): lock failed\n", 35064565Sgshapiro host, m->m_name); 35138032Speter mci->mci_exitstat = EX_TEMPFAIL; 35238032Speter mci->mci_state = MCIS_CLOSED; 35338032Speter mci->mci_statfile = NULL; 35438032Speter return mci; 35538032Speter } 35638032Speter 35738032Speter if (tTd(42, 2)) 35838032Speter { 35990795Sgshapiro sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 36038032Speter host, m->m_name, mci->mci_state, mci->mci_flags, 36138032Speter mci->mci_exitstat, mci->mci_errno); 36238032Speter } 36338032Speter 36438032Speter if (mci->mci_state == MCIS_OPEN) 36538032Speter { 36638032Speter /* poke the connection to see if it's still alive */ 36738032Speter (void) smtpprobe(mci); 36838032Speter 36938032Speter /* reset the stored state in the event of a timeout */ 37038032Speter if (mci->mci_state != MCIS_OPEN) 37138032Speter { 37238032Speter mci->mci_errno = 0; 37338032Speter mci->mci_exitstat = EX_OK; 37438032Speter mci->mci_state = MCIS_CLOSED; 37538032Speter } 37638032Speter else 37738032Speter { 37890795Sgshapiro /* get peer host address */ 37938032Speter /* (this should really be in the mci struct) */ 38038032Speter SOCKADDR_LEN_T socklen = sizeof CurHostAddr; 38138032Speter 38290795Sgshapiro (void) getpeername(sm_io_getinfo(mci->mci_in, 38390795Sgshapiro SM_IO_WHAT_FD, NULL), 38438032Speter (struct sockaddr *) &CurHostAddr, &socklen); 38538032Speter } 38638032Speter } 38738032Speter if (mci->mci_state == MCIS_CLOSED) 38838032Speter { 38938032Speter time_t now = curtime(); 39038032Speter 39138032Speter /* if this info is stale, ignore it */ 39294337Sgshapiro if (mci->mci_lastuse + MciInfoTimeout <= now) 39338032Speter { 39438032Speter mci->mci_lastuse = now; 39538032Speter mci->mci_errno = 0; 39638032Speter mci->mci_exitstat = EX_OK; 39738032Speter } 39838032Speter } 39938032Speter 40038032Speter return mci; 40138032Speter} 402141887Sgshapiro 40390795Sgshapiro/* 404141887Sgshapiro** MCI_CLOSE -- (forcefully) close files used for a connection. 405141887Sgshapiro** Note: this is a last resort, usually smtpquit() or endmailer() 406141887Sgshapiro** should be used to close a connection. 407141887Sgshapiro** 408141887Sgshapiro** Parameters: 409141887Sgshapiro** mci -- the connection to close. 410141887Sgshapiro** where -- where has this been called? 411141887Sgshapiro** 412141887Sgshapiro** Returns: 413141887Sgshapiro** none. 414141887Sgshapiro*/ 415141887Sgshapiro 416141887Sgshapirovoid 417141887Sgshapiromci_close(mci, where) 418141887Sgshapiro MCI *mci; 419141887Sgshapiro char *where; 420141887Sgshapiro{ 421141887Sgshapiro bool dumped; 422141887Sgshapiro 423141887Sgshapiro if (mci == NULL) 424141887Sgshapiro return; 425141887Sgshapiro dumped = false; 426141887Sgshapiro if (mci->mci_out != NULL) 427141887Sgshapiro { 428141887Sgshapiro if (tTd(56, 1)) 429141887Sgshapiro { 430141887Sgshapiro sm_dprintf("mci_close: mci_out!=NULL, where=%s\n", 431141887Sgshapiro where); 432141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 433141887Sgshapiro dumped = true; 434141887Sgshapiro } 435141887Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 436141887Sgshapiro mci->mci_out = NULL; 437141887Sgshapiro } 438141887Sgshapiro if (mci->mci_in != NULL) 439141887Sgshapiro { 440141887Sgshapiro if (tTd(56, 1)) 441141887Sgshapiro { 442141887Sgshapiro sm_dprintf("mci_close: mci_in!=NULL, where=%s\n", 443141887Sgshapiro where); 444141887Sgshapiro if (!dumped) 445141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 446141887Sgshapiro } 447141887Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 448141887Sgshapiro mci->mci_in = NULL; 449141887Sgshapiro } 450141887Sgshapiro mci->mci_state = MCIS_CLOSED; 451141887Sgshapiro} 452141887Sgshapiro 453141887Sgshapiro/* 45490795Sgshapiro** MCI_NEW -- allocate new MCI structure 45590795Sgshapiro** 45690795Sgshapiro** Parameters: 45790795Sgshapiro** rpool -- if non-NULL: allocate from that rpool. 45890795Sgshapiro** 45990795Sgshapiro** Returns: 46090795Sgshapiro** mci (new). 46190795Sgshapiro*/ 46290795Sgshapiro 46390795SgshapiroMCI * 46490795Sgshapiromci_new(rpool) 46590795Sgshapiro SM_RPOOL_T *rpool; 46690795Sgshapiro{ 46790795Sgshapiro register MCI *mci; 46890795Sgshapiro 46990795Sgshapiro if (rpool == NULL) 47090795Sgshapiro mci = (MCI *) sm_malloc_x(sizeof *mci); 47190795Sgshapiro else 47290795Sgshapiro mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); 47390795Sgshapiro memset((char *) mci, '\0', sizeof *mci); 47490795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 47590795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 47690795Sgshapiro return mci; 47790795Sgshapiro} 47890795Sgshapiro/* 47964565Sgshapiro** MCI_MATCH -- check connection cache for a particular host 48090795Sgshapiro** 48190795Sgshapiro** Parameters: 48290795Sgshapiro** host -- host to look for. 48390795Sgshapiro** m -- mailer. 48490795Sgshapiro** 48590795Sgshapiro** Returns: 48690795Sgshapiro** true iff open connection exists. 48764565Sgshapiro*/ 48864565Sgshapiro 48964565Sgshapirobool 49064565Sgshapiromci_match(host, m) 49164565Sgshapiro char *host; 49264565Sgshapiro MAILER *m; 49364565Sgshapiro{ 49464565Sgshapiro register MCI *mci; 49564565Sgshapiro register STAB *s; 49664565Sgshapiro 49771348Sgshapiro if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 49890795Sgshapiro return false; 49964565Sgshapiro s = stab(host, ST_MCI + m->m_mno, ST_FIND); 50064565Sgshapiro if (s == NULL) 50190795Sgshapiro return false; 50264565Sgshapiro 50364565Sgshapiro mci = &s->s_mci; 50490795Sgshapiro return mci->mci_state == MCIS_OPEN; 50564565Sgshapiro} 50690795Sgshapiro/* 50738032Speter** MCI_SETSTAT -- set status codes in MCI structure. 50838032Speter** 50938032Speter** Parameters: 51038032Speter** mci -- the MCI structure to set. 51138032Speter** xstat -- the exit status code. 51238032Speter** dstat -- the DSN status code. 51338032Speter** rstat -- the SMTP status code. 51438032Speter** 51538032Speter** Returns: 51638032Speter** none. 51738032Speter*/ 51838032Speter 51938032Spetervoid 52038032Spetermci_setstat(mci, xstat, dstat, rstat) 52138032Speter MCI *mci; 52238032Speter int xstat; 52338032Speter char *dstat; 52438032Speter char *rstat; 52538032Speter{ 52638032Speter /* protocol errors should never be interpreted as sticky */ 52738032Speter if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 52838032Speter mci->mci_exitstat = xstat; 52938032Speter 53090795Sgshapiro SM_FREE_CLR(mci->mci_status); 53190795Sgshapiro if (dstat != NULL) 53290795Sgshapiro mci->mci_status = sm_strdup_x(dstat); 53390795Sgshapiro 53490795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 53538032Speter if (rstat != NULL) 53690795Sgshapiro mci->mci_rstatus = sm_strdup_x(rstat); 53738032Speter} 53890795Sgshapiro/* 53938032Speter** MCI_DUMP -- dump the contents of an MCI structure. 54038032Speter** 54138032Speter** Parameters: 542132946Sgshapiro** fp -- output file pointer 54338032Speter** mci -- the MCI structure to dump. 54438032Speter** 54538032Speter** Returns: 54638032Speter** none. 54738032Speter** 54838032Speter** Side Effects: 54938032Speter** none. 55038032Speter*/ 55138032Speter 55238032Speterstruct mcifbits 55338032Speter{ 55438032Speter int mcif_bit; /* flag bit */ 55538032Speter char *mcif_name; /* flag name */ 55638032Speter}; 55764565Sgshapirostatic struct mcifbits MciFlags[] = 55838032Speter{ 55938032Speter { MCIF_VALID, "VALID" }, 56038032Speter { MCIF_CACHED, "CACHED" }, 56138032Speter { MCIF_ESMTP, "ESMTP" }, 56238032Speter { MCIF_EXPN, "EXPN" }, 56338032Speter { MCIF_SIZE, "SIZE" }, 56438032Speter { MCIF_8BITMIME, "8BITMIME" }, 56538032Speter { MCIF_7BIT, "7BIT" }, 56638032Speter { MCIF_INHEADER, "INHEADER" }, 56738032Speter { MCIF_CVT8TO7, "CVT8TO7" }, 56838032Speter { MCIF_DSN, "DSN" }, 56938032Speter { MCIF_8BITOK, "8BITOK" }, 57038032Speter { MCIF_CVT7TO8, "CVT7TO8" }, 57138032Speter { MCIF_INMIME, "INMIME" }, 57290795Sgshapiro { MCIF_AUTH, "AUTH" }, 57390795Sgshapiro { MCIF_AUTHACT, "AUTHACT" }, 57490795Sgshapiro { MCIF_ENHSTAT, "ENHSTAT" }, 57590795Sgshapiro { MCIF_PIPELINED, "PIPELINED" }, 57690795Sgshapiro#if STARTTLS 57790795Sgshapiro { MCIF_TLS, "TLS" }, 57890795Sgshapiro { MCIF_TLSACT, "TLSACT" }, 57990795Sgshapiro#endif /* STARTTLS */ 58090795Sgshapiro { MCIF_DLVR_BY, "DLVR_BY" }, 58171348Sgshapiro { 0, NULL } 58238032Speter}; 58338032Speter 58438032Spetervoid 585132946Sgshapiromci_dump(fp, mci, logit) 586132946Sgshapiro SM_FILE_T *fp; 58738032Speter register MCI *mci; 58838032Speter bool logit; 58938032Speter{ 59038032Speter register char *p; 59138032Speter char *sep; 59238032Speter char buf[4000]; 59338032Speter 59438032Speter sep = logit ? " " : "\n\t"; 59538032Speter p = buf; 59690795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 59738032Speter p += strlen(p); 59838032Speter if (mci == NULL) 59938032Speter { 60090795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 60138032Speter goto printit; 60238032Speter } 60390795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 60438032Speter p += strlen(p); 605120259Sgshapiro 606120259Sgshapiro /* 607120259Sgshapiro ** The following check is just for paranoia. It protects the 608120259Sgshapiro ** assignment in the if() clause. If there's not some minimum 609120259Sgshapiro ** amount of space we can stop right now. The check will not 610120259Sgshapiro ** trigger as long as sizeof(buf)=4000. 611120259Sgshapiro */ 612120259Sgshapiro 613120259Sgshapiro if (p >= buf + sizeof(buf) - 4) 614120259Sgshapiro goto printit; 61538032Speter if (mci->mci_flags != 0) 61638032Speter { 61738032Speter struct mcifbits *f; 61838032Speter 619120259Sgshapiro *p++ = '<'; /* protected above */ 62038032Speter for (f = MciFlags; f->mcif_bit != 0; f++) 62138032Speter { 62238032Speter if (!bitset(f->mcif_bit, mci->mci_flags)) 62338032Speter continue; 62490795Sgshapiro (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 62590795Sgshapiro f->mcif_name, ","); 62638032Speter p += strlen(p); 62738032Speter } 62838032Speter p[-1] = '>'; 62938032Speter } 63090795Sgshapiro 63190795Sgshapiro /* Note: sm_snprintf() takes care of NULL arguments for %s */ 63290795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 63338032Speter ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 63438032Speter sep, mci->mci_errno, mci->mci_herrno, 63564565Sgshapiro mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 63638032Speter p += strlen(p); 63790795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 63838032Speter "maxsize=%ld, phase=%s, mailer=%s,%s", 63990795Sgshapiro mci->mci_maxsize, mci->mci_phase, 64038032Speter mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 64138032Speter sep); 64238032Speter p += strlen(p); 64390795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 64438032Speter "status=%s, rstatus=%s,%s", 64590795Sgshapiro mci->mci_status, mci->mci_rstatus, sep); 64638032Speter p += strlen(p); 64790795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 64838032Speter "host=%s, lastuse=%s", 64990795Sgshapiro mci->mci_host, ctime(&mci->mci_lastuse)); 65038032Speterprintit: 65138032Speter if (logit) 65238032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 65338032Speter else 654132946Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf); 65538032Speter} 65690795Sgshapiro/* 65738032Speter** MCI_DUMP_ALL -- print the entire MCI cache 65838032Speter** 65938032Speter** Parameters: 660132946Sgshapiro** fp -- output file pointer 66138032Speter** logit -- if set, log the result instead of printing 66238032Speter** to stdout. 66338032Speter** 66438032Speter** Returns: 66538032Speter** none. 66638032Speter*/ 66738032Speter 66838032Spetervoid 669132946Sgshapiromci_dump_all(fp, logit) 670132946Sgshapiro SM_FILE_T *fp; 67138032Speter bool logit; 67238032Speter{ 67338032Speter register int i; 67438032Speter 67538032Speter if (MciCache == NULL) 67638032Speter return; 67738032Speter 67838032Speter for (i = 0; i < MaxMciCache; i++) 679132946Sgshapiro mci_dump(fp, MciCache[i], logit); 68038032Speter} 68190795Sgshapiro/* 68242580Speter** MCI_LOCK_HOST -- Lock host while sending. 68338032Speter** 68438032Speter** If we are contacting a host, we'll need to 68538032Speter** update the status information in the host status 68638032Speter** file, and if we want to do that, we ought to have 68738032Speter** locked it. This has the (according to some) 68838032Speter** desirable effect of serializing connectivity with 68990795Sgshapiro** remote hosts -- i.e.: one connection to a given 69038032Speter** host at a time. 69138032Speter** 69238032Speter** Parameters: 69338032Speter** mci -- containing the host we want to lock. 69438032Speter** 69538032Speter** Returns: 69664565Sgshapiro** EX_OK -- got the lock. 69738032Speter** EX_TEMPFAIL -- didn't get the lock. 69838032Speter*/ 69938032Speter 70038032Speterint 70138032Spetermci_lock_host(mci) 70238032Speter MCI *mci; 70338032Speter{ 70438032Speter if (mci == NULL) 70538032Speter { 70638032Speter if (tTd(56, 1)) 70790795Sgshapiro sm_dprintf("mci_lock_host: NULL mci\n"); 70838032Speter return EX_OK; 70938032Speter } 71038032Speter 71138032Speter if (!SingleThreadDelivery) 71238032Speter return EX_OK; 71338032Speter 71438032Speter return mci_lock_host_statfile(mci); 71538032Speter} 71638032Speter 71764565Sgshapirostatic int 71838032Spetermci_lock_host_statfile(mci) 71938032Speter MCI *mci; 72038032Speter{ 72164565Sgshapiro int save_errno = errno; 72238032Speter int retVal = EX_OK; 72398125Sgshapiro char fname[MAXPATHLEN]; 72438032Speter 72538032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 72638032Speter return EX_OK; 72738032Speter 72838032Speter if (tTd(56, 2)) 72990795Sgshapiro sm_dprintf("mci_lock_host: attempting to lock %s\n", 73090795Sgshapiro mci->mci_host); 73138032Speter 73290795Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 73390795Sgshapiro true) < 0) 73438032Speter { 73538032Speter /* of course this should never happen */ 73638032Speter if (tTd(56, 2)) 73790795Sgshapiro sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 73890795Sgshapiro mci->mci_host); 73938032Speter 74038032Speter retVal = EX_TEMPFAIL; 74138032Speter goto cleanup; 74238032Speter } 74338032Speter 74438032Speter mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 74564565Sgshapiro SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 74638032Speter 74738032Speter if (mci->mci_statfile == NULL) 74838032Speter { 74990795Sgshapiro syserr("mci_lock_host: cannot create host lock file %s", fname); 75038032Speter goto cleanup; 75138032Speter } 75238032Speter 75390795Sgshapiro if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 75490795Sgshapiro fname, "", LOCK_EX|LOCK_NB)) 75538032Speter { 75638032Speter if (tTd(56, 2)) 75790795Sgshapiro sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 75864565Sgshapiro fname); 75990795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 76038032Speter mci->mci_statfile = NULL; 76138032Speter retVal = EX_TEMPFAIL; 76238032Speter goto cleanup; 76338032Speter } 76438032Speter 76538032Speter if (tTd(56, 12) && mci->mci_statfile != NULL) 76690795Sgshapiro sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 76738032Speter 76838032Spetercleanup: 76964565Sgshapiro errno = save_errno; 77038032Speter return retVal; 77138032Speter} 77290795Sgshapiro/* 77338032Speter** MCI_UNLOCK_HOST -- unlock host 77438032Speter** 77538032Speter** Clean up the lock on a host, close the file, let 77638032Speter** someone else use it. 77738032Speter** 77838032Speter** Parameters: 77938032Speter** mci -- us. 78038032Speter** 78138032Speter** Returns: 78238032Speter** nothing. 78338032Speter*/ 78438032Speter 78538032Spetervoid 78638032Spetermci_unlock_host(mci) 78738032Speter MCI *mci; 78838032Speter{ 78964565Sgshapiro int save_errno = errno; 79038032Speter 79138032Speter if (mci == NULL) 79238032Speter { 79338032Speter if (tTd(56, 1)) 79490795Sgshapiro sm_dprintf("mci_unlock_host: NULL mci\n"); 79538032Speter return; 79638032Speter } 79738032Speter 79838032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 79938032Speter return; 80038032Speter 80138032Speter if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 80238032Speter { 80338032Speter if (tTd(56, 1)) 80490795Sgshapiro sm_dprintf("mci_unlock_host: stat file already locked\n"); 80538032Speter } 80638032Speter else 80738032Speter { 80838032Speter if (tTd(56, 2)) 80990795Sgshapiro sm_dprintf("mci_unlock_host: store prior to unlock\n"); 81038032Speter mci_store_persistent(mci); 81138032Speter } 81238032Speter 81338032Speter if (mci->mci_statfile != NULL) 81438032Speter { 81590795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 81638032Speter mci->mci_statfile = NULL; 81738032Speter } 81838032Speter 81964565Sgshapiro errno = save_errno; 82038032Speter} 82190795Sgshapiro/* 82242580Speter** MCI_LOAD_PERSISTENT -- load persistent host info 82338032Speter** 82438032Speter** Load information about host that is kept 82538032Speter** in common for all running sendmails. 82638032Speter** 82738032Speter** Parameters: 82890795Sgshapiro** mci -- the host/connection to load persistent info for. 82938032Speter** 83038032Speter** Returns: 83190795Sgshapiro** true -- lock was successful 83290795Sgshapiro** false -- lock failed 83338032Speter*/ 83438032Speter 83564565Sgshapirostatic bool 83638032Spetermci_load_persistent(mci) 83738032Speter MCI *mci; 83838032Speter{ 83964565Sgshapiro int save_errno = errno; 84090795Sgshapiro bool locked = true; 84190795Sgshapiro SM_FILE_T *fp; 84298125Sgshapiro char fname[MAXPATHLEN]; 84338032Speter 84438032Speter if (mci == NULL) 84538032Speter { 84638032Speter if (tTd(56, 1)) 84790795Sgshapiro sm_dprintf("mci_load_persistent: NULL mci\n"); 84890795Sgshapiro return true; 84938032Speter } 85038032Speter 85138032Speter if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 85290795Sgshapiro return true; 85364565Sgshapiro 85438032Speter /* Already have the persistent information in memory */ 85538032Speter if (SingleThreadDelivery && mci->mci_statfile != NULL) 85690795Sgshapiro return true; 85738032Speter 85838032Speter if (tTd(56, 1)) 85990795Sgshapiro sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 86090795Sgshapiro mci->mci_host); 86164565Sgshapiro 86290795Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 86390795Sgshapiro false) < 0) 86438032Speter { 86538032Speter /* Not much we can do if the file isn't there... */ 86638032Speter if (tTd(56, 1)) 86790795Sgshapiro sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 86838032Speter goto cleanup; 86938032Speter } 87038032Speter 87138032Speter fp = safefopen(fname, O_RDONLY, FileMode, 87238032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 87338032Speter if (fp == NULL) 87438032Speter { 87538032Speter /* I can't think of any reason this should ever happen */ 87638032Speter if (tTd(56, 1)) 87790795Sgshapiro sm_dprintf("mci_load_persistent: open(%s): %s\n", 87890795Sgshapiro fname, sm_errstring(errno)); 87938032Speter goto cleanup; 88038032Speter } 88138032Speter 88238032Speter FileName = fname; 88390795Sgshapiro locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 88490795Sgshapiro LOCK_SH|LOCK_NB); 88564565Sgshapiro if (locked) 88664565Sgshapiro { 88764565Sgshapiro (void) mci_read_persistent(fp, mci); 88890795Sgshapiro (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 88990795Sgshapiro "", LOCK_UN); 89064565Sgshapiro } 89138032Speter FileName = NULL; 89290795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 89338032Speter 89438032Spetercleanup: 89564565Sgshapiro errno = save_errno; 89638032Speter return locked; 89738032Speter} 89890795Sgshapiro/* 89938032Speter** MCI_READ_PERSISTENT -- read persistent host status file 90038032Speter** 90138032Speter** Parameters: 90238032Speter** fp -- the file pointer to read. 90338032Speter** mci -- the pointer to fill in. 90438032Speter** 90538032Speter** Returns: 90638032Speter** -1 -- if the file was corrupt. 90738032Speter** 0 -- otherwise. 90838032Speter** 90938032Speter** Warning: 91038032Speter** This code makes the assumption that this data 91138032Speter** will be read in an atomic fashion, and that the data 91238032Speter** was written in an atomic fashion. Any other functioning 91338032Speter** may lead to some form of insanity. This should be 91438032Speter** perfectly safe due to underlying stdio buffering. 91538032Speter*/ 91638032Speter 91764565Sgshapirostatic int 91838032Spetermci_read_persistent(fp, mci) 91990795Sgshapiro SM_FILE_T *fp; 92038032Speter register MCI *mci; 92138032Speter{ 92238032Speter int ver; 92338032Speter register char *p; 92438032Speter int saveLineNumber = LineNumber; 92538032Speter char buf[MAXLINE]; 92638032Speter 92738032Speter if (fp == NULL) 92838032Speter syserr("mci_read_persistent: NULL fp"); 92938032Speter if (mci == NULL) 93038032Speter syserr("mci_read_persistent: NULL mci"); 93138032Speter if (tTd(56, 93)) 93238032Speter { 93390795Sgshapiro sm_dprintf("mci_read_persistent: fp=%lx, mci=", 93490795Sgshapiro (unsigned long) fp); 93538032Speter } 93638032Speter 93790795Sgshapiro SM_FREE_CLR(mci->mci_status); 93890795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 93938032Speter 94090795Sgshapiro sm_io_rewind(fp, SM_TIME_DEFAULT); 94138032Speter ver = -1; 94238032Speter LineNumber = 0; 94390795Sgshapiro while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 94438032Speter { 94538032Speter LineNumber++; 94638032Speter p = strchr(buf, '\n'); 94738032Speter if (p != NULL) 94838032Speter *p = '\0'; 94938032Speter switch (buf[0]) 95038032Speter { 95138032Speter case 'V': /* version stamp */ 95238032Speter ver = atoi(&buf[1]); 95338032Speter if (ver < 0 || ver > 0) 95438032Speter syserr("Unknown host status version %d: %d max", 95538032Speter ver, 0); 95638032Speter break; 95738032Speter 95838032Speter case 'E': /* UNIX error number */ 95938032Speter mci->mci_errno = atoi(&buf[1]); 96038032Speter break; 96138032Speter 96238032Speter case 'H': /* DNS error number */ 96338032Speter mci->mci_herrno = atoi(&buf[1]); 96438032Speter break; 96538032Speter 96638032Speter case 'S': /* UNIX exit status */ 96738032Speter mci->mci_exitstat = atoi(&buf[1]); 96838032Speter break; 96938032Speter 97038032Speter case 'D': /* DSN status */ 97138032Speter mci->mci_status = newstr(&buf[1]); 97238032Speter break; 97338032Speter 97438032Speter case 'R': /* SMTP status */ 97538032Speter mci->mci_rstatus = newstr(&buf[1]); 97638032Speter break; 97738032Speter 97838032Speter case 'U': /* last usage time */ 97938032Speter mci->mci_lastuse = atol(&buf[1]); 98038032Speter break; 98138032Speter 98238032Speter case '.': /* end of file */ 98390795Sgshapiro if (tTd(56, 93)) 984132946Sgshapiro mci_dump(sm_debug_file(), mci, false); 98538032Speter return 0; 98638032Speter 98738032Speter default: 98838032Speter sm_syslog(LOG_CRIT, NOQID, 98938032Speter "%s: line %d: Unknown host status line \"%s\"", 99038032Speter FileName == NULL ? mci->mci_host : FileName, 99138032Speter LineNumber, buf); 99238032Speter LineNumber = saveLineNumber; 99338032Speter return -1; 99438032Speter } 99538032Speter } 99638032Speter LineNumber = saveLineNumber; 99790795Sgshapiro if (tTd(56, 93)) 99890795Sgshapiro sm_dprintf("incomplete (missing dot for EOF)\n"); 99938032Speter if (ver < 0) 100038032Speter return -1; 100138032Speter return 0; 100238032Speter} 100390795Sgshapiro/* 100438032Speter** MCI_STORE_PERSISTENT -- Store persistent MCI information 100538032Speter** 100638032Speter** Store information about host that is kept 100738032Speter** in common for all running sendmails. 100838032Speter** 100938032Speter** Parameters: 101038032Speter** mci -- the host/connection to store persistent info for. 101138032Speter** 101238032Speter** Returns: 101338032Speter** none. 101438032Speter*/ 101538032Speter 101638032Spetervoid 101738032Spetermci_store_persistent(mci) 101838032Speter MCI *mci; 101938032Speter{ 102064565Sgshapiro int save_errno = errno; 102138032Speter 102238032Speter if (mci == NULL) 102338032Speter { 102438032Speter if (tTd(56, 1)) 102590795Sgshapiro sm_dprintf("mci_store_persistent: NULL mci\n"); 102638032Speter return; 102738032Speter } 102838032Speter 102938032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 103038032Speter return; 103138032Speter 103238032Speter if (tTd(56, 1)) 103390795Sgshapiro sm_dprintf("mci_store_persistent: Storing information for %s\n", 103490795Sgshapiro mci->mci_host); 103538032Speter 103638032Speter if (mci->mci_statfile == NULL) 103738032Speter { 103838032Speter if (tTd(56, 1)) 103990795Sgshapiro sm_dprintf("mci_store_persistent: no statfile\n"); 104038032Speter return; 104138032Speter } 104238032Speter 104390795Sgshapiro sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 104438032Speter#if !NOFTRUNCATE 104590795Sgshapiro (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 104690795Sgshapiro (off_t) 0); 104764565Sgshapiro#endif /* !NOFTRUNCATE */ 104838032Speter 104990795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 105090795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 105190795Sgshapiro mci->mci_errno); 105290795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 105390795Sgshapiro mci->mci_herrno); 105490795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 105590795Sgshapiro mci->mci_exitstat); 105638032Speter if (mci->mci_status != NULL) 105790795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 105890795Sgshapiro "D%.80s\n", 105990795Sgshapiro denlstring(mci->mci_status, true, false)); 106038032Speter if (mci->mci_rstatus != NULL) 106190795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 106290795Sgshapiro "R%.80s\n", 106390795Sgshapiro denlstring(mci->mci_rstatus, true, false)); 106490795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 106590795Sgshapiro (long)(mci->mci_lastuse)); 106690795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 106738032Speter 106890795Sgshapiro (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 106938032Speter 107064565Sgshapiro errno = save_errno; 107138032Speter return; 107238032Speter} 107390795Sgshapiro/* 107438032Speter** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 107538032Speter** 107638032Speter** Recursively find all the mci host files in `pathname'. Default to 107738032Speter** main host status directory if no path is provided. 107838032Speter** Call (*action)(pathname, host) for each file found. 107938032Speter** 108038032Speter** Note: all information is collected in a list before it is processed. 108138032Speter** This may not be the best way to do it, but it seems safest, since 108238032Speter** the file system would be touched while we are attempting to traverse 108338032Speter** the directory tree otherwise (during purges). 108438032Speter** 108538032Speter** Parameters: 108638032Speter** action -- function to call on each node. If returns < 0, 108738032Speter** return immediately. 108838032Speter** pathname -- root of tree. If null, use main host status 108938032Speter** directory. 109038032Speter** 109138032Speter** Returns: 109238032Speter** < 0 -- if any action routine returns a negative value, that 109338032Speter** value is returned. 109438032Speter** 0 -- if we successfully went to completion. 109564565Sgshapiro** > 0 -- return status from action() 109638032Speter*/ 109738032Speter 109838032Speterint 109938032Spetermci_traverse_persistent(action, pathname) 1100141862Sgshapiro int (*action)__P((char *, char *)); 110138032Speter char *pathname; 110238032Speter{ 110338032Speter struct stat statbuf; 110438032Speter DIR *d; 110538032Speter int ret; 110638032Speter 110738032Speter if (pathname == NULL) 110838032Speter pathname = HostStatDir; 110938032Speter if (pathname == NULL) 111038032Speter return -1; 111138032Speter 111238032Speter if (tTd(56, 1)) 111390795Sgshapiro sm_dprintf("mci_traverse: pathname is %s\n", pathname); 111438032Speter 111538032Speter ret = stat(pathname, &statbuf); 111638032Speter if (ret < 0) 111738032Speter { 111838032Speter if (tTd(56, 2)) 111990795Sgshapiro sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 112090795Sgshapiro pathname, sm_errstring(errno)); 112138032Speter return ret; 112238032Speter } 112338032Speter if (S_ISDIR(statbuf.st_mode)) 112438032Speter { 112590795Sgshapiro bool leftone, removedone; 112690795Sgshapiro size_t len; 112790795Sgshapiro char *newptr; 112838032Speter struct dirent *e; 112998125Sgshapiro char newpath[MAXPATHLEN]; 113038032Speter 113138032Speter if ((d = opendir(pathname)) == NULL) 113238032Speter { 113338032Speter if (tTd(56, 2)) 113490795Sgshapiro sm_dprintf("mci_traverse: opendir %s: %s\n", 113590795Sgshapiro pathname, sm_errstring(errno)); 113638032Speter return -1; 113738032Speter } 113890795Sgshapiro len = sizeof(newpath) - MAXNAMLEN - 3; 113990795Sgshapiro if (sm_strlcpy(newpath, pathname, len) >= len) 114038032Speter { 114138032Speter if (tTd(56, 2)) 114290795Sgshapiro sm_dprintf("mci_traverse: path \"%s\" too long", 114338032Speter pathname); 114438032Speter return -1; 114538032Speter } 114638032Speter newptr = newpath + strlen(newpath); 114738032Speter *newptr++ = '/'; 114838032Speter 114964565Sgshapiro /* 115064565Sgshapiro ** repeat until no file has been removed 115164565Sgshapiro ** this may become ugly when several files "expire" 115264565Sgshapiro ** during these loops, but it's better than doing 115364565Sgshapiro ** a rewinddir() inside the inner loop 115464565Sgshapiro */ 115590795Sgshapiro 115664565Sgshapiro do 115738032Speter { 115890795Sgshapiro leftone = removedone = false; 115964565Sgshapiro while ((e = readdir(d)) != NULL) 116064565Sgshapiro { 116164565Sgshapiro if (e->d_name[0] == '.') 116264565Sgshapiro continue; 116338032Speter 116490795Sgshapiro (void) sm_strlcpy(newptr, e->d_name, 116564565Sgshapiro sizeof newpath - 116664565Sgshapiro (newptr - newpath)); 116738032Speter 116877352Sgshapiro if (StopRequest) 116977352Sgshapiro stop_sendmail(); 117064565Sgshapiro ret = mci_traverse_persistent(action, newpath); 117164565Sgshapiro if (ret < 0) 117264565Sgshapiro break; 117364565Sgshapiro if (ret == 1) 117490795Sgshapiro leftone = true; 117564565Sgshapiro if (!removedone && ret == 0 && 117664565Sgshapiro action == mci_purge_persistent) 117790795Sgshapiro removedone = true; 117864565Sgshapiro } 117938032Speter if (ret < 0) 118038032Speter break; 118190795Sgshapiro 118238032Speter /* 118338032Speter ** The following appears to be 118438032Speter ** necessary during purges, since 118538032Speter ** we modify the directory structure 118638032Speter */ 118790795Sgshapiro 118864565Sgshapiro if (removedone) 118938032Speter rewinddir(d); 119064565Sgshapiro if (tTd(56, 40)) 119190795Sgshapiro sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 119264565Sgshapiro pathname, ret, removedone, leftone); 119364565Sgshapiro } while (removedone); 119438032Speter 119538032Speter /* purge (or whatever) the directory proper */ 119664565Sgshapiro if (!leftone) 119764565Sgshapiro { 119864565Sgshapiro *--newptr = '\0'; 119964565Sgshapiro ret = (*action)(newpath, NULL); 120064565Sgshapiro } 120164565Sgshapiro (void) closedir(d); 120238032Speter } 120338032Speter else if (S_ISREG(statbuf.st_mode)) 120438032Speter { 120538032Speter char *end = pathname + strlen(pathname) - 1; 120638032Speter char *start; 120738032Speter char *scan; 120838032Speter char host[MAXHOSTNAMELEN]; 120938032Speter char *hostptr = host; 121064565Sgshapiro 121138032Speter /* 121238032Speter ** Reconstruct the host name from the path to the 121338032Speter ** persistent information. 121438032Speter */ 121538032Speter 121638032Speter do 121738032Speter { 121838032Speter if (hostptr != host) 121938032Speter *(hostptr++) = '.'; 122038032Speter start = end; 1221120259Sgshapiro while (start > pathname && *(start - 1) != '/') 122238032Speter start--; 122338032Speter 122438032Speter if (*end == '.') 122538032Speter end--; 122638032Speter 122738032Speter for (scan = start; scan <= end; scan++) 122838032Speter *(hostptr++) = *scan; 122938032Speter 123038032Speter end = start - 2; 1231120259Sgshapiro } while (end > pathname && *end == '.'); 123238032Speter 123338032Speter *hostptr = '\0'; 123438032Speter 123538032Speter /* 123642580Speter ** Do something with the file containing the persistent 123742580Speter ** information. 123838032Speter */ 123990795Sgshapiro 124038032Speter ret = (*action)(pathname, host); 124138032Speter } 124238032Speter 124338032Speter return ret; 124438032Speter} 124590795Sgshapiro/* 124664565Sgshapiro** MCI_PRINT_PERSISTENT -- print persistent info 124738032Speter** 124838032Speter** Dump the persistent information in the file 'pathname' 124938032Speter** 125038032Speter** Parameters: 125138032Speter** pathname -- the pathname to the status file. 125238032Speter** hostname -- the corresponding host name. 125338032Speter** 125438032Speter** Returns: 125538032Speter** 0 125638032Speter*/ 125738032Speter 125838032Speterint 125938032Spetermci_print_persistent(pathname, hostname) 126038032Speter char *pathname; 126138032Speter char *hostname; 126238032Speter{ 126390795Sgshapiro static bool initflag = false; 126490795Sgshapiro SM_FILE_T *fp; 126538032Speter int width = Verbose ? 78 : 25; 126638032Speter bool locked; 126738032Speter MCI mcib; 126838032Speter 126938032Speter /* skip directories */ 127038032Speter if (hostname == NULL) 127138032Speter return 0; 127238032Speter 127377352Sgshapiro if (StopRequest) 127477352Sgshapiro stop_sendmail(); 127577352Sgshapiro 127638032Speter if (!initflag) 127738032Speter { 127890795Sgshapiro initflag = true; 127990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 128090795Sgshapiro " -------------- Hostname --------------- How long ago ---------Results---------\n"); 128138032Speter } 128238032Speter 128390795Sgshapiro fp = safefopen(pathname, O_RDONLY, FileMode, 128438032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 128538032Speter 128638032Speter if (fp == NULL) 128738032Speter { 128838032Speter if (tTd(56, 1)) 128990795Sgshapiro sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 129090795Sgshapiro pathname, sm_errstring(errno)); 129138032Speter return 0; 129238032Speter } 129338032Speter 129438032Speter FileName = pathname; 129564565Sgshapiro memset(&mcib, '\0', sizeof mcib); 129638032Speter if (mci_read_persistent(fp, &mcib) < 0) 129738032Speter { 129838032Speter syserr("%s: could not read status file", pathname); 129990795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 130038032Speter FileName = NULL; 130138032Speter return 0; 130238032Speter } 130338032Speter 130490795Sgshapiro locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 130590795Sgshapiro "", LOCK_SH|LOCK_NB); 130690795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 130738032Speter FileName = NULL; 130838032Speter 130990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 131090795Sgshapiro locked ? '*' : ' ', hostname, 131190795Sgshapiro pintvl(curtime() - mcib.mci_lastuse, true)); 131238032Speter if (mcib.mci_rstatus != NULL) 131390795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 131490795Sgshapiro mcib.mci_rstatus); 131538032Speter else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 131690795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 131790795Sgshapiro "Deferred: %.*s\n", width - 10, 131890795Sgshapiro sm_errstring(mcib.mci_errno)); 131938032Speter else if (mcib.mci_exitstat != 0) 132038032Speter { 132190795Sgshapiro char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 132238032Speter 132390795Sgshapiro if (exmsg == NULL) 132438032Speter { 132538032Speter char buf[80]; 132638032Speter 132790795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 132890795Sgshapiro "Unknown mailer error %d", 132938032Speter mcib.mci_exitstat); 133090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 133190795Sgshapiro width, buf); 133238032Speter } 133338032Speter else 133490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 133590795Sgshapiro width, &exmsg[5]); 133638032Speter } 133738032Speter else if (mcib.mci_errno == 0) 133890795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 133938032Speter else 134090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 134190795Sgshapiro width - 4, sm_errstring(mcib.mci_errno)); 134238032Speter 134338032Speter return 0; 134438032Speter} 134590795Sgshapiro/* 134638032Speter** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 134738032Speter** 134838032Speter** Parameters: 134938032Speter** pathname -- path to the status file. 135038032Speter** hostname -- name of host corresponding to that file. 135138032Speter** NULL if this is a directory (domain). 135238032Speter** 135338032Speter** Returns: 135464565Sgshapiro** 0 -- ok 135564565Sgshapiro** 1 -- file not deleted (too young, incorrect format) 135664565Sgshapiro** < 0 -- some error occurred 135738032Speter*/ 135838032Speter 135938032Speterint 136038032Spetermci_purge_persistent(pathname, hostname) 136138032Speter char *pathname; 136238032Speter char *hostname; 136338032Speter{ 136464565Sgshapiro struct stat statbuf; 136538032Speter char *end = pathname + strlen(pathname) - 1; 136664565Sgshapiro int ret; 136738032Speter 136838032Speter if (tTd(56, 1)) 136990795Sgshapiro sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 137038032Speter 137164565Sgshapiro ret = stat(pathname, &statbuf); 137264565Sgshapiro if (ret < 0) 137364565Sgshapiro { 137464565Sgshapiro if (tTd(56, 2)) 137590795Sgshapiro sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 137690795Sgshapiro pathname, sm_errstring(errno)); 137764565Sgshapiro return ret; 137864565Sgshapiro } 137994337Sgshapiro if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 138064565Sgshapiro return 1; 138138032Speter if (hostname != NULL) 138238032Speter { 138338032Speter /* remove the file */ 138490795Sgshapiro ret = unlink(pathname); 138590795Sgshapiro if (ret < 0) 138638032Speter { 138790795Sgshapiro if (LogLevel > 8) 138890795Sgshapiro sm_syslog(LOG_ERR, NOQID, 138990795Sgshapiro "mci_purge_persistent: failed to unlink %s: %s", 139090795Sgshapiro pathname, sm_errstring(errno)); 139138032Speter if (tTd(56, 2)) 139290795Sgshapiro sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 139390795Sgshapiro pathname, sm_errstring(errno)); 139490795Sgshapiro return ret; 139538032Speter } 139638032Speter } 139738032Speter else 139838032Speter { 139938032Speter /* remove the directory */ 140038032Speter if (*end != '.') 140164565Sgshapiro return 1; 140238032Speter 140338032Speter if (tTd(56, 1)) 140490795Sgshapiro sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 140538032Speter 140690795Sgshapiro ret = rmdir(pathname); 140790795Sgshapiro if (ret < 0) 140838032Speter { 140938032Speter if (tTd(56, 2)) 141090795Sgshapiro sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 141190795Sgshapiro pathname, sm_errstring(errno)); 141290795Sgshapiro return ret; 141338032Speter } 141438032Speter } 141538032Speter 141638032Speter return 0; 141738032Speter} 141890795Sgshapiro/* 141942580Speter** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 142038032Speter** 1421120259Sgshapiro** Given `host', convert from a.b.c to $HostStatDir/c./b./a, 142238032Speter** putting the result into `path'. if `createflag' is set, intervening 142338032Speter** directories will be created as needed. 142438032Speter** 142538032Speter** Parameters: 142638032Speter** host -- host name to convert from. 142738032Speter** path -- place to store result. 142838032Speter** pathlen -- length of path buffer. 142938032Speter** createflag -- if set, create intervening directories as 143038032Speter** needed. 143138032Speter** 143238032Speter** Returns: 143338032Speter** 0 -- success 143438032Speter** -1 -- failure 143538032Speter*/ 143638032Speter 143764565Sgshapirostatic int 143838032Spetermci_generate_persistent_path(host, path, pathlen, createflag) 143938032Speter const char *host; 144038032Speter char *path; 144138032Speter int pathlen; 144238032Speter bool createflag; 144338032Speter{ 144438032Speter char *elem, *p, *x, ch; 144538032Speter int ret = 0; 144638032Speter int len; 144738032Speter char t_host[MAXHOSTNAMELEN]; 144864565Sgshapiro#if NETINET6 144964565Sgshapiro struct in6_addr in6_addr; 145064565Sgshapiro#endif /* NETINET6 */ 145138032Speter 145238032Speter /* 145338032Speter ** Rationality check the arguments. 145438032Speter */ 145538032Speter 145638032Speter if (host == NULL) 145738032Speter { 145838032Speter syserr("mci_generate_persistent_path: null host"); 145938032Speter return -1; 146038032Speter } 146138032Speter if (path == NULL) 146238032Speter { 146338032Speter syserr("mci_generate_persistent_path: null path"); 146438032Speter return -1; 146538032Speter } 146638032Speter 146738032Speter if (tTd(56, 80)) 146890795Sgshapiro sm_dprintf("mci_generate_persistent_path(%s): ", host); 146938032Speter 147038032Speter if (*host == '\0' || *host == '.') 147138032Speter return -1; 147238032Speter 147338032Speter /* make certain this is not a bracketed host number */ 147438032Speter if (strlen(host) > sizeof t_host - 1) 147538032Speter return -1; 147638032Speter if (host[0] == '[') 147790795Sgshapiro (void) sm_strlcpy(t_host, host + 1, sizeof t_host); 147864565Sgshapiro else 147990795Sgshapiro (void) sm_strlcpy(t_host, host, sizeof t_host); 148038032Speter 148138032Speter /* 148238032Speter ** Delete any trailing dots from the hostname. 148338032Speter ** Leave 'elem' pointing at the \0. 148438032Speter */ 148538032Speter 148638032Speter elem = t_host + strlen(t_host); 148738032Speter while (elem > t_host && 148838032Speter (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 148938032Speter *--elem = '\0'; 149038032Speter 149138032Speter /* check for bogus bracketed address */ 149290795Sgshapiro if (host[0] == '[') 149390795Sgshapiro { 149490795Sgshapiro bool good = false; 149564565Sgshapiro# if NETINET6 149690795Sgshapiro if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 149790795Sgshapiro good = true; 149864565Sgshapiro# endif /* NETINET6 */ 149964565Sgshapiro# if NETINET 150090795Sgshapiro if (inet_addr(t_host) != INADDR_NONE) 150190795Sgshapiro good = true; 150264565Sgshapiro# endif /* NETINET */ 150390795Sgshapiro if (!good) 150490795Sgshapiro return -1; 150590795Sgshapiro } 150638032Speter 150738032Speter /* check for what will be the final length of the path */ 150838032Speter len = strlen(HostStatDir) + 2; 150938032Speter for (p = (char *) t_host; *p != '\0'; p++) 151038032Speter { 151138032Speter if (*p == '.') 151238032Speter len++; 151338032Speter len++; 151438032Speter if (p[0] == '.' && p[1] == '.') 151538032Speter return -1; 151638032Speter } 151738032Speter if (len > pathlen || len < 1) 151838032Speter return -1; 151990795Sgshapiro (void) sm_strlcpy(path, HostStatDir, pathlen); 152038032Speter p = path + strlen(path); 152138032Speter while (elem > t_host) 152238032Speter { 152338032Speter if (!path_is_dir(path, createflag)) 152438032Speter { 152538032Speter ret = -1; 152638032Speter break; 152738032Speter } 152838032Speter elem--; 152938032Speter while (elem >= t_host && *elem != '.') 153038032Speter elem--; 153138032Speter *p++ = '/'; 153238032Speter x = elem + 1; 153338032Speter while ((ch = *x++) != '\0' && ch != '.') 153438032Speter { 153538032Speter if (isascii(ch) && isupper(ch)) 153638032Speter ch = tolower(ch); 153738032Speter if (ch == '/') 153838032Speter ch = ':'; /* / -> : */ 153938032Speter *p++ = ch; 154038032Speter } 154138032Speter if (elem >= t_host) 154238032Speter *p++ = '.'; 154338032Speter *p = '\0'; 154438032Speter } 154538032Speter if (tTd(56, 80)) 154638032Speter { 154738032Speter if (ret < 0) 154890795Sgshapiro sm_dprintf("FAILURE %d\n", ret); 154938032Speter else 155090795Sgshapiro sm_dprintf("SUCCESS %s\n", path); 155138032Speter } 155264565Sgshapiro return ret; 155338032Speter} 1554