138032Speter/* 2261363Sgshapiro * Copyright (c) 1998-2005, 2010 Proofpoint, 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 * 1238032Speter */ 1338032Speter 1464565Sgshapiro#include <sendmail.h> 1564565Sgshapiro 16266692SgshapiroSM_RCSID("@(#)$Id: mci.c,v 8.225 2013-11-22 20:51:56 ca Exp $") 1764565Sgshapiro 1864565Sgshapiro#if NETINET || NETINET6 1964565Sgshapiro# include <arpa/inet.h> 2064565Sgshapiro#endif /* NETINET || NETINET6 */ 2164565Sgshapiro 2238032Speter#include <dirent.h> 2338032Speter 2464565Sgshapirostatic int mci_generate_persistent_path __P((const char *, char *, 2564565Sgshapiro int, bool)); 2664565Sgshapirostatic bool mci_load_persistent __P((MCI *)); 2764565Sgshapirostatic void mci_uncache __P((MCI **, bool)); 2864565Sgshapirostatic int mci_lock_host_statfile __P((MCI *)); 2990795Sgshapirostatic int mci_read_persistent __P((SM_FILE_T *, MCI *)); 3064565Sgshapiro 3138032Speter/* 3238032Speter** Mail Connection Information (MCI) Caching Module. 3338032Speter** 3438032Speter** There are actually two separate things cached. The first is 3538032Speter** the set of all open connections -- these are stored in a 3638032Speter** (small) list. The second is stored in the symbol table; it 3738032Speter** has the overall status for all hosts, whether or not there 3838032Speter** is a connection open currently. 3938032Speter** 4038032Speter** There should never be too many connections open (since this 4138032Speter** could flood the socket table), nor should a connection be 4238032Speter** allowed to sit idly for too long. 4338032Speter** 4438032Speter** MaxMciCache is the maximum number of open connections that 4538032Speter** will be supported. 4638032Speter** 4738032Speter** MciCacheTimeout is the time (in seconds) that a connection 4838032Speter** is permitted to survive without activity. 4938032Speter** 50157006Sgshapiro** We actually try any cached connections by sending a RSET 51157006Sgshapiro** before we use them; if the RSET fails we close down the 52157006Sgshapiro** connection and reopen it (see smtpprobe()). 5338032Speter** 5438032Speter** The persistent MCI code is donated by Mark Lovell and Paul 5538032Speter** Vixie. It is based on the long term host status code in KJS 5638032Speter** written by Paul but has been adapted by Mark to fit into the 5738032Speter** MCI structure. 5838032Speter*/ 5938032Speter 6064565Sgshapirostatic MCI **MciCache; /* the open connection cache */ 6138032Speter 6290795Sgshapiro/* 6338032Speter** MCI_CACHE -- enter a connection structure into the open connection cache 6438032Speter** 6538032Speter** This may cause something else to be flushed. 6638032Speter** 6738032Speter** Parameters: 6838032Speter** mci -- the connection to cache. 6938032Speter** 7038032Speter** Returns: 7138032Speter** none. 7238032Speter*/ 7338032Speter 7438032Spetervoid 7538032Spetermci_cache(mci) 7638032Speter register MCI *mci; 7738032Speter{ 7838032Speter register MCI **mcislot; 7938032Speter 8038032Speter /* 8138032Speter ** Find the best slot. This may cause expired connections 8238032Speter ** to be closed. 8338032Speter */ 8438032Speter 8538032Speter mcislot = mci_scan(mci); 8638032Speter if (mcislot == NULL) 8738032Speter { 8838032Speter /* we don't support caching */ 8938032Speter return; 9038032Speter } 9138032Speter 9238032Speter if (mci->mci_host == NULL) 9338032Speter return; 9438032Speter 9538032Speter /* if this is already cached, we are done */ 9638032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 9738032Speter return; 9838032Speter 9938032Speter /* otherwise we may have to clear the slot */ 10038032Speter if (*mcislot != NULL) 10190795Sgshapiro mci_uncache(mcislot, true); 10238032Speter 10338032Speter if (tTd(42, 5)) 10490795Sgshapiro sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 10590795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache)); 10638032Speter if (tTd(91, 100)) 10738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 10864565Sgshapiro "mci_cache: caching %lx (%.100s) in slot %d", 10990795Sgshapiro (unsigned long) mci, mci->mci_host, 11090795Sgshapiro (int) (mcislot - MciCache)); 11138032Speter 11238032Speter *mcislot = mci; 11338032Speter mci->mci_flags |= MCIF_CACHED; 11438032Speter} 11590795Sgshapiro/* 11638032Speter** MCI_SCAN -- scan the cache, flush junk, and return best slot 11738032Speter** 11838032Speter** Parameters: 11938032Speter** savemci -- never flush this one. Can be null. 12038032Speter** 12138032Speter** Returns: 12238032Speter** The LRU (or empty) slot. 12338032Speter*/ 12438032Speter 12538032SpeterMCI ** 12638032Spetermci_scan(savemci) 12738032Speter MCI *savemci; 12838032Speter{ 12938032Speter time_t now; 13038032Speter register MCI **bestmci; 13138032Speter register MCI *mci; 13238032Speter register int i; 13338032Speter 13438032Speter if (MaxMciCache <= 0) 13538032Speter { 13638032Speter /* we don't support caching */ 13738032Speter return NULL; 13838032Speter } 13938032Speter 14038032Speter if (MciCache == NULL) 14138032Speter { 14238032Speter /* first call */ 143168520Sgshapiro MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof(*MciCache)); 144168520Sgshapiro memset((char *) MciCache, '\0', MaxMciCache * sizeof(*MciCache)); 14564565Sgshapiro return &MciCache[0]; 14638032Speter } 14738032Speter 14838032Speter now = curtime(); 14938032Speter bestmci = &MciCache[0]; 15038032Speter for (i = 0; i < MaxMciCache; i++) 15138032Speter { 15238032Speter mci = MciCache[i]; 15338032Speter if (mci == NULL || mci->mci_state == MCIS_CLOSED) 15438032Speter { 15538032Speter bestmci = &MciCache[i]; 15638032Speter continue; 15738032Speter } 15894337Sgshapiro if ((mci->mci_lastuse + MciCacheTimeout <= now || 15964565Sgshapiro (mci->mci_mailer != NULL && 16064565Sgshapiro mci->mci_mailer->m_maxdeliveries > 0 && 16164565Sgshapiro mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 16264565Sgshapiro mci != savemci) 16338032Speter { 16464565Sgshapiro /* connection idle too long or too many deliveries */ 16538032Speter bestmci = &MciCache[i]; 16664565Sgshapiro 16764565Sgshapiro /* close it */ 16890795Sgshapiro mci_uncache(bestmci, true); 16938032Speter continue; 17038032Speter } 17138032Speter if (*bestmci == NULL) 17238032Speter continue; 17338032Speter if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 17438032Speter bestmci = &MciCache[i]; 17538032Speter } 17638032Speter return bestmci; 17738032Speter} 17890795Sgshapiro/* 17938032Speter** MCI_UNCACHE -- remove a connection from a slot. 18038032Speter** 18138032Speter** May close a connection. 18238032Speter** 18338032Speter** Parameters: 18438032Speter** mcislot -- the slot to empty. 18590795Sgshapiro** doquit -- if true, send QUIT protocol on this connection. 18690795Sgshapiro** if false, we are assumed to be in a forked child; 18738032Speter** all we want to do is close the file(s). 18838032Speter** 18938032Speter** Returns: 19038032Speter** none. 19138032Speter*/ 19238032Speter 19364565Sgshapirostatic void 19438032Spetermci_uncache(mcislot, doquit) 19538032Speter register MCI **mcislot; 19638032Speter bool doquit; 19738032Speter{ 19838032Speter register MCI *mci; 19938032Speter extern ENVELOPE BlankEnvelope; 20038032Speter 20138032Speter mci = *mcislot; 20238032Speter if (mci == NULL) 20338032Speter return; 20438032Speter *mcislot = NULL; 20538032Speter if (mci->mci_host == NULL) 20638032Speter return; 20738032Speter 20838032Speter mci_unlock_host(mci); 20938032Speter 21038032Speter if (tTd(42, 5)) 21190795Sgshapiro sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 21290795Sgshapiro mci, mci->mci_host, (int) (mcislot - MciCache), 21390795Sgshapiro doquit); 21438032Speter if (tTd(91, 100)) 21538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 21664565Sgshapiro "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 21790795Sgshapiro (unsigned long) mci, mci->mci_host, 21890795Sgshapiro (int) (mcislot - MciCache), doquit); 21938032Speter 22064565Sgshapiro mci->mci_deliveries = 0; 22138032Speter if (doquit) 22238032Speter { 22338032Speter message("Closing connection to %s", mci->mci_host); 22438032Speter 22538032Speter mci->mci_flags &= ~MCIF_CACHED; 22638032Speter 22738032Speter /* only uses the envelope to flush the transcript file */ 22838032Speter if (mci->mci_state != MCIS_CLOSED) 22938032Speter smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 23090795Sgshapiro#if XLA 23138032Speter xla_host_end(mci->mci_host); 23290795Sgshapiro#endif /* XLA */ 23338032Speter } 23438032Speter else 23538032Speter { 23638032Speter if (mci->mci_in != NULL) 23790795Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 23838032Speter if (mci->mci_out != NULL) 23990795Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 24038032Speter mci->mci_in = mci->mci_out = NULL; 24138032Speter mci->mci_state = MCIS_CLOSED; 24238032Speter mci->mci_exitstat = EX_OK; 24338032Speter mci->mci_errno = 0; 24438032Speter mci->mci_flags = 0; 24590795Sgshapiro 24690795Sgshapiro mci->mci_retryrcpt = false; 24790795Sgshapiro mci->mci_tolist = NULL; 24890795Sgshapiro#if PIPELINING 24990795Sgshapiro mci->mci_okrcpts = 0; 25090795Sgshapiro#endif /* PIPELINING */ 25138032Speter } 25290795Sgshapiro 25390795Sgshapiro SM_FREE_CLR(mci->mci_status); 25490795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 25590795Sgshapiro SM_FREE_CLR(mci->mci_heloname); 25690795Sgshapiro if (mci->mci_rpool != NULL) 25790795Sgshapiro { 25890795Sgshapiro sm_rpool_free(mci->mci_rpool); 25990795Sgshapiro mci->mci_macro.mac_rpool = NULL; 26090795Sgshapiro mci->mci_rpool = NULL; 26190795Sgshapiro } 26238032Speter} 26390795Sgshapiro/* 26438032Speter** MCI_FLUSH -- flush the entire cache 26538032Speter** 26638032Speter** Parameters: 26790795Sgshapiro** doquit -- if true, send QUIT protocol. 26890795Sgshapiro** if false, just close the connection. 26938032Speter** allbut -- but leave this one open. 27038032Speter** 27138032Speter** Returns: 27238032Speter** none. 27338032Speter*/ 27438032Speter 27538032Spetervoid 27638032Spetermci_flush(doquit, allbut) 27738032Speter bool doquit; 27838032Speter MCI *allbut; 27938032Speter{ 28038032Speter register int i; 28138032Speter 28238032Speter if (MciCache == NULL) 28338032Speter return; 28438032Speter 28538032Speter for (i = 0; i < MaxMciCache; i++) 28671348Sgshapiro { 28738032Speter if (allbut != MciCache[i]) 28838032Speter mci_uncache(&MciCache[i], doquit); 28971348Sgshapiro } 29038032Speter} 291223067Sgshapiro 29290795Sgshapiro/* 293223067Sgshapiro** MCI_CLR_EXTENSIONS -- clear knowledge about SMTP extensions 294223067Sgshapiro** 295223067Sgshapiro** Parameters: 296223067Sgshapiro** mci -- the connection to clear. 297223067Sgshapiro** 298223067Sgshapiro** Returns: 299223067Sgshapiro** none. 300223067Sgshapiro*/ 301223067Sgshapiro 302223067Sgshapirovoid 303223067Sgshapiromci_clr_extensions(mci) 304223067Sgshapiro MCI *mci; 305223067Sgshapiro{ 306223067Sgshapiro if (mci == NULL) 307223067Sgshapiro return; 308223067Sgshapiro 309223067Sgshapiro mci->mci_flags &= ~MCIF_EXTENS; 310223067Sgshapiro mci->mci_maxsize = 0; 311223067Sgshapiro mci->mci_min_by = 0; 312223067Sgshapiro#if SASL 313223067Sgshapiro mci->mci_saslcap = NULL; 314223067Sgshapiro#endif /* SASL */ 315223067Sgshapiro} 316223067Sgshapiro 317223067Sgshapiro/* 31838032Speter** MCI_GET -- get information about a particular host 31990795Sgshapiro** 32090795Sgshapiro** Parameters: 32190795Sgshapiro** host -- host to look for. 32290795Sgshapiro** m -- mailer. 32390795Sgshapiro** 32490795Sgshapiro** Returns: 32590795Sgshapiro** mci for this host (might be new). 32638032Speter*/ 32738032Speter 32838032SpeterMCI * 32938032Spetermci_get(host, m) 33038032Speter char *host; 33138032Speter MAILER *m; 33238032Speter{ 33338032Speter register MCI *mci; 33438032Speter register STAB *s; 33538032Speter extern SOCKADDR CurHostAddr; 33638032Speter 33738032Speter /* clear CurHostAddr so we don't get a bogus address with this name */ 338168520Sgshapiro memset(&CurHostAddr, '\0', sizeof(CurHostAddr)); 33938032Speter 34038032Speter /* clear out any expired connections */ 34138032Speter (void) mci_scan(NULL); 34238032Speter 34338032Speter if (m->m_mno < 0) 34471348Sgshapiro syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 34564565Sgshapiro 34638032Speter s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 34738032Speter mci = &s->s_mci; 34838032Speter 34990795Sgshapiro /* initialize per-message data */ 35090795Sgshapiro mci->mci_retryrcpt = false; 35190795Sgshapiro mci->mci_tolist = NULL; 35290795Sgshapiro#if PIPELINING 35390795Sgshapiro mci->mci_okrcpts = 0; 35490795Sgshapiro#endif /* PIPELINING */ 355285303Sgshapiro mci->mci_flags &= ~MCIF_NOTSTICKY; 35690795Sgshapiro 35790795Sgshapiro if (mci->mci_rpool == NULL) 35890795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 35990795Sgshapiro 36090795Sgshapiro if (mci->mci_macro.mac_rpool == NULL) 36190795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 36290795Sgshapiro 36364565Sgshapiro /* 36490795Sgshapiro ** We don't need to load the persistent data if we have data 36564565Sgshapiro ** already loaded in the cache. 36664565Sgshapiro */ 36764565Sgshapiro 36864565Sgshapiro if (mci->mci_host == NULL && 36964565Sgshapiro (mci->mci_host = s->s_name) != NULL && 37064565Sgshapiro !mci_load_persistent(mci)) 37138032Speter { 37238032Speter if (tTd(42, 2)) 37390795Sgshapiro sm_dprintf("mci_get(%s %s): lock failed\n", 37464565Sgshapiro host, m->m_name); 37538032Speter mci->mci_exitstat = EX_TEMPFAIL; 37638032Speter mci->mci_state = MCIS_CLOSED; 37738032Speter mci->mci_statfile = NULL; 37838032Speter return mci; 37938032Speter } 38038032Speter 38138032Speter if (tTd(42, 2)) 38238032Speter { 38390795Sgshapiro sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 38438032Speter host, m->m_name, mci->mci_state, mci->mci_flags, 38538032Speter mci->mci_exitstat, mci->mci_errno); 38638032Speter } 38738032Speter 38838032Speter if (mci->mci_state == MCIS_OPEN) 38938032Speter { 39038032Speter /* poke the connection to see if it's still alive */ 39138032Speter (void) smtpprobe(mci); 39238032Speter 39338032Speter /* reset the stored state in the event of a timeout */ 39438032Speter if (mci->mci_state != MCIS_OPEN) 39538032Speter { 39638032Speter mci->mci_errno = 0; 39738032Speter mci->mci_exitstat = EX_OK; 39838032Speter mci->mci_state = MCIS_CLOSED; 39938032Speter } 40038032Speter else 40138032Speter { 40290795Sgshapiro /* get peer host address */ 40338032Speter /* (this should really be in the mci struct) */ 404168520Sgshapiro SOCKADDR_LEN_T socklen = sizeof(CurHostAddr); 40538032Speter 40690795Sgshapiro (void) getpeername(sm_io_getinfo(mci->mci_in, 40790795Sgshapiro SM_IO_WHAT_FD, NULL), 40838032Speter (struct sockaddr *) &CurHostAddr, &socklen); 40938032Speter } 41038032Speter } 41138032Speter if (mci->mci_state == MCIS_CLOSED) 41238032Speter { 41338032Speter time_t now = curtime(); 41438032Speter 41538032Speter /* if this info is stale, ignore it */ 41694337Sgshapiro if (mci->mci_lastuse + MciInfoTimeout <= now) 41738032Speter { 41838032Speter mci->mci_lastuse = now; 41938032Speter mci->mci_errno = 0; 42038032Speter mci->mci_exitstat = EX_OK; 42138032Speter } 42238032Speter } 42338032Speter 42438032Speter return mci; 42538032Speter} 426141887Sgshapiro 42790795Sgshapiro/* 428141887Sgshapiro** MCI_CLOSE -- (forcefully) close files used for a connection. 429141887Sgshapiro** Note: this is a last resort, usually smtpquit() or endmailer() 430141887Sgshapiro** should be used to close a connection. 431141887Sgshapiro** 432141887Sgshapiro** Parameters: 433141887Sgshapiro** mci -- the connection to close. 434141887Sgshapiro** where -- where has this been called? 435141887Sgshapiro** 436141887Sgshapiro** Returns: 437141887Sgshapiro** none. 438141887Sgshapiro*/ 439141887Sgshapiro 440141887Sgshapirovoid 441141887Sgshapiromci_close(mci, where) 442141887Sgshapiro MCI *mci; 443141887Sgshapiro char *where; 444141887Sgshapiro{ 445141887Sgshapiro bool dumped; 446141887Sgshapiro 447141887Sgshapiro if (mci == NULL) 448141887Sgshapiro return; 449141887Sgshapiro dumped = false; 450141887Sgshapiro if (mci->mci_out != NULL) 451141887Sgshapiro { 452141887Sgshapiro if (tTd(56, 1)) 453141887Sgshapiro { 454141887Sgshapiro sm_dprintf("mci_close: mci_out!=NULL, where=%s\n", 455141887Sgshapiro where); 456141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 457141887Sgshapiro dumped = true; 458141887Sgshapiro } 459141887Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 460141887Sgshapiro mci->mci_out = NULL; 461141887Sgshapiro } 462141887Sgshapiro if (mci->mci_in != NULL) 463141887Sgshapiro { 464141887Sgshapiro if (tTd(56, 1)) 465141887Sgshapiro { 466141887Sgshapiro sm_dprintf("mci_close: mci_in!=NULL, where=%s\n", 467141887Sgshapiro where); 468141887Sgshapiro if (!dumped) 469141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 470141887Sgshapiro } 471141887Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 472141887Sgshapiro mci->mci_in = NULL; 473141887Sgshapiro } 474141887Sgshapiro mci->mci_state = MCIS_CLOSED; 475141887Sgshapiro} 476141887Sgshapiro 477141887Sgshapiro/* 47890795Sgshapiro** MCI_NEW -- allocate new MCI structure 47990795Sgshapiro** 48090795Sgshapiro** Parameters: 48190795Sgshapiro** rpool -- if non-NULL: allocate from that rpool. 48290795Sgshapiro** 48390795Sgshapiro** Returns: 48490795Sgshapiro** mci (new). 48590795Sgshapiro*/ 48690795Sgshapiro 48790795SgshapiroMCI * 48890795Sgshapiromci_new(rpool) 48990795Sgshapiro SM_RPOOL_T *rpool; 49090795Sgshapiro{ 49190795Sgshapiro register MCI *mci; 49290795Sgshapiro 49390795Sgshapiro if (rpool == NULL) 494168520Sgshapiro mci = (MCI *) sm_malloc_x(sizeof(*mci)); 49590795Sgshapiro else 496168520Sgshapiro mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof(*mci)); 497168520Sgshapiro memset((char *) mci, '\0', sizeof(*mci)); 49890795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 49990795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 50090795Sgshapiro return mci; 50190795Sgshapiro} 50290795Sgshapiro/* 50364565Sgshapiro** MCI_MATCH -- check connection cache for a particular host 50490795Sgshapiro** 50590795Sgshapiro** Parameters: 50690795Sgshapiro** host -- host to look for. 50790795Sgshapiro** m -- mailer. 50890795Sgshapiro** 50990795Sgshapiro** Returns: 51090795Sgshapiro** true iff open connection exists. 51164565Sgshapiro*/ 51264565Sgshapiro 51364565Sgshapirobool 51464565Sgshapiromci_match(host, m) 51564565Sgshapiro char *host; 51664565Sgshapiro MAILER *m; 51764565Sgshapiro{ 51864565Sgshapiro register MCI *mci; 51964565Sgshapiro register STAB *s; 52064565Sgshapiro 52171348Sgshapiro if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 52290795Sgshapiro return false; 52364565Sgshapiro s = stab(host, ST_MCI + m->m_mno, ST_FIND); 52464565Sgshapiro if (s == NULL) 52590795Sgshapiro return false; 52664565Sgshapiro 52764565Sgshapiro mci = &s->s_mci; 52890795Sgshapiro return mci->mci_state == MCIS_OPEN; 52964565Sgshapiro} 53090795Sgshapiro/* 53138032Speter** MCI_SETSTAT -- set status codes in MCI structure. 53238032Speter** 53338032Speter** Parameters: 53438032Speter** mci -- the MCI structure to set. 53538032Speter** xstat -- the exit status code. 53638032Speter** dstat -- the DSN status code. 53738032Speter** rstat -- the SMTP status code. 53838032Speter** 53938032Speter** Returns: 54038032Speter** none. 54138032Speter*/ 54238032Speter 54338032Spetervoid 54438032Spetermci_setstat(mci, xstat, dstat, rstat) 54538032Speter MCI *mci; 54638032Speter int xstat; 54738032Speter char *dstat; 54838032Speter char *rstat; 54938032Speter{ 55038032Speter /* protocol errors should never be interpreted as sticky */ 55138032Speter if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 55238032Speter mci->mci_exitstat = xstat; 55338032Speter 55490795Sgshapiro SM_FREE_CLR(mci->mci_status); 55590795Sgshapiro if (dstat != NULL) 55690795Sgshapiro mci->mci_status = sm_strdup_x(dstat); 55790795Sgshapiro 55890795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 55938032Speter if (rstat != NULL) 56090795Sgshapiro mci->mci_rstatus = sm_strdup_x(rstat); 56138032Speter} 56290795Sgshapiro/* 56338032Speter** MCI_DUMP -- dump the contents of an MCI structure. 56438032Speter** 56538032Speter** Parameters: 566132946Sgshapiro** fp -- output file pointer 56738032Speter** mci -- the MCI structure to dump. 56838032Speter** 56938032Speter** Returns: 57038032Speter** none. 57138032Speter** 57238032Speter** Side Effects: 57338032Speter** none. 57438032Speter*/ 57538032Speter 57638032Speterstruct mcifbits 57738032Speter{ 57838032Speter int mcif_bit; /* flag bit */ 57938032Speter char *mcif_name; /* flag name */ 58038032Speter}; 58164565Sgshapirostatic struct mcifbits MciFlags[] = 58238032Speter{ 58338032Speter { MCIF_VALID, "VALID" }, 58438032Speter { MCIF_CACHED, "CACHED" }, 58538032Speter { MCIF_ESMTP, "ESMTP" }, 58638032Speter { MCIF_EXPN, "EXPN" }, 58738032Speter { MCIF_SIZE, "SIZE" }, 58838032Speter { MCIF_8BITMIME, "8BITMIME" }, 58938032Speter { MCIF_7BIT, "7BIT" }, 59038032Speter { MCIF_INHEADER, "INHEADER" }, 59138032Speter { MCIF_CVT8TO7, "CVT8TO7" }, 59238032Speter { MCIF_DSN, "DSN" }, 59338032Speter { MCIF_8BITOK, "8BITOK" }, 59438032Speter { MCIF_CVT7TO8, "CVT7TO8" }, 59538032Speter { MCIF_INMIME, "INMIME" }, 59690795Sgshapiro { MCIF_AUTH, "AUTH" }, 597223067Sgshapiro { MCIF_AUTH2, "AUTH2" }, 59890795Sgshapiro { MCIF_AUTHACT, "AUTHACT" }, 59990795Sgshapiro { MCIF_ENHSTAT, "ENHSTAT" }, 60090795Sgshapiro { MCIF_PIPELINED, "PIPELINED" }, 60190795Sgshapiro#if STARTTLS 60290795Sgshapiro { MCIF_TLS, "TLS" }, 60390795Sgshapiro { MCIF_TLSACT, "TLSACT" }, 60490795Sgshapiro#endif /* STARTTLS */ 60590795Sgshapiro { MCIF_DLVR_BY, "DLVR_BY" }, 60671348Sgshapiro { 0, NULL } 60738032Speter}; 60838032Speter 60938032Spetervoid 610132946Sgshapiromci_dump(fp, mci, logit) 611132946Sgshapiro SM_FILE_T *fp; 61238032Speter register MCI *mci; 61338032Speter bool logit; 61438032Speter{ 61538032Speter register char *p; 61638032Speter char *sep; 61738032Speter char buf[4000]; 61838032Speter 61938032Speter sep = logit ? " " : "\n\t"; 62038032Speter p = buf; 62190795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 62238032Speter p += strlen(p); 62338032Speter if (mci == NULL) 62438032Speter { 62590795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 62638032Speter goto printit; 62738032Speter } 62890795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 62938032Speter p += strlen(p); 630120259Sgshapiro 631120259Sgshapiro /* 632120259Sgshapiro ** The following check is just for paranoia. It protects the 633120259Sgshapiro ** assignment in the if() clause. If there's not some minimum 634120259Sgshapiro ** amount of space we can stop right now. The check will not 635120259Sgshapiro ** trigger as long as sizeof(buf)=4000. 636120259Sgshapiro */ 637120259Sgshapiro 638120259Sgshapiro if (p >= buf + sizeof(buf) - 4) 639120259Sgshapiro goto printit; 64038032Speter if (mci->mci_flags != 0) 64138032Speter { 64238032Speter struct mcifbits *f; 64338032Speter 644120259Sgshapiro *p++ = '<'; /* protected above */ 64538032Speter for (f = MciFlags; f->mcif_bit != 0; f++) 64638032Speter { 64738032Speter if (!bitset(f->mcif_bit, mci->mci_flags)) 64838032Speter continue; 64990795Sgshapiro (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 65090795Sgshapiro f->mcif_name, ","); 65138032Speter p += strlen(p); 65238032Speter } 65338032Speter p[-1] = '>'; 65438032Speter } 65590795Sgshapiro 65690795Sgshapiro /* Note: sm_snprintf() takes care of NULL arguments for %s */ 65790795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 65838032Speter ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 65938032Speter sep, mci->mci_errno, mci->mci_herrno, 66064565Sgshapiro mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 66138032Speter p += strlen(p); 66290795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 66338032Speter "maxsize=%ld, phase=%s, mailer=%s,%s", 66490795Sgshapiro mci->mci_maxsize, mci->mci_phase, 66538032Speter mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 66638032Speter sep); 66738032Speter p += strlen(p); 66890795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 66938032Speter "status=%s, rstatus=%s,%s", 67090795Sgshapiro mci->mci_status, mci->mci_rstatus, sep); 67138032Speter p += strlen(p); 67290795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 67338032Speter "host=%s, lastuse=%s", 67490795Sgshapiro mci->mci_host, ctime(&mci->mci_lastuse)); 67538032Speterprintit: 67638032Speter if (logit) 67738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 67838032Speter else 679132946Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf); 68038032Speter} 68190795Sgshapiro/* 68238032Speter** MCI_DUMP_ALL -- print the entire MCI cache 68338032Speter** 68438032Speter** Parameters: 685132946Sgshapiro** fp -- output file pointer 68638032Speter** logit -- if set, log the result instead of printing 68738032Speter** to stdout. 68838032Speter** 68938032Speter** Returns: 69038032Speter** none. 69138032Speter*/ 69238032Speter 69338032Spetervoid 694132946Sgshapiromci_dump_all(fp, logit) 695132946Sgshapiro SM_FILE_T *fp; 69638032Speter bool logit; 69738032Speter{ 69838032Speter register int i; 69938032Speter 70038032Speter if (MciCache == NULL) 70138032Speter return; 70238032Speter 70338032Speter for (i = 0; i < MaxMciCache; i++) 704132946Sgshapiro mci_dump(fp, MciCache[i], logit); 70538032Speter} 70690795Sgshapiro/* 70742580Speter** MCI_LOCK_HOST -- Lock host while sending. 70838032Speter** 70938032Speter** If we are contacting a host, we'll need to 71038032Speter** update the status information in the host status 71138032Speter** file, and if we want to do that, we ought to have 71238032Speter** locked it. This has the (according to some) 71338032Speter** desirable effect of serializing connectivity with 71490795Sgshapiro** remote hosts -- i.e.: one connection to a given 71538032Speter** host at a time. 71638032Speter** 71738032Speter** Parameters: 71838032Speter** mci -- containing the host we want to lock. 71938032Speter** 72038032Speter** Returns: 72164565Sgshapiro** EX_OK -- got the lock. 72238032Speter** EX_TEMPFAIL -- didn't get the lock. 72338032Speter*/ 72438032Speter 72538032Speterint 72638032Spetermci_lock_host(mci) 72738032Speter MCI *mci; 72838032Speter{ 72938032Speter if (mci == NULL) 73038032Speter { 73138032Speter if (tTd(56, 1)) 73290795Sgshapiro sm_dprintf("mci_lock_host: NULL mci\n"); 73338032Speter return EX_OK; 73438032Speter } 73538032Speter 73638032Speter if (!SingleThreadDelivery) 73738032Speter return EX_OK; 73838032Speter 73938032Speter return mci_lock_host_statfile(mci); 74038032Speter} 74138032Speter 74264565Sgshapirostatic int 74338032Spetermci_lock_host_statfile(mci) 74438032Speter MCI *mci; 74538032Speter{ 74664565Sgshapiro int save_errno = errno; 74738032Speter int retVal = EX_OK; 74898125Sgshapiro char fname[MAXPATHLEN]; 74938032Speter 75038032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 75138032Speter return EX_OK; 75238032Speter 75338032Speter if (tTd(56, 2)) 75490795Sgshapiro sm_dprintf("mci_lock_host: attempting to lock %s\n", 75590795Sgshapiro mci->mci_host); 75638032Speter 757168520Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname), 75890795Sgshapiro true) < 0) 75938032Speter { 76038032Speter /* of course this should never happen */ 76138032Speter if (tTd(56, 2)) 76290795Sgshapiro sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 76390795Sgshapiro mci->mci_host); 76438032Speter 76538032Speter retVal = EX_TEMPFAIL; 76638032Speter goto cleanup; 76738032Speter } 76838032Speter 76938032Speter mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 77064565Sgshapiro SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 77138032Speter 77238032Speter if (mci->mci_statfile == NULL) 77338032Speter { 77490795Sgshapiro syserr("mci_lock_host: cannot create host lock file %s", fname); 77538032Speter goto cleanup; 77638032Speter } 77738032Speter 77890795Sgshapiro if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 77990795Sgshapiro fname, "", LOCK_EX|LOCK_NB)) 78038032Speter { 78138032Speter if (tTd(56, 2)) 78290795Sgshapiro sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 78364565Sgshapiro fname); 78490795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 78538032Speter mci->mci_statfile = NULL; 78638032Speter retVal = EX_TEMPFAIL; 78738032Speter goto cleanup; 78838032Speter } 78938032Speter 79038032Speter if (tTd(56, 12) && mci->mci_statfile != NULL) 79190795Sgshapiro sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 79238032Speter 79338032Spetercleanup: 79464565Sgshapiro errno = save_errno; 79538032Speter return retVal; 79638032Speter} 79790795Sgshapiro/* 79838032Speter** MCI_UNLOCK_HOST -- unlock host 79938032Speter** 80038032Speter** Clean up the lock on a host, close the file, let 80138032Speter** someone else use it. 80238032Speter** 80338032Speter** Parameters: 80438032Speter** mci -- us. 80538032Speter** 80638032Speter** Returns: 80738032Speter** nothing. 80838032Speter*/ 80938032Speter 81038032Spetervoid 81138032Spetermci_unlock_host(mci) 81238032Speter MCI *mci; 81338032Speter{ 81464565Sgshapiro int save_errno = errno; 81538032Speter 81638032Speter if (mci == NULL) 81738032Speter { 81838032Speter if (tTd(56, 1)) 81990795Sgshapiro sm_dprintf("mci_unlock_host: NULL mci\n"); 82038032Speter return; 82138032Speter } 82238032Speter 82338032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 82438032Speter return; 82538032Speter 82638032Speter if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 82738032Speter { 82838032Speter if (tTd(56, 1)) 82990795Sgshapiro sm_dprintf("mci_unlock_host: stat file already locked\n"); 83038032Speter } 83138032Speter else 83238032Speter { 83338032Speter if (tTd(56, 2)) 83490795Sgshapiro sm_dprintf("mci_unlock_host: store prior to unlock\n"); 83538032Speter mci_store_persistent(mci); 83638032Speter } 83738032Speter 83838032Speter if (mci->mci_statfile != NULL) 83938032Speter { 84090795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 84138032Speter mci->mci_statfile = NULL; 84238032Speter } 84338032Speter 84464565Sgshapiro errno = save_errno; 84538032Speter} 84690795Sgshapiro/* 84742580Speter** MCI_LOAD_PERSISTENT -- load persistent host info 84838032Speter** 84938032Speter** Load information about host that is kept 85038032Speter** in common for all running sendmails. 85138032Speter** 85238032Speter** Parameters: 85390795Sgshapiro** mci -- the host/connection to load persistent info for. 85438032Speter** 85538032Speter** Returns: 85690795Sgshapiro** true -- lock was successful 85790795Sgshapiro** false -- lock failed 85838032Speter*/ 85938032Speter 86064565Sgshapirostatic bool 86138032Spetermci_load_persistent(mci) 86238032Speter MCI *mci; 86338032Speter{ 86464565Sgshapiro int save_errno = errno; 86590795Sgshapiro bool locked = true; 86690795Sgshapiro SM_FILE_T *fp; 86798125Sgshapiro char fname[MAXPATHLEN]; 86838032Speter 86938032Speter if (mci == NULL) 87038032Speter { 87138032Speter if (tTd(56, 1)) 87290795Sgshapiro sm_dprintf("mci_load_persistent: NULL mci\n"); 87390795Sgshapiro return true; 87438032Speter } 87538032Speter 87638032Speter if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 87790795Sgshapiro return true; 87864565Sgshapiro 87938032Speter /* Already have the persistent information in memory */ 88038032Speter if (SingleThreadDelivery && mci->mci_statfile != NULL) 88190795Sgshapiro return true; 88238032Speter 88338032Speter if (tTd(56, 1)) 88490795Sgshapiro sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 88590795Sgshapiro mci->mci_host); 88664565Sgshapiro 887168520Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname), 88890795Sgshapiro false) < 0) 88938032Speter { 89038032Speter /* Not much we can do if the file isn't there... */ 89138032Speter if (tTd(56, 1)) 89290795Sgshapiro sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 89338032Speter goto cleanup; 89438032Speter } 89538032Speter 89638032Speter fp = safefopen(fname, O_RDONLY, FileMode, 89738032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 89838032Speter if (fp == NULL) 89938032Speter { 90038032Speter /* I can't think of any reason this should ever happen */ 90138032Speter if (tTd(56, 1)) 90290795Sgshapiro sm_dprintf("mci_load_persistent: open(%s): %s\n", 90390795Sgshapiro fname, sm_errstring(errno)); 90438032Speter goto cleanup; 90538032Speter } 90638032Speter 90738032Speter FileName = fname; 90890795Sgshapiro locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 90990795Sgshapiro LOCK_SH|LOCK_NB); 91064565Sgshapiro if (locked) 91164565Sgshapiro { 91264565Sgshapiro (void) mci_read_persistent(fp, mci); 91390795Sgshapiro (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 91490795Sgshapiro "", LOCK_UN); 91564565Sgshapiro } 91638032Speter FileName = NULL; 91790795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 91838032Speter 91938032Spetercleanup: 92064565Sgshapiro errno = save_errno; 92138032Speter return locked; 92238032Speter} 92390795Sgshapiro/* 92438032Speter** MCI_READ_PERSISTENT -- read persistent host status file 92538032Speter** 92638032Speter** Parameters: 92738032Speter** fp -- the file pointer to read. 92838032Speter** mci -- the pointer to fill in. 92938032Speter** 93038032Speter** Returns: 93138032Speter** -1 -- if the file was corrupt. 93238032Speter** 0 -- otherwise. 93338032Speter** 93438032Speter** Warning: 93538032Speter** This code makes the assumption that this data 93638032Speter** will be read in an atomic fashion, and that the data 93738032Speter** was written in an atomic fashion. Any other functioning 93838032Speter** may lead to some form of insanity. This should be 93938032Speter** perfectly safe due to underlying stdio buffering. 94038032Speter*/ 94138032Speter 94264565Sgshapirostatic int 94338032Spetermci_read_persistent(fp, mci) 94490795Sgshapiro SM_FILE_T *fp; 94538032Speter register MCI *mci; 94638032Speter{ 94738032Speter int ver; 94838032Speter register char *p; 94938032Speter int saveLineNumber = LineNumber; 95038032Speter char buf[MAXLINE]; 95138032Speter 95238032Speter if (fp == NULL) 953159613Sgshapiro { 95438032Speter syserr("mci_read_persistent: NULL fp"); 955159613Sgshapiro /* NOTREACHED */ 956159613Sgshapiro return -1; 957159613Sgshapiro } 95838032Speter if (mci == NULL) 959159613Sgshapiro { 96038032Speter syserr("mci_read_persistent: NULL mci"); 961159613Sgshapiro /* NOTREACHED */ 962159613Sgshapiro return -1; 963159613Sgshapiro } 96438032Speter if (tTd(56, 93)) 96538032Speter { 96690795Sgshapiro sm_dprintf("mci_read_persistent: fp=%lx, mci=", 96790795Sgshapiro (unsigned long) fp); 96838032Speter } 96938032Speter 97090795Sgshapiro SM_FREE_CLR(mci->mci_status); 97190795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 97238032Speter 97390795Sgshapiro sm_io_rewind(fp, SM_TIME_DEFAULT); 97438032Speter ver = -1; 97538032Speter LineNumber = 0; 976249729Sgshapiro while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 97738032Speter { 97838032Speter LineNumber++; 97938032Speter p = strchr(buf, '\n'); 98038032Speter if (p != NULL) 98138032Speter *p = '\0'; 98238032Speter switch (buf[0]) 98338032Speter { 98438032Speter case 'V': /* version stamp */ 98538032Speter ver = atoi(&buf[1]); 98638032Speter if (ver < 0 || ver > 0) 98738032Speter syserr("Unknown host status version %d: %d max", 98838032Speter ver, 0); 98938032Speter break; 99038032Speter 99138032Speter case 'E': /* UNIX error number */ 99238032Speter mci->mci_errno = atoi(&buf[1]); 99338032Speter break; 99438032Speter 99538032Speter case 'H': /* DNS error number */ 99638032Speter mci->mci_herrno = atoi(&buf[1]); 99738032Speter break; 99838032Speter 99938032Speter case 'S': /* UNIX exit status */ 100038032Speter mci->mci_exitstat = atoi(&buf[1]); 100138032Speter break; 100238032Speter 100338032Speter case 'D': /* DSN status */ 100438032Speter mci->mci_status = newstr(&buf[1]); 100538032Speter break; 100638032Speter 100738032Speter case 'R': /* SMTP status */ 100838032Speter mci->mci_rstatus = newstr(&buf[1]); 100938032Speter break; 101038032Speter 101138032Speter case 'U': /* last usage time */ 101238032Speter mci->mci_lastuse = atol(&buf[1]); 101338032Speter break; 101438032Speter 101538032Speter case '.': /* end of file */ 101690795Sgshapiro if (tTd(56, 93)) 1017132946Sgshapiro mci_dump(sm_debug_file(), mci, false); 101838032Speter return 0; 101938032Speter 102038032Speter default: 102138032Speter sm_syslog(LOG_CRIT, NOQID, 102238032Speter "%s: line %d: Unknown host status line \"%s\"", 102338032Speter FileName == NULL ? mci->mci_host : FileName, 102438032Speter LineNumber, buf); 102538032Speter LineNumber = saveLineNumber; 102638032Speter return -1; 102738032Speter } 102838032Speter } 102938032Speter LineNumber = saveLineNumber; 103090795Sgshapiro if (tTd(56, 93)) 103190795Sgshapiro sm_dprintf("incomplete (missing dot for EOF)\n"); 103238032Speter if (ver < 0) 103338032Speter return -1; 103438032Speter return 0; 103538032Speter} 103690795Sgshapiro/* 103738032Speter** MCI_STORE_PERSISTENT -- Store persistent MCI information 103838032Speter** 103938032Speter** Store information about host that is kept 104038032Speter** in common for all running sendmails. 104138032Speter** 104238032Speter** Parameters: 104338032Speter** mci -- the host/connection to store persistent info for. 104438032Speter** 104538032Speter** Returns: 104638032Speter** none. 104738032Speter*/ 104838032Speter 104938032Spetervoid 105038032Spetermci_store_persistent(mci) 105138032Speter MCI *mci; 105238032Speter{ 105364565Sgshapiro int save_errno = errno; 105438032Speter 105538032Speter if (mci == NULL) 105638032Speter { 105738032Speter if (tTd(56, 1)) 105890795Sgshapiro sm_dprintf("mci_store_persistent: NULL mci\n"); 105938032Speter return; 106038032Speter } 106138032Speter 106238032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 106338032Speter return; 106438032Speter 106538032Speter if (tTd(56, 1)) 106690795Sgshapiro sm_dprintf("mci_store_persistent: Storing information for %s\n", 106790795Sgshapiro mci->mci_host); 106838032Speter 106938032Speter if (mci->mci_statfile == NULL) 107038032Speter { 107138032Speter if (tTd(56, 1)) 107290795Sgshapiro sm_dprintf("mci_store_persistent: no statfile\n"); 107338032Speter return; 107438032Speter } 107538032Speter 107690795Sgshapiro sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 107738032Speter#if !NOFTRUNCATE 107890795Sgshapiro (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 107990795Sgshapiro (off_t) 0); 108064565Sgshapiro#endif /* !NOFTRUNCATE */ 108138032Speter 108290795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 108390795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 108490795Sgshapiro mci->mci_errno); 108590795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 108690795Sgshapiro mci->mci_herrno); 108790795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 108890795Sgshapiro mci->mci_exitstat); 108938032Speter if (mci->mci_status != NULL) 109090795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 109190795Sgshapiro "D%.80s\n", 109290795Sgshapiro denlstring(mci->mci_status, true, false)); 109338032Speter if (mci->mci_rstatus != NULL) 109490795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 109590795Sgshapiro "R%.80s\n", 109690795Sgshapiro denlstring(mci->mci_rstatus, true, false)); 109790795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 109890795Sgshapiro (long)(mci->mci_lastuse)); 109990795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 110038032Speter 110190795Sgshapiro (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 110238032Speter 110364565Sgshapiro errno = save_errno; 110438032Speter return; 110538032Speter} 110690795Sgshapiro/* 110738032Speter** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 110838032Speter** 110938032Speter** Recursively find all the mci host files in `pathname'. Default to 111038032Speter** main host status directory if no path is provided. 111138032Speter** Call (*action)(pathname, host) for each file found. 111238032Speter** 111338032Speter** Note: all information is collected in a list before it is processed. 111438032Speter** This may not be the best way to do it, but it seems safest, since 111538032Speter** the file system would be touched while we are attempting to traverse 111638032Speter** the directory tree otherwise (during purges). 111738032Speter** 111838032Speter** Parameters: 111938032Speter** action -- function to call on each node. If returns < 0, 112038032Speter** return immediately. 112138032Speter** pathname -- root of tree. If null, use main host status 112238032Speter** directory. 112338032Speter** 112438032Speter** Returns: 112538032Speter** < 0 -- if any action routine returns a negative value, that 112638032Speter** value is returned. 112738032Speter** 0 -- if we successfully went to completion. 112864565Sgshapiro** > 0 -- return status from action() 112938032Speter*/ 113038032Speter 113138032Speterint 113238032Spetermci_traverse_persistent(action, pathname) 1133141862Sgshapiro int (*action)__P((char *, char *)); 113438032Speter char *pathname; 113538032Speter{ 113638032Speter struct stat statbuf; 113738032Speter DIR *d; 113838032Speter int ret; 113938032Speter 114038032Speter if (pathname == NULL) 114138032Speter pathname = HostStatDir; 114238032Speter if (pathname == NULL) 114338032Speter return -1; 114438032Speter 114538032Speter if (tTd(56, 1)) 114690795Sgshapiro sm_dprintf("mci_traverse: pathname is %s\n", pathname); 114738032Speter 114838032Speter ret = stat(pathname, &statbuf); 114938032Speter if (ret < 0) 115038032Speter { 115138032Speter if (tTd(56, 2)) 115290795Sgshapiro sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 115390795Sgshapiro pathname, sm_errstring(errno)); 115438032Speter return ret; 115538032Speter } 115638032Speter if (S_ISDIR(statbuf.st_mode)) 115738032Speter { 115890795Sgshapiro bool leftone, removedone; 115990795Sgshapiro size_t len; 116090795Sgshapiro char *newptr; 116138032Speter struct dirent *e; 116298125Sgshapiro char newpath[MAXPATHLEN]; 1163157006Sgshapiro#if MAXPATHLEN <= MAXNAMLEN - 3 1164157006Sgshapiro ERROR "MAXPATHLEN <= MAXNAMLEN - 3" 1165157006Sgshapiro#endif /* MAXPATHLEN <= MAXNAMLEN - 3 */ 116638032Speter 116738032Speter if ((d = opendir(pathname)) == NULL) 116838032Speter { 116938032Speter if (tTd(56, 2)) 117090795Sgshapiro sm_dprintf("mci_traverse: opendir %s: %s\n", 117190795Sgshapiro pathname, sm_errstring(errno)); 117238032Speter return -1; 117338032Speter } 1174182352Sgshapiro 1175182352Sgshapiro /* 1176182352Sgshapiro ** Reserve space for trailing '/', at least one 1177182352Sgshapiro ** character, and '\0' 1178182352Sgshapiro */ 1179182352Sgshapiro 1180182352Sgshapiro len = sizeof(newpath) - 3; 118190795Sgshapiro if (sm_strlcpy(newpath, pathname, len) >= len) 118238032Speter { 1183182352Sgshapiro int save_errno = errno; 1184182352Sgshapiro 118538032Speter if (tTd(56, 2)) 118690795Sgshapiro sm_dprintf("mci_traverse: path \"%s\" too long", 118738032Speter pathname); 1188182352Sgshapiro (void) closedir(d); 1189182352Sgshapiro errno = save_errno; 119038032Speter return -1; 119138032Speter } 119238032Speter newptr = newpath + strlen(newpath); 119338032Speter *newptr++ = '/'; 1194182352Sgshapiro len = sizeof(newpath) - (newptr - newpath); 119538032Speter 119664565Sgshapiro /* 119764565Sgshapiro ** repeat until no file has been removed 119864565Sgshapiro ** this may become ugly when several files "expire" 119964565Sgshapiro ** during these loops, but it's better than doing 120064565Sgshapiro ** a rewinddir() inside the inner loop 120164565Sgshapiro */ 120290795Sgshapiro 120364565Sgshapiro do 120438032Speter { 120590795Sgshapiro leftone = removedone = false; 120664565Sgshapiro while ((e = readdir(d)) != NULL) 120764565Sgshapiro { 120864565Sgshapiro if (e->d_name[0] == '.') 120964565Sgshapiro continue; 121038032Speter 1211182352Sgshapiro if (sm_strlcpy(newptr, e->d_name, len) >= len) 1212182352Sgshapiro { 1213182352Sgshapiro /* Skip truncated copies */ 1214182352Sgshapiro if (tTd(56, 4)) 1215182352Sgshapiro { 1216182352Sgshapiro *newptr = '\0'; 1217182352Sgshapiro sm_dprintf("mci_traverse: path \"%s%s\" too long", 1218182352Sgshapiro newpath, e->d_name); 1219182352Sgshapiro } 1220182352Sgshapiro continue; 1221182352Sgshapiro } 122238032Speter 122377352Sgshapiro if (StopRequest) 122477352Sgshapiro stop_sendmail(); 122564565Sgshapiro ret = mci_traverse_persistent(action, newpath); 122664565Sgshapiro if (ret < 0) 122764565Sgshapiro break; 122864565Sgshapiro if (ret == 1) 122990795Sgshapiro leftone = true; 123064565Sgshapiro if (!removedone && ret == 0 && 123164565Sgshapiro action == mci_purge_persistent) 123290795Sgshapiro removedone = true; 123364565Sgshapiro } 123438032Speter if (ret < 0) 123538032Speter break; 123690795Sgshapiro 123738032Speter /* 123838032Speter ** The following appears to be 123938032Speter ** necessary during purges, since 124038032Speter ** we modify the directory structure 124138032Speter */ 124290795Sgshapiro 124364565Sgshapiro if (removedone) 124438032Speter rewinddir(d); 124564565Sgshapiro if (tTd(56, 40)) 124690795Sgshapiro sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 124764565Sgshapiro pathname, ret, removedone, leftone); 124864565Sgshapiro } while (removedone); 124938032Speter 125038032Speter /* purge (or whatever) the directory proper */ 125164565Sgshapiro if (!leftone) 125264565Sgshapiro { 125364565Sgshapiro *--newptr = '\0'; 125464565Sgshapiro ret = (*action)(newpath, NULL); 125564565Sgshapiro } 125664565Sgshapiro (void) closedir(d); 125738032Speter } 125838032Speter else if (S_ISREG(statbuf.st_mode)) 125938032Speter { 126038032Speter char *end = pathname + strlen(pathname) - 1; 126138032Speter char *start; 126238032Speter char *scan; 126338032Speter char host[MAXHOSTNAMELEN]; 126438032Speter char *hostptr = host; 126564565Sgshapiro 126638032Speter /* 126738032Speter ** Reconstruct the host name from the path to the 126838032Speter ** persistent information. 126938032Speter */ 127038032Speter 127138032Speter do 127238032Speter { 127338032Speter if (hostptr != host) 127438032Speter *(hostptr++) = '.'; 127538032Speter start = end; 1276120259Sgshapiro while (start > pathname && *(start - 1) != '/') 127738032Speter start--; 127838032Speter 127938032Speter if (*end == '.') 128038032Speter end--; 128138032Speter 128238032Speter for (scan = start; scan <= end; scan++) 128338032Speter *(hostptr++) = *scan; 128438032Speter 128538032Speter end = start - 2; 1286120259Sgshapiro } while (end > pathname && *end == '.'); 128738032Speter 128838032Speter *hostptr = '\0'; 128938032Speter 129038032Speter /* 129142580Speter ** Do something with the file containing the persistent 129242580Speter ** information. 129338032Speter */ 129490795Sgshapiro 129538032Speter ret = (*action)(pathname, host); 129638032Speter } 129738032Speter 129838032Speter return ret; 129938032Speter} 130090795Sgshapiro/* 130164565Sgshapiro** MCI_PRINT_PERSISTENT -- print persistent info 130238032Speter** 130338032Speter** Dump the persistent information in the file 'pathname' 130438032Speter** 130538032Speter** Parameters: 130638032Speter** pathname -- the pathname to the status file. 130738032Speter** hostname -- the corresponding host name. 130838032Speter** 130938032Speter** Returns: 131038032Speter** 0 131138032Speter*/ 131238032Speter 131338032Speterint 131438032Spetermci_print_persistent(pathname, hostname) 131538032Speter char *pathname; 131638032Speter char *hostname; 131738032Speter{ 131890795Sgshapiro static bool initflag = false; 131990795Sgshapiro SM_FILE_T *fp; 132038032Speter int width = Verbose ? 78 : 25; 132138032Speter bool locked; 132238032Speter MCI mcib; 132338032Speter 132438032Speter /* skip directories */ 132538032Speter if (hostname == NULL) 132638032Speter return 0; 132738032Speter 132877352Sgshapiro if (StopRequest) 132977352Sgshapiro stop_sendmail(); 133077352Sgshapiro 133138032Speter if (!initflag) 133238032Speter { 133390795Sgshapiro initflag = true; 133490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 133590795Sgshapiro " -------------- Hostname --------------- How long ago ---------Results---------\n"); 133638032Speter } 133738032Speter 133890795Sgshapiro fp = safefopen(pathname, O_RDONLY, FileMode, 133938032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 134038032Speter 134138032Speter if (fp == NULL) 134238032Speter { 134338032Speter if (tTd(56, 1)) 134490795Sgshapiro sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 134590795Sgshapiro pathname, sm_errstring(errno)); 134638032Speter return 0; 134738032Speter } 134838032Speter 134938032Speter FileName = pathname; 1350168520Sgshapiro memset(&mcib, '\0', sizeof(mcib)); 135138032Speter if (mci_read_persistent(fp, &mcib) < 0) 135238032Speter { 135338032Speter syserr("%s: could not read status file", pathname); 135490795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 135538032Speter FileName = NULL; 135638032Speter return 0; 135738032Speter } 135838032Speter 135990795Sgshapiro locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 136090795Sgshapiro "", LOCK_SH|LOCK_NB); 136190795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 136238032Speter FileName = NULL; 136338032Speter 136490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 136590795Sgshapiro locked ? '*' : ' ', hostname, 136690795Sgshapiro pintvl(curtime() - mcib.mci_lastuse, true)); 136738032Speter if (mcib.mci_rstatus != NULL) 136890795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 136990795Sgshapiro mcib.mci_rstatus); 137038032Speter else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 137190795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 137290795Sgshapiro "Deferred: %.*s\n", width - 10, 137390795Sgshapiro sm_errstring(mcib.mci_errno)); 137438032Speter else if (mcib.mci_exitstat != 0) 137538032Speter { 137690795Sgshapiro char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 137738032Speter 137890795Sgshapiro if (exmsg == NULL) 137938032Speter { 138038032Speter char buf[80]; 138138032Speter 1382168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 138390795Sgshapiro "Unknown mailer error %d", 138438032Speter mcib.mci_exitstat); 138590795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 138690795Sgshapiro width, buf); 138738032Speter } 138838032Speter else 138990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 139090795Sgshapiro width, &exmsg[5]); 139138032Speter } 139238032Speter else if (mcib.mci_errno == 0) 139390795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 139438032Speter else 139590795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 139690795Sgshapiro width - 4, sm_errstring(mcib.mci_errno)); 139738032Speter 139838032Speter return 0; 139938032Speter} 140090795Sgshapiro/* 140138032Speter** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 140238032Speter** 140338032Speter** Parameters: 140438032Speter** pathname -- path to the status file. 140538032Speter** hostname -- name of host corresponding to that file. 140638032Speter** NULL if this is a directory (domain). 140738032Speter** 140838032Speter** Returns: 140964565Sgshapiro** 0 -- ok 141064565Sgshapiro** 1 -- file not deleted (too young, incorrect format) 141164565Sgshapiro** < 0 -- some error occurred 141238032Speter*/ 141338032Speter 141438032Speterint 141538032Spetermci_purge_persistent(pathname, hostname) 141638032Speter char *pathname; 141738032Speter char *hostname; 141838032Speter{ 141964565Sgshapiro struct stat statbuf; 142038032Speter char *end = pathname + strlen(pathname) - 1; 142164565Sgshapiro int ret; 142238032Speter 142338032Speter if (tTd(56, 1)) 142490795Sgshapiro sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 142538032Speter 142664565Sgshapiro ret = stat(pathname, &statbuf); 142764565Sgshapiro if (ret < 0) 142864565Sgshapiro { 142964565Sgshapiro if (tTd(56, 2)) 143090795Sgshapiro sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 143190795Sgshapiro pathname, sm_errstring(errno)); 143264565Sgshapiro return ret; 143364565Sgshapiro } 143494337Sgshapiro if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 143564565Sgshapiro return 1; 143638032Speter if (hostname != NULL) 143738032Speter { 143838032Speter /* remove the file */ 143990795Sgshapiro ret = unlink(pathname); 144090795Sgshapiro if (ret < 0) 144138032Speter { 144290795Sgshapiro if (LogLevel > 8) 144390795Sgshapiro sm_syslog(LOG_ERR, NOQID, 144490795Sgshapiro "mci_purge_persistent: failed to unlink %s: %s", 144590795Sgshapiro pathname, sm_errstring(errno)); 144638032Speter if (tTd(56, 2)) 144790795Sgshapiro sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 144890795Sgshapiro pathname, sm_errstring(errno)); 144990795Sgshapiro return ret; 145038032Speter } 145138032Speter } 145238032Speter else 145338032Speter { 145438032Speter /* remove the directory */ 145538032Speter if (*end != '.') 145664565Sgshapiro return 1; 145738032Speter 145838032Speter if (tTd(56, 1)) 145990795Sgshapiro sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 146038032Speter 146190795Sgshapiro ret = rmdir(pathname); 146290795Sgshapiro if (ret < 0) 146338032Speter { 146438032Speter if (tTd(56, 2)) 146590795Sgshapiro sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 146690795Sgshapiro pathname, sm_errstring(errno)); 146790795Sgshapiro return ret; 146838032Speter } 146938032Speter } 147038032Speter 147138032Speter return 0; 147238032Speter} 147390795Sgshapiro/* 147442580Speter** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 147538032Speter** 1476120259Sgshapiro** Given `host', convert from a.b.c to $HostStatDir/c./b./a, 147738032Speter** putting the result into `path'. if `createflag' is set, intervening 147838032Speter** directories will be created as needed. 147938032Speter** 148038032Speter** Parameters: 148138032Speter** host -- host name to convert from. 148238032Speter** path -- place to store result. 148338032Speter** pathlen -- length of path buffer. 148438032Speter** createflag -- if set, create intervening directories as 148538032Speter** needed. 148638032Speter** 148738032Speter** Returns: 148838032Speter** 0 -- success 148938032Speter** -1 -- failure 149038032Speter*/ 149138032Speter 149264565Sgshapirostatic int 149338032Spetermci_generate_persistent_path(host, path, pathlen, createflag) 149438032Speter const char *host; 149538032Speter char *path; 149638032Speter int pathlen; 149738032Speter bool createflag; 149838032Speter{ 149938032Speter char *elem, *p, *x, ch; 150038032Speter int ret = 0; 150138032Speter int len; 150238032Speter char t_host[MAXHOSTNAMELEN]; 150364565Sgshapiro#if NETINET6 150464565Sgshapiro struct in6_addr in6_addr; 150564565Sgshapiro#endif /* NETINET6 */ 150638032Speter 150738032Speter /* 150838032Speter ** Rationality check the arguments. 150938032Speter */ 151038032Speter 151138032Speter if (host == NULL) 151238032Speter { 151338032Speter syserr("mci_generate_persistent_path: null host"); 151438032Speter return -1; 151538032Speter } 151638032Speter if (path == NULL) 151738032Speter { 151838032Speter syserr("mci_generate_persistent_path: null path"); 151938032Speter return -1; 152038032Speter } 152138032Speter 152238032Speter if (tTd(56, 80)) 152390795Sgshapiro sm_dprintf("mci_generate_persistent_path(%s): ", host); 152438032Speter 152538032Speter if (*host == '\0' || *host == '.') 152638032Speter return -1; 152738032Speter 152838032Speter /* make certain this is not a bracketed host number */ 1529168520Sgshapiro if (strlen(host) > sizeof(t_host) - 1) 153038032Speter return -1; 153138032Speter if (host[0] == '[') 1532168520Sgshapiro (void) sm_strlcpy(t_host, host + 1, sizeof(t_host)); 153364565Sgshapiro else 1534168520Sgshapiro (void) sm_strlcpy(t_host, host, sizeof(t_host)); 153538032Speter 153638032Speter /* 153738032Speter ** Delete any trailing dots from the hostname. 153838032Speter ** Leave 'elem' pointing at the \0. 153938032Speter */ 154038032Speter 154138032Speter elem = t_host + strlen(t_host); 154238032Speter while (elem > t_host && 154338032Speter (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 154438032Speter *--elem = '\0'; 154538032Speter 154638032Speter /* check for bogus bracketed address */ 154790795Sgshapiro if (host[0] == '[') 154890795Sgshapiro { 154990795Sgshapiro bool good = false; 155064565Sgshapiro# if NETINET6 155190795Sgshapiro if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 155290795Sgshapiro good = true; 155364565Sgshapiro# endif /* NETINET6 */ 155464565Sgshapiro# if NETINET 155590795Sgshapiro if (inet_addr(t_host) != INADDR_NONE) 155690795Sgshapiro good = true; 155764565Sgshapiro# endif /* NETINET */ 155890795Sgshapiro if (!good) 155990795Sgshapiro return -1; 156090795Sgshapiro } 156138032Speter 156238032Speter /* check for what will be the final length of the path */ 156338032Speter len = strlen(HostStatDir) + 2; 156438032Speter for (p = (char *) t_host; *p != '\0'; p++) 156538032Speter { 156638032Speter if (*p == '.') 156738032Speter len++; 156838032Speter len++; 156938032Speter if (p[0] == '.' && p[1] == '.') 157038032Speter return -1; 157138032Speter } 157238032Speter if (len > pathlen || len < 1) 157338032Speter return -1; 157490795Sgshapiro (void) sm_strlcpy(path, HostStatDir, pathlen); 157538032Speter p = path + strlen(path); 157638032Speter while (elem > t_host) 157738032Speter { 157838032Speter if (!path_is_dir(path, createflag)) 157938032Speter { 158038032Speter ret = -1; 158138032Speter break; 158238032Speter } 158338032Speter elem--; 158438032Speter while (elem >= t_host && *elem != '.') 158538032Speter elem--; 158638032Speter *p++ = '/'; 158738032Speter x = elem + 1; 158838032Speter while ((ch = *x++) != '\0' && ch != '.') 158938032Speter { 159038032Speter if (isascii(ch) && isupper(ch)) 159138032Speter ch = tolower(ch); 159238032Speter if (ch == '/') 159338032Speter ch = ':'; /* / -> : */ 159438032Speter *p++ = ch; 159538032Speter } 159638032Speter if (elem >= t_host) 159738032Speter *p++ = '.'; 159838032Speter *p = '\0'; 159938032Speter } 160038032Speter if (tTd(56, 80)) 160138032Speter { 160238032Speter if (ret < 0) 160390795Sgshapiro sm_dprintf("FAILURE %d\n", ret); 160438032Speter else 160590795Sgshapiro sm_dprintf("SUCCESS %s\n", path); 160638032Speter } 160764565Sgshapiro return ret; 160838032Speter} 1609