138032Speter/* 2261370Sgshapiro * Copyright (c) 1998-2007, 2009 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 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 1464562Sgshapiro#include <sendmail.h> 1564562Sgshapiro 16266711SgshapiroSM_RCSID("@(#)$Id: util.c,v 8.427 2013-11-22 20:51:57 ca Exp $") 1764562Sgshapiro 18168515Sgshapiro#include <sm/sendmail.h> 1990792Sgshapiro#include <sysexits.h> 2090792Sgshapiro#include <sm/xtrap.h> 2164562Sgshapiro 2290792Sgshapiro/* 23132943Sgshapiro** NEWSTR -- Create a copy of a C string 24132943Sgshapiro** 25132943Sgshapiro** Parameters: 26132943Sgshapiro** s -- the string to copy. 27132943Sgshapiro** 28132943Sgshapiro** Returns: 29132943Sgshapiro** pointer to newly allocated string. 30132943Sgshapiro*/ 31132943Sgshapiro 32132943Sgshapirochar * 33132943Sgshapironewstr(s) 34132943Sgshapiro const char *s; 35132943Sgshapiro{ 36132943Sgshapiro size_t l; 37132943Sgshapiro char *n; 38132943Sgshapiro 39132943Sgshapiro l = strlen(s); 40132943Sgshapiro SM_ASSERT(l + 1 > l); 41132943Sgshapiro n = xalloc(l + 1); 42132943Sgshapiro sm_strlcpy(n, s, l + 1); 43132943Sgshapiro return n; 44132943Sgshapiro} 45132943Sgshapiro 46132943Sgshapiro/* 4738032Speter** ADDQUOTES -- Adds quotes & quote bits to a string. 4838032Speter** 4990792Sgshapiro** Runs through a string and adds backslashes and quote bits. 5038032Speter** 5138032Speter** Parameters: 5238032Speter** s -- the string to modify. 5390792Sgshapiro** rpool -- resource pool from which to allocate result 5438032Speter** 5538032Speter** Returns: 5638032Speter** pointer to quoted string. 5738032Speter*/ 5838032Speter 5938032Speterchar * 6090792Sgshapiroaddquotes(s, rpool) 6138032Speter char *s; 6290792Sgshapiro SM_RPOOL_T *rpool; 6338032Speter{ 6438032Speter int len = 0; 6538032Speter char c; 6638032Speter char *p = s, *q, *r; 6738032Speter 6838032Speter if (s == NULL) 6938032Speter return NULL; 7038032Speter 7138032Speter /* Find length of quoted string */ 7238032Speter while ((c = *p++) != '\0') 7338032Speter { 7438032Speter len++; 7538032Speter if (c == '\\' || c == '"') 7638032Speter len++; 7738032Speter } 7864562Sgshapiro 7990792Sgshapiro q = r = sm_rpool_malloc_x(rpool, len + 3); 8038032Speter p = s; 8138032Speter 8238032Speter /* add leading quote */ 8338032Speter *q++ = '"'; 8438032Speter while ((c = *p++) != '\0') 8538032Speter { 8638032Speter /* quote \ or " */ 8738032Speter if (c == '\\' || c == '"') 8838032Speter *q++ = '\\'; 8938032Speter *q++ = c; 9038032Speter } 9138032Speter *q++ = '"'; 9238032Speter *q = '\0'; 9338032Speter return r; 9438032Speter} 95110560Sgshapiro 9690792Sgshapiro/* 97141858Sgshapiro** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided 98141858Sgshapiro** the following character is alpha-numerical. 99110560Sgshapiro** 100110560Sgshapiro** This is done in place. 101110560Sgshapiro** 102110560Sgshapiro** Parameters: 103110560Sgshapiro** s -- the string to strip. 104110560Sgshapiro** 105110560Sgshapiro** Returns: 106110560Sgshapiro** none. 107110560Sgshapiro*/ 108110560Sgshapiro 109110560Sgshapirovoid 110110560Sgshapirostripbackslash(s) 111110560Sgshapiro char *s; 112110560Sgshapiro{ 113110560Sgshapiro char *p, *q, c; 114110560Sgshapiro 115110560Sgshapiro if (s == NULL || *s == '\0') 116110560Sgshapiro return; 117110560Sgshapiro p = q = s; 118110560Sgshapiro while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1])))) 119110560Sgshapiro p++; 120110560Sgshapiro do 121110560Sgshapiro { 122110560Sgshapiro c = *q++ = *p++; 123110560Sgshapiro } while (c != '\0'); 124110560Sgshapiro} 125110560Sgshapiro 126110560Sgshapiro/* 12738032Speter** RFC822_STRING -- Checks string for proper RFC822 string quoting. 12838032Speter** 12938032Speter** Runs through a string and verifies RFC822 special characters 13038032Speter** are only found inside comments, quoted strings, or backslash 13138032Speter** escaped. Also verified balanced quotes and parenthesis. 13238032Speter** 13338032Speter** Parameters: 13438032Speter** s -- the string to modify. 13538032Speter** 13638032Speter** Returns: 13790792Sgshapiro** true iff the string is RFC822 compliant, false otherwise. 13838032Speter*/ 13938032Speter 14038032Speterbool 14138032Speterrfc822_string(s) 14238032Speter char *s; 14338032Speter{ 14490792Sgshapiro bool quoted = false; 14538032Speter int commentlev = 0; 14638032Speter char *c = s; 14738032Speter 14838032Speter if (s == NULL) 14990792Sgshapiro return false; 15038032Speter 15138032Speter while (*c != '\0') 15238032Speter { 15338032Speter /* escaped character */ 15438032Speter if (*c == '\\') 15538032Speter { 15638032Speter c++; 15738032Speter if (*c == '\0') 15890792Sgshapiro return false; 15938032Speter } 16038032Speter else if (commentlev == 0 && *c == '"') 16138032Speter quoted = !quoted; 16238032Speter else if (!quoted) 16338032Speter { 16438032Speter if (*c == ')') 16538032Speter { 16638032Speter /* unbalanced ')' */ 16738032Speter if (commentlev == 0) 16890792Sgshapiro return false; 16938032Speter else 17038032Speter commentlev--; 17138032Speter } 17238032Speter else if (*c == '(') 17338032Speter commentlev++; 17438032Speter else if (commentlev == 0 && 17538032Speter strchr(MustQuoteChars, *c) != NULL) 17690792Sgshapiro return false; 17738032Speter } 17838032Speter c++; 17938032Speter } 18090792Sgshapiro 18138032Speter /* unbalanced '"' or '(' */ 18290792Sgshapiro return !quoted && commentlev == 0; 18338032Speter} 184168515Sgshapiro 18590792Sgshapiro/* 18642575Speter** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 18742575Speter** 18864562Sgshapiro** Arbitrarily shorten (in place) an RFC822 string and rebalance 18942575Speter** comments and quotes. 19042575Speter** 19142575Speter** Parameters: 19242575Speter** string -- the string to shorten 19342575Speter** length -- the maximum size, 0 if no maximum 19442575Speter** 19542575Speter** Returns: 19690792Sgshapiro** true if string is changed, false otherwise 19742575Speter** 19842575Speter** Side Effects: 19942575Speter** Changes string in place, possibly resulting 20042575Speter** in a shorter string. 20142575Speter*/ 20242575Speter 20342575Speterbool 20442575Spetershorten_rfc822_string(string, length) 20542575Speter char *string; 20642575Speter size_t length; 20742575Speter{ 20890792Sgshapiro bool backslash = false; 20990792Sgshapiro bool modified = false; 21090792Sgshapiro bool quoted = false; 21142575Speter size_t slen; 21242575Speter int parencount = 0; 21342575Speter char *ptr = string; 21464562Sgshapiro 21542575Speter /* 21642575Speter ** If have to rebalance an already short enough string, 21742575Speter ** need to do it within allocated space. 21842575Speter */ 21971345Sgshapiro 22042575Speter slen = strlen(string); 22142575Speter if (length == 0 || slen < length) 22242575Speter length = slen; 22342575Speter 22442575Speter while (*ptr != '\0') 22542575Speter { 22642575Speter if (backslash) 22742575Speter { 22890792Sgshapiro backslash = false; 22942575Speter goto increment; 23042575Speter } 23142575Speter 23242575Speter if (*ptr == '\\') 23390792Sgshapiro backslash = true; 23442575Speter else if (*ptr == '(') 23542575Speter { 23642575Speter if (!quoted) 23742575Speter parencount++; 23842575Speter } 23942575Speter else if (*ptr == ')') 24042575Speter { 24142575Speter if (--parencount < 0) 24242575Speter parencount = 0; 24342575Speter } 24464562Sgshapiro 24542575Speter /* Inside a comment, quotes don't matter */ 24642575Speter if (parencount <= 0 && *ptr == '"') 24742575Speter quoted = !quoted; 24842575Speter 24942575Speterincrement: 25042575Speter /* Check for sufficient space for next character */ 25164562Sgshapiro if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 25242575Speter parencount + 25342575Speter (quoted ? 1 : 0))) 25442575Speter { 25542575Speter /* Not enough, backtrack */ 25642575Speter if (*ptr == '\\') 25790792Sgshapiro backslash = false; 25842575Speter else if (*ptr == '(' && !quoted) 25942575Speter parencount--; 26042575Speter else if (*ptr == '"' && parencount == 0) 26190792Sgshapiro quoted = false; 26242575Speter break; 26342575Speter } 26442575Speter ptr++; 26542575Speter } 26642575Speter 26742575Speter /* Rebalance */ 26842575Speter while (parencount-- > 0) 26942575Speter { 27042575Speter if (*ptr != ')') 27142575Speter { 27290792Sgshapiro modified = true; 27342575Speter *ptr = ')'; 27442575Speter } 27542575Speter ptr++; 27642575Speter } 27742575Speter if (quoted) 27842575Speter { 27942575Speter if (*ptr != '"') 28042575Speter { 28190792Sgshapiro modified = true; 28242575Speter *ptr = '"'; 28342575Speter } 28442575Speter ptr++; 28542575Speter } 28642575Speter if (*ptr != '\0') 28742575Speter { 28890792Sgshapiro modified = true; 28942575Speter *ptr = '\0'; 29042575Speter } 29142575Speter return modified; 29242575Speter} 293168515Sgshapiro 29490792Sgshapiro/* 29542575Speter** FIND_CHARACTER -- find an unquoted character in an RFC822 string 29642575Speter** 29742575Speter** Find an unquoted, non-commented character in an RFC822 29842575Speter** string and return a pointer to its location in the 29942575Speter** string. 30042575Speter** 30142575Speter** Parameters: 30242575Speter** string -- the string to search 30342575Speter** character -- the character to find 30442575Speter** 30542575Speter** Returns: 30642575Speter** pointer to the character, or 30742575Speter** a pointer to the end of the line if character is not found 30842575Speter*/ 30942575Speter 31042575Speterchar * 31142575Speterfind_character(string, character) 31242575Speter char *string; 31364562Sgshapiro int character; 31442575Speter{ 31590792Sgshapiro bool backslash = false; 31690792Sgshapiro bool quoted = false; 31742575Speter int parencount = 0; 31864562Sgshapiro 31942575Speter while (string != NULL && *string != '\0') 32042575Speter { 32142575Speter if (backslash) 32242575Speter { 32390792Sgshapiro backslash = false; 32442575Speter if (!quoted && character == '\\' && *string == '\\') 32542575Speter break; 32642575Speter string++; 32742575Speter continue; 32842575Speter } 32942575Speter switch (*string) 33042575Speter { 33142575Speter case '\\': 33290792Sgshapiro backslash = true; 33342575Speter break; 33464562Sgshapiro 33542575Speter case '(': 33642575Speter if (!quoted) 33742575Speter parencount++; 33842575Speter break; 33964562Sgshapiro 34042575Speter case ')': 34142575Speter if (--parencount < 0) 34242575Speter parencount = 0; 34342575Speter break; 34442575Speter } 34564562Sgshapiro 34642575Speter /* Inside a comment, nothing matters */ 34742575Speter if (parencount > 0) 34842575Speter { 34942575Speter string++; 35042575Speter continue; 35142575Speter } 35264562Sgshapiro 35342575Speter if (*string == '"') 35442575Speter quoted = !quoted; 35542575Speter else if (*string == character && !quoted) 35642575Speter break; 35742575Speter string++; 35842575Speter } 35942575Speter 36042575Speter /* Return pointer to the character */ 36142575Speter return string; 36242575Speter} 36390792Sgshapiro 36490792Sgshapiro/* 36590792Sgshapiro** CHECK_BODYTYPE -- check bodytype parameter 36638032Speter** 36738032Speter** Parameters: 36890792Sgshapiro** bodytype -- bodytype parameter 36938032Speter** 37038032Speter** Returns: 37190792Sgshapiro** BODYTYPE_* according to parameter 37238032Speter** 37338032Speter*/ 37438032Speter 37590792Sgshapiroint 37690792Sgshapirocheck_bodytype(bodytype) 37790792Sgshapiro char *bodytype; 37838032Speter{ 37990792Sgshapiro /* check body type for legality */ 38090792Sgshapiro if (bodytype == NULL) 38190792Sgshapiro return BODYTYPE_NONE; 38290792Sgshapiro if (sm_strcasecmp(bodytype, "7BIT") == 0) 38390792Sgshapiro return BODYTYPE_7BIT; 38490792Sgshapiro if (sm_strcasecmp(bodytype, "8BITMIME") == 0) 38590792Sgshapiro return BODYTYPE_8BITMIME; 38690792Sgshapiro return BODYTYPE_ILLEGAL; 38790792Sgshapiro} 38838032Speter 38990792Sgshapiro/* 39090792Sgshapiro** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." 39177349Sgshapiro** 39277349Sgshapiro** Parameters: 39390792Sgshapiro** str -- string to truncate 39490792Sgshapiro** len -- maximum length (including '\0') (0 for unlimited) 39590792Sgshapiro** delim -- delimiter character 39677349Sgshapiro** 39777349Sgshapiro** Returns: 39890792Sgshapiro** None. 39977349Sgshapiro*/ 40077349Sgshapiro 40190792Sgshapirovoid 40290792Sgshapirotruncate_at_delim(str, len, delim) 40390792Sgshapiro char *str; 40490792Sgshapiro size_t len; 40590792Sgshapiro int delim; 40677349Sgshapiro{ 40790792Sgshapiro char *p; 40877349Sgshapiro 40990792Sgshapiro if (str == NULL || len == 0 || strlen(str) < len) 41090792Sgshapiro return; 41177349Sgshapiro 41290792Sgshapiro *(str + len - 1) = '\0'; 41390792Sgshapiro while ((p = strrchr(str, delim)) != NULL) 41477349Sgshapiro { 41590792Sgshapiro *p = '\0'; 41690792Sgshapiro if (p - str + 4 < len) 41790792Sgshapiro { 418120256Sgshapiro *p++ = (char) delim; 41990792Sgshapiro *p = '\0'; 42090792Sgshapiro (void) sm_strlcat(str, "...", len); 42190792Sgshapiro return; 42290792Sgshapiro } 42390792Sgshapiro } 42477349Sgshapiro 42590792Sgshapiro /* Couldn't find a place to append "..." */ 42690792Sgshapiro if (len > 3) 42790792Sgshapiro (void) sm_strlcpy(str, "...", len); 42890792Sgshapiro else 42990792Sgshapiro str[0] = '\0'; 43077349Sgshapiro} 431168515Sgshapiro 43290792Sgshapiro/* 43390792Sgshapiro** XALLOC -- Allocate memory, raise an exception on error 43477349Sgshapiro** 43577349Sgshapiro** Parameters: 43690792Sgshapiro** sz -- size of area to allocate. 43777349Sgshapiro** 43877349Sgshapiro** Returns: 43977349Sgshapiro** pointer to data region. 44077349Sgshapiro** 44190792Sgshapiro** Exceptions: 44290792Sgshapiro** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory 44390792Sgshapiro** 44477349Sgshapiro** Side Effects: 44577349Sgshapiro** Memory is allocated. 44677349Sgshapiro*/ 44777349Sgshapiro 44877349Sgshapirochar * 44990792Sgshapiro#if SM_HEAP_CHECK 45090792Sgshapiroxalloc_tagged(sz, file, line) 45190792Sgshapiro register int sz; 45290792Sgshapiro char *file; 45390792Sgshapiro int line; 45490792Sgshapiro#else /* SM_HEAP_CHECK */ 45590792Sgshapiroxalloc(sz) 45690792Sgshapiro register int sz; 45790792Sgshapiro#endif /* SM_HEAP_CHECK */ 45877349Sgshapiro{ 45977349Sgshapiro register char *p; 46077349Sgshapiro 461157001Sgshapiro SM_REQUIRE(sz >= 0); 462157001Sgshapiro 46377349Sgshapiro /* some systems can't handle size zero mallocs */ 46477349Sgshapiro if (sz <= 0) 46577349Sgshapiro sz = 1; 46677349Sgshapiro 46790792Sgshapiro /* scaffolding for testing error handling code */ 46890792Sgshapiro sm_xtrap_raise_x(&SmHeapOutOfMemory); 46990792Sgshapiro 47090792Sgshapiro p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); 47177349Sgshapiro if (p == NULL) 47277349Sgshapiro { 47390792Sgshapiro sm_exc_raise_x(&SmHeapOutOfMemory); 47477349Sgshapiro } 47577349Sgshapiro return p; 47677349Sgshapiro} 477168515Sgshapiro 47890792Sgshapiro/* 47938032Speter** COPYPLIST -- copy list of pointers. 48038032Speter** 48190792Sgshapiro** This routine is the equivalent of strdup for lists of 48238032Speter** pointers. 48338032Speter** 48438032Speter** Parameters: 48538032Speter** list -- list of pointers to copy. 48638032Speter** Must be NULL terminated. 48790792Sgshapiro** copycont -- if true, copy the contents of the vector 48838032Speter** (which must be a string) also. 48990792Sgshapiro** rpool -- resource pool from which to allocate storage, 49090792Sgshapiro** or NULL 49138032Speter** 49238032Speter** Returns: 49338032Speter** a copy of 'list'. 49438032Speter*/ 49538032Speter 49638032Speterchar ** 49790792Sgshapirocopyplist(list, copycont, rpool) 49838032Speter char **list; 49938032Speter bool copycont; 50090792Sgshapiro SM_RPOOL_T *rpool; 50138032Speter{ 50238032Speter register char **vp; 50338032Speter register char **newvp; 50438032Speter 50538032Speter for (vp = list; *vp != NULL; vp++) 50638032Speter continue; 50738032Speter 50838032Speter vp++; 50938032Speter 510168515Sgshapiro newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp)); 511168515Sgshapiro memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp)); 51238032Speter 51338032Speter if (copycont) 51438032Speter { 51538032Speter for (vp = newvp; *vp != NULL; vp++) 51690792Sgshapiro *vp = sm_rpool_strdup_x(rpool, *vp); 51738032Speter } 51838032Speter 51964562Sgshapiro return newvp; 52038032Speter} 521168515Sgshapiro 52290792Sgshapiro/* 52338032Speter** COPYQUEUE -- copy address queue. 52438032Speter** 52590792Sgshapiro** This routine is the equivalent of strdup for address queues; 52664562Sgshapiro** addresses marked as QS_IS_DEAD() aren't copied 52738032Speter** 52838032Speter** Parameters: 52938032Speter** addr -- list of address structures to copy. 53090792Sgshapiro** rpool -- resource pool from which to allocate storage 53138032Speter** 53238032Speter** Returns: 53338032Speter** a copy of 'addr'. 53438032Speter*/ 53538032Speter 53638032SpeterADDRESS * 53790792Sgshapirocopyqueue(addr, rpool) 53838032Speter ADDRESS *addr; 53990792Sgshapiro SM_RPOOL_T *rpool; 54038032Speter{ 54138032Speter register ADDRESS *newaddr; 54238032Speter ADDRESS *ret; 54338032Speter register ADDRESS **tail = &ret; 54438032Speter 54538032Speter while (addr != NULL) 54638032Speter { 54764562Sgshapiro if (!QS_IS_DEAD(addr->q_state)) 54838032Speter { 54990792Sgshapiro newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, 550168515Sgshapiro sizeof(*newaddr)); 55138032Speter STRUCTCOPY(*addr, *newaddr); 55238032Speter *tail = newaddr; 55338032Speter tail = &newaddr->q_next; 55438032Speter } 55538032Speter addr = addr->q_next; 55638032Speter } 55738032Speter *tail = NULL; 55864562Sgshapiro 55938032Speter return ret; 56038032Speter} 561168515Sgshapiro 56290792Sgshapiro/* 56364562Sgshapiro** LOG_SENDMAIL_PID -- record sendmail pid and command line. 56464562Sgshapiro** 56564562Sgshapiro** Parameters: 56664562Sgshapiro** e -- the current envelope. 56764562Sgshapiro** 56864562Sgshapiro** Returns: 56964562Sgshapiro** none. 57064562Sgshapiro** 57164562Sgshapiro** Side Effects: 57290792Sgshapiro** writes pidfile, logs command line. 573132943Sgshapiro** keeps file open and locked to prevent overwrite of active file 57464562Sgshapiro*/ 57564562Sgshapiro 576132943Sgshapirostatic SM_FILE_T *Pidf = NULL; 577132943Sgshapiro 57864562Sgshapirovoid 57964562Sgshapirolog_sendmail_pid(e) 58064562Sgshapiro ENVELOPE *e; 58164562Sgshapiro{ 58264562Sgshapiro long sff; 58398121Sgshapiro char pidpath[MAXPATHLEN]; 58490792Sgshapiro extern char *CommandLineArgs; 58564562Sgshapiro 58664562Sgshapiro /* write the pid to the log file for posterity */ 587132943Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK; 58864562Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 58964562Sgshapiro sff |= SFF_OPENASROOT; 590168515Sgshapiro expand(PidFile, pidpath, sizeof(pidpath), e); 591132943Sgshapiro Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff); 592132943Sgshapiro if (Pidf == NULL) 59364562Sgshapiro { 594132943Sgshapiro if (errno == EWOULDBLOCK) 595132943Sgshapiro sm_syslog(LOG_ERR, NOQID, 596132943Sgshapiro "unable to write pid to %s: file in use by another process", 597132943Sgshapiro pidpath); 598132943Sgshapiro else 599132943Sgshapiro sm_syslog(LOG_ERR, NOQID, 600132943Sgshapiro "unable to write pid to %s: %s", 601132943Sgshapiro pidpath, sm_errstring(errno)); 60264562Sgshapiro } 60364562Sgshapiro else 60464562Sgshapiro { 605132943Sgshapiro PidFilePid = getpid(); 60664562Sgshapiro 60764562Sgshapiro /* write the process id on line 1 */ 608132943Sgshapiro (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n", 609132943Sgshapiro (long) PidFilePid); 61064562Sgshapiro 61164562Sgshapiro /* line 2 contains all command line flags */ 612132943Sgshapiro (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n", 61390792Sgshapiro CommandLineArgs); 61464562Sgshapiro 615132943Sgshapiro /* flush */ 616132943Sgshapiro (void) sm_io_flush(Pidf, SM_TIME_DEFAULT); 617132943Sgshapiro 618132943Sgshapiro /* 619132943Sgshapiro ** Leave pid file open until process ends 620132943Sgshapiro ** so it's not overwritten by another 621132943Sgshapiro ** process. 622132943Sgshapiro */ 62364562Sgshapiro } 62490792Sgshapiro if (LogLevel > 9) 62590792Sgshapiro sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); 62664562Sgshapiro} 627132943Sgshapiro 62890792Sgshapiro/* 629132943Sgshapiro** CLOSE_SENDMAIL_PID -- close sendmail pid file 630132943Sgshapiro** 631132943Sgshapiro** Parameters: 632132943Sgshapiro** none. 633132943Sgshapiro** 634132943Sgshapiro** Returns: 635132943Sgshapiro** none. 636132943Sgshapiro*/ 637132943Sgshapiro 638132943Sgshapirovoid 639132943Sgshapiroclose_sendmail_pid() 640132943Sgshapiro{ 641132943Sgshapiro if (Pidf == NULL) 642132943Sgshapiro return; 643132943Sgshapiro 644132943Sgshapiro (void) sm_io_close(Pidf, SM_TIME_DEFAULT); 645132943Sgshapiro Pidf = NULL; 646132943Sgshapiro} 647132943Sgshapiro 648132943Sgshapiro/* 64964562Sgshapiro** SET_DELIVERY_MODE -- set and record the delivery mode 65064562Sgshapiro** 65164562Sgshapiro** Parameters: 65264562Sgshapiro** mode -- delivery mode 65364562Sgshapiro** e -- the current envelope. 65464562Sgshapiro** 65564562Sgshapiro** Returns: 65664562Sgshapiro** none. 65764562Sgshapiro** 65864562Sgshapiro** Side Effects: 65990792Sgshapiro** sets {deliveryMode} macro 66064562Sgshapiro*/ 66164562Sgshapiro 66264562Sgshapirovoid 66364562Sgshapiroset_delivery_mode(mode, e) 66464562Sgshapiro int mode; 66564562Sgshapiro ENVELOPE *e; 66664562Sgshapiro{ 66764562Sgshapiro char buf[2]; 66864562Sgshapiro 66990792Sgshapiro e->e_sendmode = (char) mode; 67090792Sgshapiro buf[0] = (char) mode; 67164562Sgshapiro buf[1] = '\0'; 67290792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); 67364562Sgshapiro} 674168515Sgshapiro 67590792Sgshapiro/* 67690792Sgshapiro** SET_OP_MODE -- set and record the op mode 67790792Sgshapiro** 67890792Sgshapiro** Parameters: 67990792Sgshapiro** mode -- op mode 68090792Sgshapiro** e -- the current envelope. 68190792Sgshapiro** 68290792Sgshapiro** Returns: 68390792Sgshapiro** none. 68490792Sgshapiro** 68590792Sgshapiro** Side Effects: 68690792Sgshapiro** sets {opMode} macro 68790792Sgshapiro*/ 68890792Sgshapiro 68990792Sgshapirovoid 69090792Sgshapiroset_op_mode(mode) 69190792Sgshapiro int mode; 69290792Sgshapiro{ 69390792Sgshapiro char buf[2]; 69490792Sgshapiro extern ENVELOPE BlankEnvelope; 69590792Sgshapiro 69690792Sgshapiro OpMode = (char) mode; 69790792Sgshapiro buf[0] = (char) mode; 69890792Sgshapiro buf[1] = '\0'; 69990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); 70090792Sgshapiro} 701168515Sgshapiro 70290792Sgshapiro/* 70338032Speter** PRINTAV -- print argument vector. 70438032Speter** 70538032Speter** Parameters: 706132943Sgshapiro** fp -- output file pointer. 70738032Speter** av -- argument vector. 70838032Speter** 70938032Speter** Returns: 71038032Speter** none. 71138032Speter** 71238032Speter** Side Effects: 71338032Speter** prints av. 71438032Speter*/ 71538032Speter 71638032Spetervoid 717132943Sgshapiroprintav(fp, av) 718132943Sgshapiro SM_FILE_T *fp; 719168515Sgshapiro char **av; 72038032Speter{ 72138032Speter while (*av != NULL) 72238032Speter { 72338032Speter if (tTd(0, 44)) 72490792Sgshapiro sm_dprintf("\n\t%08lx=", (unsigned long) *av); 72538032Speter else 726132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' '); 727168515Sgshapiro if (tTd(0, 99)) 728168515Sgshapiro sm_dprintf("%s", str2prt(*av++)); 729168515Sgshapiro else 730168515Sgshapiro xputs(fp, *av++); 73138032Speter } 732132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n'); 73338032Speter} 734168515Sgshapiro 73590792Sgshapiro/* 73638032Speter** XPUTS -- put string doing control escapes. 73738032Speter** 73838032Speter** Parameters: 739132943Sgshapiro** fp -- output file pointer. 74038032Speter** s -- string to put. 74138032Speter** 74238032Speter** Returns: 74338032Speter** none. 74438032Speter** 74538032Speter** Side Effects: 74638032Speter** output to stdout 74738032Speter*/ 74838032Speter 74938032Spetervoid 750132943Sgshapiroxputs(fp, s) 751132943Sgshapiro SM_FILE_T *fp; 752168515Sgshapiro const char *s; 75338032Speter{ 754168515Sgshapiro int c; 755168515Sgshapiro struct metamac *mp; 75690792Sgshapiro bool shiftout = false; 75738032Speter extern struct metamac MetaMacros[]; 75890792Sgshapiro static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 75990792Sgshapiro "@(#)$Debug: ANSI - enable reverse video in debug output $"); 76038032Speter 76190792Sgshapiro /* 76290792Sgshapiro ** TermEscape is set here, rather than in main(), 76390792Sgshapiro ** because ANSI mode can be turned on or off at any time 76490792Sgshapiro ** if we are in -bt rule testing mode. 76590792Sgshapiro */ 76690792Sgshapiro 76790792Sgshapiro if (sm_debug_unknown(&DebugANSI)) 76890792Sgshapiro { 76990792Sgshapiro if (sm_debug_active(&DebugANSI, 1)) 77090792Sgshapiro { 77190792Sgshapiro TermEscape.te_rv_on = "\033[7m"; 772168515Sgshapiro TermEscape.te_normal = "\033[0m"; 77390792Sgshapiro } 77490792Sgshapiro else 77590792Sgshapiro { 77690792Sgshapiro TermEscape.te_rv_on = ""; 777168515Sgshapiro TermEscape.te_normal = ""; 77890792Sgshapiro } 77990792Sgshapiro } 78090792Sgshapiro 78138032Speter if (s == NULL) 78238032Speter { 783132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s", 784168515Sgshapiro TermEscape.te_rv_on, TermEscape.te_normal); 78538032Speter return; 78638032Speter } 78738032Speter while ((c = (*s++ & 0377)) != '\0') 78838032Speter { 78938032Speter if (shiftout) 79038032Speter { 791132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 792168515Sgshapiro TermEscape.te_normal); 79390792Sgshapiro shiftout = false; 79438032Speter } 795168515Sgshapiro if (!isascii(c) && !tTd(84, 1)) 79638032Speter { 79738032Speter if (c == MATCHREPL) 79838032Speter { 799132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 80090792Sgshapiro "%s$", 80190792Sgshapiro TermEscape.te_rv_on); 80290792Sgshapiro shiftout = true; 80338032Speter if (*s == '\0') 80438032Speter continue; 80538032Speter c = *s++ & 0377; 80638032Speter goto printchar; 80738032Speter } 80838032Speter if (c == MACROEXPAND || c == MACRODEXPAND) 80938032Speter { 810132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 81190792Sgshapiro "%s$", 81290792Sgshapiro TermEscape.te_rv_on); 81338032Speter if (c == MACRODEXPAND) 814132943Sgshapiro (void) sm_io_putc(fp, 81590792Sgshapiro SM_TIME_DEFAULT, '&'); 81690792Sgshapiro shiftout = true; 81738032Speter if (*s == '\0') 81838032Speter continue; 81938032Speter if (strchr("=~&?", *s) != NULL) 820132943Sgshapiro (void) sm_io_putc(fp, 82190792Sgshapiro SM_TIME_DEFAULT, 82290792Sgshapiro *s++); 82338032Speter if (bitset(0200, *s)) 824132943Sgshapiro (void) sm_io_fprintf(fp, 82590792Sgshapiro SM_TIME_DEFAULT, 82690792Sgshapiro "{%s}", 82790792Sgshapiro macname(bitidx(*s++))); 82838032Speter else 829132943Sgshapiro (void) sm_io_fprintf(fp, 83090792Sgshapiro SM_TIME_DEFAULT, 83190792Sgshapiro "%c", 83290792Sgshapiro *s++); 83338032Speter continue; 83438032Speter } 83538032Speter for (mp = MetaMacros; mp->metaname != '\0'; mp++) 83638032Speter { 83790792Sgshapiro if (bitidx(mp->metaval) == c) 83838032Speter { 839132943Sgshapiro (void) sm_io_fprintf(fp, 84090792Sgshapiro SM_TIME_DEFAULT, 84190792Sgshapiro "%s$%c", 84290792Sgshapiro TermEscape.te_rv_on, 84390792Sgshapiro mp->metaname); 84490792Sgshapiro shiftout = true; 84538032Speter break; 84638032Speter } 84738032Speter } 84838032Speter if (c == MATCHCLASS || c == MATCHNCLASS) 84938032Speter { 85038032Speter if (bitset(0200, *s)) 851132943Sgshapiro (void) sm_io_fprintf(fp, 85290792Sgshapiro SM_TIME_DEFAULT, 85390792Sgshapiro "{%s}", 85490792Sgshapiro macname(bitidx(*s++))); 85538032Speter else if (*s != '\0') 856132943Sgshapiro (void) sm_io_fprintf(fp, 85790792Sgshapiro SM_TIME_DEFAULT, 85890792Sgshapiro "%c", 85990792Sgshapiro *s++); 86038032Speter } 86138032Speter if (mp->metaname != '\0') 86238032Speter continue; 86338032Speter 86438032Speter /* unrecognized meta character */ 865132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-", 86690792Sgshapiro TermEscape.te_rv_on); 86790792Sgshapiro shiftout = true; 86838032Speter c &= 0177; 86938032Speter } 87038032Speter printchar: 871203004Sgshapiro if (isascii(c) && isprint(c)) 87238032Speter { 873132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 87438032Speter continue; 87538032Speter } 87638032Speter 87738032Speter /* wasn't a meta-macro -- find another way to print it */ 87838032Speter switch (c) 87938032Speter { 88038032Speter case '\n': 88138032Speter c = 'n'; 88238032Speter break; 88338032Speter 88438032Speter case '\r': 88538032Speter c = 'r'; 88638032Speter break; 88738032Speter 88838032Speter case '\t': 88938032Speter c = 't'; 89038032Speter break; 89138032Speter } 89238032Speter if (!shiftout) 89338032Speter { 894132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 89590792Sgshapiro TermEscape.te_rv_on); 89690792Sgshapiro shiftout = true; 89738032Speter } 898203004Sgshapiro if (isascii(c) && isprint(c)) 89938032Speter { 900132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\'); 901132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 90238032Speter } 903168515Sgshapiro else if (tTd(84, 2)) 904168515Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c); 905168515Sgshapiro else if (tTd(84, 1)) 906168515Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c); 907168515Sgshapiro else if (!isascii(c) && !tTd(84, 1)) 90838032Speter { 909132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^'); 910132943Sgshapiro (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100); 91138032Speter } 91238032Speter } 91338032Speter if (shiftout) 914132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 915168515Sgshapiro TermEscape.te_normal); 916132943Sgshapiro (void) sm_io_flush(fp, SM_TIME_DEFAULT); 91738032Speter} 918168515Sgshapiro 91990792Sgshapiro/* 92038032Speter** MAKELOWER -- Translate a line into lower case 92138032Speter** 92238032Speter** Parameters: 92338032Speter** p -- the string to translate. If NULL, return is 92438032Speter** immediate. 92538032Speter** 92638032Speter** Returns: 92738032Speter** none. 92838032Speter** 92938032Speter** Side Effects: 93038032Speter** String pointed to by p is translated to lower case. 93138032Speter*/ 93238032Speter 93338032Spetervoid 93438032Spetermakelower(p) 93538032Speter register char *p; 93638032Speter{ 93738032Speter register char c; 93838032Speter 93938032Speter if (p == NULL) 94038032Speter return; 94138032Speter for (; (c = *p) != '\0'; p++) 94238032Speter if (isascii(c) && isupper(c)) 94338032Speter *p = tolower(c); 94438032Speter} 945168515Sgshapiro 94690792Sgshapiro/* 94738032Speter** FIXCRLF -- fix <CR><LF> in line. 94838032Speter** 94938032Speter** Looks for the <CR><LF> combination and turns it into the 95038032Speter** UNIX canonical <NL> character. It only takes one line, 95138032Speter** i.e., it is assumed that the first <NL> found is the end 95238032Speter** of the line. 95338032Speter** 95438032Speter** Parameters: 95538032Speter** line -- the line to fix. 95638032Speter** stripnl -- if true, strip the newline also. 95738032Speter** 95838032Speter** Returns: 95938032Speter** none. 96038032Speter** 96138032Speter** Side Effects: 96238032Speter** line is changed in place. 96338032Speter*/ 96438032Speter 96538032Spetervoid 96638032Speterfixcrlf(line, stripnl) 96738032Speter char *line; 96838032Speter bool stripnl; 96938032Speter{ 97038032Speter register char *p; 97138032Speter 97238032Speter p = strchr(line, '\n'); 97338032Speter if (p == NULL) 97438032Speter return; 97538032Speter if (p > line && p[-1] == '\r') 97638032Speter p--; 97738032Speter if (!stripnl) 97838032Speter *p++ = '\n'; 97938032Speter *p = '\0'; 98038032Speter} 981168515Sgshapiro 98290792Sgshapiro/* 98338032Speter** PUTLINE -- put a line like fputs obeying SMTP conventions 98438032Speter** 98538032Speter** This routine always guarantees outputing a newline (or CRLF, 98638032Speter** as appropriate) at the end of the string. 98738032Speter** 98838032Speter** Parameters: 98938032Speter** l -- line to put. 99038032Speter** mci -- the mailer connection information. 99138032Speter** 99238032Speter** Returns: 993157001Sgshapiro** true iff line was written successfully 99438032Speter** 99538032Speter** Side Effects: 99690792Sgshapiro** output of l to mci->mci_out. 99738032Speter*/ 99838032Speter 999157001Sgshapirobool 100038032Speterputline(l, mci) 100138032Speter register char *l; 100238032Speter register MCI *mci; 100338032Speter{ 1004157001Sgshapiro return putxline(l, strlen(l), mci, PXLF_MAPFROM); 100538032Speter} 1006168515Sgshapiro 100790792Sgshapiro/* 100838032Speter** PUTXLINE -- putline with flags bits. 100938032Speter** 101038032Speter** This routine always guarantees outputing a newline (or CRLF, 101138032Speter** as appropriate) at the end of the string. 101238032Speter** 101338032Speter** Parameters: 101438032Speter** l -- line to put. 101538032Speter** len -- the length of the line. 101638032Speter** mci -- the mailer connection information. 101738032Speter** pxflags -- flag bits: 101838032Speter** PXLF_MAPFROM -- map From_ to >From_. 101938032Speter** PXLF_STRIP8BIT -- strip 8th bit. 102038032Speter** PXLF_HEADER -- map bare newline in header to newline space. 102194334Sgshapiro** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 1022168515Sgshapiro** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes. 102338032Speter** 102438032Speter** Returns: 1025157001Sgshapiro** true iff line was written successfully 102638032Speter** 102738032Speter** Side Effects: 102890792Sgshapiro** output of l to mci->mci_out. 102938032Speter*/ 103038032Speter 1031168515Sgshapiro 1032168515Sgshapiro#define PUTX(limit) \ 1033168515Sgshapiro do \ 1034168515Sgshapiro { \ 1035168515Sgshapiro quotenext = false; \ 1036168515Sgshapiro while (l < limit) \ 1037168515Sgshapiro { \ 1038168515Sgshapiro unsigned char c = (unsigned char) *l++; \ 1039168515Sgshapiro \ 1040168515Sgshapiro if (bitset(PXLF_STRIPMQUOTE, pxflags) && \ 1041168515Sgshapiro !quotenext && c == METAQUOTE) \ 1042168515Sgshapiro { \ 1043168515Sgshapiro quotenext = true; \ 1044168515Sgshapiro continue; \ 1045168515Sgshapiro } \ 1046168515Sgshapiro quotenext = false; \ 1047168515Sgshapiro if (strip8bit) \ 1048168515Sgshapiro c &= 0177; \ 1049168515Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \ 1050168515Sgshapiro c) == SM_IO_EOF) \ 1051168515Sgshapiro { \ 1052168515Sgshapiro dead = true; \ 1053168515Sgshapiro break; \ 1054168515Sgshapiro } \ 1055168515Sgshapiro if (TrafficLogFile != NULL) \ 1056168515Sgshapiro (void) sm_io_putc(TrafficLogFile, \ 1057168515Sgshapiro SM_TIME_DEFAULT, \ 1058168515Sgshapiro c); \ 1059168515Sgshapiro } \ 1060168515Sgshapiro } while (0) 1061168515Sgshapiro 1062157001Sgshapirobool 106338032Speterputxline(l, len, mci, pxflags) 106438032Speter register char *l; 106538032Speter size_t len; 106638032Speter register MCI *mci; 106738032Speter int pxflags; 106838032Speter{ 106938032Speter register char *p, *end; 1070168515Sgshapiro int slop; 1071168515Sgshapiro bool dead, quotenext, strip8bit; 107238032Speter 107338032Speter /* strip out 0200 bits -- these can look like TELNET protocol */ 1074168515Sgshapiro strip8bit = bitset(MCIF_7BIT, mci->mci_flags) || 1075168515Sgshapiro bitset(PXLF_STRIP8BIT, pxflags); 1076168515Sgshapiro dead = false; 1077168515Sgshapiro slop = 0; 107838032Speter 107938032Speter end = l + len; 108038032Speter do 108138032Speter { 108294334Sgshapiro bool noeol = false; 108394334Sgshapiro 108438032Speter /* find the end of the line */ 108538032Speter p = memchr(l, '\n', end - l); 108638032Speter if (p == NULL) 108794334Sgshapiro { 108838032Speter p = end; 108994334Sgshapiro noeol = true; 109094334Sgshapiro } 109138032Speter 109238032Speter if (TrafficLogFile != NULL) 109390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 109490792Sgshapiro "%05d >>> ", (int) CurrentPid); 109538032Speter 109638032Speter /* check for line overflow */ 109738032Speter while (mci->mci_mailer->m_linelimit > 0 && 109838032Speter (p - l + slop) > mci->mci_mailer->m_linelimit) 109938032Speter { 110038032Speter register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 110138032Speter 110238032Speter if (l[0] == '.' && slop == 0 && 110338032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 110438032Speter { 110590792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 110690792Sgshapiro '.') == SM_IO_EOF) 110790792Sgshapiro dead = true; 110838032Speter if (TrafficLogFile != NULL) 110990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 111090792Sgshapiro SM_TIME_DEFAULT, '.'); 111138032Speter } 111238032Speter else if (l[0] == 'F' && slop == 0 && 111338032Speter bitset(PXLF_MAPFROM, pxflags) && 111438032Speter strncmp(l, "From ", 5) == 0 && 111538032Speter bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 111638032Speter { 111790792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 111890792Sgshapiro '>') == SM_IO_EOF) 111990792Sgshapiro dead = true; 112038032Speter if (TrafficLogFile != NULL) 112190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 112290792Sgshapiro SM_TIME_DEFAULT, 112390792Sgshapiro '>'); 112438032Speter } 112564562Sgshapiro if (dead) 112664562Sgshapiro break; 112764562Sgshapiro 1128168515Sgshapiro PUTX(q); 112964562Sgshapiro if (dead) 113064562Sgshapiro break; 113164562Sgshapiro 1132168515Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1133168515Sgshapiro '!') == SM_IO_EOF || 113490792Sgshapiro sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1135168515Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF || 1136168515Sgshapiro sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1137168515Sgshapiro ' ') == SM_IO_EOF) 113864562Sgshapiro { 113990792Sgshapiro dead = true; 114064562Sgshapiro break; 114164562Sgshapiro } 114238032Speter if (TrafficLogFile != NULL) 114338032Speter { 114490792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 114590792Sgshapiro SM_TIME_DEFAULT, 114690792Sgshapiro "!\n%05d >>> ", 114790792Sgshapiro (int) CurrentPid); 114838032Speter } 114938032Speter slop = 1; 115038032Speter } 115138032Speter 115264562Sgshapiro if (dead) 115364562Sgshapiro break; 115464562Sgshapiro 115538032Speter /* output last part */ 115638032Speter if (l[0] == '.' && slop == 0 && 1157173340Sgshapiro bitnset(M_XDOT, mci->mci_mailer->m_flags) && 1158173340Sgshapiro !bitset(MCIF_INLONGLINE, mci->mci_flags)) 115938032Speter { 116090792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 116190792Sgshapiro SM_IO_EOF) 1162157001Sgshapiro { 1163157001Sgshapiro dead = true; 116464562Sgshapiro break; 116571345Sgshapiro } 116638032Speter if (TrafficLogFile != NULL) 116790792Sgshapiro (void) sm_io_putc(TrafficLogFile, 116890792Sgshapiro SM_TIME_DEFAULT, '.'); 116938032Speter } 117038032Speter else if (l[0] == 'F' && slop == 0 && 117138032Speter bitset(PXLF_MAPFROM, pxflags) && 117238032Speter strncmp(l, "From ", 5) == 0 && 1173173340Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && 1174173340Sgshapiro !bitset(MCIF_INLONGLINE, mci->mci_flags)) 117538032Speter { 117690792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 117790792Sgshapiro SM_IO_EOF) 1178157001Sgshapiro { 1179157001Sgshapiro dead = true; 118064562Sgshapiro break; 118171345Sgshapiro } 118238032Speter if (TrafficLogFile != NULL) 118390792Sgshapiro (void) sm_io_putc(TrafficLogFile, 118490792Sgshapiro SM_TIME_DEFAULT, '>'); 118538032Speter } 1186168515Sgshapiro PUTX(p); 118764562Sgshapiro if (dead) 118864562Sgshapiro break; 118964562Sgshapiro 119038032Speter if (TrafficLogFile != NULL) 119190792Sgshapiro (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 119290792Sgshapiro '\n'); 1193173340Sgshapiro if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol)) 1194157001Sgshapiro { 1195173340Sgshapiro mci->mci_flags &= ~MCIF_INLONGLINE; 1196173340Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1197173340Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 1198173340Sgshapiro { 1199173340Sgshapiro dead = true; 1200173340Sgshapiro break; 1201173340Sgshapiro } 120271345Sgshapiro } 1203173340Sgshapiro else 1204173340Sgshapiro mci->mci_flags |= MCIF_INLONGLINE; 1205173340Sgshapiro 120638032Speter if (l < end && *l == '\n') 120738032Speter { 120838032Speter if (*++l != ' ' && *l != '\t' && *l != '\0' && 120938032Speter bitset(PXLF_HEADER, pxflags)) 121038032Speter { 121190792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 121290792Sgshapiro ' ') == SM_IO_EOF) 1213157001Sgshapiro { 1214157001Sgshapiro dead = true; 121564562Sgshapiro break; 121671345Sgshapiro } 121790792Sgshapiro 121838032Speter if (TrafficLogFile != NULL) 121990792Sgshapiro (void) sm_io_putc(TrafficLogFile, 122090792Sgshapiro SM_TIME_DEFAULT, ' '); 122138032Speter } 122238032Speter } 122390792Sgshapiro 122438032Speter } while (l < end); 1225157001Sgshapiro return !dead; 122638032Speter} 1227157001Sgshapiro 122890792Sgshapiro/* 122938032Speter** XUNLINK -- unlink a file, doing logging as appropriate. 123038032Speter** 123138032Speter** Parameters: 123238032Speter** f -- name of file to unlink. 123338032Speter** 123438032Speter** Returns: 123590792Sgshapiro** return value of unlink() 123638032Speter** 123738032Speter** Side Effects: 123838032Speter** f is unlinked. 123938032Speter*/ 124038032Speter 124190792Sgshapiroint 124238032Speterxunlink(f) 124338032Speter char *f; 124438032Speter{ 124538032Speter register int i; 124690792Sgshapiro int save_errno; 124738032Speter 124838032Speter if (LogLevel > 98) 124990792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 125038032Speter 125138032Speter i = unlink(f); 125290792Sgshapiro save_errno = errno; 125338032Speter if (i < 0 && LogLevel > 97) 125490792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 125564562Sgshapiro f, errno); 125690792Sgshapiro if (i >= 0) 125790792Sgshapiro SYNC_DIR(f, false); 125890792Sgshapiro errno = save_errno; 125990792Sgshapiro return i; 126038032Speter} 1261168515Sgshapiro 126290792Sgshapiro/* 126338032Speter** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 126438032Speter** 126538032Speter** Parameters: 126638032Speter** buf -- place to put the input line. 126738032Speter** siz -- size of buf. 126838032Speter** fp -- file to read from. 126938032Speter** timeout -- the timeout before error occurs. 127038032Speter** during -- what we are trying to read (for error messages). 127138032Speter** 127238032Speter** Returns: 127390792Sgshapiro** NULL on error (including timeout). This may also leave 127438032Speter** buf containing a null string. 127538032Speter** buf otherwise. 127638032Speter*/ 127738032Speter 127864562Sgshapiro 127938032Speterchar * 128038032Spetersfgets(buf, siz, fp, timeout, during) 128138032Speter char *buf; 128238032Speter int siz; 128390792Sgshapiro SM_FILE_T *fp; 128438032Speter time_t timeout; 128538032Speter char *during; 128638032Speter{ 128738032Speter register char *p; 1288249865Sgshapiro int save_errno, io_timeout, l; 128938032Speter 129090792Sgshapiro SM_REQUIRE(siz > 0); 129190792Sgshapiro SM_REQUIRE(buf != NULL); 129290792Sgshapiro 129338032Speter if (fp == NULL) 129438032Speter { 129538032Speter buf[0] = '\0'; 129690792Sgshapiro errno = EBADF; 129738032Speter return NULL; 129838032Speter } 129938032Speter 130090792Sgshapiro /* try to read */ 1301249865Sgshapiro l = -1; 130290792Sgshapiro errno = 0; 130390792Sgshapiro 130490792Sgshapiro /* convert the timeout to sm_io notation */ 130590792Sgshapiro io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 130690792Sgshapiro while (!sm_io_eof(fp) && !sm_io_error(fp)) 130738032Speter { 130890792Sgshapiro errno = 0; 1309249865Sgshapiro l = sm_io_fgets(fp, io_timeout, buf, siz); 1310249865Sgshapiro if (l < 0 && errno == EAGAIN) 131138032Speter { 131290792Sgshapiro /* The sm_io_fgets() call timedout */ 131338032Speter if (LogLevel > 1) 131438032Speter sm_syslog(LOG_NOTICE, CurEnv->e_id, 131564562Sgshapiro "timeout waiting for input from %.100s during %s", 131690792Sgshapiro CURHOSTNAME, 131764562Sgshapiro during); 131838032Speter buf[0] = '\0'; 131938032Speter#if XDEBUG 132038032Speter checkfd012(during); 132164562Sgshapiro#endif /* XDEBUG */ 132238032Speter if (TrafficLogFile != NULL) 132390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 132490792Sgshapiro SM_TIME_DEFAULT, 132590792Sgshapiro "%05d <<< [TIMEOUT]\n", 132690792Sgshapiro (int) CurrentPid); 132790792Sgshapiro errno = ETIMEDOUT; 132864562Sgshapiro return NULL; 132938032Speter } 1330249865Sgshapiro if (l >= 0 || errno != EINTR) 133138032Speter break; 133290792Sgshapiro (void) sm_io_clearerr(fp); 133338032Speter } 133443730Speter save_errno = errno; 133538032Speter 133638032Speter /* clean up the books and exit */ 133738032Speter LineNumber++; 1338249865Sgshapiro if (l < 0) 133938032Speter { 134038032Speter buf[0] = '\0'; 134138032Speter if (TrafficLogFile != NULL) 134290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 134390792Sgshapiro "%05d <<< [EOF]\n", 134490792Sgshapiro (int) CurrentPid); 134543730Speter errno = save_errno; 134664562Sgshapiro return NULL; 134738032Speter } 134838032Speter if (TrafficLogFile != NULL) 134990792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 135090792Sgshapiro "%05d <<< %s", (int) CurrentPid, buf); 135138032Speter if (SevenBitInput) 135238032Speter { 135338032Speter for (p = buf; *p != '\0'; p++) 135438032Speter *p &= ~0200; 135538032Speter } 135638032Speter else if (!HasEightBits) 135738032Speter { 135838032Speter for (p = buf; *p != '\0'; p++) 135938032Speter { 136038032Speter if (bitset(0200, *p)) 136138032Speter { 136290792Sgshapiro HasEightBits = true; 136338032Speter break; 136438032Speter } 136538032Speter } 136638032Speter } 136764562Sgshapiro return buf; 136838032Speter} 1369168515Sgshapiro 137090792Sgshapiro/* 137190792Sgshapiro** FGETFOLDED -- like fgets, but knows about folded lines. 137238032Speter** 137338032Speter** Parameters: 137438032Speter** buf -- place to put result. 1375168515Sgshapiro** np -- pointer to bytes available; will be updated with 1376168515Sgshapiro** the actual buffer size (not number of bytes filled) 1377168515Sgshapiro** on return. 137838032Speter** f -- file to read from. 137938032Speter** 138038032Speter** Returns: 138190792Sgshapiro** input line(s) on success, NULL on error or SM_IO_EOF. 138238032Speter** This will normally be buf -- unless the line is too 138390792Sgshapiro** long, when it will be sm_malloc_x()ed. 138438032Speter** 138538032Speter** Side Effects: 138638032Speter** buf gets lines from f, with continuation lines (lines 138738032Speter** with leading white space) appended. CRLF's are mapped 138838032Speter** into single newlines. Any trailing NL is stripped. 138938032Speter*/ 139038032Speter 139138032Speterchar * 1392168515Sgshapirofgetfolded(buf, np, f) 139338032Speter char *buf; 1394168515Sgshapiro int *np; 139590792Sgshapiro SM_FILE_T *f; 139638032Speter{ 139738032Speter register char *p = buf; 139838032Speter char *bp = buf; 139938032Speter register int i; 1400168515Sgshapiro int n; 140138032Speter 1402168515Sgshapiro SM_REQUIRE(np != NULL); 1403168515Sgshapiro n = *np; 140490792Sgshapiro SM_REQUIRE(n > 0); 140590792Sgshapiro SM_REQUIRE(buf != NULL); 140690792Sgshapiro if (f == NULL) 140790792Sgshapiro { 140890792Sgshapiro buf[0] = '\0'; 140990792Sgshapiro errno = EBADF; 141090792Sgshapiro return NULL; 141190792Sgshapiro } 141290792Sgshapiro 141338032Speter n--; 141490792Sgshapiro while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 141538032Speter { 141638032Speter if (i == '\r') 141738032Speter { 141890792Sgshapiro i = sm_io_getc(f, SM_TIME_DEFAULT); 141938032Speter if (i != '\n') 142038032Speter { 142190792Sgshapiro if (i != SM_IO_EOF) 142290792Sgshapiro (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 142390792Sgshapiro i); 142438032Speter i = '\r'; 142538032Speter } 142638032Speter } 142738032Speter if (--n <= 0) 142838032Speter { 142938032Speter /* allocate new space */ 143038032Speter char *nbp; 143138032Speter int nn; 143238032Speter 143338032Speter nn = (p - bp); 143438032Speter if (nn < MEMCHUNKSIZE) 143538032Speter nn *= 2; 143638032Speter else 143738032Speter nn += MEMCHUNKSIZE; 143890792Sgshapiro nbp = sm_malloc_x(nn); 143964562Sgshapiro memmove(nbp, bp, p - bp); 144038032Speter p = &nbp[p - bp]; 144138032Speter if (bp != buf) 144277349Sgshapiro sm_free(bp); 144338032Speter bp = nbp; 144438032Speter n = nn - (p - bp); 1445168515Sgshapiro *np = nn; 144638032Speter } 144738032Speter *p++ = i; 144838032Speter if (i == '\n') 144938032Speter { 145038032Speter LineNumber++; 145190792Sgshapiro i = sm_io_getc(f, SM_TIME_DEFAULT); 145290792Sgshapiro if (i != SM_IO_EOF) 145390792Sgshapiro (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 145438032Speter if (i != ' ' && i != '\t') 145538032Speter break; 145638032Speter } 145738032Speter } 145838032Speter if (p == bp) 145964562Sgshapiro return NULL; 146038032Speter if (p[-1] == '\n') 146138032Speter p--; 146238032Speter *p = '\0'; 146364562Sgshapiro return bp; 146438032Speter} 1465168515Sgshapiro 146690792Sgshapiro/* 146738032Speter** CURTIME -- return current time. 146838032Speter** 146938032Speter** Parameters: 147038032Speter** none. 147138032Speter** 147238032Speter** Returns: 147338032Speter** the current time. 147438032Speter*/ 147538032Speter 147638032Spetertime_t 147738032Spetercurtime() 147838032Speter{ 147938032Speter auto time_t t; 148038032Speter 148138032Speter (void) time(&t); 148264562Sgshapiro return t; 148338032Speter} 1484168515Sgshapiro 148590792Sgshapiro/* 148638032Speter** ATOBOOL -- convert a string representation to boolean. 148738032Speter** 148890792Sgshapiro** Defaults to false 148938032Speter** 149038032Speter** Parameters: 149190792Sgshapiro** s -- string to convert. Takes "tTyY", empty, and NULL as true, 149238032Speter** others as false. 149338032Speter** 149438032Speter** Returns: 149538032Speter** A boolean representation of the string. 149638032Speter*/ 149738032Speter 149838032Speterbool 149938032Speteratobool(s) 150038032Speter register char *s; 150138032Speter{ 150238032Speter if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 150390792Sgshapiro return true; 150490792Sgshapiro return false; 150538032Speter} 1506168515Sgshapiro 150790792Sgshapiro/* 150838032Speter** ATOOCT -- convert a string representation to octal. 150938032Speter** 151038032Speter** Parameters: 151138032Speter** s -- string to convert. 151238032Speter** 151338032Speter** Returns: 151438032Speter** An integer representing the string interpreted as an 151538032Speter** octal number. 151638032Speter*/ 151738032Speter 151838032Speterint 151938032Speteratooct(s) 152038032Speter register char *s; 152138032Speter{ 152238032Speter register int i = 0; 152338032Speter 152438032Speter while (*s >= '0' && *s <= '7') 152538032Speter i = (i << 3) | (*s++ - '0'); 152664562Sgshapiro return i; 152738032Speter} 1528168515Sgshapiro 152990792Sgshapiro/* 153038032Speter** BITINTERSECT -- tell if two bitmaps intersect 153138032Speter** 153238032Speter** Parameters: 153338032Speter** a, b -- the bitmaps in question 153438032Speter** 153538032Speter** Returns: 153690792Sgshapiro** true if they have a non-null intersection 153790792Sgshapiro** false otherwise 153838032Speter*/ 153938032Speter 154038032Speterbool 154138032Speterbitintersect(a, b) 154264562Sgshapiro BITMAP256 a; 154364562Sgshapiro BITMAP256 b; 154438032Speter{ 154538032Speter int i; 154638032Speter 1547168515Sgshapiro for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 154871345Sgshapiro { 154938032Speter if ((a[i] & b[i]) != 0) 155090792Sgshapiro return true; 155171345Sgshapiro } 155290792Sgshapiro return false; 155338032Speter} 1554168515Sgshapiro 155590792Sgshapiro/* 155638032Speter** BITZEROP -- tell if a bitmap is all zero 155738032Speter** 155838032Speter** Parameters: 155938032Speter** map -- the bit map to check 156038032Speter** 156138032Speter** Returns: 156290792Sgshapiro** true if map is all zero. 156390792Sgshapiro** false if there are any bits set in map. 156438032Speter*/ 156538032Speter 156638032Speterbool 156738032Speterbitzerop(map) 156864562Sgshapiro BITMAP256 map; 156938032Speter{ 157038032Speter int i; 157138032Speter 1572168515Sgshapiro for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 157371345Sgshapiro { 157438032Speter if (map[i] != 0) 157590792Sgshapiro return false; 157671345Sgshapiro } 157790792Sgshapiro return true; 157838032Speter} 1579168515Sgshapiro 158090792Sgshapiro/* 158138032Speter** STRCONTAINEDIN -- tell if one string is contained in another 158238032Speter** 158338032Speter** Parameters: 158490792Sgshapiro** icase -- ignore case? 158538032Speter** a -- possible substring. 158638032Speter** b -- possible superstring. 158738032Speter** 158838032Speter** Returns: 158990792Sgshapiro** true if a is contained in b (case insensitive). 159090792Sgshapiro** false otherwise. 159138032Speter*/ 159238032Speter 159338032Speterbool 159490792Sgshapirostrcontainedin(icase, a, b) 159590792Sgshapiro bool icase; 159638032Speter register char *a; 159738032Speter register char *b; 159838032Speter{ 159938032Speter int la; 160038032Speter int lb; 160138032Speter int c; 160238032Speter 160338032Speter la = strlen(a); 160438032Speter lb = strlen(b); 160538032Speter c = *a; 160690792Sgshapiro if (icase && isascii(c) && isupper(c)) 160738032Speter c = tolower(c); 160838032Speter for (; lb-- >= la; b++) 160938032Speter { 161090792Sgshapiro if (icase) 161190792Sgshapiro { 161290792Sgshapiro if (*b != c && 161390792Sgshapiro isascii(*b) && isupper(*b) && tolower(*b) != c) 161490792Sgshapiro continue; 161590792Sgshapiro if (sm_strncasecmp(a, b, la) == 0) 161690792Sgshapiro return true; 161790792Sgshapiro } 161890792Sgshapiro else 161990792Sgshapiro { 162090792Sgshapiro if (*b != c) 162190792Sgshapiro continue; 162290792Sgshapiro if (strncmp(a, b, la) == 0) 162390792Sgshapiro return true; 162490792Sgshapiro } 162538032Speter } 162690792Sgshapiro return false; 162738032Speter} 1628168515Sgshapiro 162990792Sgshapiro/* 163038032Speter** CHECKFD012 -- check low numbered file descriptors 163138032Speter** 163238032Speter** File descriptors 0, 1, and 2 should be open at all times. 163338032Speter** This routine verifies that, and fixes it if not true. 163438032Speter** 163538032Speter** Parameters: 163638032Speter** where -- a tag printed if the assertion failed 163738032Speter** 163838032Speter** Returns: 163938032Speter** none 164038032Speter*/ 164138032Speter 164238032Spetervoid 164338032Spetercheckfd012(where) 164438032Speter char *where; 164538032Speter{ 164638032Speter#if XDEBUG 164738032Speter register int i; 164838032Speter 164938032Speter for (i = 0; i < 3; i++) 165038032Speter fill_fd(i, where); 165138032Speter#endif /* XDEBUG */ 165238032Speter} 1653168515Sgshapiro 165490792Sgshapiro/* 165538032Speter** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 165638032Speter** 165738032Speter** Parameters: 165838032Speter** fd -- file descriptor to check. 165938032Speter** where -- tag to print on failure. 166038032Speter** 166138032Speter** Returns: 166238032Speter** none. 166338032Speter*/ 166438032Speter 166538032Spetervoid 166638032Spetercheckfdopen(fd, where) 166738032Speter int fd; 166838032Speter char *where; 166938032Speter{ 167038032Speter#if XDEBUG 167138032Speter struct stat st; 167238032Speter 167338032Speter if (fstat(fd, &st) < 0 && errno == EBADF) 167438032Speter { 167538032Speter syserr("checkfdopen(%d): %s not open as expected!", fd, where); 167690792Sgshapiro printopenfds(true); 167738032Speter } 167864562Sgshapiro#endif /* XDEBUG */ 167938032Speter} 1680168515Sgshapiro 168190792Sgshapiro/* 168238032Speter** CHECKFDS -- check for new or missing file descriptors 168338032Speter** 168438032Speter** Parameters: 168538032Speter** where -- tag for printing. If null, take a base line. 168638032Speter** 168738032Speter** Returns: 168838032Speter** none 168938032Speter** 169038032Speter** Side Effects: 169138032Speter** If where is set, shows changes since the last call. 169238032Speter*/ 169338032Speter 169438032Spetervoid 169538032Spetercheckfds(where) 169638032Speter char *where; 169738032Speter{ 169838032Speter int maxfd; 169938032Speter register int fd; 170090792Sgshapiro bool printhdr = true; 170138032Speter int save_errno = errno; 170264562Sgshapiro static BITMAP256 baseline; 170338032Speter extern int DtableSize; 170438032Speter 170571345Sgshapiro if (DtableSize > BITMAPBITS) 170671345Sgshapiro maxfd = BITMAPBITS; 170738032Speter else 170838032Speter maxfd = DtableSize; 170938032Speter if (where == NULL) 171038032Speter clrbitmap(baseline); 171138032Speter 171238032Speter for (fd = 0; fd < maxfd; fd++) 171338032Speter { 171438032Speter struct stat stbuf; 171538032Speter 171638032Speter if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 171738032Speter { 171838032Speter if (!bitnset(fd, baseline)) 171938032Speter continue; 172038032Speter clrbitn(fd, baseline); 172138032Speter } 172238032Speter else if (!bitnset(fd, baseline)) 172338032Speter setbitn(fd, baseline); 172438032Speter else 172538032Speter continue; 172638032Speter 172738032Speter /* file state has changed */ 172838032Speter if (where == NULL) 172938032Speter continue; 173038032Speter if (printhdr) 173138032Speter { 173238032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 173364562Sgshapiro "%s: changed fds:", 173464562Sgshapiro where); 173590792Sgshapiro printhdr = false; 173638032Speter } 173790792Sgshapiro dumpfd(fd, true, true); 173838032Speter } 173938032Speter errno = save_errno; 174038032Speter} 1741168515Sgshapiro 174290792Sgshapiro/* 174338032Speter** PRINTOPENFDS -- print the open file descriptors (for debugging) 174438032Speter** 174538032Speter** Parameters: 174638032Speter** logit -- if set, send output to syslog; otherwise 174738032Speter** print for debugging. 174838032Speter** 174938032Speter** Returns: 175038032Speter** none. 175138032Speter*/ 175238032Speter 175364562Sgshapiro#if NETINET || NETINET6 175464562Sgshapiro# include <arpa/inet.h> 175564562Sgshapiro#endif /* NETINET || NETINET6 */ 175638032Speter 175738032Spetervoid 175838032Speterprintopenfds(logit) 175938032Speter bool logit; 176038032Speter{ 176138032Speter register int fd; 176238032Speter extern int DtableSize; 176338032Speter 176438032Speter for (fd = 0; fd < DtableSize; fd++) 176590792Sgshapiro dumpfd(fd, false, logit); 176638032Speter} 1767168515Sgshapiro 176890792Sgshapiro/* 176938032Speter** DUMPFD -- dump a file descriptor 177038032Speter** 177138032Speter** Parameters: 177238032Speter** fd -- the file descriptor to dump. 177338032Speter** printclosed -- if set, print a notification even if 177438032Speter** it is closed; otherwise print nothing. 1775132943Sgshapiro** logit -- if set, use sm_syslog instead of sm_dprintf() 177690792Sgshapiro** 177790792Sgshapiro** Returns: 177890792Sgshapiro** none. 177938032Speter*/ 178038032Speter 178138032Spetervoid 178238032Speterdumpfd(fd, printclosed, logit) 178338032Speter int fd; 178438032Speter bool printclosed; 178538032Speter bool logit; 178638032Speter{ 178738032Speter register char *p; 178838032Speter char *hp; 178938032Speter#ifdef S_IFSOCK 179038032Speter SOCKADDR sa; 179164562Sgshapiro#endif /* S_IFSOCK */ 179238032Speter auto SOCKADDR_LEN_T slen; 179338032Speter int i; 179438032Speter#if STAT64 > 0 179538032Speter struct stat64 st; 179664562Sgshapiro#else /* STAT64 > 0 */ 179738032Speter struct stat st; 179864562Sgshapiro#endif /* STAT64 > 0 */ 179938032Speter char buf[200]; 180038032Speter 180138032Speter p = buf; 180290792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 180338032Speter p += strlen(p); 180438032Speter 180538032Speter if ( 180638032Speter#if STAT64 > 0 180738032Speter fstat64(fd, &st) 180864562Sgshapiro#else /* STAT64 > 0 */ 180938032Speter fstat(fd, &st) 181064562Sgshapiro#endif /* STAT64 > 0 */ 181138032Speter < 0) 181238032Speter { 181338032Speter if (errno != EBADF) 181438032Speter { 181590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 181690792Sgshapiro "CANNOT STAT (%s)", 181790792Sgshapiro sm_errstring(errno)); 181838032Speter goto printit; 181938032Speter } 182038032Speter else if (printclosed) 182138032Speter { 182290792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 182338032Speter goto printit; 182438032Speter } 182538032Speter return; 182638032Speter } 182738032Speter 182894334Sgshapiro i = fcntl(fd, F_GETFL, 0); 182938032Speter if (i != -1) 183038032Speter { 183190792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 183238032Speter p += strlen(p); 183338032Speter } 183438032Speter 183590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 183690792Sgshapiro (int) st.st_mode); 183738032Speter p += strlen(p); 183838032Speter switch (st.st_mode & S_IFMT) 183938032Speter { 184038032Speter#ifdef S_IFSOCK 184138032Speter case S_IFSOCK: 184290792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 184338032Speter p += strlen(p); 1844168515Sgshapiro memset(&sa, '\0', sizeof(sa)); 1845168515Sgshapiro slen = sizeof(sa); 184638032Speter if (getsockname(fd, &sa.sa, &slen) < 0) 184790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 184890792Sgshapiro sm_errstring(errno)); 184938032Speter else 185038032Speter { 185138032Speter hp = hostnamebyanyaddr(&sa); 185264562Sgshapiro if (hp == NULL) 185364562Sgshapiro { 185464562Sgshapiro /* EMPTY */ 185564562Sgshapiro /* do nothing */ 185664562Sgshapiro } 185764562Sgshapiro# if NETINET 185864562Sgshapiro else if (sa.sa.sa_family == AF_INET) 185990792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 186090792Sgshapiro "%s/%d", hp, ntohs(sa.sin.sin_port)); 186164562Sgshapiro# endif /* NETINET */ 186264562Sgshapiro# if NETINET6 186364562Sgshapiro else if (sa.sa.sa_family == AF_INET6) 186490792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 186590792Sgshapiro "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 186664562Sgshapiro# endif /* NETINET6 */ 186738032Speter else 186890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 186990792Sgshapiro "%s", hp); 187038032Speter } 187138032Speter p += strlen(p); 187290792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 187338032Speter p += strlen(p); 1874168515Sgshapiro slen = sizeof(sa); 187538032Speter if (getpeername(fd, &sa.sa, &slen) < 0) 187690792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 187790792Sgshapiro sm_errstring(errno)); 187838032Speter else 187938032Speter { 188038032Speter hp = hostnamebyanyaddr(&sa); 188164562Sgshapiro if (hp == NULL) 188264562Sgshapiro { 188364562Sgshapiro /* EMPTY */ 188464562Sgshapiro /* do nothing */ 188564562Sgshapiro } 188664562Sgshapiro# if NETINET 188764562Sgshapiro else if (sa.sa.sa_family == AF_INET) 188890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 188990792Sgshapiro "%s/%d", hp, ntohs(sa.sin.sin_port)); 189064562Sgshapiro# endif /* NETINET */ 189164562Sgshapiro# if NETINET6 189264562Sgshapiro else if (sa.sa.sa_family == AF_INET6) 189390792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 189490792Sgshapiro "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 189564562Sgshapiro# endif /* NETINET6 */ 189638032Speter else 189790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 189890792Sgshapiro "%s", hp); 189938032Speter } 190038032Speter break; 190164562Sgshapiro#endif /* S_IFSOCK */ 190238032Speter 190338032Speter case S_IFCHR: 190490792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 190538032Speter p += strlen(p); 190638032Speter goto defprint; 190738032Speter 190890792Sgshapiro#ifdef S_IFBLK 190938032Speter case S_IFBLK: 191090792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 191138032Speter p += strlen(p); 191238032Speter goto defprint; 191390792Sgshapiro#endif /* S_IFBLK */ 191438032Speter 191538032Speter#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 191638032Speter case S_IFIFO: 191790792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 191838032Speter p += strlen(p); 191938032Speter goto defprint; 192064562Sgshapiro#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 192138032Speter 192238032Speter#ifdef S_IFDIR 192338032Speter case S_IFDIR: 192490792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 192538032Speter p += strlen(p); 192638032Speter goto defprint; 192764562Sgshapiro#endif /* S_IFDIR */ 192838032Speter 192938032Speter#ifdef S_IFLNK 193038032Speter case S_IFLNK: 193190792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 193238032Speter p += strlen(p); 193338032Speter goto defprint; 193464562Sgshapiro#endif /* S_IFLNK */ 193538032Speter 193638032Speter default: 193738032Speterdefprint: 193890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), 193990792Sgshapiro "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 194090792Sgshapiro major(st.st_dev), minor(st.st_dev), 194190792Sgshapiro (ULONGLONG_T) st.st_ino, 194290792Sgshapiro (int) st.st_nlink, (int) st.st_uid, 194390792Sgshapiro (int) st.st_gid); 194490792Sgshapiro p += strlen(p); 194590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 194690792Sgshapiro (ULONGLONG_T) st.st_size); 194738032Speter break; 194838032Speter } 194938032Speter 195038032Speterprintit: 195138032Speter if (logit) 195238032Speter sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 195364562Sgshapiro "%.800s", buf); 195438032Speter else 1955132943Sgshapiro sm_dprintf("%s\n", buf); 195638032Speter} 1957168515Sgshapiro 195890792Sgshapiro/* 195938032Speter** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 196038032Speter** 196138032Speter** Parameters: 196238032Speter** host -- the host to shorten (stripped in place). 196338032Speter** 196438032Speter** Returns: 196590792Sgshapiro** place where string was truncated, NULL if not truncated. 196638032Speter*/ 196738032Speter 196873188Sgshapirochar * 196938032Spetershorten_hostname(host) 197038032Speter char host[]; 197138032Speter{ 197238032Speter register char *p; 197338032Speter char *mydom; 197438032Speter int i; 197590792Sgshapiro bool canon = false; 197638032Speter 197738032Speter /* strip off final dot */ 197890792Sgshapiro i = strlen(host); 197990792Sgshapiro p = &host[(i == 0) ? 0 : i - 1]; 198038032Speter if (*p == '.') 198138032Speter { 198238032Speter *p = '\0'; 198390792Sgshapiro canon = true; 198438032Speter } 198538032Speter 198638032Speter /* see if there is any domain at all -- if not, we are done */ 198738032Speter p = strchr(host, '.'); 198838032Speter if (p == NULL) 198973188Sgshapiro return NULL; 199038032Speter 199138032Speter /* yes, we have a domain -- see if it looks like us */ 199238032Speter mydom = macvalue('m', CurEnv); 199338032Speter if (mydom == NULL) 199438032Speter mydom = ""; 199538032Speter i = strlen(++p); 199690792Sgshapiro if ((canon ? sm_strcasecmp(p, mydom) 199790792Sgshapiro : sm_strncasecmp(p, mydom, i)) == 0 && 199890792Sgshapiro (mydom[i] == '.' || mydom[i] == '\0')) 199973188Sgshapiro { 200038032Speter *--p = '\0'; 200173188Sgshapiro return p; 200273188Sgshapiro } 200373188Sgshapiro return NULL; 200438032Speter} 2005168515Sgshapiro 200690792Sgshapiro/* 200738032Speter** PROG_OPEN -- open a program for reading 200838032Speter** 200938032Speter** Parameters: 201038032Speter** argv -- the argument list. 201138032Speter** pfd -- pointer to a place to store the file descriptor. 201238032Speter** e -- the current envelope. 201338032Speter** 201438032Speter** Returns: 201538032Speter** pid of the process -- -1 if it failed. 201638032Speter*/ 201738032Speter 201877349Sgshapiropid_t 201938032Speterprog_open(argv, pfd, e) 202038032Speter char **argv; 202138032Speter int *pfd; 202238032Speter ENVELOPE *e; 202338032Speter{ 202477349Sgshapiro pid_t pid; 202564562Sgshapiro int save_errno; 202690792Sgshapiro int sff; 202790792Sgshapiro int ret; 202838032Speter int fdv[2]; 202938032Speter char *p, *q; 203098121Sgshapiro char buf[MAXPATHLEN]; 203138032Speter extern int DtableSize; 203238032Speter 203338032Speter if (pipe(fdv) < 0) 203438032Speter { 203538032Speter syserr("%s: cannot create pipe for stdout", argv[0]); 203638032Speter return -1; 203738032Speter } 203838032Speter pid = fork(); 203938032Speter if (pid < 0) 204038032Speter { 204138032Speter syserr("%s: cannot fork", argv[0]); 204264562Sgshapiro (void) close(fdv[0]); 204364562Sgshapiro (void) close(fdv[1]); 204438032Speter return -1; 204538032Speter } 204638032Speter if (pid > 0) 204738032Speter { 204838032Speter /* parent */ 204964562Sgshapiro (void) close(fdv[1]); 205038032Speter *pfd = fdv[0]; 205138032Speter return pid; 205238032Speter } 205338032Speter 205477349Sgshapiro /* Reset global flags */ 205577349Sgshapiro RestartRequest = NULL; 205690792Sgshapiro RestartWorkGroup = false; 205777349Sgshapiro ShutdownRequest = NULL; 205877349Sgshapiro PendingSignal = 0; 205990792Sgshapiro CurrentPid = getpid(); 206077349Sgshapiro 206190792Sgshapiro /* 206290792Sgshapiro ** Initialize exception stack and default exception 206390792Sgshapiro ** handler for child process. 206490792Sgshapiro */ 206590792Sgshapiro 206690792Sgshapiro sm_exc_newthread(fatal_error); 206790792Sgshapiro 206890792Sgshapiro /* child -- close stdin */ 206990792Sgshapiro (void) close(0); 207090792Sgshapiro 207138032Speter /* stdout goes back to parent */ 207264562Sgshapiro (void) close(fdv[0]); 207338032Speter if (dup2(fdv[1], 1) < 0) 207438032Speter { 207538032Speter syserr("%s: cannot dup2 for stdout", argv[0]); 207638032Speter _exit(EX_OSERR); 207738032Speter } 207864562Sgshapiro (void) close(fdv[1]); 207938032Speter 208038032Speter /* stderr goes to transcript if available */ 208138032Speter if (e->e_xfp != NULL) 208238032Speter { 208364562Sgshapiro int xfd; 208464562Sgshapiro 208590792Sgshapiro xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 208664562Sgshapiro if (xfd >= 0 && dup2(xfd, 2) < 0) 208738032Speter { 208838032Speter syserr("%s: cannot dup2 for stderr", argv[0]); 208938032Speter _exit(EX_OSERR); 209038032Speter } 209138032Speter } 209238032Speter 209338032Speter /* this process has no right to the queue file */ 209438032Speter if (e->e_lockfp != NULL) 2095159609Sgshapiro { 2096159609Sgshapiro int fd; 209738032Speter 2098159609Sgshapiro fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 2099159609Sgshapiro if (fd >= 0) 2100159609Sgshapiro (void) close(fd); 2101159609Sgshapiro else 2102159609Sgshapiro syserr("%s: lockfp does not have a fd", argv[0]); 2103159609Sgshapiro } 2104159609Sgshapiro 210564562Sgshapiro /* chroot to the program mailer directory, if defined */ 210664562Sgshapiro if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 210764562Sgshapiro { 2108168515Sgshapiro expand(ProgMailer->m_rootdir, buf, sizeof(buf), e); 210964562Sgshapiro if (chroot(buf) < 0) 211064562Sgshapiro { 211164562Sgshapiro syserr("prog_open: cannot chroot(%s)", buf); 211264562Sgshapiro exit(EX_TEMPFAIL); 211364562Sgshapiro } 211464562Sgshapiro if (chdir("/") < 0) 211564562Sgshapiro { 211664562Sgshapiro syserr("prog_open: cannot chdir(/)"); 211764562Sgshapiro exit(EX_TEMPFAIL); 211864562Sgshapiro } 211964562Sgshapiro } 212064562Sgshapiro 212138032Speter /* run as default user */ 212238032Speter endpwent(); 212390792Sgshapiro sm_mbdb_terminate(); 2124157001Sgshapiro#if _FFR_MEMSTAT 2125157001Sgshapiro (void) sm_memstat_close(); 2126157001Sgshapiro#endif /* _FFR_MEMSTAT */ 212738032Speter if (setgid(DefGid) < 0 && geteuid() == 0) 212864562Sgshapiro { 212938032Speter syserr("prog_open: setgid(%ld) failed", (long) DefGid); 213064562Sgshapiro exit(EX_TEMPFAIL); 213164562Sgshapiro } 213238032Speter if (setuid(DefUid) < 0 && geteuid() == 0) 213364562Sgshapiro { 213438032Speter syserr("prog_open: setuid(%ld) failed", (long) DefUid); 213564562Sgshapiro exit(EX_TEMPFAIL); 213664562Sgshapiro } 213738032Speter 213838032Speter /* run in some directory */ 213938032Speter if (ProgMailer != NULL) 214038032Speter p = ProgMailer->m_execdir; 214138032Speter else 214238032Speter p = NULL; 214338032Speter for (; p != NULL; p = q) 214438032Speter { 214538032Speter q = strchr(p, ':'); 214638032Speter if (q != NULL) 214738032Speter *q = '\0'; 2148168515Sgshapiro expand(p, buf, sizeof(buf), e); 214938032Speter if (q != NULL) 215038032Speter *q++ = ':'; 215138032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 215238032Speter break; 215338032Speter } 215438032Speter if (p == NULL) 215538032Speter { 215638032Speter /* backup directories */ 215738032Speter if (chdir("/tmp") < 0) 215838032Speter (void) chdir("/"); 215938032Speter } 216038032Speter 216190792Sgshapiro /* Check safety of program to be run */ 216290792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 216390792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 216490792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 216590792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 216690792Sgshapiro sff |= SFF_NOPATHCHECK; 216790792Sgshapiro else 216890792Sgshapiro sff |= SFF_SAFEDIRPATH; 216990792Sgshapiro ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 217090792Sgshapiro if (ret != 0) 217190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 217290792Sgshapiro "Warning: prog_open: program %s unsafe: %s", 217390792Sgshapiro argv[0], sm_errstring(ret)); 217490792Sgshapiro 217538032Speter /* arrange for all the files to be closed */ 2176132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 217738032Speter 217838032Speter /* now exec the process */ 217964562Sgshapiro (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 218038032Speter 218138032Speter /* woops! failed */ 218264562Sgshapiro save_errno = errno; 218338032Speter syserr("%s: cannot exec", argv[0]); 218464562Sgshapiro if (transienterror(save_errno)) 218538032Speter _exit(EX_OSERR); 218638032Speter _exit(EX_CONFIG); 218738032Speter return -1; /* avoid compiler warning on IRIX */ 218838032Speter} 2189168515Sgshapiro 219090792Sgshapiro/* 219164562Sgshapiro** GET_COLUMN -- look up a Column in a line buffer 219238032Speter** 219338032Speter** Parameters: 219438032Speter** line -- the raw text line to search. 219538032Speter** col -- the column number to fetch. 219638032Speter** delim -- the delimiter between columns. If null, 219738032Speter** use white space. 219838032Speter** buf -- the output buffer. 219938032Speter** buflen -- the length of buf. 220038032Speter** 220138032Speter** Returns: 220238032Speter** buf if successful. 220338032Speter** NULL otherwise. 220438032Speter*/ 220538032Speter 220638032Speterchar * 220738032Speterget_column(line, col, delim, buf, buflen) 220838032Speter char line[]; 220938032Speter int col; 221064562Sgshapiro int delim; 221138032Speter char buf[]; 221238032Speter int buflen; 221338032Speter{ 221438032Speter char *p; 221538032Speter char *begin, *end; 221638032Speter int i; 221738032Speter char delimbuf[4]; 221864562Sgshapiro 221990792Sgshapiro if ((char) delim == '\0') 2220168515Sgshapiro (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf)); 222138032Speter else 222238032Speter { 222390792Sgshapiro delimbuf[0] = (char) delim; 222438032Speter delimbuf[1] = '\0'; 222538032Speter } 222638032Speter 222738032Speter p = line; 222838032Speter if (*p == '\0') 222938032Speter return NULL; /* line empty */ 223090792Sgshapiro if (*p == (char) delim && col == 0) 223138032Speter return NULL; /* first column empty */ 223238032Speter 223338032Speter begin = line; 223438032Speter 223590792Sgshapiro if (col == 0 && (char) delim == '\0') 223638032Speter { 223738032Speter while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 223838032Speter begin++; 223938032Speter } 224038032Speter 224138032Speter for (i = 0; i < col; i++) 224238032Speter { 224338032Speter if ((begin = strpbrk(begin, delimbuf)) == NULL) 224438032Speter return NULL; /* no such column */ 224538032Speter begin++; 224690792Sgshapiro if ((char) delim == '\0') 224738032Speter { 224838032Speter while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 224938032Speter begin++; 225038032Speter } 225138032Speter } 225264562Sgshapiro 225338032Speter end = strpbrk(begin, delimbuf); 225438032Speter if (end == NULL) 225538032Speter i = strlen(begin); 225638032Speter else 225738032Speter i = end - begin; 225838032Speter if (i >= buflen) 225938032Speter i = buflen - 1; 226090792Sgshapiro (void) sm_strlcpy(buf, begin, i + 1); 226138032Speter return buf; 226238032Speter} 2263168515Sgshapiro 226490792Sgshapiro/* 226538032Speter** CLEANSTRCPY -- copy string keeping out bogus characters 226638032Speter** 226738032Speter** Parameters: 226838032Speter** t -- "to" string. 226938032Speter** f -- "from" string. 227038032Speter** l -- length of space available in "to" string. 227138032Speter** 227238032Speter** Returns: 227338032Speter** none. 227438032Speter*/ 227538032Speter 227638032Spetervoid 227738032Spetercleanstrcpy(t, f, l) 227838032Speter register char *t; 227938032Speter register char *f; 228038032Speter int l; 228138032Speter{ 228238032Speter /* check for newlines and log if necessary */ 228390792Sgshapiro (void) denlstring(f, true, true); 228438032Speter 228564562Sgshapiro if (l <= 0) 228664562Sgshapiro syserr("!cleanstrcpy: length == 0"); 228764562Sgshapiro 228838032Speter l--; 228938032Speter while (l > 0 && *f != '\0') 229038032Speter { 229138032Speter if (isascii(*f) && 229238032Speter (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 229338032Speter { 229438032Speter l--; 229538032Speter *t++ = *f; 229638032Speter } 229738032Speter f++; 229838032Speter } 229938032Speter *t = '\0'; 230038032Speter} 2301168515Sgshapiro 230290792Sgshapiro/* 230338032Speter** DENLSTRING -- convert newlines in a string to spaces 230438032Speter** 230538032Speter** Parameters: 230638032Speter** s -- the input string 230738032Speter** strict -- if set, don't permit continuation lines. 230838032Speter** logattacks -- if set, log attempted attacks. 230938032Speter** 231038032Speter** Returns: 231138032Speter** A pointer to a version of the string with newlines 231238032Speter** mapped to spaces. This should be copied. 231338032Speter*/ 231438032Speter 231538032Speterchar * 231638032Speterdenlstring(s, strict, logattacks) 231738032Speter char *s; 231838032Speter bool strict; 231938032Speter bool logattacks; 232038032Speter{ 232138032Speter register char *p; 232238032Speter int l; 232338032Speter static char *bp = NULL; 232438032Speter static int bl = 0; 232538032Speter 232638032Speter p = s; 232738032Speter while ((p = strchr(p, '\n')) != NULL) 232838032Speter if (strict || (*++p != ' ' && *p != '\t')) 232938032Speter break; 233038032Speter if (p == NULL) 233138032Speter return s; 233238032Speter 233338032Speter l = strlen(s) + 1; 233438032Speter if (bl < l) 233538032Speter { 233638032Speter /* allocate more space */ 233790792Sgshapiro char *nbp = sm_pmalloc_x(l); 233890792Sgshapiro 233938032Speter if (bp != NULL) 234077349Sgshapiro sm_free(bp); 234190792Sgshapiro bp = nbp; 234238032Speter bl = l; 234338032Speter } 234490792Sgshapiro (void) sm_strlcpy(bp, s, l); 234538032Speter for (p = bp; (p = strchr(p, '\n')) != NULL; ) 234638032Speter *p++ = ' '; 234738032Speter 234838032Speter if (logattacks) 234938032Speter { 2350168515Sgshapiro sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL, 235164562Sgshapiro "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 235264562Sgshapiro RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 235364562Sgshapiro shortenstring(bp, MAXSHORTSTR)); 235438032Speter } 235538032Speter 235638032Speter return bp; 235738032Speter} 235898841Sgshapiro 235990792Sgshapiro/* 236098841Sgshapiro** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 236198841Sgshapiro** 236298841Sgshapiro** Parameters: 236398841Sgshapiro** s -- string to manipulate (in place) 236498841Sgshapiro** subst -- character to use as replacement 236598841Sgshapiro** 236698841Sgshapiro** Returns: 236798841Sgshapiro** true iff string did not contain "unprintable" characters 236898841Sgshapiro*/ 236998841Sgshapiro 237098841Sgshapirobool 237198841Sgshapirostrreplnonprt(s, c) 237298841Sgshapiro char *s; 237398841Sgshapiro int c; 237498841Sgshapiro{ 237598841Sgshapiro bool ok; 237698841Sgshapiro 237798841Sgshapiro ok = true; 237898841Sgshapiro if (s == NULL) 237998841Sgshapiro return ok; 238098841Sgshapiro while (*s != '\0') 238198841Sgshapiro { 238298841Sgshapiro if (!(isascii(*s) && isprint(*s))) 238398841Sgshapiro { 238498841Sgshapiro *s = c; 238598841Sgshapiro ok = false; 238698841Sgshapiro } 238798841Sgshapiro ++s; 238898841Sgshapiro } 238998841Sgshapiro return ok; 239098841Sgshapiro} 239198841Sgshapiro 239298841Sgshapiro/* 239338032Speter** PATH_IS_DIR -- check to see if file exists and is a directory. 239438032Speter** 239538032Speter** There are some additional checks for security violations in 239638032Speter** here. This routine is intended to be used for the host status 239738032Speter** support. 239838032Speter** 239938032Speter** Parameters: 240038032Speter** pathname -- pathname to check for directory-ness. 240138032Speter** createflag -- if set, create directory if needed. 240238032Speter** 240338032Speter** Returns: 240490792Sgshapiro** true -- if the indicated pathname is a directory 240590792Sgshapiro** false -- otherwise 240638032Speter*/ 240738032Speter 2408120256Sgshapirobool 240938032Speterpath_is_dir(pathname, createflag) 241038032Speter char *pathname; 241138032Speter bool createflag; 241238032Speter{ 241338032Speter struct stat statbuf; 241438032Speter 241538032Speter#if HASLSTAT 241638032Speter if (lstat(pathname, &statbuf) < 0) 241764562Sgshapiro#else /* HASLSTAT */ 241838032Speter if (stat(pathname, &statbuf) < 0) 241964562Sgshapiro#endif /* HASLSTAT */ 242038032Speter { 242138032Speter if (errno != ENOENT || !createflag) 242290792Sgshapiro return false; 242338032Speter if (mkdir(pathname, 0755) < 0) 242490792Sgshapiro return false; 242590792Sgshapiro return true; 242638032Speter } 242738032Speter if (!S_ISDIR(statbuf.st_mode)) 242838032Speter { 242938032Speter errno = ENOTDIR; 243090792Sgshapiro return false; 243138032Speter } 243238032Speter 243338032Speter /* security: don't allow writable directories */ 243438032Speter if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 243538032Speter { 243638032Speter errno = EACCES; 243790792Sgshapiro return false; 243838032Speter } 243990792Sgshapiro return true; 244038032Speter} 2441168515Sgshapiro 244290792Sgshapiro/* 244338032Speter** PROC_LIST_ADD -- add process id to list of our children 244438032Speter** 244538032Speter** Parameters: 244638032Speter** pid -- pid to add to list. 244764562Sgshapiro** task -- task of pid. 244864562Sgshapiro** type -- type of process. 244990792Sgshapiro** count -- number of processes. 245090792Sgshapiro** other -- other information for this type. 245138032Speter** 245238032Speter** Returns: 245338032Speter** none 245490792Sgshapiro** 245590792Sgshapiro** Side Effects: 245690792Sgshapiro** May increase CurChildren. May grow ProcList. 245738032Speter*/ 245838032Speter 245990792Sgshapirotypedef struct procs PROCS_T; 246042575Speter 246190792Sgshapirostruct procs 246290792Sgshapiro{ 2463132943Sgshapiro pid_t proc_pid; 2464132943Sgshapiro char *proc_task; 2465132943Sgshapiro int proc_type; 2466132943Sgshapiro int proc_count; 2467132943Sgshapiro int proc_other; 2468132943Sgshapiro SOCKADDR proc_hostaddr; 246990792Sgshapiro}; 247090792Sgshapiro 247190792Sgshapirostatic PROCS_T *volatile ProcListVec = NULL; 247290792Sgshapirostatic int ProcListSize = 0; 247390792Sgshapiro 247438032Spetervoid 2475132943Sgshapiroproc_list_add(pid, task, type, count, other, hostaddr) 247638032Speter pid_t pid; 247742575Speter char *task; 247864562Sgshapiro int type; 247990792Sgshapiro int count; 248090792Sgshapiro int other; 2481132943Sgshapiro SOCKADDR *hostaddr; 248238032Speter{ 248338032Speter int i; 248438032Speter 248538032Speter for (i = 0; i < ProcListSize; i++) 248638032Speter { 248742575Speter if (ProcListVec[i].proc_pid == NO_PID) 248838032Speter break; 248938032Speter } 249038032Speter if (i >= ProcListSize) 249138032Speter { 249238032Speter /* probe the existing vector to avoid growing infinitely */ 249338032Speter proc_list_probe(); 249438032Speter 249538032Speter /* now scan again */ 249638032Speter for (i = 0; i < ProcListSize; i++) 249738032Speter { 249842575Speter if (ProcListVec[i].proc_pid == NO_PID) 249938032Speter break; 250038032Speter } 250138032Speter } 250238032Speter if (i >= ProcListSize) 250338032Speter { 250438032Speter /* grow process list */ 2505168515Sgshapiro int chldwasblocked; 250690792Sgshapiro PROCS_T *npv; 250738032Speter 250890792Sgshapiro SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2509168515Sgshapiro npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) * 251090792Sgshapiro (ProcListSize + PROC_LIST_SEG)); 2511168515Sgshapiro 2512168515Sgshapiro /* Block SIGCHLD so reapchild() doesn't mess with us */ 2513168515Sgshapiro chldwasblocked = sm_blocksignal(SIGCHLD); 251438032Speter if (ProcListSize > 0) 251538032Speter { 251664562Sgshapiro memmove(npv, ProcListVec, 2517168515Sgshapiro ProcListSize * sizeof(PROCS_T)); 251877349Sgshapiro sm_free(ProcListVec); 251938032Speter } 252090792Sgshapiro 252190792Sgshapiro /* XXX just use memset() to initialize this part? */ 252238032Speter for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 252342575Speter { 252442575Speter npv[i].proc_pid = NO_PID; 252542575Speter npv[i].proc_task = NULL; 252664562Sgshapiro npv[i].proc_type = PROC_NONE; 252742575Speter } 252838032Speter i = ProcListSize; 252938032Speter ProcListSize += PROC_LIST_SEG; 253038032Speter ProcListVec = npv; 2531168515Sgshapiro if (chldwasblocked == 0) 2532168515Sgshapiro (void) sm_releasesignal(SIGCHLD); 253338032Speter } 253442575Speter ProcListVec[i].proc_pid = pid; 253590792Sgshapiro PSTRSET(ProcListVec[i].proc_task, task); 253664562Sgshapiro ProcListVec[i].proc_type = type; 253790792Sgshapiro ProcListVec[i].proc_count = count; 253890792Sgshapiro ProcListVec[i].proc_other = other; 2539132943Sgshapiro if (hostaddr != NULL) 2540132943Sgshapiro ProcListVec[i].proc_hostaddr = *hostaddr; 2541132943Sgshapiro else 2542132943Sgshapiro memset(&ProcListVec[i].proc_hostaddr, 0, 2543132943Sgshapiro sizeof(ProcListVec[i].proc_hostaddr)); 254442575Speter 254542575Speter /* if process adding itself, it's not a child */ 254690792Sgshapiro if (pid != CurrentPid) 254790792Sgshapiro { 254890792Sgshapiro SM_ASSERT(CurChildren < INT_MAX); 254942575Speter CurChildren++; 255090792Sgshapiro } 255138032Speter} 2552168515Sgshapiro 255390792Sgshapiro/* 255442575Speter** PROC_LIST_SET -- set pid task in process list 255542575Speter** 255642575Speter** Parameters: 255742575Speter** pid -- pid to set 255842575Speter** task -- task of pid 255942575Speter** 256042575Speter** Returns: 256142575Speter** none. 256242575Speter*/ 256342575Speter 256442575Spetervoid 256542575Speterproc_list_set(pid, task) 256642575Speter pid_t pid; 256742575Speter char *task; 256842575Speter{ 256942575Speter int i; 257042575Speter 257142575Speter for (i = 0; i < ProcListSize; i++) 257242575Speter { 257342575Speter if (ProcListVec[i].proc_pid == pid) 257442575Speter { 257590792Sgshapiro PSTRSET(ProcListVec[i].proc_task, task); 257642575Speter break; 257742575Speter } 257842575Speter } 257942575Speter} 2580168515Sgshapiro 258190792Sgshapiro/* 258238032Speter** PROC_LIST_DROP -- drop pid from process list 258338032Speter** 258438032Speter** Parameters: 258538032Speter** pid -- pid to drop 258690792Sgshapiro** st -- process status 258790792Sgshapiro** other -- storage for proc_other (return). 258838032Speter** 258938032Speter** Returns: 259090792Sgshapiro** none. 259177349Sgshapiro** 259290792Sgshapiro** Side Effects: 259390792Sgshapiro** May decrease CurChildren, CurRunners, or 259490792Sgshapiro** set RestartRequest or ShutdownRequest. 259590792Sgshapiro** 259677349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 259777349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 259877349Sgshapiro** DOING. 259938032Speter*/ 260038032Speter 260190792Sgshapirovoid 260290792Sgshapiroproc_list_drop(pid, st, other) 260338032Speter pid_t pid; 260490792Sgshapiro int st; 260590792Sgshapiro int *other; 260638032Speter{ 260738032Speter int i; 260864562Sgshapiro int type = PROC_NONE; 260938032Speter 261038032Speter for (i = 0; i < ProcListSize; i++) 261138032Speter { 261242575Speter if (ProcListVec[i].proc_pid == pid) 261338032Speter { 261442575Speter ProcListVec[i].proc_pid = NO_PID; 261564562Sgshapiro type = ProcListVec[i].proc_type; 261690792Sgshapiro if (other != NULL) 261790792Sgshapiro *other = ProcListVec[i].proc_other; 2618157001Sgshapiro if (CurChildren > 0) 2619157001Sgshapiro CurChildren--; 262038032Speter break; 262138032Speter } 262238032Speter } 262364562Sgshapiro 262464562Sgshapiro 262590792Sgshapiro if (type == PROC_CONTROL && WIFEXITED(st)) 262690792Sgshapiro { 262790792Sgshapiro /* if so, see if we need to restart or shutdown */ 262890792Sgshapiro if (WEXITSTATUS(st) == EX_RESTART) 262990792Sgshapiro RestartRequest = "control socket"; 263090792Sgshapiro else if (WEXITSTATUS(st) == EX_SHUTDOWN) 263190792Sgshapiro ShutdownRequest = "control socket"; 263290792Sgshapiro } 263390792Sgshapiro else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 263490792Sgshapiro ProcListVec[i].proc_other > -1) 263590792Sgshapiro { 263690792Sgshapiro /* restart this persistent runner */ 263790792Sgshapiro mark_work_group_restart(ProcListVec[i].proc_other, st); 263890792Sgshapiro } 263990792Sgshapiro else if (type == PROC_QUEUE) 2640244928Sgshapiro { 264190792Sgshapiro CurRunners -= ProcListVec[i].proc_count; 2642244928Sgshapiro 2643244928Sgshapiro /* CHK_CUR_RUNNERS() can't be used here: uses syslog() */ 2644244928Sgshapiro if (CurRunners < 0) 2645244928Sgshapiro CurRunners = 0; 2646244928Sgshapiro } 264738032Speter} 2648168515Sgshapiro 264990792Sgshapiro/* 265038032Speter** PROC_LIST_CLEAR -- clear the process list 265138032Speter** 265238032Speter** Parameters: 265338032Speter** none. 265438032Speter** 265538032Speter** Returns: 265638032Speter** none. 265790792Sgshapiro** 265890792Sgshapiro** Side Effects: 265990792Sgshapiro** Sets CurChildren to zero. 266038032Speter*/ 266138032Speter 266238032Spetervoid 266338032Speterproc_list_clear() 266438032Speter{ 266538032Speter int i; 266638032Speter 266742575Speter /* start from 1 since 0 is the daemon itself */ 266842575Speter for (i = 1; i < ProcListSize; i++) 266942575Speter ProcListVec[i].proc_pid = NO_PID; 267038032Speter CurChildren = 0; 267138032Speter} 2672168515Sgshapiro 267390792Sgshapiro/* 267438032Speter** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 267538032Speter** 267638032Speter** Parameters: 267738032Speter** none 267838032Speter** 267938032Speter** Returns: 268038032Speter** none 268190792Sgshapiro** 268290792Sgshapiro** Side Effects: 268390792Sgshapiro** May decrease CurChildren. 268438032Speter*/ 268538032Speter 268638032Spetervoid 268738032Speterproc_list_probe() 268838032Speter{ 2689157001Sgshapiro int i, children; 2690157001Sgshapiro int chldwasblocked; 2691157001Sgshapiro pid_t pid; 269238032Speter 2693157001Sgshapiro children = 0; 2694157001Sgshapiro chldwasblocked = sm_blocksignal(SIGCHLD); 2695157001Sgshapiro 269642575Speter /* start from 1 since 0 is the daemon itself */ 269742575Speter for (i = 1; i < ProcListSize; i++) 269838032Speter { 2699157001Sgshapiro pid = ProcListVec[i].proc_pid; 2700157001Sgshapiro if (pid == NO_PID || pid == CurrentPid) 270138032Speter continue; 2702157001Sgshapiro if (kill(pid, 0) < 0) 270338032Speter { 270438032Speter if (LogLevel > 3) 270538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 270664562Sgshapiro "proc_list_probe: lost pid %d", 270764562Sgshapiro (int) ProcListVec[i].proc_pid); 270842575Speter ProcListVec[i].proc_pid = NO_PID; 270990792Sgshapiro SM_FREE_CLR(ProcListVec[i].proc_task); 2710244928Sgshapiro 2711244928Sgshapiro if (ProcListVec[i].proc_type == PROC_QUEUE) 2712244928Sgshapiro { 2713244928Sgshapiro CurRunners -= ProcListVec[i].proc_count; 2714244928Sgshapiro CHK_CUR_RUNNERS("proc_list_probe", i, 2715244928Sgshapiro ProcListVec[i].proc_count); 2716244928Sgshapiro } 2717244928Sgshapiro 271838032Speter CurChildren--; 271938032Speter } 2720157001Sgshapiro else 2721157001Sgshapiro { 2722157001Sgshapiro ++children; 2723157001Sgshapiro } 272438032Speter } 272538032Speter if (CurChildren < 0) 272638032Speter CurChildren = 0; 2727157001Sgshapiro if (chldwasblocked == 0) 2728157001Sgshapiro (void) sm_releasesignal(SIGCHLD); 2729159609Sgshapiro if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid) 2730157001Sgshapiro { 2731157001Sgshapiro sm_syslog(LOG_ERR, NOQID, 2732157001Sgshapiro "proc_list_probe: found %d children, expected %d", 2733157001Sgshapiro children, CurChildren); 2734157001Sgshapiro } 273538032Speter} 273690792Sgshapiro 273790792Sgshapiro/* 273842575Speter** PROC_LIST_DISPLAY -- display the process list 273942575Speter** 274042575Speter** Parameters: 274142575Speter** out -- output file pointer 274290792Sgshapiro** prefix -- string to output in front of each line. 274342575Speter** 274442575Speter** Returns: 274542575Speter** none. 274642575Speter*/ 274742575Speter 274842575Spetervoid 274990792Sgshapiroproc_list_display(out, prefix) 275090792Sgshapiro SM_FILE_T *out; 275190792Sgshapiro char *prefix; 275242575Speter{ 275342575Speter int i; 275442575Speter 275542575Speter for (i = 0; i < ProcListSize; i++) 275642575Speter { 275742575Speter if (ProcListVec[i].proc_pid == NO_PID) 275842575Speter continue; 275942575Speter 276090792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 276190792Sgshapiro prefix, 276290792Sgshapiro (int) ProcListVec[i].proc_pid, 276390792Sgshapiro ProcListVec[i].proc_task != NULL ? 276490792Sgshapiro ProcListVec[i].proc_task : "(unknown)", 276590792Sgshapiro (OpMode == MD_SMTP || 276690792Sgshapiro OpMode == MD_DAEMON || 276790792Sgshapiro OpMode == MD_ARPAFTP) ? "\r" : ""); 276842575Speter } 276942575Speter} 277090792Sgshapiro 277190792Sgshapiro/* 277290792Sgshapiro** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 277380785Sgshapiro** 277480785Sgshapiro** Parameters: 277590792Sgshapiro** type -- type of process to signal 277690792Sgshapiro** signal -- the type of signal to send 277780785Sgshapiro** 277890792Sgshapiro** Results: 277990792Sgshapiro** none. 278090792Sgshapiro** 278190792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 278290792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 278390792Sgshapiro** DOING. 278480785Sgshapiro*/ 278580785Sgshapiro 278690792Sgshapirovoid 278790792Sgshapiroproc_list_signal(type, signal) 278890792Sgshapiro int type; 278990792Sgshapiro int signal; 279080785Sgshapiro{ 279190792Sgshapiro int chldwasblocked; 279290792Sgshapiro int alrmwasblocked; 279390792Sgshapiro int i; 279490792Sgshapiro pid_t mypid = getpid(); 279580785Sgshapiro 279690792Sgshapiro /* block these signals so that we may signal cleanly */ 279790792Sgshapiro chldwasblocked = sm_blocksignal(SIGCHLD); 279890792Sgshapiro alrmwasblocked = sm_blocksignal(SIGALRM); 279980785Sgshapiro 280090792Sgshapiro /* Find all processes of type and send signal */ 280190792Sgshapiro for (i = 0; i < ProcListSize; i++) 280280785Sgshapiro { 280390792Sgshapiro if (ProcListVec[i].proc_pid == NO_PID || 280490792Sgshapiro ProcListVec[i].proc_pid == mypid) 280590792Sgshapiro continue; 280690792Sgshapiro if (ProcListVec[i].proc_type != type) 280790792Sgshapiro continue; 280890792Sgshapiro (void) kill(ProcListVec[i].proc_pid, signal); 280980785Sgshapiro } 281080785Sgshapiro 281190792Sgshapiro /* restore the signals */ 281290792Sgshapiro if (alrmwasblocked == 0) 281390792Sgshapiro (void) sm_releasesignal(SIGALRM); 281490792Sgshapiro if (chldwasblocked == 0) 281590792Sgshapiro (void) sm_releasesignal(SIGCHLD); 281680785Sgshapiro} 2817132943Sgshapiro 2818132943Sgshapiro/* 2819132943Sgshapiro** COUNT_OPEN_CONNECTIONS 2820132943Sgshapiro** 2821132943Sgshapiro** Parameters: 2822132943Sgshapiro** hostaddr - ClientAddress 2823132943Sgshapiro** 2824132943Sgshapiro** Returns: 2825132943Sgshapiro** the number of open connections for this client 2826132943Sgshapiro** 2827132943Sgshapiro*/ 2828132943Sgshapiro 2829132943Sgshapiroint 2830132943Sgshapirocount_open_connections(hostaddr) 2831132943Sgshapiro SOCKADDR *hostaddr; 2832132943Sgshapiro{ 2833132943Sgshapiro int i, n; 2834132943Sgshapiro 2835132943Sgshapiro if (hostaddr == NULL) 2836132943Sgshapiro return 0; 2837173340Sgshapiro 2838173340Sgshapiro /* 2839182352Sgshapiro ** This code gets called before proc_list_add() gets called, 2840182352Sgshapiro ** so we (the daemon child for this connection) have not yet 2841182352Sgshapiro ** counted ourselves. Hence initialize the counter to 1 2842182352Sgshapiro ** instead of 0 to compensate. 2843173340Sgshapiro */ 2844173340Sgshapiro 2845173340Sgshapiro n = 1; 2846132943Sgshapiro for (i = 0; i < ProcListSize; i++) 2847132943Sgshapiro { 2848132943Sgshapiro if (ProcListVec[i].proc_pid == NO_PID) 2849132943Sgshapiro continue; 2850132943Sgshapiro if (hostaddr->sa.sa_family != 2851132943Sgshapiro ProcListVec[i].proc_hostaddr.sa.sa_family) 2852132943Sgshapiro continue; 2853132943Sgshapiro#if NETINET 2854132943Sgshapiro if (hostaddr->sa.sa_family == AF_INET && 2855132943Sgshapiro (hostaddr->sin.sin_addr.s_addr == 2856132943Sgshapiro ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 2857132943Sgshapiro n++; 2858132943Sgshapiro#endif /* NETINET */ 2859132943Sgshapiro#if NETINET6 2860132943Sgshapiro if (hostaddr->sa.sa_family == AF_INET6 && 2861132943Sgshapiro IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 2862132943Sgshapiro &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 2863132943Sgshapiro n++; 2864132943Sgshapiro#endif /* NETINET6 */ 2865132943Sgshapiro } 2866132943Sgshapiro return n; 2867132943Sgshapiro} 2868244928Sgshapiro 2869