util.c revision 132943
192282Sobrien/* 279968Sobrien * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. 379968Sobrien * All rights reserved. 479968Sobrien * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 579968Sobrien * Copyright (c) 1988, 1993 679968Sobrien * The Regents of the University of California. All rights reserved. 779968Sobrien * 879968Sobrien * By using this file, you agree to the terms and conditions set 979968Sobrien * forth in the LICENSE file which can be found at the top level of 1079968Sobrien * the sendmail distribution. 1179968Sobrien * 1279968Sobrien */ 1379968Sobrien 1479968Sobrien#include <sendmail.h> 1579968Sobrien 1679968SobrienSM_RCSID("@(#)$Id: util.c,v 8.382 2004/03/26 19:01:10 ca Exp $") 1779968Sobrien 1879968Sobrien#include <sysexits.h> 1979968Sobrien#include <sm/xtrap.h> 2079968Sobrien 2179968Sobrien/* 2279968Sobrien** NEWSTR -- Create a copy of a C string 2379968Sobrien** 2479968Sobrien** Parameters: 2579968Sobrien** s -- the string to copy. 2679968Sobrien** 2779968Sobrien** Returns: 2879968Sobrien** pointer to newly allocated string. 2979968Sobrien*/ 3079968Sobrien 3179968Sobrienchar * 3279968Sobriennewstr(s) 3379968Sobrien const char *s; 3479968Sobrien{ 3579968Sobrien size_t l; 3679968Sobrien char *n; 3779968Sobrien 3879968Sobrien l = strlen(s); 3979968Sobrien SM_ASSERT(l + 1 > l); 4079968Sobrien n = xalloc(l + 1); 4179968Sobrien sm_strlcpy(n, s, l + 1); 4279968Sobrien return n; 4379968Sobrien} 4479968Sobrien 4579968Sobrien/* 4679968Sobrien** ADDQUOTES -- Adds quotes & quote bits to a string. 4779968Sobrien** 4879968Sobrien** Runs through a string and adds backslashes and quote bits. 4979968Sobrien** 5079968Sobrien** Parameters: 5179968Sobrien** s -- the string to modify. 5279968Sobrien** rpool -- resource pool from which to allocate result 5379968Sobrien** 5479968Sobrien** Returns: 5579968Sobrien** pointer to quoted string. 5679968Sobrien*/ 5779968Sobrien 5879968Sobrienchar * 5979968Sobrienaddquotes(s, rpool) 6079968Sobrien char *s; 6179968Sobrien SM_RPOOL_T *rpool; 6279968Sobrien{ 6379968Sobrien int len = 0; 6479968Sobrien char c; 6579968Sobrien char *p = s, *q, *r; 6679968Sobrien 6779968Sobrien if (s == NULL) 6879968Sobrien return NULL; 6979968Sobrien 7079968Sobrien /* Find length of quoted string */ 7179968Sobrien while ((c = *p++) != '\0') 7279968Sobrien { 7379968Sobrien len++; 7479968Sobrien if (c == '\\' || c == '"') 7579968Sobrien len++; 7679968Sobrien } 7779968Sobrien 7879968Sobrien q = r = sm_rpool_malloc_x(rpool, len + 3); 7979968Sobrien p = s; 8079968Sobrien 8179968Sobrien /* add leading quote */ 8279968Sobrien *q++ = '"'; 8379968Sobrien while ((c = *p++) != '\0') 8479968Sobrien { 8579968Sobrien /* quote \ or " */ 8679968Sobrien if (c == '\\' || c == '"') 8779968Sobrien *q++ = '\\'; 8879968Sobrien *q++ = c; 8979968Sobrien } 9079968Sobrien *q++ = '"'; 9179968Sobrien *q = '\0'; 9279968Sobrien return r; 9379968Sobrien} 9479968Sobrien 9579968Sobrien/* 9679968Sobrien** STRIPBACKSLASH -- Strip leading backslash from a string. 9779968Sobrien** 9879968Sobrien** This is done in place. 9979968Sobrien** 10079968Sobrien** Parameters: 10179968Sobrien** s -- the string to strip. 10279968Sobrien** 10379968Sobrien** Returns: 10479968Sobrien** none. 10592282Sobrien*/ 10692282Sobrien 10792282Sobrienvoid 10892282Sobrienstripbackslash(s) 10992282Sobrien char *s; 11079968Sobrien{ 11179968Sobrien char *p, *q, c; 11279968Sobrien 11379968Sobrien if (s == NULL || *s == '\0') 11479968Sobrien return; 11592282Sobrien p = q = s; 11679968Sobrien while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1])))) 11779968Sobrien p++; 11879968Sobrien do 11979968Sobrien { 12079968Sobrien c = *q++ = *p++; 12179968Sobrien } while (c != '\0'); 12279968Sobrien} 12379968Sobrien 12479968Sobrien/* 12579968Sobrien** RFC822_STRING -- Checks string for proper RFC822 string quoting. 12679968Sobrien** 12779968Sobrien** Runs through a string and verifies RFC822 special characters 12879968Sobrien** are only found inside comments, quoted strings, or backslash 12979968Sobrien** escaped. Also verified balanced quotes and parenthesis. 13079968Sobrien** 13179968Sobrien** Parameters: 13279968Sobrien** s -- the string to modify. 13379968Sobrien** 13479968Sobrien** Returns: 13579968Sobrien** true iff the string is RFC822 compliant, false otherwise. 13679968Sobrien*/ 13779968Sobrien 13879968Sobrienbool 13979968Sobrienrfc822_string(s) 14079968Sobrien char *s; 14179968Sobrien{ 14279968Sobrien bool quoted = false; 14379968Sobrien int commentlev = 0; 14479968Sobrien char *c = s; 14579968Sobrien 14679968Sobrien if (s == NULL) 14779968Sobrien return false; 14879968Sobrien 14979968Sobrien while (*c != '\0') 15079968Sobrien { 15179968Sobrien /* escaped character */ 15279968Sobrien if (*c == '\\') 15379968Sobrien { 15479968Sobrien c++; 15579968Sobrien if (*c == '\0') 15679968Sobrien return false; 15779968Sobrien } 15879968Sobrien else if (commentlev == 0 && *c == '"') 15979968Sobrien quoted = !quoted; 16079968Sobrien else if (!quoted) 16179968Sobrien { 16279968Sobrien if (*c == ')') 16379968Sobrien { 16479968Sobrien /* unbalanced ')' */ 16579968Sobrien if (commentlev == 0) 16679968Sobrien return false; 16779968Sobrien else 16879968Sobrien commentlev--; 16979968Sobrien } 17079968Sobrien else if (*c == '(') 17179968Sobrien commentlev++; 17279968Sobrien else if (commentlev == 0 && 17379968Sobrien strchr(MustQuoteChars, *c) != NULL) 17479968Sobrien return false; 17579968Sobrien } 17679968Sobrien c++; 17779968Sobrien } 17879968Sobrien 17979968Sobrien /* unbalanced '"' or '(' */ 18079968Sobrien return !quoted && commentlev == 0; 18179968Sobrien} 18279968Sobrien/* 18379968Sobrien** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 18479968Sobrien** 18579968Sobrien** Arbitrarily shorten (in place) an RFC822 string and rebalance 18679968Sobrien** comments and quotes. 18779968Sobrien** 18879968Sobrien** Parameters: 18979968Sobrien** string -- the string to shorten 19079968Sobrien** length -- the maximum size, 0 if no maximum 19179968Sobrien** 19279968Sobrien** Returns: 19379968Sobrien** true if string is changed, false otherwise 19479968Sobrien** 19579968Sobrien** Side Effects: 19679968Sobrien** Changes string in place, possibly resulting 19779968Sobrien** in a shorter string. 19879968Sobrien*/ 19979968Sobrien 20079968Sobrienbool 20179968Sobrienshorten_rfc822_string(string, length) 20279968Sobrien char *string; 20379968Sobrien size_t length; 20479968Sobrien{ 20579968Sobrien bool backslash = false; 20679968Sobrien bool modified = false; 20779968Sobrien bool quoted = false; 20879968Sobrien size_t slen; 20979968Sobrien int parencount = 0; 21079968Sobrien char *ptr = string; 21179968Sobrien 21279968Sobrien /* 21379968Sobrien ** If have to rebalance an already short enough string, 21479968Sobrien ** need to do it within allocated space. 21579968Sobrien */ 21679968Sobrien 21779968Sobrien slen = strlen(string); 21879968Sobrien if (length == 0 || slen < length) 21979968Sobrien length = slen; 22079968Sobrien 22179968Sobrien while (*ptr != '\0') 22279968Sobrien { 22379968Sobrien if (backslash) 22479968Sobrien { 22579968Sobrien backslash = false; 22679968Sobrien goto increment; 22779968Sobrien } 22879968Sobrien 22979968Sobrien if (*ptr == '\\') 23079968Sobrien backslash = true; 23179968Sobrien else if (*ptr == '(') 23292282Sobrien { 23392282Sobrien if (!quoted) 23492282Sobrien parencount++; 23579968Sobrien } 23679968Sobrien else if (*ptr == ')') 23779968Sobrien { 23879968Sobrien if (--parencount < 0) 23992282Sobrien parencount = 0; 24079968Sobrien } 24192282Sobrien 24279968Sobrien /* Inside a comment, quotes don't matter */ 24379968Sobrien if (parencount <= 0 && *ptr == '"') 24479968Sobrien quoted = !quoted; 24579968Sobrien 24679968Sobrienincrement: 24779968Sobrien /* Check for sufficient space for next character */ 24879968Sobrien if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 24992282Sobrien parencount + 25079968Sobrien (quoted ? 1 : 0))) 25179968Sobrien { 25279968Sobrien /* Not enough, backtrack */ 25379968Sobrien if (*ptr == '\\') 25479968Sobrien backslash = false; 25579968Sobrien else if (*ptr == '(' && !quoted) 25679968Sobrien parencount--; 25779968Sobrien else if (*ptr == '"' && parencount == 0) 25892282Sobrien quoted = false; 25979968Sobrien break; 26079968Sobrien } 26179968Sobrien ptr++; 26279968Sobrien } 26379968Sobrien 26479968Sobrien /* Rebalance */ 26579968Sobrien while (parencount-- > 0) 26679968Sobrien { 26779968Sobrien if (*ptr != ')') 26879968Sobrien { 26979968Sobrien modified = true; 27079968Sobrien *ptr = ')'; 27179968Sobrien } 27279968Sobrien ptr++; 27379968Sobrien } 27479968Sobrien if (quoted) 27579968Sobrien { 27679968Sobrien if (*ptr != '"') 27779968Sobrien { 27879968Sobrien modified = true; 27979968Sobrien *ptr = '"'; 28079968Sobrien } 28179968Sobrien ptr++; 28279968Sobrien } 28379968Sobrien if (*ptr != '\0') 28479968Sobrien { 28579968Sobrien modified = true; 28679968Sobrien *ptr = '\0'; 28779968Sobrien } 28879968Sobrien return modified; 28992282Sobrien} 29079968Sobrien/* 29179968Sobrien** FIND_CHARACTER -- find an unquoted character in an RFC822 string 29279968Sobrien** 29379968Sobrien** Find an unquoted, non-commented character in an RFC822 29479968Sobrien** string and return a pointer to its location in the 29579968Sobrien** string. 29679968Sobrien** 29779968Sobrien** Parameters: 29879968Sobrien** string -- the string to search 29979968Sobrien** character -- the character to find 30079968Sobrien** 30179968Sobrien** Returns: 30279968Sobrien** pointer to the character, or 30379968Sobrien** a pointer to the end of the line if character is not found 30479968Sobrien*/ 30579968Sobrien 30679968Sobrienchar * 30779968Sobrienfind_character(string, character) 30879968Sobrien char *string; 30979968Sobrien int character; 31079968Sobrien{ 31179968Sobrien bool backslash = false; 31279968Sobrien bool quoted = false; 31379968Sobrien int parencount = 0; 31479968Sobrien 31579968Sobrien while (string != NULL && *string != '\0') 31679968Sobrien { 31779968Sobrien if (backslash) 31879968Sobrien { 31979968Sobrien backslash = false; 32079968Sobrien if (!quoted && character == '\\' && *string == '\\') 32179968Sobrien break; 32279968Sobrien string++; 32379968Sobrien continue; 32479968Sobrien } 32579968Sobrien switch (*string) 32679968Sobrien { 32779968Sobrien case '\\': 32879968Sobrien backslash = true; 32979968Sobrien break; 33079968Sobrien 33179968Sobrien case '(': 33279968Sobrien if (!quoted) 33379968Sobrien parencount++; 33479968Sobrien break; 33579968Sobrien 33679968Sobrien case ')': 33779968Sobrien if (--parencount < 0) 33879968Sobrien parencount = 0; 33979968Sobrien break; 34079968Sobrien } 34179968Sobrien 34279968Sobrien /* Inside a comment, nothing matters */ 34379968Sobrien if (parencount > 0) 34479968Sobrien { 34579968Sobrien string++; 34679968Sobrien continue; 34779968Sobrien } 34879968Sobrien 34979968Sobrien if (*string == '"') 35079968Sobrien quoted = !quoted; 35179968Sobrien else if (*string == character && !quoted) 35279968Sobrien break; 35379968Sobrien string++; 35479968Sobrien } 35579968Sobrien 35679968Sobrien /* Return pointer to the character */ 35779968Sobrien return string; 35879968Sobrien} 35979968Sobrien 36079968Sobrien/* 36179968Sobrien** CHECK_BODYTYPE -- check bodytype parameter 36279968Sobrien** 36379968Sobrien** Parameters: 36479968Sobrien** bodytype -- bodytype parameter 36579968Sobrien** 36679968Sobrien** Returns: 36779968Sobrien** BODYTYPE_* according to parameter 36879968Sobrien** 36979968Sobrien*/ 37079968Sobrien 37179968Sobrienint 37279968Sobriencheck_bodytype(bodytype) 37379968Sobrien char *bodytype; 37479968Sobrien{ 37579968Sobrien /* check body type for legality */ 37679968Sobrien if (bodytype == NULL) 37779968Sobrien return BODYTYPE_NONE; 37879968Sobrien if (sm_strcasecmp(bodytype, "7BIT") == 0) 37979968Sobrien return BODYTYPE_7BIT; 38079968Sobrien if (sm_strcasecmp(bodytype, "8BITMIME") == 0) 38179968Sobrien return BODYTYPE_8BITMIME; 38279968Sobrien return BODYTYPE_ILLEGAL; 38379968Sobrien} 38479968Sobrien 38579968Sobrien#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI 38679968Sobrien/* 38779968Sobrien** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." 38879968Sobrien** 38979968Sobrien** Parameters: 39079968Sobrien** str -- string to truncate 39179968Sobrien** len -- maximum length (including '\0') (0 for unlimited) 39279968Sobrien** delim -- delimiter character 39379968Sobrien** 39479968Sobrien** Returns: 39579968Sobrien** None. 39679968Sobrien*/ 39779968Sobrien 39879968Sobrienvoid 39979968Sobrientruncate_at_delim(str, len, delim) 40079968Sobrien char *str; 40179968Sobrien size_t len; 40279968Sobrien int delim; 40379968Sobrien{ 40479968Sobrien char *p; 40579968Sobrien 40679968Sobrien if (str == NULL || len == 0 || strlen(str) < len) 40779968Sobrien return; 40879968Sobrien 40979968Sobrien *(str + len - 1) = '\0'; 41079968Sobrien while ((p = strrchr(str, delim)) != NULL) 41179968Sobrien { 41279968Sobrien *p = '\0'; 41379968Sobrien if (p - str + 4 < len) 41479968Sobrien { 41579968Sobrien *p++ = (char) delim; 41679968Sobrien *p = '\0'; 41779968Sobrien (void) sm_strlcat(str, "...", len); 41879968Sobrien return; 41979968Sobrien } 42079968Sobrien } 42179968Sobrien 42279968Sobrien /* Couldn't find a place to append "..." */ 42379968Sobrien if (len > 3) 42479968Sobrien (void) sm_strlcpy(str, "...", len); 42579968Sobrien else 42679968Sobrien str[0] = '\0'; 42779968Sobrien} 42879968Sobrien#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */ 42979968Sobrien/* 43079968Sobrien** XALLOC -- Allocate memory, raise an exception on error 43179968Sobrien** 43279968Sobrien** Parameters: 43379968Sobrien** sz -- size of area to allocate. 43479968Sobrien** 43579968Sobrien** Returns: 43679968Sobrien** pointer to data region. 43779968Sobrien** 43879968Sobrien** Exceptions: 43979968Sobrien** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory 44079968Sobrien** 44179968Sobrien** Side Effects: 44279968Sobrien** Memory is allocated. 44379968Sobrien*/ 44479968Sobrien 44579968Sobrienchar * 44679968Sobrien#if SM_HEAP_CHECK 44779968Sobrienxalloc_tagged(sz, file, line) 44879968Sobrien register int sz; 44979968Sobrien char *file; 45079968Sobrien int line; 45179968Sobrien#else /* SM_HEAP_CHECK */ 45279968Sobrienxalloc(sz) 45379968Sobrien register int sz; 45479968Sobrien#endif /* SM_HEAP_CHECK */ 45579968Sobrien{ 45679968Sobrien register char *p; 45779968Sobrien 45879968Sobrien /* some systems can't handle size zero mallocs */ 45979968Sobrien if (sz <= 0) 46092282Sobrien sz = 1; 46192282Sobrien 46292282Sobrien /* scaffolding for testing error handling code */ 46392282Sobrien sm_xtrap_raise_x(&SmHeapOutOfMemory); 46492282Sobrien 46579968Sobrien p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); 46679968Sobrien if (p == NULL) 46792282Sobrien { 46879968Sobrien sm_exc_raise_x(&SmHeapOutOfMemory); 46979968Sobrien } 47079968Sobrien return p; 47179968Sobrien} 47279968Sobrien/* 47379968Sobrien** COPYPLIST -- copy list of pointers. 47479968Sobrien** 47579968Sobrien** This routine is the equivalent of strdup for lists of 47679968Sobrien** pointers. 47779968Sobrien** 47879968Sobrien** Parameters: 47979968Sobrien** list -- list of pointers to copy. 48079968Sobrien** Must be NULL terminated. 48179968Sobrien** copycont -- if true, copy the contents of the vector 48279968Sobrien** (which must be a string) also. 48379968Sobrien** rpool -- resource pool from which to allocate storage, 48479968Sobrien** or NULL 48579968Sobrien** 48679968Sobrien** Returns: 48779968Sobrien** a copy of 'list'. 48879968Sobrien*/ 48979968Sobrien 49079968Sobrienchar ** 49179968Sobriencopyplist(list, copycont, rpool) 49279968Sobrien char **list; 49379968Sobrien bool copycont; 49479968Sobrien SM_RPOOL_T *rpool; 49579968Sobrien{ 49679968Sobrien register char **vp; 49779968Sobrien register char **newvp; 49879968Sobrien 49979968Sobrien for (vp = list; *vp != NULL; vp++) 50079968Sobrien continue; 50179968Sobrien 50279968Sobrien vp++; 50379968Sobrien 50479968Sobrien newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp); 50579968Sobrien memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp); 50679968Sobrien 50779968Sobrien if (copycont) 50879968Sobrien { 50979968Sobrien for (vp = newvp; *vp != NULL; vp++) 51079968Sobrien *vp = sm_rpool_strdup_x(rpool, *vp); 51179968Sobrien } 51279968Sobrien 51379968Sobrien return newvp; 51479968Sobrien} 51579968Sobrien/* 51679968Sobrien** COPYQUEUE -- copy address queue. 51779968Sobrien** 51879968Sobrien** This routine is the equivalent of strdup for address queues; 51979968Sobrien** addresses marked as QS_IS_DEAD() aren't copied 52079968Sobrien** 52179968Sobrien** Parameters: 52279968Sobrien** addr -- list of address structures to copy. 52379968Sobrien** rpool -- resource pool from which to allocate storage 52479968Sobrien** 52579968Sobrien** Returns: 52679968Sobrien** a copy of 'addr'. 52779968Sobrien*/ 52879968Sobrien 52979968SobrienADDRESS * 53079968Sobriencopyqueue(addr, rpool) 53179968Sobrien ADDRESS *addr; 53279968Sobrien SM_RPOOL_T *rpool; 53379968Sobrien{ 53479968Sobrien register ADDRESS *newaddr; 53579968Sobrien ADDRESS *ret; 53679968Sobrien register ADDRESS **tail = &ret; 53779968Sobrien 53879968Sobrien while (addr != NULL) 53979968Sobrien { 54079968Sobrien if (!QS_IS_DEAD(addr->q_state)) 54179968Sobrien { 54279968Sobrien newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, 54379968Sobrien sizeof *newaddr); 54479968Sobrien STRUCTCOPY(*addr, *newaddr); 54579968Sobrien *tail = newaddr; 54679968Sobrien tail = &newaddr->q_next; 54779968Sobrien } 54879968Sobrien addr = addr->q_next; 54979968Sobrien } 55079968Sobrien *tail = NULL; 55179968Sobrien 55279968Sobrien return ret; 55379968Sobrien} 55479968Sobrien/* 55579968Sobrien** LOG_SENDMAIL_PID -- record sendmail pid and command line. 55679968Sobrien** 55779968Sobrien** Parameters: 55879968Sobrien** e -- the current envelope. 55979968Sobrien** 56079968Sobrien** Returns: 56179968Sobrien** none. 56279968Sobrien** 56379968Sobrien** Side Effects: 56479968Sobrien** writes pidfile, logs command line. 56579968Sobrien** keeps file open and locked to prevent overwrite of active file 56679968Sobrien*/ 56779968Sobrien 56879968Sobrienstatic SM_FILE_T *Pidf = NULL; 56979968Sobrien 57079968Sobrienvoid 57179968Sobrienlog_sendmail_pid(e) 57279968Sobrien ENVELOPE *e; 57379968Sobrien{ 57479968Sobrien long sff; 57579968Sobrien char pidpath[MAXPATHLEN]; 57679968Sobrien extern char *CommandLineArgs; 57779968Sobrien 57879968Sobrien /* write the pid to the log file for posterity */ 57979968Sobrien sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK; 58079968Sobrien if (TrustedUid != 0 && RealUid == TrustedUid) 58179968Sobrien sff |= SFF_OPENASROOT; 58279968Sobrien expand(PidFile, pidpath, sizeof pidpath, e); 58379968Sobrien Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff); 58479968Sobrien if (Pidf == NULL) 58579968Sobrien { 58679968Sobrien if (errno == EWOULDBLOCK) 58779968Sobrien sm_syslog(LOG_ERR, NOQID, 58879968Sobrien "unable to write pid to %s: file in use by another process", 58979968Sobrien pidpath); 59079968Sobrien else 59179968Sobrien sm_syslog(LOG_ERR, NOQID, 59279968Sobrien "unable to write pid to %s: %s", 59379968Sobrien pidpath, sm_errstring(errno)); 59479968Sobrien } 59579968Sobrien else 59679968Sobrien { 59779968Sobrien PidFilePid = getpid(); 59879968Sobrien 59979968Sobrien /* write the process id on line 1 */ 60079968Sobrien (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n", 60179968Sobrien (long) PidFilePid); 60279968Sobrien 60379968Sobrien /* line 2 contains all command line flags */ 60479968Sobrien (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n", 60579968Sobrien CommandLineArgs); 60679968Sobrien 60779968Sobrien /* flush */ 60879968Sobrien (void) sm_io_flush(Pidf, SM_TIME_DEFAULT); 60979968Sobrien 61079968Sobrien /* 61179968Sobrien ** Leave pid file open until process ends 61279968Sobrien ** so it's not overwritten by another 61379968Sobrien ** process. 61479968Sobrien */ 61579968Sobrien } 61679968Sobrien if (LogLevel > 9) 61779968Sobrien sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); 61879968Sobrien} 61979968Sobrien 62079968Sobrien/* 62179968Sobrien** CLOSE_SENDMAIL_PID -- close sendmail pid file 62279968Sobrien** 62379968Sobrien** Parameters: 62479968Sobrien** none. 62579968Sobrien** 62679968Sobrien** Returns: 62779968Sobrien** none. 62879968Sobrien*/ 62979968Sobrien 63079968Sobrienvoid 63179968Sobrienclose_sendmail_pid() 63279968Sobrien{ 63379968Sobrien if (Pidf == NULL) 63479968Sobrien return; 63579968Sobrien 63679968Sobrien (void) sm_io_close(Pidf, SM_TIME_DEFAULT); 63779968Sobrien Pidf = NULL; 63879968Sobrien} 63979968Sobrien 64079968Sobrien/* 64179968Sobrien** SET_DELIVERY_MODE -- set and record the delivery mode 64279968Sobrien** 64379968Sobrien** Parameters: 64479968Sobrien** mode -- delivery mode 64579968Sobrien** e -- the current envelope. 64679968Sobrien** 64779968Sobrien** Returns: 64879968Sobrien** none. 64979968Sobrien** 65079968Sobrien** Side Effects: 65179968Sobrien** sets {deliveryMode} macro 65279968Sobrien*/ 65379968Sobrien 65479968Sobrienvoid 65579968Sobrienset_delivery_mode(mode, e) 65679968Sobrien int mode; 65779968Sobrien ENVELOPE *e; 65879968Sobrien{ 65979968Sobrien char buf[2]; 66079968Sobrien 66179968Sobrien e->e_sendmode = (char) mode; 66279968Sobrien buf[0] = (char) mode; 66379968Sobrien buf[1] = '\0'; 66479968Sobrien macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); 66579968Sobrien} 66679968Sobrien/* 66779968Sobrien** SET_OP_MODE -- set and record the op mode 66879968Sobrien** 66979968Sobrien** Parameters: 67079968Sobrien** mode -- op mode 67179968Sobrien** e -- the current envelope. 67279968Sobrien** 67379968Sobrien** Returns: 67479968Sobrien** none. 67579968Sobrien** 67679968Sobrien** Side Effects: 67779968Sobrien** sets {opMode} macro 67879968Sobrien*/ 67979968Sobrien 68079968Sobrienvoid 68179968Sobrienset_op_mode(mode) 68279968Sobrien int mode; 68379968Sobrien{ 68479968Sobrien char buf[2]; 68579968Sobrien extern ENVELOPE BlankEnvelope; 68679968Sobrien 68779968Sobrien OpMode = (char) mode; 68879968Sobrien buf[0] = (char) mode; 68979968Sobrien buf[1] = '\0'; 69079968Sobrien macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); 69179968Sobrien} 69279968Sobrien/* 69379968Sobrien** PRINTAV -- print argument vector. 69479968Sobrien** 69579968Sobrien** Parameters: 69679968Sobrien** fp -- output file pointer. 69779968Sobrien** av -- argument vector. 69879968Sobrien** 69979968Sobrien** Returns: 70079968Sobrien** none. 70179968Sobrien** 70279968Sobrien** Side Effects: 70379968Sobrien** prints av. 70479968Sobrien*/ 70579968Sobrien 70679968Sobrienvoid 70779968Sobrienprintav(fp, av) 70879968Sobrien SM_FILE_T *fp; 70979968Sobrien register char **av; 71079968Sobrien{ 71179968Sobrien while (*av != NULL) 71279968Sobrien { 71392282Sobrien if (tTd(0, 44)) 71492282Sobrien sm_dprintf("\n\t%08lx=", (unsigned long) *av); 71592282Sobrien else 71692282Sobrien (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' '); 71792282Sobrien xputs(fp, *av++); 71892282Sobrien } 71992282Sobrien (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n'); 72092282Sobrien} 72179968Sobrien/* 72292282Sobrien** XPUTS -- put string doing control escapes. 72379968Sobrien** 72479968Sobrien** Parameters: 72579968Sobrien** fp -- output file pointer. 72679968Sobrien** s -- string to put. 72779968Sobrien** 72879968Sobrien** Returns: 72979968Sobrien** none. 73079968Sobrien** 73179968Sobrien** Side Effects: 73279968Sobrien** output to stdout 73379968Sobrien*/ 73479968Sobrien 73579968Sobrienvoid 73679968Sobrienxputs(fp, s) 73779968Sobrien SM_FILE_T *fp; 73879968Sobrien register const char *s; 73979968Sobrien{ 74079968Sobrien register int c; 74179968Sobrien register struct metamac *mp; 74279968Sobrien bool shiftout = false; 74379968Sobrien extern struct metamac MetaMacros[]; 74479968Sobrien static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 74579968Sobrien "@(#)$Debug: ANSI - enable reverse video in debug output $"); 74679968Sobrien 74779968Sobrien /* 74879968Sobrien ** TermEscape is set here, rather than in main(), 74979968Sobrien ** because ANSI mode can be turned on or off at any time 75079968Sobrien ** if we are in -bt rule testing mode. 75179968Sobrien */ 75279968Sobrien 75379968Sobrien if (sm_debug_unknown(&DebugANSI)) 75479968Sobrien { 75579968Sobrien if (sm_debug_active(&DebugANSI, 1)) 75679968Sobrien { 75779968Sobrien TermEscape.te_rv_on = "\033[7m"; 75879968Sobrien TermEscape.te_rv_off = "\033[0m"; 75979968Sobrien } 76079968Sobrien else 76179968Sobrien { 76279968Sobrien TermEscape.te_rv_on = ""; 76379968Sobrien TermEscape.te_rv_off = ""; 76479968Sobrien } 76579968Sobrien } 76679968Sobrien 76779968Sobrien if (s == NULL) 76879968Sobrien { 76979968Sobrien (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s", 77079968Sobrien TermEscape.te_rv_on, TermEscape.te_rv_off); 77179968Sobrien return; 77279968Sobrien } 77379968Sobrien while ((c = (*s++ & 0377)) != '\0') 77479968Sobrien { 77579968Sobrien if (shiftout) 77679968Sobrien { 77792282Sobrien (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 77892282Sobrien TermEscape.te_rv_off); 77979968Sobrien shiftout = false; 78079968Sobrien } 78179968Sobrien if (!isascii(c)) 78279968Sobrien { 78379968Sobrien if (c == MATCHREPL) 78492282Sobrien { 78592282Sobrien (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 78692282Sobrien "%s$", 78792282Sobrien TermEscape.te_rv_on); 78892282Sobrien shiftout = true; 78992282Sobrien if (*s == '\0') 79092282Sobrien continue; 79192282Sobrien c = *s++ & 0377; 79292282Sobrien goto printchar; 79392282Sobrien } 79479968Sobrien if (c == MACROEXPAND || c == MACRODEXPAND) 79579968Sobrien { 79679968Sobrien (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 79779968Sobrien "%s$", 79879968Sobrien TermEscape.te_rv_on); 79979968Sobrien if (c == MACRODEXPAND) 80079968Sobrien (void) sm_io_putc(fp, 80179968Sobrien SM_TIME_DEFAULT, '&'); 80279968Sobrien shiftout = true; 80379968Sobrien if (*s == '\0') 80479968Sobrien continue; 80579968Sobrien if (strchr("=~&?", *s) != NULL) 80679968Sobrien (void) sm_io_putc(fp, 80779968Sobrien SM_TIME_DEFAULT, 80879968Sobrien *s++); 80979968Sobrien if (bitset(0200, *s)) 81079968Sobrien (void) sm_io_fprintf(fp, 81179968Sobrien SM_TIME_DEFAULT, 81279968Sobrien "{%s}", 81379968Sobrien macname(bitidx(*s++))); 81479968Sobrien else 81579968Sobrien (void) sm_io_fprintf(fp, 81679968Sobrien SM_TIME_DEFAULT, 81779968Sobrien "%c", 81879968Sobrien *s++); 819 continue; 820 } 821 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 822 { 823 if (bitidx(mp->metaval) == c) 824 { 825 (void) sm_io_fprintf(fp, 826 SM_TIME_DEFAULT, 827 "%s$%c", 828 TermEscape.te_rv_on, 829 mp->metaname); 830 shiftout = true; 831 break; 832 } 833 } 834 if (c == MATCHCLASS || c == MATCHNCLASS) 835 { 836 if (bitset(0200, *s)) 837 (void) sm_io_fprintf(fp, 838 SM_TIME_DEFAULT, 839 "{%s}", 840 macname(bitidx(*s++))); 841 else if (*s != '\0') 842 (void) sm_io_fprintf(fp, 843 SM_TIME_DEFAULT, 844 "%c", 845 *s++); 846 } 847 if (mp->metaname != '\0') 848 continue; 849 850 /* unrecognized meta character */ 851 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-", 852 TermEscape.te_rv_on); 853 shiftout = true; 854 c &= 0177; 855 } 856 printchar: 857 if (isprint(c)) 858 { 859 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 860 continue; 861 } 862 863 /* wasn't a meta-macro -- find another way to print it */ 864 switch (c) 865 { 866 case '\n': 867 c = 'n'; 868 break; 869 870 case '\r': 871 c = 'r'; 872 break; 873 874 case '\t': 875 c = 't'; 876 break; 877 } 878 if (!shiftout) 879 { 880 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 881 TermEscape.te_rv_on); 882 shiftout = true; 883 } 884 if (isprint(c)) 885 { 886 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\'); 887 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 888 } 889 else 890 { 891 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^'); 892 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100); 893 } 894 } 895 if (shiftout) 896 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 897 TermEscape.te_rv_off); 898 (void) sm_io_flush(fp, SM_TIME_DEFAULT); 899} 900/* 901** MAKELOWER -- Translate a line into lower case 902** 903** Parameters: 904** p -- the string to translate. If NULL, return is 905** immediate. 906** 907** Returns: 908** none. 909** 910** Side Effects: 911** String pointed to by p is translated to lower case. 912*/ 913 914void 915makelower(p) 916 register char *p; 917{ 918 register char c; 919 920 if (p == NULL) 921 return; 922 for (; (c = *p) != '\0'; p++) 923 if (isascii(c) && isupper(c)) 924 *p = tolower(c); 925} 926/* 927** FIXCRLF -- fix <CR><LF> in line. 928** 929** Looks for the <CR><LF> combination and turns it into the 930** UNIX canonical <NL> character. It only takes one line, 931** i.e., it is assumed that the first <NL> found is the end 932** of the line. 933** 934** Parameters: 935** line -- the line to fix. 936** stripnl -- if true, strip the newline also. 937** 938** Returns: 939** none. 940** 941** Side Effects: 942** line is changed in place. 943*/ 944 945void 946fixcrlf(line, stripnl) 947 char *line; 948 bool stripnl; 949{ 950 register char *p; 951 952 p = strchr(line, '\n'); 953 if (p == NULL) 954 return; 955 if (p > line && p[-1] == '\r') 956 p--; 957 if (!stripnl) 958 *p++ = '\n'; 959 *p = '\0'; 960} 961/* 962** PUTLINE -- put a line like fputs obeying SMTP conventions 963** 964** This routine always guarantees outputing a newline (or CRLF, 965** as appropriate) at the end of the string. 966** 967** Parameters: 968** l -- line to put. 969** mci -- the mailer connection information. 970** 971** Returns: 972** none 973** 974** Side Effects: 975** output of l to mci->mci_out. 976*/ 977 978void 979putline(l, mci) 980 register char *l; 981 register MCI *mci; 982{ 983 putxline(l, strlen(l), mci, PXLF_MAPFROM); 984} 985/* 986** PUTXLINE -- putline with flags bits. 987** 988** This routine always guarantees outputing a newline (or CRLF, 989** as appropriate) at the end of the string. 990** 991** Parameters: 992** l -- line to put. 993** len -- the length of the line. 994** mci -- the mailer connection information. 995** pxflags -- flag bits: 996** PXLF_MAPFROM -- map From_ to >From_. 997** PXLF_STRIP8BIT -- strip 8th bit. 998** PXLF_HEADER -- map bare newline in header to newline space. 999** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 1000** 1001** Returns: 1002** none 1003** 1004** Side Effects: 1005** output of l to mci->mci_out. 1006*/ 1007 1008void 1009putxline(l, len, mci, pxflags) 1010 register char *l; 1011 size_t len; 1012 register MCI *mci; 1013 int pxflags; 1014{ 1015 bool dead = false; 1016 register char *p, *end; 1017 int slop = 0; 1018 1019 /* strip out 0200 bits -- these can look like TELNET protocol */ 1020 if (bitset(MCIF_7BIT, mci->mci_flags) || 1021 bitset(PXLF_STRIP8BIT, pxflags)) 1022 { 1023 register char svchar; 1024 1025 for (p = l; (svchar = *p) != '\0'; ++p) 1026 if (bitset(0200, svchar)) 1027 *p = svchar &~ 0200; 1028 } 1029 1030 end = l + len; 1031 do 1032 { 1033 bool noeol = false; 1034 1035 /* find the end of the line */ 1036 p = memchr(l, '\n', end - l); 1037 if (p == NULL) 1038 { 1039 p = end; 1040 noeol = true; 1041 } 1042 1043 if (TrafficLogFile != NULL) 1044 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1045 "%05d >>> ", (int) CurrentPid); 1046 1047 /* check for line overflow */ 1048 while (mci->mci_mailer->m_linelimit > 0 && 1049 (p - l + slop) > mci->mci_mailer->m_linelimit) 1050 { 1051 char *l_base = l; 1052 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 1053 1054 if (l[0] == '.' && slop == 0 && 1055 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1056 { 1057 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1058 '.') == SM_IO_EOF) 1059 dead = true; 1060 else 1061 { 1062 /* record progress for DATA timeout */ 1063 DataProgress = true; 1064 } 1065 if (TrafficLogFile != NULL) 1066 (void) sm_io_putc(TrafficLogFile, 1067 SM_TIME_DEFAULT, '.'); 1068 } 1069 else if (l[0] == 'F' && slop == 0 && 1070 bitset(PXLF_MAPFROM, pxflags) && 1071 strncmp(l, "From ", 5) == 0 && 1072 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1073 { 1074 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1075 '>') == SM_IO_EOF) 1076 dead = true; 1077 else 1078 { 1079 /* record progress for DATA timeout */ 1080 DataProgress = true; 1081 } 1082 if (TrafficLogFile != NULL) 1083 (void) sm_io_putc(TrafficLogFile, 1084 SM_TIME_DEFAULT, 1085 '>'); 1086 } 1087 if (dead) 1088 break; 1089 1090 while (l < q) 1091 { 1092 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1093 (unsigned char) *l++) == SM_IO_EOF) 1094 { 1095 dead = true; 1096 break; 1097 } 1098 else 1099 { 1100 /* record progress for DATA timeout */ 1101 DataProgress = true; 1102 } 1103 } 1104 if (dead) 1105 break; 1106 1107 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') == 1108 SM_IO_EOF || 1109 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1110 mci->mci_mailer->m_eol) == 1111 SM_IO_EOF || 1112 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') == 1113 SM_IO_EOF) 1114 { 1115 dead = true; 1116 break; 1117 } 1118 else 1119 { 1120 /* record progress for DATA timeout */ 1121 DataProgress = true; 1122 } 1123 if (TrafficLogFile != NULL) 1124 { 1125 for (l = l_base; l < q; l++) 1126 (void) sm_io_putc(TrafficLogFile, 1127 SM_TIME_DEFAULT, 1128 (unsigned char)*l); 1129 (void) sm_io_fprintf(TrafficLogFile, 1130 SM_TIME_DEFAULT, 1131 "!\n%05d >>> ", 1132 (int) CurrentPid); 1133 } 1134 slop = 1; 1135 } 1136 1137 if (dead) 1138 break; 1139 1140 /* output last part */ 1141 if (l[0] == '.' && slop == 0 && 1142 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1143 { 1144 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1145 SM_IO_EOF) 1146 break; 1147 else 1148 { 1149 /* record progress for DATA timeout */ 1150 DataProgress = true; 1151 } 1152 if (TrafficLogFile != NULL) 1153 (void) sm_io_putc(TrafficLogFile, 1154 SM_TIME_DEFAULT, '.'); 1155 } 1156 else if (l[0] == 'F' && slop == 0 && 1157 bitset(PXLF_MAPFROM, pxflags) && 1158 strncmp(l, "From ", 5) == 0 && 1159 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1160 { 1161 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1162 SM_IO_EOF) 1163 break; 1164 else 1165 { 1166 /* record progress for DATA timeout */ 1167 DataProgress = true; 1168 } 1169 if (TrafficLogFile != NULL) 1170 (void) sm_io_putc(TrafficLogFile, 1171 SM_TIME_DEFAULT, '>'); 1172 } 1173 for ( ; l < p; ++l) 1174 { 1175 if (TrafficLogFile != NULL) 1176 (void) sm_io_putc(TrafficLogFile, 1177 SM_TIME_DEFAULT, 1178 (unsigned char)*l); 1179 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1180 (unsigned char) *l) == SM_IO_EOF) 1181 { 1182 dead = true; 1183 break; 1184 } 1185 else 1186 { 1187 /* record progress for DATA timeout */ 1188 DataProgress = true; 1189 } 1190 } 1191 if (dead) 1192 break; 1193 1194 if (TrafficLogFile != NULL) 1195 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1196 '\n'); 1197 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) && 1198 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1199 mci->mci_mailer->m_eol) == SM_IO_EOF) 1200 break; 1201 else 1202 { 1203 /* record progress for DATA timeout */ 1204 DataProgress = true; 1205 } 1206 if (l < end && *l == '\n') 1207 { 1208 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1209 bitset(PXLF_HEADER, pxflags)) 1210 { 1211 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1212 ' ') == SM_IO_EOF) 1213 break; 1214 else 1215 { 1216 /* record progress for DATA timeout */ 1217 DataProgress = true; 1218 } 1219 1220 if (TrafficLogFile != NULL) 1221 (void) sm_io_putc(TrafficLogFile, 1222 SM_TIME_DEFAULT, ' '); 1223 } 1224 } 1225 1226 /* record progress for DATA timeout */ 1227 DataProgress = true; 1228 } while (l < end); 1229} 1230/* 1231** XUNLINK -- unlink a file, doing logging as appropriate. 1232** 1233** Parameters: 1234** f -- name of file to unlink. 1235** 1236** Returns: 1237** return value of unlink() 1238** 1239** Side Effects: 1240** f is unlinked. 1241*/ 1242 1243int 1244xunlink(f) 1245 char *f; 1246{ 1247 register int i; 1248 int save_errno; 1249 1250 if (LogLevel > 98) 1251 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1252 1253 i = unlink(f); 1254 save_errno = errno; 1255 if (i < 0 && LogLevel > 97) 1256 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1257 f, errno); 1258 if (i >= 0) 1259 SYNC_DIR(f, false); 1260 errno = save_errno; 1261 return i; 1262} 1263/* 1264** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1265** 1266** Parameters: 1267** buf -- place to put the input line. 1268** siz -- size of buf. 1269** fp -- file to read from. 1270** timeout -- the timeout before error occurs. 1271** during -- what we are trying to read (for error messages). 1272** 1273** Returns: 1274** NULL on error (including timeout). This may also leave 1275** buf containing a null string. 1276** buf otherwise. 1277*/ 1278 1279 1280char * 1281sfgets(buf, siz, fp, timeout, during) 1282 char *buf; 1283 int siz; 1284 SM_FILE_T *fp; 1285 time_t timeout; 1286 char *during; 1287{ 1288 register char *p; 1289 int save_errno; 1290 int io_timeout; 1291 1292 SM_REQUIRE(siz > 0); 1293 SM_REQUIRE(buf != NULL); 1294 1295 if (fp == NULL) 1296 { 1297 buf[0] = '\0'; 1298 errno = EBADF; 1299 return NULL; 1300 } 1301 1302 /* try to read */ 1303 p = NULL; 1304 errno = 0; 1305 1306 /* convert the timeout to sm_io notation */ 1307 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1308 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1309 { 1310 errno = 0; 1311 p = sm_io_fgets(fp, io_timeout, buf, siz); 1312 if (p == NULL && errno == EAGAIN) 1313 { 1314 /* The sm_io_fgets() call timedout */ 1315 if (LogLevel > 1) 1316 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1317 "timeout waiting for input from %.100s during %s", 1318 CURHOSTNAME, 1319 during); 1320 buf[0] = '\0'; 1321#if XDEBUG 1322 checkfd012(during); 1323#endif /* XDEBUG */ 1324 if (TrafficLogFile != NULL) 1325 (void) sm_io_fprintf(TrafficLogFile, 1326 SM_TIME_DEFAULT, 1327 "%05d <<< [TIMEOUT]\n", 1328 (int) CurrentPid); 1329 errno = ETIMEDOUT; 1330 return NULL; 1331 } 1332 if (p != NULL || errno != EINTR) 1333 break; 1334 (void) sm_io_clearerr(fp); 1335 } 1336 save_errno = errno; 1337 1338 /* clean up the books and exit */ 1339 LineNumber++; 1340 if (p == NULL) 1341 { 1342 buf[0] = '\0'; 1343 if (TrafficLogFile != NULL) 1344 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1345 "%05d <<< [EOF]\n", 1346 (int) CurrentPid); 1347 errno = save_errno; 1348 return NULL; 1349 } 1350 if (TrafficLogFile != NULL) 1351 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1352 "%05d <<< %s", (int) CurrentPid, buf); 1353 if (SevenBitInput) 1354 { 1355 for (p = buf; *p != '\0'; p++) 1356 *p &= ~0200; 1357 } 1358 else if (!HasEightBits) 1359 { 1360 for (p = buf; *p != '\0'; p++) 1361 { 1362 if (bitset(0200, *p)) 1363 { 1364 HasEightBits = true; 1365 break; 1366 } 1367 } 1368 } 1369 return buf; 1370} 1371/* 1372** FGETFOLDED -- like fgets, but knows about folded lines. 1373** 1374** Parameters: 1375** buf -- place to put result. 1376** n -- bytes available. 1377** f -- file to read from. 1378** 1379** Returns: 1380** input line(s) on success, NULL on error or SM_IO_EOF. 1381** This will normally be buf -- unless the line is too 1382** long, when it will be sm_malloc_x()ed. 1383** 1384** Side Effects: 1385** buf gets lines from f, with continuation lines (lines 1386** with leading white space) appended. CRLF's are mapped 1387** into single newlines. Any trailing NL is stripped. 1388*/ 1389 1390char * 1391fgetfolded(buf, n, f) 1392 char *buf; 1393 register int n; 1394 SM_FILE_T *f; 1395{ 1396 register char *p = buf; 1397 char *bp = buf; 1398 register int i; 1399 1400 SM_REQUIRE(n > 0); 1401 SM_REQUIRE(buf != NULL); 1402 if (f == NULL) 1403 { 1404 buf[0] = '\0'; 1405 errno = EBADF; 1406 return NULL; 1407 } 1408 1409 n--; 1410 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1411 { 1412 if (i == '\r') 1413 { 1414 i = sm_io_getc(f, SM_TIME_DEFAULT); 1415 if (i != '\n') 1416 { 1417 if (i != SM_IO_EOF) 1418 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1419 i); 1420 i = '\r'; 1421 } 1422 } 1423 if (--n <= 0) 1424 { 1425 /* allocate new space */ 1426 char *nbp; 1427 int nn; 1428 1429 nn = (p - bp); 1430 if (nn < MEMCHUNKSIZE) 1431 nn *= 2; 1432 else 1433 nn += MEMCHUNKSIZE; 1434 nbp = sm_malloc_x(nn); 1435 memmove(nbp, bp, p - bp); 1436 p = &nbp[p - bp]; 1437 if (bp != buf) 1438 sm_free(bp); 1439 bp = nbp; 1440 n = nn - (p - bp); 1441 } 1442 *p++ = i; 1443 if (i == '\n') 1444 { 1445 LineNumber++; 1446 i = sm_io_getc(f, SM_TIME_DEFAULT); 1447 if (i != SM_IO_EOF) 1448 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1449 if (i != ' ' && i != '\t') 1450 break; 1451 } 1452 } 1453 if (p == bp) 1454 return NULL; 1455 if (p[-1] == '\n') 1456 p--; 1457 *p = '\0'; 1458 return bp; 1459} 1460/* 1461** CURTIME -- return current time. 1462** 1463** Parameters: 1464** none. 1465** 1466** Returns: 1467** the current time. 1468*/ 1469 1470time_t 1471curtime() 1472{ 1473 auto time_t t; 1474 1475 (void) time(&t); 1476 return t; 1477} 1478/* 1479** ATOBOOL -- convert a string representation to boolean. 1480** 1481** Defaults to false 1482** 1483** Parameters: 1484** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1485** others as false. 1486** 1487** Returns: 1488** A boolean representation of the string. 1489*/ 1490 1491bool 1492atobool(s) 1493 register char *s; 1494{ 1495 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1496 return true; 1497 return false; 1498} 1499/* 1500** ATOOCT -- convert a string representation to octal. 1501** 1502** Parameters: 1503** s -- string to convert. 1504** 1505** Returns: 1506** An integer representing the string interpreted as an 1507** octal number. 1508*/ 1509 1510int 1511atooct(s) 1512 register char *s; 1513{ 1514 register int i = 0; 1515 1516 while (*s >= '0' && *s <= '7') 1517 i = (i << 3) | (*s++ - '0'); 1518 return i; 1519} 1520/* 1521** BITINTERSECT -- tell if two bitmaps intersect 1522** 1523** Parameters: 1524** a, b -- the bitmaps in question 1525** 1526** Returns: 1527** true if they have a non-null intersection 1528** false otherwise 1529*/ 1530 1531bool 1532bitintersect(a, b) 1533 BITMAP256 a; 1534 BITMAP256 b; 1535{ 1536 int i; 1537 1538 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1539 { 1540 if ((a[i] & b[i]) != 0) 1541 return true; 1542 } 1543 return false; 1544} 1545/* 1546** BITZEROP -- tell if a bitmap is all zero 1547** 1548** Parameters: 1549** map -- the bit map to check 1550** 1551** Returns: 1552** true if map is all zero. 1553** false if there are any bits set in map. 1554*/ 1555 1556bool 1557bitzerop(map) 1558 BITMAP256 map; 1559{ 1560 int i; 1561 1562 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1563 { 1564 if (map[i] != 0) 1565 return false; 1566 } 1567 return true; 1568} 1569/* 1570** STRCONTAINEDIN -- tell if one string is contained in another 1571** 1572** Parameters: 1573** icase -- ignore case? 1574** a -- possible substring. 1575** b -- possible superstring. 1576** 1577** Returns: 1578** true if a is contained in b (case insensitive). 1579** false otherwise. 1580*/ 1581 1582bool 1583strcontainedin(icase, a, b) 1584 bool icase; 1585 register char *a; 1586 register char *b; 1587{ 1588 int la; 1589 int lb; 1590 int c; 1591 1592 la = strlen(a); 1593 lb = strlen(b); 1594 c = *a; 1595 if (icase && isascii(c) && isupper(c)) 1596 c = tolower(c); 1597 for (; lb-- >= la; b++) 1598 { 1599 if (icase) 1600 { 1601 if (*b != c && 1602 isascii(*b) && isupper(*b) && tolower(*b) != c) 1603 continue; 1604 if (sm_strncasecmp(a, b, la) == 0) 1605 return true; 1606 } 1607 else 1608 { 1609 if (*b != c) 1610 continue; 1611 if (strncmp(a, b, la) == 0) 1612 return true; 1613 } 1614 } 1615 return false; 1616} 1617/* 1618** CHECKFD012 -- check low numbered file descriptors 1619** 1620** File descriptors 0, 1, and 2 should be open at all times. 1621** This routine verifies that, and fixes it if not true. 1622** 1623** Parameters: 1624** where -- a tag printed if the assertion failed 1625** 1626** Returns: 1627** none 1628*/ 1629 1630void 1631checkfd012(where) 1632 char *where; 1633{ 1634#if XDEBUG 1635 register int i; 1636 1637 for (i = 0; i < 3; i++) 1638 fill_fd(i, where); 1639#endif /* XDEBUG */ 1640} 1641/* 1642** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1643** 1644** Parameters: 1645** fd -- file descriptor to check. 1646** where -- tag to print on failure. 1647** 1648** Returns: 1649** none. 1650*/ 1651 1652void 1653checkfdopen(fd, where) 1654 int fd; 1655 char *where; 1656{ 1657#if XDEBUG 1658 struct stat st; 1659 1660 if (fstat(fd, &st) < 0 && errno == EBADF) 1661 { 1662 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1663 printopenfds(true); 1664 } 1665#endif /* XDEBUG */ 1666} 1667/* 1668** CHECKFDS -- check for new or missing file descriptors 1669** 1670** Parameters: 1671** where -- tag for printing. If null, take a base line. 1672** 1673** Returns: 1674** none 1675** 1676** Side Effects: 1677** If where is set, shows changes since the last call. 1678*/ 1679 1680void 1681checkfds(where) 1682 char *where; 1683{ 1684 int maxfd; 1685 register int fd; 1686 bool printhdr = true; 1687 int save_errno = errno; 1688 static BITMAP256 baseline; 1689 extern int DtableSize; 1690 1691 if (DtableSize > BITMAPBITS) 1692 maxfd = BITMAPBITS; 1693 else 1694 maxfd = DtableSize; 1695 if (where == NULL) 1696 clrbitmap(baseline); 1697 1698 for (fd = 0; fd < maxfd; fd++) 1699 { 1700 struct stat stbuf; 1701 1702 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1703 { 1704 if (!bitnset(fd, baseline)) 1705 continue; 1706 clrbitn(fd, baseline); 1707 } 1708 else if (!bitnset(fd, baseline)) 1709 setbitn(fd, baseline); 1710 else 1711 continue; 1712 1713 /* file state has changed */ 1714 if (where == NULL) 1715 continue; 1716 if (printhdr) 1717 { 1718 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1719 "%s: changed fds:", 1720 where); 1721 printhdr = false; 1722 } 1723 dumpfd(fd, true, true); 1724 } 1725 errno = save_errno; 1726} 1727/* 1728** PRINTOPENFDS -- print the open file descriptors (for debugging) 1729** 1730** Parameters: 1731** logit -- if set, send output to syslog; otherwise 1732** print for debugging. 1733** 1734** Returns: 1735** none. 1736*/ 1737 1738#if NETINET || NETINET6 1739# include <arpa/inet.h> 1740#endif /* NETINET || NETINET6 */ 1741 1742void 1743printopenfds(logit) 1744 bool logit; 1745{ 1746 register int fd; 1747 extern int DtableSize; 1748 1749 for (fd = 0; fd < DtableSize; fd++) 1750 dumpfd(fd, false, logit); 1751} 1752/* 1753** DUMPFD -- dump a file descriptor 1754** 1755** Parameters: 1756** fd -- the file descriptor to dump. 1757** printclosed -- if set, print a notification even if 1758** it is closed; otherwise print nothing. 1759** logit -- if set, use sm_syslog instead of sm_dprintf() 1760** 1761** Returns: 1762** none. 1763*/ 1764 1765void 1766dumpfd(fd, printclosed, logit) 1767 int fd; 1768 bool printclosed; 1769 bool logit; 1770{ 1771 register char *p; 1772 char *hp; 1773#ifdef S_IFSOCK 1774 SOCKADDR sa; 1775#endif /* S_IFSOCK */ 1776 auto SOCKADDR_LEN_T slen; 1777 int i; 1778#if STAT64 > 0 1779 struct stat64 st; 1780#else /* STAT64 > 0 */ 1781 struct stat st; 1782#endif /* STAT64 > 0 */ 1783 char buf[200]; 1784 1785 p = buf; 1786 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1787 p += strlen(p); 1788 1789 if ( 1790#if STAT64 > 0 1791 fstat64(fd, &st) 1792#else /* STAT64 > 0 */ 1793 fstat(fd, &st) 1794#endif /* STAT64 > 0 */ 1795 < 0) 1796 { 1797 if (errno != EBADF) 1798 { 1799 (void) sm_snprintf(p, SPACELEFT(buf, p), 1800 "CANNOT STAT (%s)", 1801 sm_errstring(errno)); 1802 goto printit; 1803 } 1804 else if (printclosed) 1805 { 1806 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1807 goto printit; 1808 } 1809 return; 1810 } 1811 1812 i = fcntl(fd, F_GETFL, 0); 1813 if (i != -1) 1814 { 1815 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1816 p += strlen(p); 1817 } 1818 1819 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1820 (int) st.st_mode); 1821 p += strlen(p); 1822 switch (st.st_mode & S_IFMT) 1823 { 1824#ifdef S_IFSOCK 1825 case S_IFSOCK: 1826 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1827 p += strlen(p); 1828 memset(&sa, '\0', sizeof sa); 1829 slen = sizeof sa; 1830 if (getsockname(fd, &sa.sa, &slen) < 0) 1831 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1832 sm_errstring(errno)); 1833 else 1834 { 1835 hp = hostnamebyanyaddr(&sa); 1836 if (hp == NULL) 1837 { 1838 /* EMPTY */ 1839 /* do nothing */ 1840 } 1841# if NETINET 1842 else if (sa.sa.sa_family == AF_INET) 1843 (void) sm_snprintf(p, SPACELEFT(buf, p), 1844 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1845# endif /* NETINET */ 1846# if NETINET6 1847 else if (sa.sa.sa_family == AF_INET6) 1848 (void) sm_snprintf(p, SPACELEFT(buf, p), 1849 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1850# endif /* NETINET6 */ 1851 else 1852 (void) sm_snprintf(p, SPACELEFT(buf, p), 1853 "%s", hp); 1854 } 1855 p += strlen(p); 1856 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 1857 p += strlen(p); 1858 slen = sizeof sa; 1859 if (getpeername(fd, &sa.sa, &slen) < 0) 1860 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1861 sm_errstring(errno)); 1862 else 1863 { 1864 hp = hostnamebyanyaddr(&sa); 1865 if (hp == NULL) 1866 { 1867 /* EMPTY */ 1868 /* do nothing */ 1869 } 1870# if NETINET 1871 else if (sa.sa.sa_family == AF_INET) 1872 (void) sm_snprintf(p, SPACELEFT(buf, p), 1873 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1874# endif /* NETINET */ 1875# if NETINET6 1876 else if (sa.sa.sa_family == AF_INET6) 1877 (void) sm_snprintf(p, SPACELEFT(buf, p), 1878 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1879# endif /* NETINET6 */ 1880 else 1881 (void) sm_snprintf(p, SPACELEFT(buf, p), 1882 "%s", hp); 1883 } 1884 break; 1885#endif /* S_IFSOCK */ 1886 1887 case S_IFCHR: 1888 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 1889 p += strlen(p); 1890 goto defprint; 1891 1892#ifdef S_IFBLK 1893 case S_IFBLK: 1894 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 1895 p += strlen(p); 1896 goto defprint; 1897#endif /* S_IFBLK */ 1898 1899#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1900 case S_IFIFO: 1901 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1902 p += strlen(p); 1903 goto defprint; 1904#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1905 1906#ifdef S_IFDIR 1907 case S_IFDIR: 1908 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 1909 p += strlen(p); 1910 goto defprint; 1911#endif /* S_IFDIR */ 1912 1913#ifdef S_IFLNK 1914 case S_IFLNK: 1915 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 1916 p += strlen(p); 1917 goto defprint; 1918#endif /* S_IFLNK */ 1919 1920 default: 1921defprint: 1922 (void) sm_snprintf(p, SPACELEFT(buf, p), 1923 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 1924 major(st.st_dev), minor(st.st_dev), 1925 (ULONGLONG_T) st.st_ino, 1926 (int) st.st_nlink, (int) st.st_uid, 1927 (int) st.st_gid); 1928 p += strlen(p); 1929 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 1930 (ULONGLONG_T) st.st_size); 1931 break; 1932 } 1933 1934printit: 1935 if (logit) 1936 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1937 "%.800s", buf); 1938 else 1939 sm_dprintf("%s\n", buf); 1940} 1941/* 1942** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1943** 1944** Parameters: 1945** host -- the host to shorten (stripped in place). 1946** 1947** Returns: 1948** place where string was truncated, NULL if not truncated. 1949*/ 1950 1951char * 1952shorten_hostname(host) 1953 char host[]; 1954{ 1955 register char *p; 1956 char *mydom; 1957 int i; 1958 bool canon = false; 1959 1960 /* strip off final dot */ 1961 i = strlen(host); 1962 p = &host[(i == 0) ? 0 : i - 1]; 1963 if (*p == '.') 1964 { 1965 *p = '\0'; 1966 canon = true; 1967 } 1968 1969 /* see if there is any domain at all -- if not, we are done */ 1970 p = strchr(host, '.'); 1971 if (p == NULL) 1972 return NULL; 1973 1974 /* yes, we have a domain -- see if it looks like us */ 1975 mydom = macvalue('m', CurEnv); 1976 if (mydom == NULL) 1977 mydom = ""; 1978 i = strlen(++p); 1979 if ((canon ? sm_strcasecmp(p, mydom) 1980 : sm_strncasecmp(p, mydom, i)) == 0 && 1981 (mydom[i] == '.' || mydom[i] == '\0')) 1982 { 1983 *--p = '\0'; 1984 return p; 1985 } 1986 return NULL; 1987} 1988/* 1989** PROG_OPEN -- open a program for reading 1990** 1991** Parameters: 1992** argv -- the argument list. 1993** pfd -- pointer to a place to store the file descriptor. 1994** e -- the current envelope. 1995** 1996** Returns: 1997** pid of the process -- -1 if it failed. 1998*/ 1999 2000pid_t 2001prog_open(argv, pfd, e) 2002 char **argv; 2003 int *pfd; 2004 ENVELOPE *e; 2005{ 2006 pid_t pid; 2007 int save_errno; 2008 int sff; 2009 int ret; 2010 int fdv[2]; 2011 char *p, *q; 2012 char buf[MAXPATHLEN]; 2013 extern int DtableSize; 2014 2015 if (pipe(fdv) < 0) 2016 { 2017 syserr("%s: cannot create pipe for stdout", argv[0]); 2018 return -1; 2019 } 2020 pid = fork(); 2021 if (pid < 0) 2022 { 2023 syserr("%s: cannot fork", argv[0]); 2024 (void) close(fdv[0]); 2025 (void) close(fdv[1]); 2026 return -1; 2027 } 2028 if (pid > 0) 2029 { 2030 /* parent */ 2031 (void) close(fdv[1]); 2032 *pfd = fdv[0]; 2033 return pid; 2034 } 2035 2036 /* Reset global flags */ 2037 RestartRequest = NULL; 2038 RestartWorkGroup = false; 2039 ShutdownRequest = NULL; 2040 PendingSignal = 0; 2041 CurrentPid = getpid(); 2042 2043 /* 2044 ** Initialize exception stack and default exception 2045 ** handler for child process. 2046 */ 2047 2048 sm_exc_newthread(fatal_error); 2049 2050 /* child -- close stdin */ 2051 (void) close(0); 2052 2053 /* stdout goes back to parent */ 2054 (void) close(fdv[0]); 2055 if (dup2(fdv[1], 1) < 0) 2056 { 2057 syserr("%s: cannot dup2 for stdout", argv[0]); 2058 _exit(EX_OSERR); 2059 } 2060 (void) close(fdv[1]); 2061 2062 /* stderr goes to transcript if available */ 2063 if (e->e_xfp != NULL) 2064 { 2065 int xfd; 2066 2067 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 2068 if (xfd >= 0 && dup2(xfd, 2) < 0) 2069 { 2070 syserr("%s: cannot dup2 for stderr", argv[0]); 2071 _exit(EX_OSERR); 2072 } 2073 } 2074 2075 /* this process has no right to the queue file */ 2076 if (e->e_lockfp != NULL) 2077 (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL)); 2078 2079 /* chroot to the program mailer directory, if defined */ 2080 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 2081 { 2082 expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 2083 if (chroot(buf) < 0) 2084 { 2085 syserr("prog_open: cannot chroot(%s)", buf); 2086 exit(EX_TEMPFAIL); 2087 } 2088 if (chdir("/") < 0) 2089 { 2090 syserr("prog_open: cannot chdir(/)"); 2091 exit(EX_TEMPFAIL); 2092 } 2093 } 2094 2095 /* run as default user */ 2096 endpwent(); 2097 sm_mbdb_terminate(); 2098 if (setgid(DefGid) < 0 && geteuid() == 0) 2099 { 2100 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2101 exit(EX_TEMPFAIL); 2102 } 2103 if (setuid(DefUid) < 0 && geteuid() == 0) 2104 { 2105 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2106 exit(EX_TEMPFAIL); 2107 } 2108 2109 /* run in some directory */ 2110 if (ProgMailer != NULL) 2111 p = ProgMailer->m_execdir; 2112 else 2113 p = NULL; 2114 for (; p != NULL; p = q) 2115 { 2116 q = strchr(p, ':'); 2117 if (q != NULL) 2118 *q = '\0'; 2119 expand(p, buf, sizeof buf, e); 2120 if (q != NULL) 2121 *q++ = ':'; 2122 if (buf[0] != '\0' && chdir(buf) >= 0) 2123 break; 2124 } 2125 if (p == NULL) 2126 { 2127 /* backup directories */ 2128 if (chdir("/tmp") < 0) 2129 (void) chdir("/"); 2130 } 2131 2132 /* Check safety of program to be run */ 2133 sff = SFF_ROOTOK|SFF_EXECOK; 2134 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2135 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2136 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2137 sff |= SFF_NOPATHCHECK; 2138 else 2139 sff |= SFF_SAFEDIRPATH; 2140 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2141 if (ret != 0) 2142 sm_syslog(LOG_INFO, e->e_id, 2143 "Warning: prog_open: program %s unsafe: %s", 2144 argv[0], sm_errstring(ret)); 2145 2146 /* arrange for all the files to be closed */ 2147 sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2148 2149 /* now exec the process */ 2150 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2151 2152 /* woops! failed */ 2153 save_errno = errno; 2154 syserr("%s: cannot exec", argv[0]); 2155 if (transienterror(save_errno)) 2156 _exit(EX_OSERR); 2157 _exit(EX_CONFIG); 2158 return -1; /* avoid compiler warning on IRIX */ 2159} 2160/* 2161** GET_COLUMN -- look up a Column in a line buffer 2162** 2163** Parameters: 2164** line -- the raw text line to search. 2165** col -- the column number to fetch. 2166** delim -- the delimiter between columns. If null, 2167** use white space. 2168** buf -- the output buffer. 2169** buflen -- the length of buf. 2170** 2171** Returns: 2172** buf if successful. 2173** NULL otherwise. 2174*/ 2175 2176char * 2177get_column(line, col, delim, buf, buflen) 2178 char line[]; 2179 int col; 2180 int delim; 2181 char buf[]; 2182 int buflen; 2183{ 2184 char *p; 2185 char *begin, *end; 2186 int i; 2187 char delimbuf[4]; 2188 2189 if ((char) delim == '\0') 2190 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 2191 else 2192 { 2193 delimbuf[0] = (char) delim; 2194 delimbuf[1] = '\0'; 2195 } 2196 2197 p = line; 2198 if (*p == '\0') 2199 return NULL; /* line empty */ 2200 if (*p == (char) delim && col == 0) 2201 return NULL; /* first column empty */ 2202 2203 begin = line; 2204 2205 if (col == 0 && (char) delim == '\0') 2206 { 2207 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2208 begin++; 2209 } 2210 2211 for (i = 0; i < col; i++) 2212 { 2213 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2214 return NULL; /* no such column */ 2215 begin++; 2216 if ((char) delim == '\0') 2217 { 2218 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2219 begin++; 2220 } 2221 } 2222 2223 end = strpbrk(begin, delimbuf); 2224 if (end == NULL) 2225 i = strlen(begin); 2226 else 2227 i = end - begin; 2228 if (i >= buflen) 2229 i = buflen - 1; 2230 (void) sm_strlcpy(buf, begin, i + 1); 2231 return buf; 2232} 2233/* 2234** CLEANSTRCPY -- copy string keeping out bogus characters 2235** 2236** Parameters: 2237** t -- "to" string. 2238** f -- "from" string. 2239** l -- length of space available in "to" string. 2240** 2241** Returns: 2242** none. 2243*/ 2244 2245void 2246cleanstrcpy(t, f, l) 2247 register char *t; 2248 register char *f; 2249 int l; 2250{ 2251 /* check for newlines and log if necessary */ 2252 (void) denlstring(f, true, true); 2253 2254 if (l <= 0) 2255 syserr("!cleanstrcpy: length == 0"); 2256 2257 l--; 2258 while (l > 0 && *f != '\0') 2259 { 2260 if (isascii(*f) && 2261 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2262 { 2263 l--; 2264 *t++ = *f; 2265 } 2266 f++; 2267 } 2268 *t = '\0'; 2269} 2270/* 2271** DENLSTRING -- convert newlines in a string to spaces 2272** 2273** Parameters: 2274** s -- the input string 2275** strict -- if set, don't permit continuation lines. 2276** logattacks -- if set, log attempted attacks. 2277** 2278** Returns: 2279** A pointer to a version of the string with newlines 2280** mapped to spaces. This should be copied. 2281*/ 2282 2283char * 2284denlstring(s, strict, logattacks) 2285 char *s; 2286 bool strict; 2287 bool logattacks; 2288{ 2289 register char *p; 2290 int l; 2291 static char *bp = NULL; 2292 static int bl = 0; 2293 2294 p = s; 2295 while ((p = strchr(p, '\n')) != NULL) 2296 if (strict || (*++p != ' ' && *p != '\t')) 2297 break; 2298 if (p == NULL) 2299 return s; 2300 2301 l = strlen(s) + 1; 2302 if (bl < l) 2303 { 2304 /* allocate more space */ 2305 char *nbp = sm_pmalloc_x(l); 2306 2307 if (bp != NULL) 2308 sm_free(bp); 2309 bp = nbp; 2310 bl = l; 2311 } 2312 (void) sm_strlcpy(bp, s, l); 2313 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2314 *p++ = ' '; 2315 2316 if (logattacks) 2317 { 2318 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2319 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2320 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2321 shortenstring(bp, MAXSHORTSTR)); 2322 } 2323 2324 return bp; 2325} 2326 2327/* 2328** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 2329** 2330** Parameters: 2331** s -- string to manipulate (in place) 2332** subst -- character to use as replacement 2333** 2334** Returns: 2335** true iff string did not contain "unprintable" characters 2336*/ 2337 2338bool 2339strreplnonprt(s, c) 2340 char *s; 2341 int c; 2342{ 2343 bool ok; 2344 2345 ok = true; 2346 if (s == NULL) 2347 return ok; 2348 while (*s != '\0') 2349 { 2350 if (!(isascii(*s) && isprint(*s))) 2351 { 2352 *s = c; 2353 ok = false; 2354 } 2355 ++s; 2356 } 2357 return ok; 2358} 2359 2360/* 2361** STR2PRT -- convert "unprintable" characters in a string to \oct 2362** 2363** Parameters: 2364** s -- string to convert 2365** 2366** Returns: 2367** converted string. 2368** This is a static local buffer, string must be copied 2369** before this function is called again! 2370*/ 2371 2372char * 2373str2prt(s) 2374 char *s; 2375{ 2376 int l; 2377 char c, *h; 2378 bool ok; 2379 static int len = 0; 2380 static char *buf = NULL; 2381 2382 if (s == NULL) 2383 return NULL; 2384 ok = true; 2385 for (h = s, l = 1; *h != '\0'; h++, l++) 2386 { 2387 if (*h == '\\') 2388 { 2389 ++l; 2390 ok = false; 2391 } 2392 else if (!(isascii(*h) && isprint(*h))) 2393 { 2394 l += 3; 2395 ok = false; 2396 } 2397 } 2398 if (ok) 2399 return s; 2400 if (l > len) 2401 { 2402 char *nbuf = sm_pmalloc_x(l); 2403 2404 if (buf != NULL) 2405 sm_free(buf); 2406 len = l; 2407 buf = nbuf; 2408 } 2409 for (h = buf; *s != '\0' && l > 0; s++, l--) 2410 { 2411 c = *s; 2412 if (isascii(c) && isprint(c) && c != '\\') 2413 { 2414 *h++ = c; 2415 } 2416 else 2417 { 2418 *h++ = '\\'; 2419 --l; 2420 switch (c) 2421 { 2422 case '\\': 2423 *h++ = '\\'; 2424 break; 2425 case '\t': 2426 *h++ = 't'; 2427 break; 2428 case '\n': 2429 *h++ = 'n'; 2430 break; 2431 case '\r': 2432 *h++ = 'r'; 2433 break; 2434 default: 2435 (void) sm_snprintf(h, l, "%03o", 2436 (unsigned int)((unsigned char) c)); 2437 2438 /* 2439 ** XXX since l is unsigned this may 2440 ** wrap around if the calculation is screwed 2441 ** up... 2442 */ 2443 2444 l -= 2; 2445 h += 3; 2446 break; 2447 } 2448 } 2449 } 2450 *h = '\0'; 2451 buf[len - 1] = '\0'; 2452 return buf; 2453} 2454/* 2455** PATH_IS_DIR -- check to see if file exists and is a directory. 2456** 2457** There are some additional checks for security violations in 2458** here. This routine is intended to be used for the host status 2459** support. 2460** 2461** Parameters: 2462** pathname -- pathname to check for directory-ness. 2463** createflag -- if set, create directory if needed. 2464** 2465** Returns: 2466** true -- if the indicated pathname is a directory 2467** false -- otherwise 2468*/ 2469 2470bool 2471path_is_dir(pathname, createflag) 2472 char *pathname; 2473 bool createflag; 2474{ 2475 struct stat statbuf; 2476 2477#if HASLSTAT 2478 if (lstat(pathname, &statbuf) < 0) 2479#else /* HASLSTAT */ 2480 if (stat(pathname, &statbuf) < 0) 2481#endif /* HASLSTAT */ 2482 { 2483 if (errno != ENOENT || !createflag) 2484 return false; 2485 if (mkdir(pathname, 0755) < 0) 2486 return false; 2487 return true; 2488 } 2489 if (!S_ISDIR(statbuf.st_mode)) 2490 { 2491 errno = ENOTDIR; 2492 return false; 2493 } 2494 2495 /* security: don't allow writable directories */ 2496 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2497 { 2498 errno = EACCES; 2499 return false; 2500 } 2501 return true; 2502} 2503/* 2504** PROC_LIST_ADD -- add process id to list of our children 2505** 2506** Parameters: 2507** pid -- pid to add to list. 2508** task -- task of pid. 2509** type -- type of process. 2510** count -- number of processes. 2511** other -- other information for this type. 2512** 2513** Returns: 2514** none 2515** 2516** Side Effects: 2517** May increase CurChildren. May grow ProcList. 2518*/ 2519 2520typedef struct procs PROCS_T; 2521 2522struct procs 2523{ 2524 pid_t proc_pid; 2525 char *proc_task; 2526 int proc_type; 2527 int proc_count; 2528 int proc_other; 2529 SOCKADDR proc_hostaddr; 2530}; 2531 2532static PROCS_T *volatile ProcListVec = NULL; 2533static int ProcListSize = 0; 2534 2535void 2536proc_list_add(pid, task, type, count, other, hostaddr) 2537 pid_t pid; 2538 char *task; 2539 int type; 2540 int count; 2541 int other; 2542 SOCKADDR *hostaddr; 2543{ 2544 int i; 2545 2546 for (i = 0; i < ProcListSize; i++) 2547 { 2548 if (ProcListVec[i].proc_pid == NO_PID) 2549 break; 2550 } 2551 if (i >= ProcListSize) 2552 { 2553 /* probe the existing vector to avoid growing infinitely */ 2554 proc_list_probe(); 2555 2556 /* now scan again */ 2557 for (i = 0; i < ProcListSize; i++) 2558 { 2559 if (ProcListVec[i].proc_pid == NO_PID) 2560 break; 2561 } 2562 } 2563 if (i >= ProcListSize) 2564 { 2565 /* grow process list */ 2566 PROCS_T *npv; 2567 2568 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2569 npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * 2570 (ProcListSize + PROC_LIST_SEG)); 2571 if (ProcListSize > 0) 2572 { 2573 memmove(npv, ProcListVec, 2574 ProcListSize * sizeof (PROCS_T)); 2575 sm_free(ProcListVec); 2576 } 2577 2578 /* XXX just use memset() to initialize this part? */ 2579 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2580 { 2581 npv[i].proc_pid = NO_PID; 2582 npv[i].proc_task = NULL; 2583 npv[i].proc_type = PROC_NONE; 2584 } 2585 i = ProcListSize; 2586 ProcListSize += PROC_LIST_SEG; 2587 ProcListVec = npv; 2588 } 2589 ProcListVec[i].proc_pid = pid; 2590 PSTRSET(ProcListVec[i].proc_task, task); 2591 ProcListVec[i].proc_type = type; 2592 ProcListVec[i].proc_count = count; 2593 ProcListVec[i].proc_other = other; 2594 if (hostaddr != NULL) 2595 ProcListVec[i].proc_hostaddr = *hostaddr; 2596 else 2597 memset(&ProcListVec[i].proc_hostaddr, 0, 2598 sizeof(ProcListVec[i].proc_hostaddr)); 2599 2600 /* if process adding itself, it's not a child */ 2601 if (pid != CurrentPid) 2602 { 2603 SM_ASSERT(CurChildren < INT_MAX); 2604 CurChildren++; 2605 } 2606} 2607/* 2608** PROC_LIST_SET -- set pid task in process list 2609** 2610** Parameters: 2611** pid -- pid to set 2612** task -- task of pid 2613** 2614** Returns: 2615** none. 2616*/ 2617 2618void 2619proc_list_set(pid, task) 2620 pid_t pid; 2621 char *task; 2622{ 2623 int i; 2624 2625 for (i = 0; i < ProcListSize; i++) 2626 { 2627 if (ProcListVec[i].proc_pid == pid) 2628 { 2629 PSTRSET(ProcListVec[i].proc_task, task); 2630 break; 2631 } 2632 } 2633} 2634/* 2635** PROC_LIST_DROP -- drop pid from process list 2636** 2637** Parameters: 2638** pid -- pid to drop 2639** st -- process status 2640** other -- storage for proc_other (return). 2641** 2642** Returns: 2643** none. 2644** 2645** Side Effects: 2646** May decrease CurChildren, CurRunners, or 2647** set RestartRequest or ShutdownRequest. 2648** 2649** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2650** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2651** DOING. 2652*/ 2653 2654void 2655proc_list_drop(pid, st, other) 2656 pid_t pid; 2657 int st; 2658 int *other; 2659{ 2660 int i; 2661 int type = PROC_NONE; 2662 2663 for (i = 0; i < ProcListSize; i++) 2664 { 2665 if (ProcListVec[i].proc_pid == pid) 2666 { 2667 ProcListVec[i].proc_pid = NO_PID; 2668 type = ProcListVec[i].proc_type; 2669 if (other != NULL) 2670 *other = ProcListVec[i].proc_other; 2671 break; 2672 } 2673 } 2674 if (CurChildren > 0) 2675 CurChildren--; 2676 2677 2678 if (type == PROC_CONTROL && WIFEXITED(st)) 2679 { 2680 /* if so, see if we need to restart or shutdown */ 2681 if (WEXITSTATUS(st) == EX_RESTART) 2682 RestartRequest = "control socket"; 2683 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2684 ShutdownRequest = "control socket"; 2685 } 2686 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2687 ProcListVec[i].proc_other > -1) 2688 { 2689 /* restart this persistent runner */ 2690 mark_work_group_restart(ProcListVec[i].proc_other, st); 2691 } 2692 else if (type == PROC_QUEUE) 2693 CurRunners -= ProcListVec[i].proc_count; 2694} 2695/* 2696** PROC_LIST_CLEAR -- clear the process list 2697** 2698** Parameters: 2699** none. 2700** 2701** Returns: 2702** none. 2703** 2704** Side Effects: 2705** Sets CurChildren to zero. 2706*/ 2707 2708void 2709proc_list_clear() 2710{ 2711 int i; 2712 2713 /* start from 1 since 0 is the daemon itself */ 2714 for (i = 1; i < ProcListSize; i++) 2715 ProcListVec[i].proc_pid = NO_PID; 2716 CurChildren = 0; 2717} 2718/* 2719** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2720** 2721** Parameters: 2722** none 2723** 2724** Returns: 2725** none 2726** 2727** Side Effects: 2728** May decrease CurChildren. 2729*/ 2730 2731void 2732proc_list_probe() 2733{ 2734 int i; 2735 2736 /* start from 1 since 0 is the daemon itself */ 2737 for (i = 1; i < ProcListSize; i++) 2738 { 2739 if (ProcListVec[i].proc_pid == NO_PID) 2740 continue; 2741 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2742 { 2743 if (LogLevel > 3) 2744 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2745 "proc_list_probe: lost pid %d", 2746 (int) ProcListVec[i].proc_pid); 2747 ProcListVec[i].proc_pid = NO_PID; 2748 SM_FREE_CLR(ProcListVec[i].proc_task); 2749 CurChildren--; 2750 } 2751 } 2752 if (CurChildren < 0) 2753 CurChildren = 0; 2754} 2755 2756/* 2757** PROC_LIST_DISPLAY -- display the process list 2758** 2759** Parameters: 2760** out -- output file pointer 2761** prefix -- string to output in front of each line. 2762** 2763** Returns: 2764** none. 2765*/ 2766 2767void 2768proc_list_display(out, prefix) 2769 SM_FILE_T *out; 2770 char *prefix; 2771{ 2772 int i; 2773 2774 for (i = 0; i < ProcListSize; i++) 2775 { 2776 if (ProcListVec[i].proc_pid == NO_PID) 2777 continue; 2778 2779 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2780 prefix, 2781 (int) ProcListVec[i].proc_pid, 2782 ProcListVec[i].proc_task != NULL ? 2783 ProcListVec[i].proc_task : "(unknown)", 2784 (OpMode == MD_SMTP || 2785 OpMode == MD_DAEMON || 2786 OpMode == MD_ARPAFTP) ? "\r" : ""); 2787 } 2788} 2789 2790/* 2791** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2792** 2793** Parameters: 2794** type -- type of process to signal 2795** signal -- the type of signal to send 2796** 2797** Results: 2798** none. 2799** 2800** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2801** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2802** DOING. 2803*/ 2804 2805void 2806proc_list_signal(type, signal) 2807 int type; 2808 int signal; 2809{ 2810 int chldwasblocked; 2811 int alrmwasblocked; 2812 int i; 2813 pid_t mypid = getpid(); 2814 2815 /* block these signals so that we may signal cleanly */ 2816 chldwasblocked = sm_blocksignal(SIGCHLD); 2817 alrmwasblocked = sm_blocksignal(SIGALRM); 2818 2819 /* Find all processes of type and send signal */ 2820 for (i = 0; i < ProcListSize; i++) 2821 { 2822 if (ProcListVec[i].proc_pid == NO_PID || 2823 ProcListVec[i].proc_pid == mypid) 2824 continue; 2825 if (ProcListVec[i].proc_type != type) 2826 continue; 2827 (void) kill(ProcListVec[i].proc_pid, signal); 2828 } 2829 2830 /* restore the signals */ 2831 if (alrmwasblocked == 0) 2832 (void) sm_releasesignal(SIGALRM); 2833 if (chldwasblocked == 0) 2834 (void) sm_releasesignal(SIGCHLD); 2835} 2836 2837/* 2838** COUNT_OPEN_CONNECTIONS 2839** 2840** Parameters: 2841** hostaddr - ClientAddress 2842** 2843** Returns: 2844** the number of open connections for this client 2845** 2846*/ 2847 2848int 2849count_open_connections(hostaddr) 2850 SOCKADDR *hostaddr; 2851{ 2852 int i, n; 2853 2854 if (hostaddr == NULL) 2855 return 0; 2856 n = 0; 2857 for (i = 0; i < ProcListSize; i++) 2858 { 2859 if (ProcListVec[i].proc_pid == NO_PID) 2860 continue; 2861 2862 if (hostaddr->sa.sa_family != 2863 ProcListVec[i].proc_hostaddr.sa.sa_family) 2864 continue; 2865#if NETINET 2866 if (hostaddr->sa.sa_family == AF_INET && 2867 (hostaddr->sin.sin_addr.s_addr == 2868 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 2869 n++; 2870#endif /* NETINET */ 2871#if NETINET6 2872 if (hostaddr->sa.sa_family == AF_INET6 && 2873 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 2874 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 2875 n++; 2876#endif /* NETINET6 */ 2877 } 2878 return n; 2879} 2880