138032Speter/* 2223067Sgshapiro * Copyright (c) 1998-2005, 2010 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 * 1238032Speter */ 1338032Speter 1464565Sgshapiro#include <sendmail.h> 1564565Sgshapiro 16249729SgshapiroSM_RCSID("@(#)$Id: mci.c,v 8.224 2013/03/12 15:24:53 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 */ 35590795Sgshapiro 35690795Sgshapiro if (mci->mci_rpool == NULL) 35790795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 35890795Sgshapiro 35990795Sgshapiro if (mci->mci_macro.mac_rpool == NULL) 36090795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 36190795Sgshapiro 36264565Sgshapiro /* 36390795Sgshapiro ** We don't need to load the persistent data if we have data 36464565Sgshapiro ** already loaded in the cache. 36564565Sgshapiro */ 36664565Sgshapiro 36764565Sgshapiro if (mci->mci_host == NULL && 36864565Sgshapiro (mci->mci_host = s->s_name) != NULL && 36964565Sgshapiro !mci_load_persistent(mci)) 37038032Speter { 37138032Speter if (tTd(42, 2)) 37290795Sgshapiro sm_dprintf("mci_get(%s %s): lock failed\n", 37364565Sgshapiro host, m->m_name); 37438032Speter mci->mci_exitstat = EX_TEMPFAIL; 37538032Speter mci->mci_state = MCIS_CLOSED; 37638032Speter mci->mci_statfile = NULL; 37738032Speter return mci; 37838032Speter } 37938032Speter 38038032Speter if (tTd(42, 2)) 38138032Speter { 38290795Sgshapiro sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 38338032Speter host, m->m_name, mci->mci_state, mci->mci_flags, 38438032Speter mci->mci_exitstat, mci->mci_errno); 38538032Speter } 38638032Speter 38738032Speter if (mci->mci_state == MCIS_OPEN) 38838032Speter { 38938032Speter /* poke the connection to see if it's still alive */ 39038032Speter (void) smtpprobe(mci); 39138032Speter 39238032Speter /* reset the stored state in the event of a timeout */ 39338032Speter if (mci->mci_state != MCIS_OPEN) 39438032Speter { 39538032Speter mci->mci_errno = 0; 39638032Speter mci->mci_exitstat = EX_OK; 39738032Speter mci->mci_state = MCIS_CLOSED; 39838032Speter } 39938032Speter else 40038032Speter { 40190795Sgshapiro /* get peer host address */ 40238032Speter /* (this should really be in the mci struct) */ 403168520Sgshapiro SOCKADDR_LEN_T socklen = sizeof(CurHostAddr); 40438032Speter 40590795Sgshapiro (void) getpeername(sm_io_getinfo(mci->mci_in, 40690795Sgshapiro SM_IO_WHAT_FD, NULL), 40738032Speter (struct sockaddr *) &CurHostAddr, &socklen); 40838032Speter } 40938032Speter } 41038032Speter if (mci->mci_state == MCIS_CLOSED) 41138032Speter { 41238032Speter time_t now = curtime(); 41338032Speter 41438032Speter /* if this info is stale, ignore it */ 41594337Sgshapiro if (mci->mci_lastuse + MciInfoTimeout <= now) 41638032Speter { 41738032Speter mci->mci_lastuse = now; 41838032Speter mci->mci_errno = 0; 41938032Speter mci->mci_exitstat = EX_OK; 42038032Speter } 42138032Speter } 42238032Speter 42338032Speter return mci; 42438032Speter} 425141887Sgshapiro 42690795Sgshapiro/* 427141887Sgshapiro** MCI_CLOSE -- (forcefully) close files used for a connection. 428141887Sgshapiro** Note: this is a last resort, usually smtpquit() or endmailer() 429141887Sgshapiro** should be used to close a connection. 430141887Sgshapiro** 431141887Sgshapiro** Parameters: 432141887Sgshapiro** mci -- the connection to close. 433141887Sgshapiro** where -- where has this been called? 434141887Sgshapiro** 435141887Sgshapiro** Returns: 436141887Sgshapiro** none. 437141887Sgshapiro*/ 438141887Sgshapiro 439141887Sgshapirovoid 440141887Sgshapiromci_close(mci, where) 441141887Sgshapiro MCI *mci; 442141887Sgshapiro char *where; 443141887Sgshapiro{ 444141887Sgshapiro bool dumped; 445141887Sgshapiro 446141887Sgshapiro if (mci == NULL) 447141887Sgshapiro return; 448141887Sgshapiro dumped = false; 449141887Sgshapiro if (mci->mci_out != NULL) 450141887Sgshapiro { 451141887Sgshapiro if (tTd(56, 1)) 452141887Sgshapiro { 453141887Sgshapiro sm_dprintf("mci_close: mci_out!=NULL, where=%s\n", 454141887Sgshapiro where); 455141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 456141887Sgshapiro dumped = true; 457141887Sgshapiro } 458141887Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 459141887Sgshapiro mci->mci_out = NULL; 460141887Sgshapiro } 461141887Sgshapiro if (mci->mci_in != NULL) 462141887Sgshapiro { 463141887Sgshapiro if (tTd(56, 1)) 464141887Sgshapiro { 465141887Sgshapiro sm_dprintf("mci_close: mci_in!=NULL, where=%s\n", 466141887Sgshapiro where); 467141887Sgshapiro if (!dumped) 468141887Sgshapiro mci_dump(sm_debug_file(), mci, false); 469141887Sgshapiro } 470141887Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 471141887Sgshapiro mci->mci_in = NULL; 472141887Sgshapiro } 473141887Sgshapiro mci->mci_state = MCIS_CLOSED; 474141887Sgshapiro} 475141887Sgshapiro 476141887Sgshapiro/* 47790795Sgshapiro** MCI_NEW -- allocate new MCI structure 47890795Sgshapiro** 47990795Sgshapiro** Parameters: 48090795Sgshapiro** rpool -- if non-NULL: allocate from that rpool. 48190795Sgshapiro** 48290795Sgshapiro** Returns: 48390795Sgshapiro** mci (new). 48490795Sgshapiro*/ 48590795Sgshapiro 48690795SgshapiroMCI * 48790795Sgshapiromci_new(rpool) 48890795Sgshapiro SM_RPOOL_T *rpool; 48990795Sgshapiro{ 49090795Sgshapiro register MCI *mci; 49190795Sgshapiro 49290795Sgshapiro if (rpool == NULL) 493168520Sgshapiro mci = (MCI *) sm_malloc_x(sizeof(*mci)); 49490795Sgshapiro else 495168520Sgshapiro mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof(*mci)); 496168520Sgshapiro memset((char *) mci, '\0', sizeof(*mci)); 49790795Sgshapiro mci->mci_rpool = sm_rpool_new_x(NULL); 49890795Sgshapiro mci->mci_macro.mac_rpool = mci->mci_rpool; 49990795Sgshapiro return mci; 50090795Sgshapiro} 50190795Sgshapiro/* 50264565Sgshapiro** MCI_MATCH -- check connection cache for a particular host 50390795Sgshapiro** 50490795Sgshapiro** Parameters: 50590795Sgshapiro** host -- host to look for. 50690795Sgshapiro** m -- mailer. 50790795Sgshapiro** 50890795Sgshapiro** Returns: 50990795Sgshapiro** true iff open connection exists. 51064565Sgshapiro*/ 51164565Sgshapiro 51264565Sgshapirobool 51364565Sgshapiromci_match(host, m) 51464565Sgshapiro char *host; 51564565Sgshapiro MAILER *m; 51664565Sgshapiro{ 51764565Sgshapiro register MCI *mci; 51864565Sgshapiro register STAB *s; 51964565Sgshapiro 52071348Sgshapiro if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 52190795Sgshapiro return false; 52264565Sgshapiro s = stab(host, ST_MCI + m->m_mno, ST_FIND); 52364565Sgshapiro if (s == NULL) 52490795Sgshapiro return false; 52564565Sgshapiro 52664565Sgshapiro mci = &s->s_mci; 52790795Sgshapiro return mci->mci_state == MCIS_OPEN; 52864565Sgshapiro} 52990795Sgshapiro/* 53038032Speter** MCI_SETSTAT -- set status codes in MCI structure. 53138032Speter** 53238032Speter** Parameters: 53338032Speter** mci -- the MCI structure to set. 53438032Speter** xstat -- the exit status code. 53538032Speter** dstat -- the DSN status code. 53638032Speter** rstat -- the SMTP status code. 53738032Speter** 53838032Speter** Returns: 53938032Speter** none. 54038032Speter*/ 54138032Speter 54238032Spetervoid 54338032Spetermci_setstat(mci, xstat, dstat, rstat) 54438032Speter MCI *mci; 54538032Speter int xstat; 54638032Speter char *dstat; 54738032Speter char *rstat; 54838032Speter{ 54938032Speter /* protocol errors should never be interpreted as sticky */ 55038032Speter if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 55138032Speter mci->mci_exitstat = xstat; 55238032Speter 55390795Sgshapiro SM_FREE_CLR(mci->mci_status); 55490795Sgshapiro if (dstat != NULL) 55590795Sgshapiro mci->mci_status = sm_strdup_x(dstat); 55690795Sgshapiro 55790795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 55838032Speter if (rstat != NULL) 55990795Sgshapiro mci->mci_rstatus = sm_strdup_x(rstat); 56038032Speter} 56190795Sgshapiro/* 56238032Speter** MCI_DUMP -- dump the contents of an MCI structure. 56338032Speter** 56438032Speter** Parameters: 565132946Sgshapiro** fp -- output file pointer 56638032Speter** mci -- the MCI structure to dump. 56738032Speter** 56838032Speter** Returns: 56938032Speter** none. 57038032Speter** 57138032Speter** Side Effects: 57238032Speter** none. 57338032Speter*/ 57438032Speter 57538032Speterstruct mcifbits 57638032Speter{ 57738032Speter int mcif_bit; /* flag bit */ 57838032Speter char *mcif_name; /* flag name */ 57938032Speter}; 58064565Sgshapirostatic struct mcifbits MciFlags[] = 58138032Speter{ 58238032Speter { MCIF_VALID, "VALID" }, 58338032Speter { MCIF_CACHED, "CACHED" }, 58438032Speter { MCIF_ESMTP, "ESMTP" }, 58538032Speter { MCIF_EXPN, "EXPN" }, 58638032Speter { MCIF_SIZE, "SIZE" }, 58738032Speter { MCIF_8BITMIME, "8BITMIME" }, 58838032Speter { MCIF_7BIT, "7BIT" }, 58938032Speter { MCIF_INHEADER, "INHEADER" }, 59038032Speter { MCIF_CVT8TO7, "CVT8TO7" }, 59138032Speter { MCIF_DSN, "DSN" }, 59238032Speter { MCIF_8BITOK, "8BITOK" }, 59338032Speter { MCIF_CVT7TO8, "CVT7TO8" }, 59438032Speter { MCIF_INMIME, "INMIME" }, 59590795Sgshapiro { MCIF_AUTH, "AUTH" }, 596223067Sgshapiro { MCIF_AUTH2, "AUTH2" }, 59790795Sgshapiro { MCIF_AUTHACT, "AUTHACT" }, 59890795Sgshapiro { MCIF_ENHSTAT, "ENHSTAT" }, 59990795Sgshapiro { MCIF_PIPELINED, "PIPELINED" }, 60090795Sgshapiro#if STARTTLS 60190795Sgshapiro { MCIF_TLS, "TLS" }, 60290795Sgshapiro { MCIF_TLSACT, "TLSACT" }, 60390795Sgshapiro#endif /* STARTTLS */ 60490795Sgshapiro { MCIF_DLVR_BY, "DLVR_BY" }, 60571348Sgshapiro { 0, NULL } 60638032Speter}; 60738032Speter 60838032Spetervoid 609132946Sgshapiromci_dump(fp, mci, logit) 610132946Sgshapiro SM_FILE_T *fp; 61138032Speter register MCI *mci; 61238032Speter bool logit; 61338032Speter{ 61438032Speter register char *p; 61538032Speter char *sep; 61638032Speter char buf[4000]; 61738032Speter 61838032Speter sep = logit ? " " : "\n\t"; 61938032Speter p = buf; 62090795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 62138032Speter p += strlen(p); 62238032Speter if (mci == NULL) 62338032Speter { 62490795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 62538032Speter goto printit; 62638032Speter } 62790795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 62838032Speter p += strlen(p); 629120259Sgshapiro 630120259Sgshapiro /* 631120259Sgshapiro ** The following check is just for paranoia. It protects the 632120259Sgshapiro ** assignment in the if() clause. If there's not some minimum 633120259Sgshapiro ** amount of space we can stop right now. The check will not 634120259Sgshapiro ** trigger as long as sizeof(buf)=4000. 635120259Sgshapiro */ 636120259Sgshapiro 637120259Sgshapiro if (p >= buf + sizeof(buf) - 4) 638120259Sgshapiro goto printit; 63938032Speter if (mci->mci_flags != 0) 64038032Speter { 64138032Speter struct mcifbits *f; 64238032Speter 643120259Sgshapiro *p++ = '<'; /* protected above */ 64438032Speter for (f = MciFlags; f->mcif_bit != 0; f++) 64538032Speter { 64638032Speter if (!bitset(f->mcif_bit, mci->mci_flags)) 64738032Speter continue; 64890795Sgshapiro (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 64990795Sgshapiro f->mcif_name, ","); 65038032Speter p += strlen(p); 65138032Speter } 65238032Speter p[-1] = '>'; 65338032Speter } 65490795Sgshapiro 65590795Sgshapiro /* Note: sm_snprintf() takes care of NULL arguments for %s */ 65690795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 65738032Speter ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 65838032Speter sep, mci->mci_errno, mci->mci_herrno, 65964565Sgshapiro mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 66038032Speter p += strlen(p); 66190795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 66238032Speter "maxsize=%ld, phase=%s, mailer=%s,%s", 66390795Sgshapiro mci->mci_maxsize, mci->mci_phase, 66438032Speter mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 66538032Speter sep); 66638032Speter p += strlen(p); 66790795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 66838032Speter "status=%s, rstatus=%s,%s", 66990795Sgshapiro mci->mci_status, mci->mci_rstatus, sep); 67038032Speter p += strlen(p); 67190795Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 67238032Speter "host=%s, lastuse=%s", 67390795Sgshapiro mci->mci_host, ctime(&mci->mci_lastuse)); 67438032Speterprintit: 67538032Speter if (logit) 67638032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 67738032Speter else 678132946Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf); 67938032Speter} 68090795Sgshapiro/* 68138032Speter** MCI_DUMP_ALL -- print the entire MCI cache 68238032Speter** 68338032Speter** Parameters: 684132946Sgshapiro** fp -- output file pointer 68538032Speter** logit -- if set, log the result instead of printing 68638032Speter** to stdout. 68738032Speter** 68838032Speter** Returns: 68938032Speter** none. 69038032Speter*/ 69138032Speter 69238032Spetervoid 693132946Sgshapiromci_dump_all(fp, logit) 694132946Sgshapiro SM_FILE_T *fp; 69538032Speter bool logit; 69638032Speter{ 69738032Speter register int i; 69838032Speter 69938032Speter if (MciCache == NULL) 70038032Speter return; 70138032Speter 70238032Speter for (i = 0; i < MaxMciCache; i++) 703132946Sgshapiro mci_dump(fp, MciCache[i], logit); 70438032Speter} 70590795Sgshapiro/* 70642580Speter** MCI_LOCK_HOST -- Lock host while sending. 70738032Speter** 70838032Speter** If we are contacting a host, we'll need to 70938032Speter** update the status information in the host status 71038032Speter** file, and if we want to do that, we ought to have 71138032Speter** locked it. This has the (according to some) 71238032Speter** desirable effect of serializing connectivity with 71390795Sgshapiro** remote hosts -- i.e.: one connection to a given 71438032Speter** host at a time. 71538032Speter** 71638032Speter** Parameters: 71738032Speter** mci -- containing the host we want to lock. 71838032Speter** 71938032Speter** Returns: 72064565Sgshapiro** EX_OK -- got the lock. 72138032Speter** EX_TEMPFAIL -- didn't get the lock. 72238032Speter*/ 72338032Speter 72438032Speterint 72538032Spetermci_lock_host(mci) 72638032Speter MCI *mci; 72738032Speter{ 72838032Speter if (mci == NULL) 72938032Speter { 73038032Speter if (tTd(56, 1)) 73190795Sgshapiro sm_dprintf("mci_lock_host: NULL mci\n"); 73238032Speter return EX_OK; 73338032Speter } 73438032Speter 73538032Speter if (!SingleThreadDelivery) 73638032Speter return EX_OK; 73738032Speter 73838032Speter return mci_lock_host_statfile(mci); 73938032Speter} 74038032Speter 74164565Sgshapirostatic int 74238032Spetermci_lock_host_statfile(mci) 74338032Speter MCI *mci; 74438032Speter{ 74564565Sgshapiro int save_errno = errno; 74638032Speter int retVal = EX_OK; 74798125Sgshapiro char fname[MAXPATHLEN]; 74838032Speter 74938032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 75038032Speter return EX_OK; 75138032Speter 75238032Speter if (tTd(56, 2)) 75390795Sgshapiro sm_dprintf("mci_lock_host: attempting to lock %s\n", 75490795Sgshapiro mci->mci_host); 75538032Speter 756168520Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname), 75790795Sgshapiro true) < 0) 75838032Speter { 75938032Speter /* of course this should never happen */ 76038032Speter if (tTd(56, 2)) 76190795Sgshapiro sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 76290795Sgshapiro mci->mci_host); 76338032Speter 76438032Speter retVal = EX_TEMPFAIL; 76538032Speter goto cleanup; 76638032Speter } 76738032Speter 76838032Speter mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 76964565Sgshapiro SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 77038032Speter 77138032Speter if (mci->mci_statfile == NULL) 77238032Speter { 77390795Sgshapiro syserr("mci_lock_host: cannot create host lock file %s", fname); 77438032Speter goto cleanup; 77538032Speter } 77638032Speter 77790795Sgshapiro if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 77890795Sgshapiro fname, "", LOCK_EX|LOCK_NB)) 77938032Speter { 78038032Speter if (tTd(56, 2)) 78190795Sgshapiro sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 78264565Sgshapiro fname); 78390795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 78438032Speter mci->mci_statfile = NULL; 78538032Speter retVal = EX_TEMPFAIL; 78638032Speter goto cleanup; 78738032Speter } 78838032Speter 78938032Speter if (tTd(56, 12) && mci->mci_statfile != NULL) 79090795Sgshapiro sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 79138032Speter 79238032Spetercleanup: 79364565Sgshapiro errno = save_errno; 79438032Speter return retVal; 79538032Speter} 79690795Sgshapiro/* 79738032Speter** MCI_UNLOCK_HOST -- unlock host 79838032Speter** 79938032Speter** Clean up the lock on a host, close the file, let 80038032Speter** someone else use it. 80138032Speter** 80238032Speter** Parameters: 80338032Speter** mci -- us. 80438032Speter** 80538032Speter** Returns: 80638032Speter** nothing. 80738032Speter*/ 80838032Speter 80938032Spetervoid 81038032Spetermci_unlock_host(mci) 81138032Speter MCI *mci; 81238032Speter{ 81364565Sgshapiro int save_errno = errno; 81438032Speter 81538032Speter if (mci == NULL) 81638032Speter { 81738032Speter if (tTd(56, 1)) 81890795Sgshapiro sm_dprintf("mci_unlock_host: NULL mci\n"); 81938032Speter return; 82038032Speter } 82138032Speter 82238032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 82338032Speter return; 82438032Speter 82538032Speter if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 82638032Speter { 82738032Speter if (tTd(56, 1)) 82890795Sgshapiro sm_dprintf("mci_unlock_host: stat file already locked\n"); 82938032Speter } 83038032Speter else 83138032Speter { 83238032Speter if (tTd(56, 2)) 83390795Sgshapiro sm_dprintf("mci_unlock_host: store prior to unlock\n"); 83438032Speter mci_store_persistent(mci); 83538032Speter } 83638032Speter 83738032Speter if (mci->mci_statfile != NULL) 83838032Speter { 83990795Sgshapiro (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 84038032Speter mci->mci_statfile = NULL; 84138032Speter } 84238032Speter 84364565Sgshapiro errno = save_errno; 84438032Speter} 84590795Sgshapiro/* 84642580Speter** MCI_LOAD_PERSISTENT -- load persistent host info 84738032Speter** 84838032Speter** Load information about host that is kept 84938032Speter** in common for all running sendmails. 85038032Speter** 85138032Speter** Parameters: 85290795Sgshapiro** mci -- the host/connection to load persistent info for. 85338032Speter** 85438032Speter** Returns: 85590795Sgshapiro** true -- lock was successful 85690795Sgshapiro** false -- lock failed 85738032Speter*/ 85838032Speter 85964565Sgshapirostatic bool 86038032Spetermci_load_persistent(mci) 86138032Speter MCI *mci; 86238032Speter{ 86364565Sgshapiro int save_errno = errno; 86490795Sgshapiro bool locked = true; 86590795Sgshapiro SM_FILE_T *fp; 86698125Sgshapiro char fname[MAXPATHLEN]; 86738032Speter 86838032Speter if (mci == NULL) 86938032Speter { 87038032Speter if (tTd(56, 1)) 87190795Sgshapiro sm_dprintf("mci_load_persistent: NULL mci\n"); 87290795Sgshapiro return true; 87338032Speter } 87438032Speter 87538032Speter if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 87690795Sgshapiro return true; 87764565Sgshapiro 87838032Speter /* Already have the persistent information in memory */ 87938032Speter if (SingleThreadDelivery && mci->mci_statfile != NULL) 88090795Sgshapiro return true; 88138032Speter 88238032Speter if (tTd(56, 1)) 88390795Sgshapiro sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 88490795Sgshapiro mci->mci_host); 88564565Sgshapiro 886168520Sgshapiro if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname), 88790795Sgshapiro false) < 0) 88838032Speter { 88938032Speter /* Not much we can do if the file isn't there... */ 89038032Speter if (tTd(56, 1)) 89190795Sgshapiro sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 89238032Speter goto cleanup; 89338032Speter } 89438032Speter 89538032Speter fp = safefopen(fname, O_RDONLY, FileMode, 89638032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 89738032Speter if (fp == NULL) 89838032Speter { 89938032Speter /* I can't think of any reason this should ever happen */ 90038032Speter if (tTd(56, 1)) 90190795Sgshapiro sm_dprintf("mci_load_persistent: open(%s): %s\n", 90290795Sgshapiro fname, sm_errstring(errno)); 90338032Speter goto cleanup; 90438032Speter } 90538032Speter 90638032Speter FileName = fname; 90790795Sgshapiro locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 90890795Sgshapiro LOCK_SH|LOCK_NB); 90964565Sgshapiro if (locked) 91064565Sgshapiro { 91164565Sgshapiro (void) mci_read_persistent(fp, mci); 91290795Sgshapiro (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 91390795Sgshapiro "", LOCK_UN); 91464565Sgshapiro } 91538032Speter FileName = NULL; 91690795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 91738032Speter 91838032Spetercleanup: 91964565Sgshapiro errno = save_errno; 92038032Speter return locked; 92138032Speter} 92290795Sgshapiro/* 92338032Speter** MCI_READ_PERSISTENT -- read persistent host status file 92438032Speter** 92538032Speter** Parameters: 92638032Speter** fp -- the file pointer to read. 92738032Speter** mci -- the pointer to fill in. 92838032Speter** 92938032Speter** Returns: 93038032Speter** -1 -- if the file was corrupt. 93138032Speter** 0 -- otherwise. 93238032Speter** 93338032Speter** Warning: 93438032Speter** This code makes the assumption that this data 93538032Speter** will be read in an atomic fashion, and that the data 93638032Speter** was written in an atomic fashion. Any other functioning 93738032Speter** may lead to some form of insanity. This should be 93838032Speter** perfectly safe due to underlying stdio buffering. 93938032Speter*/ 94038032Speter 94164565Sgshapirostatic int 94238032Spetermci_read_persistent(fp, mci) 94390795Sgshapiro SM_FILE_T *fp; 94438032Speter register MCI *mci; 94538032Speter{ 94638032Speter int ver; 94738032Speter register char *p; 94838032Speter int saveLineNumber = LineNumber; 94938032Speter char buf[MAXLINE]; 95038032Speter 95138032Speter if (fp == NULL) 952159613Sgshapiro { 95338032Speter syserr("mci_read_persistent: NULL fp"); 954159613Sgshapiro /* NOTREACHED */ 955159613Sgshapiro return -1; 956159613Sgshapiro } 95738032Speter if (mci == NULL) 958159613Sgshapiro { 95938032Speter syserr("mci_read_persistent: NULL mci"); 960159613Sgshapiro /* NOTREACHED */ 961159613Sgshapiro return -1; 962159613Sgshapiro } 96338032Speter if (tTd(56, 93)) 96438032Speter { 96590795Sgshapiro sm_dprintf("mci_read_persistent: fp=%lx, mci=", 96690795Sgshapiro (unsigned long) fp); 96738032Speter } 96838032Speter 96990795Sgshapiro SM_FREE_CLR(mci->mci_status); 97090795Sgshapiro SM_FREE_CLR(mci->mci_rstatus); 97138032Speter 97290795Sgshapiro sm_io_rewind(fp, SM_TIME_DEFAULT); 97338032Speter ver = -1; 97438032Speter LineNumber = 0; 975249729Sgshapiro while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 97638032Speter { 97738032Speter LineNumber++; 97838032Speter p = strchr(buf, '\n'); 97938032Speter if (p != NULL) 98038032Speter *p = '\0'; 98138032Speter switch (buf[0]) 98238032Speter { 98338032Speter case 'V': /* version stamp */ 98438032Speter ver = atoi(&buf[1]); 98538032Speter if (ver < 0 || ver > 0) 98638032Speter syserr("Unknown host status version %d: %d max", 98738032Speter ver, 0); 98838032Speter break; 98938032Speter 99038032Speter case 'E': /* UNIX error number */ 99138032Speter mci->mci_errno = atoi(&buf[1]); 99238032Speter break; 99338032Speter 99438032Speter case 'H': /* DNS error number */ 99538032Speter mci->mci_herrno = atoi(&buf[1]); 99638032Speter break; 99738032Speter 99838032Speter case 'S': /* UNIX exit status */ 99938032Speter mci->mci_exitstat = atoi(&buf[1]); 100038032Speter break; 100138032Speter 100238032Speter case 'D': /* DSN status */ 100338032Speter mci->mci_status = newstr(&buf[1]); 100438032Speter break; 100538032Speter 100638032Speter case 'R': /* SMTP status */ 100738032Speter mci->mci_rstatus = newstr(&buf[1]); 100838032Speter break; 100938032Speter 101038032Speter case 'U': /* last usage time */ 101138032Speter mci->mci_lastuse = atol(&buf[1]); 101238032Speter break; 101338032Speter 101438032Speter case '.': /* end of file */ 101590795Sgshapiro if (tTd(56, 93)) 1016132946Sgshapiro mci_dump(sm_debug_file(), mci, false); 101738032Speter return 0; 101838032Speter 101938032Speter default: 102038032Speter sm_syslog(LOG_CRIT, NOQID, 102138032Speter "%s: line %d: Unknown host status line \"%s\"", 102238032Speter FileName == NULL ? mci->mci_host : FileName, 102338032Speter LineNumber, buf); 102438032Speter LineNumber = saveLineNumber; 102538032Speter return -1; 102638032Speter } 102738032Speter } 102838032Speter LineNumber = saveLineNumber; 102990795Sgshapiro if (tTd(56, 93)) 103090795Sgshapiro sm_dprintf("incomplete (missing dot for EOF)\n"); 103138032Speter if (ver < 0) 103238032Speter return -1; 103338032Speter return 0; 103438032Speter} 103590795Sgshapiro/* 103638032Speter** MCI_STORE_PERSISTENT -- Store persistent MCI information 103738032Speter** 103838032Speter** Store information about host that is kept 103938032Speter** in common for all running sendmails. 104038032Speter** 104138032Speter** Parameters: 104238032Speter** mci -- the host/connection to store persistent info for. 104338032Speter** 104438032Speter** Returns: 104538032Speter** none. 104638032Speter*/ 104738032Speter 104838032Spetervoid 104938032Spetermci_store_persistent(mci) 105038032Speter MCI *mci; 105138032Speter{ 105264565Sgshapiro int save_errno = errno; 105338032Speter 105438032Speter if (mci == NULL) 105538032Speter { 105638032Speter if (tTd(56, 1)) 105790795Sgshapiro sm_dprintf("mci_store_persistent: NULL mci\n"); 105838032Speter return; 105938032Speter } 106038032Speter 106138032Speter if (HostStatDir == NULL || mci->mci_host == NULL) 106238032Speter return; 106338032Speter 106438032Speter if (tTd(56, 1)) 106590795Sgshapiro sm_dprintf("mci_store_persistent: Storing information for %s\n", 106690795Sgshapiro mci->mci_host); 106738032Speter 106838032Speter if (mci->mci_statfile == NULL) 106938032Speter { 107038032Speter if (tTd(56, 1)) 107190795Sgshapiro sm_dprintf("mci_store_persistent: no statfile\n"); 107238032Speter return; 107338032Speter } 107438032Speter 107590795Sgshapiro sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 107638032Speter#if !NOFTRUNCATE 107790795Sgshapiro (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 107890795Sgshapiro (off_t) 0); 107964565Sgshapiro#endif /* !NOFTRUNCATE */ 108038032Speter 108190795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 108290795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 108390795Sgshapiro mci->mci_errno); 108490795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 108590795Sgshapiro mci->mci_herrno); 108690795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 108790795Sgshapiro mci->mci_exitstat); 108838032Speter if (mci->mci_status != NULL) 108990795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 109090795Sgshapiro "D%.80s\n", 109190795Sgshapiro denlstring(mci->mci_status, true, false)); 109238032Speter if (mci->mci_rstatus != NULL) 109390795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 109490795Sgshapiro "R%.80s\n", 109590795Sgshapiro denlstring(mci->mci_rstatus, true, false)); 109690795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 109790795Sgshapiro (long)(mci->mci_lastuse)); 109890795Sgshapiro (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 109938032Speter 110090795Sgshapiro (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 110138032Speter 110264565Sgshapiro errno = save_errno; 110338032Speter return; 110438032Speter} 110590795Sgshapiro/* 110638032Speter** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 110738032Speter** 110838032Speter** Recursively find all the mci host files in `pathname'. Default to 110938032Speter** main host status directory if no path is provided. 111038032Speter** Call (*action)(pathname, host) for each file found. 111138032Speter** 111238032Speter** Note: all information is collected in a list before it is processed. 111338032Speter** This may not be the best way to do it, but it seems safest, since 111438032Speter** the file system would be touched while we are attempting to traverse 111538032Speter** the directory tree otherwise (during purges). 111638032Speter** 111738032Speter** Parameters: 111838032Speter** action -- function to call on each node. If returns < 0, 111938032Speter** return immediately. 112038032Speter** pathname -- root of tree. If null, use main host status 112138032Speter** directory. 112238032Speter** 112338032Speter** Returns: 112438032Speter** < 0 -- if any action routine returns a negative value, that 112538032Speter** value is returned. 112638032Speter** 0 -- if we successfully went to completion. 112764565Sgshapiro** > 0 -- return status from action() 112838032Speter*/ 112938032Speter 113038032Speterint 113138032Spetermci_traverse_persistent(action, pathname) 1132141862Sgshapiro int (*action)__P((char *, char *)); 113338032Speter char *pathname; 113438032Speter{ 113538032Speter struct stat statbuf; 113638032Speter DIR *d; 113738032Speter int ret; 113838032Speter 113938032Speter if (pathname == NULL) 114038032Speter pathname = HostStatDir; 114138032Speter if (pathname == NULL) 114238032Speter return -1; 114338032Speter 114438032Speter if (tTd(56, 1)) 114590795Sgshapiro sm_dprintf("mci_traverse: pathname is %s\n", pathname); 114638032Speter 114738032Speter ret = stat(pathname, &statbuf); 114838032Speter if (ret < 0) 114938032Speter { 115038032Speter if (tTd(56, 2)) 115190795Sgshapiro sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 115290795Sgshapiro pathname, sm_errstring(errno)); 115338032Speter return ret; 115438032Speter } 115538032Speter if (S_ISDIR(statbuf.st_mode)) 115638032Speter { 115790795Sgshapiro bool leftone, removedone; 115890795Sgshapiro size_t len; 115990795Sgshapiro char *newptr; 116038032Speter struct dirent *e; 116198125Sgshapiro char newpath[MAXPATHLEN]; 1162157006Sgshapiro#if MAXPATHLEN <= MAXNAMLEN - 3 1163157006Sgshapiro ERROR "MAXPATHLEN <= MAXNAMLEN - 3" 1164157006Sgshapiro#endif /* MAXPATHLEN <= MAXNAMLEN - 3 */ 116538032Speter 116638032Speter if ((d = opendir(pathname)) == NULL) 116738032Speter { 116838032Speter if (tTd(56, 2)) 116990795Sgshapiro sm_dprintf("mci_traverse: opendir %s: %s\n", 117090795Sgshapiro pathname, sm_errstring(errno)); 117138032Speter return -1; 117238032Speter } 1173182352Sgshapiro 1174182352Sgshapiro /* 1175182352Sgshapiro ** Reserve space for trailing '/', at least one 1176182352Sgshapiro ** character, and '\0' 1177182352Sgshapiro */ 1178182352Sgshapiro 1179182352Sgshapiro len = sizeof(newpath) - 3; 118090795Sgshapiro if (sm_strlcpy(newpath, pathname, len) >= len) 118138032Speter { 1182182352Sgshapiro int save_errno = errno; 1183182352Sgshapiro 118438032Speter if (tTd(56, 2)) 118590795Sgshapiro sm_dprintf("mci_traverse: path \"%s\" too long", 118638032Speter pathname); 1187182352Sgshapiro (void) closedir(d); 1188182352Sgshapiro errno = save_errno; 118938032Speter return -1; 119038032Speter } 119138032Speter newptr = newpath + strlen(newpath); 119238032Speter *newptr++ = '/'; 1193182352Sgshapiro len = sizeof(newpath) - (newptr - newpath); 119438032Speter 119564565Sgshapiro /* 119664565Sgshapiro ** repeat until no file has been removed 119764565Sgshapiro ** this may become ugly when several files "expire" 119864565Sgshapiro ** during these loops, but it's better than doing 119964565Sgshapiro ** a rewinddir() inside the inner loop 120064565Sgshapiro */ 120190795Sgshapiro 120264565Sgshapiro do 120338032Speter { 120490795Sgshapiro leftone = removedone = false; 120564565Sgshapiro while ((e = readdir(d)) != NULL) 120664565Sgshapiro { 120764565Sgshapiro if (e->d_name[0] == '.') 120864565Sgshapiro continue; 120938032Speter 1210182352Sgshapiro if (sm_strlcpy(newptr, e->d_name, len) >= len) 1211182352Sgshapiro { 1212182352Sgshapiro /* Skip truncated copies */ 1213182352Sgshapiro if (tTd(56, 4)) 1214182352Sgshapiro { 1215182352Sgshapiro *newptr = '\0'; 1216182352Sgshapiro sm_dprintf("mci_traverse: path \"%s%s\" too long", 1217182352Sgshapiro newpath, e->d_name); 1218182352Sgshapiro } 1219182352Sgshapiro continue; 1220182352Sgshapiro } 122138032Speter 122277352Sgshapiro if (StopRequest) 122377352Sgshapiro stop_sendmail(); 122464565Sgshapiro ret = mci_traverse_persistent(action, newpath); 122564565Sgshapiro if (ret < 0) 122664565Sgshapiro break; 122764565Sgshapiro if (ret == 1) 122890795Sgshapiro leftone = true; 122964565Sgshapiro if (!removedone && ret == 0 && 123064565Sgshapiro action == mci_purge_persistent) 123190795Sgshapiro removedone = true; 123264565Sgshapiro } 123338032Speter if (ret < 0) 123438032Speter break; 123590795Sgshapiro 123638032Speter /* 123738032Speter ** The following appears to be 123838032Speter ** necessary during purges, since 123938032Speter ** we modify the directory structure 124038032Speter */ 124190795Sgshapiro 124264565Sgshapiro if (removedone) 124338032Speter rewinddir(d); 124464565Sgshapiro if (tTd(56, 40)) 124590795Sgshapiro sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 124664565Sgshapiro pathname, ret, removedone, leftone); 124764565Sgshapiro } while (removedone); 124838032Speter 124938032Speter /* purge (or whatever) the directory proper */ 125064565Sgshapiro if (!leftone) 125164565Sgshapiro { 125264565Sgshapiro *--newptr = '\0'; 125364565Sgshapiro ret = (*action)(newpath, NULL); 125464565Sgshapiro } 125564565Sgshapiro (void) closedir(d); 125638032Speter } 125738032Speter else if (S_ISREG(statbuf.st_mode)) 125838032Speter { 125938032Speter char *end = pathname + strlen(pathname) - 1; 126038032Speter char *start; 126138032Speter char *scan; 126238032Speter char host[MAXHOSTNAMELEN]; 126338032Speter char *hostptr = host; 126464565Sgshapiro 126538032Speter /* 126638032Speter ** Reconstruct the host name from the path to the 126738032Speter ** persistent information. 126838032Speter */ 126938032Speter 127038032Speter do 127138032Speter { 127238032Speter if (hostptr != host) 127338032Speter *(hostptr++) = '.'; 127438032Speter start = end; 1275120259Sgshapiro while (start > pathname && *(start - 1) != '/') 127638032Speter start--; 127738032Speter 127838032Speter if (*end == '.') 127938032Speter end--; 128038032Speter 128138032Speter for (scan = start; scan <= end; scan++) 128238032Speter *(hostptr++) = *scan; 128338032Speter 128438032Speter end = start - 2; 1285120259Sgshapiro } while (end > pathname && *end == '.'); 128638032Speter 128738032Speter *hostptr = '\0'; 128838032Speter 128938032Speter /* 129042580Speter ** Do something with the file containing the persistent 129142580Speter ** information. 129238032Speter */ 129390795Sgshapiro 129438032Speter ret = (*action)(pathname, host); 129538032Speter } 129638032Speter 129738032Speter return ret; 129838032Speter} 129990795Sgshapiro/* 130064565Sgshapiro** MCI_PRINT_PERSISTENT -- print persistent info 130138032Speter** 130238032Speter** Dump the persistent information in the file 'pathname' 130338032Speter** 130438032Speter** Parameters: 130538032Speter** pathname -- the pathname to the status file. 130638032Speter** hostname -- the corresponding host name. 130738032Speter** 130838032Speter** Returns: 130938032Speter** 0 131038032Speter*/ 131138032Speter 131238032Speterint 131338032Spetermci_print_persistent(pathname, hostname) 131438032Speter char *pathname; 131538032Speter char *hostname; 131638032Speter{ 131790795Sgshapiro static bool initflag = false; 131890795Sgshapiro SM_FILE_T *fp; 131938032Speter int width = Verbose ? 78 : 25; 132038032Speter bool locked; 132138032Speter MCI mcib; 132238032Speter 132338032Speter /* skip directories */ 132438032Speter if (hostname == NULL) 132538032Speter return 0; 132638032Speter 132777352Sgshapiro if (StopRequest) 132877352Sgshapiro stop_sendmail(); 132977352Sgshapiro 133038032Speter if (!initflag) 133138032Speter { 133290795Sgshapiro initflag = true; 133390795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 133490795Sgshapiro " -------------- Hostname --------------- How long ago ---------Results---------\n"); 133538032Speter } 133638032Speter 133790795Sgshapiro fp = safefopen(pathname, O_RDONLY, FileMode, 133838032Speter SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 133938032Speter 134038032Speter if (fp == NULL) 134138032Speter { 134238032Speter if (tTd(56, 1)) 134390795Sgshapiro sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 134490795Sgshapiro pathname, sm_errstring(errno)); 134538032Speter return 0; 134638032Speter } 134738032Speter 134838032Speter FileName = pathname; 1349168520Sgshapiro memset(&mcib, '\0', sizeof(mcib)); 135038032Speter if (mci_read_persistent(fp, &mcib) < 0) 135138032Speter { 135238032Speter syserr("%s: could not read status file", pathname); 135390795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 135438032Speter FileName = NULL; 135538032Speter return 0; 135638032Speter } 135738032Speter 135890795Sgshapiro locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 135990795Sgshapiro "", LOCK_SH|LOCK_NB); 136090795Sgshapiro (void) sm_io_close(fp, SM_TIME_DEFAULT); 136138032Speter FileName = NULL; 136238032Speter 136390795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 136490795Sgshapiro locked ? '*' : ' ', hostname, 136590795Sgshapiro pintvl(curtime() - mcib.mci_lastuse, true)); 136638032Speter if (mcib.mci_rstatus != NULL) 136790795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 136890795Sgshapiro mcib.mci_rstatus); 136938032Speter else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 137090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 137190795Sgshapiro "Deferred: %.*s\n", width - 10, 137290795Sgshapiro sm_errstring(mcib.mci_errno)); 137338032Speter else if (mcib.mci_exitstat != 0) 137438032Speter { 137590795Sgshapiro char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 137638032Speter 137790795Sgshapiro if (exmsg == NULL) 137838032Speter { 137938032Speter char buf[80]; 138038032Speter 1381168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 138290795Sgshapiro "Unknown mailer error %d", 138338032Speter mcib.mci_exitstat); 138490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 138590795Sgshapiro width, buf); 138638032Speter } 138738032Speter else 138890795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 138990795Sgshapiro width, &exmsg[5]); 139038032Speter } 139138032Speter else if (mcib.mci_errno == 0) 139290795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 139338032Speter else 139490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 139590795Sgshapiro width - 4, sm_errstring(mcib.mci_errno)); 139638032Speter 139738032Speter return 0; 139838032Speter} 139990795Sgshapiro/* 140038032Speter** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 140138032Speter** 140238032Speter** Parameters: 140338032Speter** pathname -- path to the status file. 140438032Speter** hostname -- name of host corresponding to that file. 140538032Speter** NULL if this is a directory (domain). 140638032Speter** 140738032Speter** Returns: 140864565Sgshapiro** 0 -- ok 140964565Sgshapiro** 1 -- file not deleted (too young, incorrect format) 141064565Sgshapiro** < 0 -- some error occurred 141138032Speter*/ 141238032Speter 141338032Speterint 141438032Spetermci_purge_persistent(pathname, hostname) 141538032Speter char *pathname; 141638032Speter char *hostname; 141738032Speter{ 141864565Sgshapiro struct stat statbuf; 141938032Speter char *end = pathname + strlen(pathname) - 1; 142064565Sgshapiro int ret; 142138032Speter 142238032Speter if (tTd(56, 1)) 142390795Sgshapiro sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 142438032Speter 142564565Sgshapiro ret = stat(pathname, &statbuf); 142664565Sgshapiro if (ret < 0) 142764565Sgshapiro { 142864565Sgshapiro if (tTd(56, 2)) 142990795Sgshapiro sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 143090795Sgshapiro pathname, sm_errstring(errno)); 143164565Sgshapiro return ret; 143264565Sgshapiro } 143394337Sgshapiro if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 143464565Sgshapiro return 1; 143538032Speter if (hostname != NULL) 143638032Speter { 143738032Speter /* remove the file */ 143890795Sgshapiro ret = unlink(pathname); 143990795Sgshapiro if (ret < 0) 144038032Speter { 144190795Sgshapiro if (LogLevel > 8) 144290795Sgshapiro sm_syslog(LOG_ERR, NOQID, 144390795Sgshapiro "mci_purge_persistent: failed to unlink %s: %s", 144490795Sgshapiro pathname, sm_errstring(errno)); 144538032Speter if (tTd(56, 2)) 144690795Sgshapiro sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 144790795Sgshapiro pathname, sm_errstring(errno)); 144890795Sgshapiro return ret; 144938032Speter } 145038032Speter } 145138032Speter else 145238032Speter { 145338032Speter /* remove the directory */ 145438032Speter if (*end != '.') 145564565Sgshapiro return 1; 145638032Speter 145738032Speter if (tTd(56, 1)) 145890795Sgshapiro sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 145938032Speter 146090795Sgshapiro ret = rmdir(pathname); 146190795Sgshapiro if (ret < 0) 146238032Speter { 146338032Speter if (tTd(56, 2)) 146490795Sgshapiro sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 146590795Sgshapiro pathname, sm_errstring(errno)); 146690795Sgshapiro return ret; 146738032Speter } 146838032Speter } 146938032Speter 147038032Speter return 0; 147138032Speter} 147290795Sgshapiro/* 147342580Speter** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 147438032Speter** 1475120259Sgshapiro** Given `host', convert from a.b.c to $HostStatDir/c./b./a, 147638032Speter** putting the result into `path'. if `createflag' is set, intervening 147738032Speter** directories will be created as needed. 147838032Speter** 147938032Speter** Parameters: 148038032Speter** host -- host name to convert from. 148138032Speter** path -- place to store result. 148238032Speter** pathlen -- length of path buffer. 148338032Speter** createflag -- if set, create intervening directories as 148438032Speter** needed. 148538032Speter** 148638032Speter** Returns: 148738032Speter** 0 -- success 148838032Speter** -1 -- failure 148938032Speter*/ 149038032Speter 149164565Sgshapirostatic int 149238032Spetermci_generate_persistent_path(host, path, pathlen, createflag) 149338032Speter const char *host; 149438032Speter char *path; 149538032Speter int pathlen; 149638032Speter bool createflag; 149738032Speter{ 149838032Speter char *elem, *p, *x, ch; 149938032Speter int ret = 0; 150038032Speter int len; 150138032Speter char t_host[MAXHOSTNAMELEN]; 150264565Sgshapiro#if NETINET6 150364565Sgshapiro struct in6_addr in6_addr; 150464565Sgshapiro#endif /* NETINET6 */ 150538032Speter 150638032Speter /* 150738032Speter ** Rationality check the arguments. 150838032Speter */ 150938032Speter 151038032Speter if (host == NULL) 151138032Speter { 151238032Speter syserr("mci_generate_persistent_path: null host"); 151338032Speter return -1; 151438032Speter } 151538032Speter if (path == NULL) 151638032Speter { 151738032Speter syserr("mci_generate_persistent_path: null path"); 151838032Speter return -1; 151938032Speter } 152038032Speter 152138032Speter if (tTd(56, 80)) 152290795Sgshapiro sm_dprintf("mci_generate_persistent_path(%s): ", host); 152338032Speter 152438032Speter if (*host == '\0' || *host == '.') 152538032Speter return -1; 152638032Speter 152738032Speter /* make certain this is not a bracketed host number */ 1528168520Sgshapiro if (strlen(host) > sizeof(t_host) - 1) 152938032Speter return -1; 153038032Speter if (host[0] == '[') 1531168520Sgshapiro (void) sm_strlcpy(t_host, host + 1, sizeof(t_host)); 153264565Sgshapiro else 1533168520Sgshapiro (void) sm_strlcpy(t_host, host, sizeof(t_host)); 153438032Speter 153538032Speter /* 153638032Speter ** Delete any trailing dots from the hostname. 153738032Speter ** Leave 'elem' pointing at the \0. 153838032Speter */ 153938032Speter 154038032Speter elem = t_host + strlen(t_host); 154138032Speter while (elem > t_host && 154238032Speter (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 154338032Speter *--elem = '\0'; 154438032Speter 154538032Speter /* check for bogus bracketed address */ 154690795Sgshapiro if (host[0] == '[') 154790795Sgshapiro { 154890795Sgshapiro bool good = false; 154964565Sgshapiro# if NETINET6 155090795Sgshapiro if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 155190795Sgshapiro good = true; 155264565Sgshapiro# endif /* NETINET6 */ 155364565Sgshapiro# if NETINET 155490795Sgshapiro if (inet_addr(t_host) != INADDR_NONE) 155590795Sgshapiro good = true; 155664565Sgshapiro# endif /* NETINET */ 155790795Sgshapiro if (!good) 155890795Sgshapiro return -1; 155990795Sgshapiro } 156038032Speter 156138032Speter /* check for what will be the final length of the path */ 156238032Speter len = strlen(HostStatDir) + 2; 156338032Speter for (p = (char *) t_host; *p != '\0'; p++) 156438032Speter { 156538032Speter if (*p == '.') 156638032Speter len++; 156738032Speter len++; 156838032Speter if (p[0] == '.' && p[1] == '.') 156938032Speter return -1; 157038032Speter } 157138032Speter if (len > pathlen || len < 1) 157238032Speter return -1; 157390795Sgshapiro (void) sm_strlcpy(path, HostStatDir, pathlen); 157438032Speter p = path + strlen(path); 157538032Speter while (elem > t_host) 157638032Speter { 157738032Speter if (!path_is_dir(path, createflag)) 157838032Speter { 157938032Speter ret = -1; 158038032Speter break; 158138032Speter } 158238032Speter elem--; 158338032Speter while (elem >= t_host && *elem != '.') 158438032Speter elem--; 158538032Speter *p++ = '/'; 158638032Speter x = elem + 1; 158738032Speter while ((ch = *x++) != '\0' && ch != '.') 158838032Speter { 158938032Speter if (isascii(ch) && isupper(ch)) 159038032Speter ch = tolower(ch); 159138032Speter if (ch == '/') 159238032Speter ch = ':'; /* / -> : */ 159338032Speter *p++ = ch; 159438032Speter } 159538032Speter if (elem >= t_host) 159638032Speter *p++ = '.'; 159738032Speter *p = '\0'; 159838032Speter } 159938032Speter if (tTd(56, 80)) 160038032Speter { 160138032Speter if (ret < 0) 160290795Sgshapiro sm_dprintf("FAILURE %d\n", ret); 160338032Speter else 160490795Sgshapiro sm_dprintf("SUCCESS %s\n", path); 160538032Speter } 160664565Sgshapiro return ret; 160738032Speter} 1608