util.c revision 71345
1/* 2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14#ifndef lint 15static char id[] = "@(#)$Id: util.c,v 8.225.2.1.2.15 2000/10/18 23:46:07 ca Exp $"; 16#endif /* ! lint */ 17 18#include <sendmail.h> 19#include <sysexits.h> 20 21 22static void readtimeout __P((time_t)); 23 24/* 25** STRIPQUOTES -- Strip quotes & quote bits from a string. 26** 27** Runs through a string and strips off unquoted quote 28** characters and quote bits. This is done in place. 29** 30** Parameters: 31** s -- the string to strip. 32** 33** Returns: 34** none. 35** 36** Side Effects: 37** none. 38*/ 39 40void 41stripquotes(s) 42 char *s; 43{ 44 register char *p; 45 register char *q; 46 register char c; 47 48 if (s == NULL) 49 return; 50 51 p = q = s; 52 do 53 { 54 c = *p++; 55 if (c == '\\') 56 c = *p++; 57 else if (c == '"') 58 continue; 59 *q++ = c; 60 } while (c != '\0'); 61} 62/* 63** ADDQUOTES -- Adds quotes & quote bits to a string. 64** 65** Runs through a string and adds characters and quote bits. 66** 67** Parameters: 68** s -- the string to modify. 69** 70** Returns: 71** pointer to quoted string. 72** 73** Side Effects: 74** none. 75** 76*/ 77 78char * 79addquotes(s) 80 char *s; 81{ 82 int len = 0; 83 char c; 84 char *p = s, *q, *r; 85 86 if (s == NULL) 87 return NULL; 88 89 /* Find length of quoted string */ 90 while ((c = *p++) != '\0') 91 { 92 len++; 93 if (c == '\\' || c == '"') 94 len++; 95 } 96 97 q = r = xalloc(len + 3); 98 p = s; 99 100 /* add leading quote */ 101 *q++ = '"'; 102 while ((c = *p++) != '\0') 103 { 104 /* quote \ or " */ 105 if (c == '\\' || c == '"') 106 *q++ = '\\'; 107 *q++ = c; 108 } 109 *q++ = '"'; 110 *q = '\0'; 111 return r; 112} 113/* 114** RFC822_STRING -- Checks string for proper RFC822 string quoting. 115** 116** Runs through a string and verifies RFC822 special characters 117** are only found inside comments, quoted strings, or backslash 118** escaped. Also verified balanced quotes and parenthesis. 119** 120** Parameters: 121** s -- the string to modify. 122** 123** Returns: 124** TRUE -- if the string is RFC822 compliant. 125** FALSE -- if the string is not RFC822 compliant. 126** 127** Side Effects: 128** none. 129** 130*/ 131 132bool 133rfc822_string(s) 134 char *s; 135{ 136 bool quoted = FALSE; 137 int commentlev = 0; 138 char *c = s; 139 140 if (s == NULL) 141 return FALSE; 142 143 while (*c != '\0') 144 { 145 /* escaped character */ 146 if (*c == '\\') 147 { 148 c++; 149 if (*c == '\0') 150 return FALSE; 151 } 152 else if (commentlev == 0 && *c == '"') 153 quoted = !quoted; 154 else if (!quoted) 155 { 156 if (*c == ')') 157 { 158 /* unbalanced ')' */ 159 if (commentlev == 0) 160 return FALSE; 161 else 162 commentlev--; 163 } 164 else if (*c == '(') 165 commentlev++; 166 else if (commentlev == 0 && 167 strchr(MustQuoteChars, *c) != NULL) 168 return FALSE; 169 } 170 c++; 171 } 172 /* unbalanced '"' or '(' */ 173 if (quoted || commentlev != 0) 174 return FALSE; 175 else 176 return TRUE; 177} 178/* 179** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 180** 181** Arbitrarily shorten (in place) an RFC822 string and rebalance 182** comments and quotes. 183** 184** Parameters: 185** string -- the string to shorten 186** length -- the maximum size, 0 if no maximum 187** 188** Returns: 189** TRUE if string is changed, FALSE otherwise 190** 191** Side Effects: 192** Changes string in place, possibly resulting 193** in a shorter string. 194*/ 195 196bool 197shorten_rfc822_string(string, length) 198 char *string; 199 size_t length; 200{ 201 bool backslash = FALSE; 202 bool modified = FALSE; 203 bool quoted = FALSE; 204 size_t slen; 205 int parencount = 0; 206 char *ptr = string; 207 208 /* 209 ** If have to rebalance an already short enough string, 210 ** need to do it within allocated space. 211 */ 212 213 slen = strlen(string); 214 if (length == 0 || slen < length) 215 length = slen; 216 217 while (*ptr != '\0') 218 { 219 if (backslash) 220 { 221 backslash = FALSE; 222 goto increment; 223 } 224 225 if (*ptr == '\\') 226 backslash = TRUE; 227 else if (*ptr == '(') 228 { 229 if (!quoted) 230 parencount++; 231 } 232 else if (*ptr == ')') 233 { 234 if (--parencount < 0) 235 parencount = 0; 236 } 237 238 /* Inside a comment, quotes don't matter */ 239 if (parencount <= 0 && *ptr == '"') 240 quoted = !quoted; 241 242increment: 243 /* Check for sufficient space for next character */ 244 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 245 parencount + 246 (quoted ? 1 : 0))) 247 { 248 /* Not enough, backtrack */ 249 if (*ptr == '\\') 250 backslash = FALSE; 251 else if (*ptr == '(' && !quoted) 252 parencount--; 253 else if (*ptr == '"' && parencount == 0) 254 quoted = FALSE; 255 break; 256 } 257 ptr++; 258 } 259 260 /* Rebalance */ 261 while (parencount-- > 0) 262 { 263 if (*ptr != ')') 264 { 265 modified = TRUE; 266 *ptr = ')'; 267 } 268 ptr++; 269 } 270 if (quoted) 271 { 272 if (*ptr != '"') 273 { 274 modified = TRUE; 275 *ptr = '"'; 276 } 277 ptr++; 278 } 279 if (*ptr != '\0') 280 { 281 modified = TRUE; 282 *ptr = '\0'; 283 } 284 return modified; 285} 286/* 287** FIND_CHARACTER -- find an unquoted character in an RFC822 string 288** 289** Find an unquoted, non-commented character in an RFC822 290** string and return a pointer to its location in the 291** string. 292** 293** Parameters: 294** string -- the string to search 295** character -- the character to find 296** 297** Returns: 298** pointer to the character, or 299** a pointer to the end of the line if character is not found 300*/ 301 302char * 303find_character(string, character) 304 char *string; 305 int character; 306{ 307 bool backslash = FALSE; 308 bool quoted = FALSE; 309 int parencount = 0; 310 311 while (string != NULL && *string != '\0') 312 { 313 if (backslash) 314 { 315 backslash = FALSE; 316 if (!quoted && character == '\\' && *string == '\\') 317 break; 318 string++; 319 continue; 320 } 321 switch (*string) 322 { 323 case '\\': 324 backslash = TRUE; 325 break; 326 327 case '(': 328 if (!quoted) 329 parencount++; 330 break; 331 332 case ')': 333 if (--parencount < 0) 334 parencount = 0; 335 break; 336 } 337 338 /* Inside a comment, nothing matters */ 339 if (parencount > 0) 340 { 341 string++; 342 continue; 343 } 344 345 if (*string == '"') 346 quoted = !quoted; 347 else if (*string == character && !quoted) 348 break; 349 string++; 350 } 351 352 /* Return pointer to the character */ 353 return string; 354} 355/* 356** XALLOC -- Allocate memory and bitch wildly on failure. 357** 358** THIS IS A CLUDGE. This should be made to give a proper 359** error -- but after all, what can we do? 360** 361** Parameters: 362** sz -- size of area to allocate. 363** 364** Returns: 365** pointer to data region. 366** 367** Side Effects: 368** Memory is allocated. 369*/ 370 371char * 372xalloc(sz) 373 register int sz; 374{ 375 register char *p; 376 377 /* some systems can't handle size zero mallocs */ 378 if (sz <= 0) 379 sz = 1; 380 381 p = malloc((unsigned) sz); 382 if (p == NULL) 383 { 384 syserr("!Out of memory!!"); 385 /* exit(EX_UNAVAILABLE); */ 386 } 387 return p; 388} 389/* 390** COPYPLIST -- copy list of pointers. 391** 392** This routine is the equivalent of newstr for lists of 393** pointers. 394** 395** Parameters: 396** list -- list of pointers to copy. 397** Must be NULL terminated. 398** copycont -- if TRUE, copy the contents of the vector 399** (which must be a string) also. 400** 401** Returns: 402** a copy of 'list'. 403** 404** Side Effects: 405** none. 406*/ 407 408char ** 409copyplist(list, copycont) 410 char **list; 411 bool copycont; 412{ 413 register char **vp; 414 register char **newvp; 415 416 for (vp = list; *vp != NULL; vp++) 417 continue; 418 419 vp++; 420 421 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 422 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp); 423 424 if (copycont) 425 { 426 for (vp = newvp; *vp != NULL; vp++) 427 *vp = newstr(*vp); 428 } 429 430 return newvp; 431} 432/* 433** COPYQUEUE -- copy address queue. 434** 435** This routine is the equivalent of newstr for address queues 436** addresses marked as QS_IS_DEAD() aren't copied 437** 438** Parameters: 439** addr -- list of address structures to copy. 440** 441** Returns: 442** a copy of 'addr'. 443** 444** Side Effects: 445** none. 446*/ 447 448ADDRESS * 449copyqueue(addr) 450 ADDRESS *addr; 451{ 452 register ADDRESS *newaddr; 453 ADDRESS *ret; 454 register ADDRESS **tail = &ret; 455 456 while (addr != NULL) 457 { 458 if (!QS_IS_DEAD(addr->q_state)) 459 { 460 newaddr = (ADDRESS *) xalloc(sizeof *newaddr); 461 STRUCTCOPY(*addr, *newaddr); 462 *tail = newaddr; 463 tail = &newaddr->q_next; 464 } 465 addr = addr->q_next; 466 } 467 *tail = NULL; 468 469 return ret; 470} 471/* 472** LOG_SENDMAIL_PID -- record sendmail pid and command line. 473** 474** Parameters: 475** e -- the current envelope. 476** 477** Returns: 478** none. 479** 480** Side Effects: 481** writes pidfile. 482*/ 483 484void 485log_sendmail_pid(e) 486 ENVELOPE *e; 487{ 488 long sff; 489 FILE *pidf; 490 char pidpath[MAXPATHLEN + 1]; 491 492 /* write the pid to the log file for posterity */ 493 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 494 if (TrustedUid != 0 && RealUid == TrustedUid) 495 sff |= SFF_OPENASROOT; 496 expand(PidFile, pidpath, sizeof pidpath, e); 497 pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, 0644, sff); 498 if (pidf == NULL) 499 { 500 sm_syslog(LOG_ERR, NOQID, "unable to write %s", pidpath); 501 } 502 else 503 { 504 long pid; 505 extern char *CommandLineArgs; 506 507 pid = (long) getpid(); 508 509 /* write the process id on line 1 */ 510 fprintf(pidf, "%ld\n", pid); 511 512 /* line 2 contains all command line flags */ 513 fprintf(pidf, "%s\n", CommandLineArgs); 514 515 /* flush and close */ 516 (void) fclose(pidf); 517 } 518} 519/* 520** SET_DELIVERY_MODE -- set and record the delivery mode 521** 522** Parameters: 523** mode -- delivery mode 524** e -- the current envelope. 525** 526** Returns: 527** none. 528** 529** Side Effects: 530** sets $&{deliveryMode} macro 531*/ 532 533void 534set_delivery_mode(mode, e) 535 int mode; 536 ENVELOPE *e; 537{ 538 char buf[2]; 539 540 e->e_sendmode = (char)mode; 541 buf[0] = (char)mode; 542 buf[1] = '\0'; 543 define(macid("{deliveryMode}", NULL), newstr(buf), e); 544} 545/* 546** PRINTAV -- print argument vector. 547** 548** Parameters: 549** av -- argument vector. 550** 551** Returns: 552** none. 553** 554** Side Effects: 555** prints av. 556*/ 557 558void 559printav(av) 560 register char **av; 561{ 562 while (*av != NULL) 563 { 564 if (tTd(0, 44)) 565 dprintf("\n\t%08lx=", (u_long) *av); 566 else 567 (void) putchar(' '); 568 xputs(*av++); 569 } 570 (void) putchar('\n'); 571} 572/* 573** LOWER -- turn letter into lower case. 574** 575** Parameters: 576** c -- character to turn into lower case. 577** 578** Returns: 579** c, in lower case. 580** 581** Side Effects: 582** none. 583*/ 584 585char 586lower(c) 587 register int c; 588{ 589 return ((isascii(c) && isupper(c)) ? tolower(c) : c); 590} 591/* 592** XPUTS -- put string doing control escapes. 593** 594** Parameters: 595** s -- string to put. 596** 597** Returns: 598** none. 599** 600** Side Effects: 601** output to stdout 602*/ 603 604void 605xputs(s) 606 register const char *s; 607{ 608 register int c; 609 register struct metamac *mp; 610 bool shiftout = FALSE; 611 extern struct metamac MetaMacros[]; 612 613 if (s == NULL) 614 { 615 printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off); 616 return; 617 } 618 while ((c = (*s++ & 0377)) != '\0') 619 { 620 if (shiftout) 621 { 622 printf("%s", TermEscape.te_rv_off); 623 shiftout = FALSE; 624 } 625 if (!isascii(c)) 626 { 627 if (c == MATCHREPL) 628 { 629 printf("%s$", TermEscape.te_rv_on); 630 shiftout = TRUE; 631 if (*s == '\0') 632 continue; 633 c = *s++ & 0377; 634 goto printchar; 635 } 636 if (c == MACROEXPAND || c == MACRODEXPAND) 637 { 638 printf("%s$", TermEscape.te_rv_on); 639 if (c == MACRODEXPAND) 640 (void) putchar('&'); 641 shiftout = TRUE; 642 if (*s == '\0') 643 continue; 644 if (strchr("=~&?", *s) != NULL) 645 (void) putchar(*s++); 646 if (bitset(0200, *s)) 647 printf("{%s}", macname(bitidx(*s++))); 648 else 649 printf("%c", *s++); 650 continue; 651 } 652 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 653 { 654 if ((mp->metaval & 0377) == c) 655 { 656 printf("%s$%c", 657 TermEscape.te_rv_on, 658 mp->metaname); 659 shiftout = TRUE; 660 break; 661 } 662 } 663 if (c == MATCHCLASS || c == MATCHNCLASS) 664 { 665 if (bitset(0200, *s)) 666 printf("{%s}", macname(*s++ & 0377)); 667 else if (*s != '\0') 668 printf("%c", *s++); 669 } 670 if (mp->metaname != '\0') 671 continue; 672 673 /* unrecognized meta character */ 674 printf("%sM-", TermEscape.te_rv_on); 675 shiftout = TRUE; 676 c &= 0177; 677 } 678 printchar: 679 if (isprint(c)) 680 { 681 (void) putchar(c); 682 continue; 683 } 684 685 /* wasn't a meta-macro -- find another way to print it */ 686 switch (c) 687 { 688 case '\n': 689 c = 'n'; 690 break; 691 692 case '\r': 693 c = 'r'; 694 break; 695 696 case '\t': 697 c = 't'; 698 break; 699 } 700 if (!shiftout) 701 { 702 printf("%s", TermEscape.te_rv_on); 703 shiftout = TRUE; 704 } 705 if (isprint(c)) 706 { 707 (void) putchar('\\'); 708 (void) putchar(c); 709 } 710 else 711 { 712 (void) putchar('^'); 713 (void) putchar(c ^ 0100); 714 } 715 } 716 if (shiftout) 717 printf("%s", TermEscape.te_rv_off); 718 (void) fflush(stdout); 719} 720/* 721** MAKELOWER -- Translate a line into lower case 722** 723** Parameters: 724** p -- the string to translate. If NULL, return is 725** immediate. 726** 727** Returns: 728** none. 729** 730** Side Effects: 731** String pointed to by p is translated to lower case. 732*/ 733 734void 735makelower(p) 736 register char *p; 737{ 738 register char c; 739 740 if (p == NULL) 741 return; 742 for (; (c = *p) != '\0'; p++) 743 if (isascii(c) && isupper(c)) 744 *p = tolower(c); 745} 746/* 747** BUILDFNAME -- build full name from gecos style entry. 748** 749** This routine interprets the strange entry that would appear 750** in the GECOS field of the password file. 751** 752** Parameters: 753** p -- name to build. 754** user -- the login name of this user (for &). 755** buf -- place to put the result. 756** buflen -- length of buf. 757** 758** Returns: 759** none. 760** 761** Side Effects: 762** none. 763*/ 764 765void 766buildfname(gecos, user, buf, buflen) 767 register char *gecos; 768 char *user; 769 char *buf; 770 int buflen; 771{ 772 register char *p; 773 register char *bp = buf; 774 775 if (*gecos == '*') 776 gecos++; 777 778 /* copy gecos, interpolating & to be full name */ 779 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 780 { 781 if (bp >= &buf[buflen - 1]) 782 { 783 /* buffer overflow -- just use login name */ 784 snprintf(buf, buflen, "%s", user); 785 return; 786 } 787 if (*p == '&') 788 { 789 /* interpolate full name */ 790 snprintf(bp, buflen - (bp - buf), "%s", user); 791 *bp = toupper(*bp); 792 bp += strlen(bp); 793 } 794 else 795 *bp++ = *p; 796 } 797 *bp = '\0'; 798} 799/* 800** FIXCRLF -- fix <CR><LF> in line. 801** 802** Looks for the <CR><LF> combination and turns it into the 803** UNIX canonical <NL> character. It only takes one line, 804** i.e., it is assumed that the first <NL> found is the end 805** of the line. 806** 807** Parameters: 808** line -- the line to fix. 809** stripnl -- if true, strip the newline also. 810** 811** Returns: 812** none. 813** 814** Side Effects: 815** line is changed in place. 816*/ 817 818void 819fixcrlf(line, stripnl) 820 char *line; 821 bool stripnl; 822{ 823 register char *p; 824 825 p = strchr(line, '\n'); 826 if (p == NULL) 827 return; 828 if (p > line && p[-1] == '\r') 829 p--; 830 if (!stripnl) 831 *p++ = '\n'; 832 *p = '\0'; 833} 834/* 835** PUTLINE -- put a line like fputs obeying SMTP conventions 836** 837** This routine always guarantees outputing a newline (or CRLF, 838** as appropriate) at the end of the string. 839** 840** Parameters: 841** l -- line to put. 842** mci -- the mailer connection information. 843** 844** Returns: 845** none 846** 847** Side Effects: 848** output of l to fp. 849*/ 850 851void 852putline(l, mci) 853 register char *l; 854 register MCI *mci; 855{ 856 putxline(l, strlen(l), mci, PXLF_MAPFROM); 857} 858/* 859** PUTXLINE -- putline with flags bits. 860** 861** This routine always guarantees outputing a newline (or CRLF, 862** as appropriate) at the end of the string. 863** 864** Parameters: 865** l -- line to put. 866** len -- the length of the line. 867** mci -- the mailer connection information. 868** pxflags -- flag bits: 869** PXLF_MAPFROM -- map From_ to >From_. 870** PXLF_STRIP8BIT -- strip 8th bit. 871** PXLF_HEADER -- map bare newline in header to newline space. 872** 873** Returns: 874** none 875** 876** Side Effects: 877** output of l to fp. 878*/ 879 880void 881putxline(l, len, mci, pxflags) 882 register char *l; 883 size_t len; 884 register MCI *mci; 885 int pxflags; 886{ 887 bool dead = FALSE; 888 register char *p, *end; 889 int slop = 0; 890 891 /* strip out 0200 bits -- these can look like TELNET protocol */ 892 if (bitset(MCIF_7BIT, mci->mci_flags) || 893 bitset(PXLF_STRIP8BIT, pxflags)) 894 { 895 register char svchar; 896 897 for (p = l; (svchar = *p) != '\0'; ++p) 898 if (bitset(0200, svchar)) 899 *p = svchar &~ 0200; 900 } 901 902 end = l + len; 903 do 904 { 905 /* find the end of the line */ 906 p = memchr(l, '\n', end - l); 907 if (p == NULL) 908 p = end; 909 910 if (TrafficLogFile != NULL) 911 fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); 912 913 /* check for line overflow */ 914 while (mci->mci_mailer->m_linelimit > 0 && 915 (p - l + slop) > mci->mci_mailer->m_linelimit) 916 { 917 char *l_base = l; 918 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 919 920 if (l[0] == '.' && slop == 0 && 921 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 922 { 923 if (putc('.', mci->mci_out) == EOF) 924 dead = TRUE; 925 else 926 { 927 /* record progress for DATA timeout */ 928 DataProgress = TRUE; 929 } 930 if (TrafficLogFile != NULL) 931 (void) putc('.', TrafficLogFile); 932 } 933 else if (l[0] == 'F' && slop == 0 && 934 bitset(PXLF_MAPFROM, pxflags) && 935 strncmp(l, "From ", 5) == 0 && 936 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 937 { 938 if (putc('>', mci->mci_out) == EOF) 939 dead = TRUE; 940 else 941 { 942 /* record progress for DATA timeout */ 943 DataProgress = TRUE; 944 } 945 if (TrafficLogFile != NULL) 946 (void) putc('>', TrafficLogFile); 947 } 948 if (dead) 949 break; 950 951 while (l < q) 952 { 953 if (putc((unsigned char) *l++, mci->mci_out) == 954 EOF) 955 { 956 dead = TRUE; 957 break; 958 } 959 else 960 { 961 /* record progress for DATA timeout */ 962 DataProgress = TRUE; 963 } 964 } 965 if (dead) 966 break; 967 968 if (putc('!', mci->mci_out) == EOF || 969 fputs(mci->mci_mailer->m_eol, 970 mci->mci_out) == EOF || 971 putc(' ', mci->mci_out) == EOF) 972 { 973 dead = TRUE; 974 break; 975 } 976 else 977 { 978 /* record progress for DATA timeout */ 979 DataProgress = TRUE; 980 } 981 if (TrafficLogFile != NULL) 982 { 983 for (l = l_base; l < q; l++) 984 (void) putc((unsigned char)*l, 985 TrafficLogFile); 986 fprintf(TrafficLogFile, "!\n%05d >>> ", 987 (int) getpid()); 988 } 989 slop = 1; 990 } 991 992 if (dead) 993 break; 994 995 /* output last part */ 996 if (l[0] == '.' && slop == 0 && 997 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 998 { 999 if (putc('.', mci->mci_out) == EOF) 1000 break; 1001 else 1002 { 1003 /* record progress for DATA timeout */ 1004 DataProgress = TRUE; 1005 } 1006 if (TrafficLogFile != NULL) 1007 (void) putc('.', TrafficLogFile); 1008 } 1009 else if (l[0] == 'F' && slop == 0 && 1010 bitset(PXLF_MAPFROM, pxflags) && 1011 strncmp(l, "From ", 5) == 0 && 1012 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1013 { 1014 if (putc('>', mci->mci_out) == EOF) 1015 break; 1016 else 1017 { 1018 /* record progress for DATA timeout */ 1019 DataProgress = TRUE; 1020 } 1021 if (TrafficLogFile != NULL) 1022 (void) putc('>', TrafficLogFile); 1023 } 1024 for ( ; l < p; ++l) 1025 { 1026 if (TrafficLogFile != NULL) 1027 (void) putc((unsigned char)*l, TrafficLogFile); 1028 if (putc((unsigned char) *l, mci->mci_out) == EOF) 1029 { 1030 dead = TRUE; 1031 break; 1032 } 1033 else 1034 { 1035 /* record progress for DATA timeout */ 1036 DataProgress = TRUE; 1037 } 1038 } 1039 if (dead) 1040 break; 1041 1042 if (TrafficLogFile != NULL) 1043 (void) putc('\n', TrafficLogFile); 1044 if (fputs(mci->mci_mailer->m_eol, mci->mci_out) == EOF) 1045 break; 1046 else 1047 { 1048 /* record progress for DATA timeout */ 1049 DataProgress = TRUE; 1050 } 1051 if (l < end && *l == '\n') 1052 { 1053 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1054 bitset(PXLF_HEADER, pxflags)) 1055 { 1056 if (putc(' ', mci->mci_out) == EOF) 1057 break; 1058 else 1059 { 1060 /* record progress for DATA timeout */ 1061 DataProgress = TRUE; 1062 } 1063 if (TrafficLogFile != NULL) 1064 (void) putc(' ', TrafficLogFile); 1065 } 1066 } 1067 } while (l < end); 1068} 1069/* 1070** XUNLINK -- unlink a file, doing logging as appropriate. 1071** 1072** Parameters: 1073** f -- name of file to unlink. 1074** 1075** Returns: 1076** none. 1077** 1078** Side Effects: 1079** f is unlinked. 1080*/ 1081 1082void 1083xunlink(f) 1084 char *f; 1085{ 1086 register int i; 1087 1088 if (LogLevel > 98) 1089 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1090 "unlink %s", 1091 f); 1092 1093 i = unlink(f); 1094 if (i < 0 && LogLevel > 97) 1095 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1096 "%s: unlink-fail %d", 1097 f, errno); 1098} 1099/* 1100** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1101** 1102** Parameters: 1103** buf -- place to put the input line. 1104** siz -- size of buf. 1105** fp -- file to read from. 1106** timeout -- the timeout before error occurs. 1107** during -- what we are trying to read (for error messages). 1108** 1109** Returns: 1110** NULL on error (including timeout). This will also leave 1111** buf containing a null string. 1112** buf otherwise. 1113** 1114** Side Effects: 1115** none. 1116*/ 1117 1118 1119static jmp_buf CtxReadTimeout; 1120 1121char * 1122sfgets(buf, siz, fp, timeout, during) 1123 char *buf; 1124 int siz; 1125 FILE *fp; 1126 time_t timeout; 1127 char *during; 1128{ 1129 register EVENT *ev = NULL; 1130 register char *p; 1131 int save_errno; 1132 1133 if (fp == NULL) 1134 { 1135 buf[0] = '\0'; 1136 return NULL; 1137 } 1138 1139 /* set the timeout */ 1140 if (timeout != 0) 1141 { 1142 if (setjmp(CtxReadTimeout) != 0) 1143 { 1144 if (LogLevel > 1) 1145 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1146 "timeout waiting for input from %.100s during %s", 1147 CurHostName ? CurHostName : "local", 1148 during); 1149 buf[0] = '\0'; 1150#if XDEBUG 1151 checkfd012(during); 1152#endif /* XDEBUG */ 1153 if (TrafficLogFile != NULL) 1154 fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", 1155 (int) getpid()); 1156 errno = 0; 1157 return NULL; 1158 } 1159 ev = setevent(timeout, readtimeout, 0); 1160 } 1161 1162 /* try to read */ 1163 p = NULL; 1164 errno = 0; 1165 while (!feof(fp) && !ferror(fp)) 1166 { 1167 errno = 0; 1168 p = fgets(buf, siz, fp); 1169 if (p != NULL || errno != EINTR) 1170 break; 1171 clearerr(fp); 1172 } 1173 save_errno = errno; 1174 1175 /* clear the event if it has not sprung */ 1176 clrevent(ev); 1177 1178 /* clean up the books and exit */ 1179 LineNumber++; 1180 if (p == NULL) 1181 { 1182 buf[0] = '\0'; 1183 if (TrafficLogFile != NULL) 1184 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); 1185 errno = save_errno; 1186 return NULL; 1187 } 1188 if (TrafficLogFile != NULL) 1189 fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf); 1190 if (SevenBitInput) 1191 { 1192 for (p = buf; *p != '\0'; p++) 1193 *p &= ~0200; 1194 } 1195 else if (!HasEightBits) 1196 { 1197 for (p = buf; *p != '\0'; p++) 1198 { 1199 if (bitset(0200, *p)) 1200 { 1201 HasEightBits = TRUE; 1202 break; 1203 } 1204 } 1205 } 1206 return buf; 1207} 1208 1209/* ARGSUSED */ 1210static void 1211readtimeout(timeout) 1212 time_t timeout; 1213{ 1214 longjmp(CtxReadTimeout, 1); 1215} 1216/* 1217** FGETFOLDED -- like fgets, but know about folded lines. 1218** 1219** Parameters: 1220** buf -- place to put result. 1221** n -- bytes available. 1222** f -- file to read from. 1223** 1224** Returns: 1225** input line(s) on success, NULL on error or EOF. 1226** This will normally be buf -- unless the line is too 1227** long, when it will be xalloc()ed. 1228** 1229** Side Effects: 1230** buf gets lines from f, with continuation lines (lines 1231** with leading white space) appended. CRLF's are mapped 1232** into single newlines. Any trailing NL is stripped. 1233*/ 1234 1235char * 1236fgetfolded(buf, n, f) 1237 char *buf; 1238 register int n; 1239 FILE *f; 1240{ 1241 register char *p = buf; 1242 char *bp = buf; 1243 register int i; 1244 1245 n--; 1246 while ((i = getc(f)) != EOF) 1247 { 1248 if (i == '\r') 1249 { 1250 i = getc(f); 1251 if (i != '\n') 1252 { 1253 if (i != EOF) 1254 (void) ungetc(i, f); 1255 i = '\r'; 1256 } 1257 } 1258 if (--n <= 0) 1259 { 1260 /* allocate new space */ 1261 char *nbp; 1262 int nn; 1263 1264 nn = (p - bp); 1265 if (nn < MEMCHUNKSIZE) 1266 nn *= 2; 1267 else 1268 nn += MEMCHUNKSIZE; 1269 nbp = xalloc(nn); 1270 memmove(nbp, bp, p - bp); 1271 p = &nbp[p - bp]; 1272 if (bp != buf) 1273 free(bp); 1274 bp = nbp; 1275 n = nn - (p - bp); 1276 } 1277 *p++ = i; 1278 if (i == '\n') 1279 { 1280 LineNumber++; 1281 i = getc(f); 1282 if (i != EOF) 1283 (void) ungetc(i, f); 1284 if (i != ' ' && i != '\t') 1285 break; 1286 } 1287 } 1288 if (p == bp) 1289 return NULL; 1290 if (p[-1] == '\n') 1291 p--; 1292 *p = '\0'; 1293 return bp; 1294} 1295/* 1296** CURTIME -- return current time. 1297** 1298** Parameters: 1299** none. 1300** 1301** Returns: 1302** the current time. 1303** 1304** Side Effects: 1305** none. 1306*/ 1307 1308time_t 1309curtime() 1310{ 1311 auto time_t t; 1312 1313 (void) time(&t); 1314 return t; 1315} 1316/* 1317** ATOBOOL -- convert a string representation to boolean. 1318** 1319** Defaults to "TRUE" 1320** 1321** Parameters: 1322** s -- string to convert. Takes "tTyY" as true, 1323** others as false. 1324** 1325** Returns: 1326** A boolean representation of the string. 1327** 1328** Side Effects: 1329** none. 1330*/ 1331 1332bool 1333atobool(s) 1334 register char *s; 1335{ 1336 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1337 return TRUE; 1338 return FALSE; 1339} 1340/* 1341** ATOOCT -- convert a string representation to octal. 1342** 1343** Parameters: 1344** s -- string to convert. 1345** 1346** Returns: 1347** An integer representing the string interpreted as an 1348** octal number. 1349** 1350** Side Effects: 1351** none. 1352*/ 1353 1354int 1355atooct(s) 1356 register char *s; 1357{ 1358 register int i = 0; 1359 1360 while (*s >= '0' && *s <= '7') 1361 i = (i << 3) | (*s++ - '0'); 1362 return i; 1363} 1364/* 1365** BITINTERSECT -- tell if two bitmaps intersect 1366** 1367** Parameters: 1368** a, b -- the bitmaps in question 1369** 1370** Returns: 1371** TRUE if they have a non-null intersection 1372** FALSE otherwise 1373** 1374** Side Effects: 1375** none. 1376*/ 1377 1378bool 1379bitintersect(a, b) 1380 BITMAP256 a; 1381 BITMAP256 b; 1382{ 1383 int i; 1384 1385 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1386 { 1387 if ((a[i] & b[i]) != 0) 1388 return TRUE; 1389 } 1390 return FALSE; 1391} 1392/* 1393** BITZEROP -- tell if a bitmap is all zero 1394** 1395** Parameters: 1396** map -- the bit map to check 1397** 1398** Returns: 1399** TRUE if map is all zero. 1400** FALSE if there are any bits set in map. 1401** 1402** Side Effects: 1403** none. 1404*/ 1405 1406bool 1407bitzerop(map) 1408 BITMAP256 map; 1409{ 1410 int i; 1411 1412 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1413 { 1414 if (map[i] != 0) 1415 return FALSE; 1416 } 1417 return TRUE; 1418} 1419/* 1420** STRCONTAINEDIN -- tell if one string is contained in another 1421** 1422** Parameters: 1423** a -- possible substring. 1424** b -- possible superstring. 1425** 1426** Returns: 1427** TRUE if a is contained in b. 1428** FALSE otherwise. 1429*/ 1430 1431bool 1432strcontainedin(a, b) 1433 register char *a; 1434 register char *b; 1435{ 1436 int la; 1437 int lb; 1438 int c; 1439 1440 la = strlen(a); 1441 lb = strlen(b); 1442 c = *a; 1443 if (isascii(c) && isupper(c)) 1444 c = tolower(c); 1445 for (; lb-- >= la; b++) 1446 { 1447 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1448 continue; 1449 if (strncasecmp(a, b, la) == 0) 1450 return TRUE; 1451 } 1452 return FALSE; 1453} 1454/* 1455** CHECKFD012 -- check low numbered file descriptors 1456** 1457** File descriptors 0, 1, and 2 should be open at all times. 1458** This routine verifies that, and fixes it if not true. 1459** 1460** Parameters: 1461** where -- a tag printed if the assertion failed 1462** 1463** Returns: 1464** none 1465*/ 1466 1467void 1468checkfd012(where) 1469 char *where; 1470{ 1471#if XDEBUG 1472 register int i; 1473 1474 for (i = 0; i < 3; i++) 1475 fill_fd(i, where); 1476#endif /* XDEBUG */ 1477} 1478/* 1479** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1480** 1481** Parameters: 1482** fd -- file descriptor to check. 1483** where -- tag to print on failure. 1484** 1485** Returns: 1486** none. 1487*/ 1488 1489void 1490checkfdopen(fd, where) 1491 int fd; 1492 char *where; 1493{ 1494#if XDEBUG 1495 struct stat st; 1496 1497 if (fstat(fd, &st) < 0 && errno == EBADF) 1498 { 1499 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1500 printopenfds(TRUE); 1501 } 1502#endif /* XDEBUG */ 1503} 1504/* 1505** CHECKFDS -- check for new or missing file descriptors 1506** 1507** Parameters: 1508** where -- tag for printing. If null, take a base line. 1509** 1510** Returns: 1511** none 1512** 1513** Side Effects: 1514** If where is set, shows changes since the last call. 1515*/ 1516 1517void 1518checkfds(where) 1519 char *where; 1520{ 1521 int maxfd; 1522 register int fd; 1523 bool printhdr = TRUE; 1524 int save_errno = errno; 1525 static BITMAP256 baseline; 1526 extern int DtableSize; 1527 1528 if (DtableSize > BITMAPBITS) 1529 maxfd = BITMAPBITS; 1530 else 1531 maxfd = DtableSize; 1532 if (where == NULL) 1533 clrbitmap(baseline); 1534 1535 for (fd = 0; fd < maxfd; fd++) 1536 { 1537 struct stat stbuf; 1538 1539 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1540 { 1541 if (!bitnset(fd, baseline)) 1542 continue; 1543 clrbitn(fd, baseline); 1544 } 1545 else if (!bitnset(fd, baseline)) 1546 setbitn(fd, baseline); 1547 else 1548 continue; 1549 1550 /* file state has changed */ 1551 if (where == NULL) 1552 continue; 1553 if (printhdr) 1554 { 1555 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1556 "%s: changed fds:", 1557 where); 1558 printhdr = FALSE; 1559 } 1560 dumpfd(fd, TRUE, TRUE); 1561 } 1562 errno = save_errno; 1563} 1564/* 1565** PRINTOPENFDS -- print the open file descriptors (for debugging) 1566** 1567** Parameters: 1568** logit -- if set, send output to syslog; otherwise 1569** print for debugging. 1570** 1571** Returns: 1572** none. 1573*/ 1574 1575#if NETINET || NETINET6 1576# include <arpa/inet.h> 1577#endif /* NETINET || NETINET6 */ 1578 1579void 1580printopenfds(logit) 1581 bool logit; 1582{ 1583 register int fd; 1584 extern int DtableSize; 1585 1586 for (fd = 0; fd < DtableSize; fd++) 1587 dumpfd(fd, FALSE, logit); 1588} 1589/* 1590** DUMPFD -- dump a file descriptor 1591** 1592** Parameters: 1593** fd -- the file descriptor to dump. 1594** printclosed -- if set, print a notification even if 1595** it is closed; otherwise print nothing. 1596** logit -- if set, send output to syslog instead of stdout. 1597*/ 1598 1599void 1600dumpfd(fd, printclosed, logit) 1601 int fd; 1602 bool printclosed; 1603 bool logit; 1604{ 1605 register char *p; 1606 char *hp; 1607#ifdef S_IFSOCK 1608 SOCKADDR sa; 1609#endif /* S_IFSOCK */ 1610 auto SOCKADDR_LEN_T slen; 1611 int i; 1612#if STAT64 > 0 1613 struct stat64 st; 1614#else /* STAT64 > 0 */ 1615 struct stat st; 1616#endif /* STAT64 > 0 */ 1617 char buf[200]; 1618 1619 p = buf; 1620 snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1621 p += strlen(p); 1622 1623 if ( 1624#if STAT64 > 0 1625 fstat64(fd, &st) 1626#else /* STAT64 > 0 */ 1627 fstat(fd, &st) 1628#endif /* STAT64 > 0 */ 1629 < 0) 1630 { 1631 if (errno != EBADF) 1632 { 1633 snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", 1634 errstring(errno)); 1635 goto printit; 1636 } 1637 else if (printclosed) 1638 { 1639 snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1640 goto printit; 1641 } 1642 return; 1643 } 1644 1645 i = fcntl(fd, F_GETFL, NULL); 1646 if (i != -1) 1647 { 1648 snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1649 p += strlen(p); 1650 } 1651 1652 snprintf(p, SPACELEFT(buf, p), "mode=%o: ", (int) st.st_mode); 1653 p += strlen(p); 1654 switch (st.st_mode & S_IFMT) 1655 { 1656#ifdef S_IFSOCK 1657 case S_IFSOCK: 1658 snprintf(p, SPACELEFT(buf, p), "SOCK "); 1659 p += strlen(p); 1660 memset(&sa, '\0', sizeof sa); 1661 slen = sizeof sa; 1662 if (getsockname(fd, &sa.sa, &slen) < 0) 1663 snprintf(p, SPACELEFT(buf, p), "(%s)", 1664 errstring(errno)); 1665 else 1666 { 1667 hp = hostnamebyanyaddr(&sa); 1668 if (hp == NULL) 1669 { 1670 /* EMPTY */ 1671 /* do nothing */ 1672 } 1673# if NETINET 1674 else if (sa.sa.sa_family == AF_INET) 1675 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1676 hp, ntohs(sa.sin.sin_port)); 1677# endif /* NETINET */ 1678# if NETINET6 1679 else if (sa.sa.sa_family == AF_INET6) 1680 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1681 hp, ntohs(sa.sin6.sin6_port)); 1682# endif /* NETINET6 */ 1683 else 1684 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1685 } 1686 p += strlen(p); 1687 snprintf(p, SPACELEFT(buf, p), "->"); 1688 p += strlen(p); 1689 slen = sizeof sa; 1690 if (getpeername(fd, &sa.sa, &slen) < 0) 1691 snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); 1692 else 1693 { 1694 hp = hostnamebyanyaddr(&sa); 1695 if (hp == NULL) 1696 { 1697 /* EMPTY */ 1698 /* do nothing */ 1699 } 1700# if NETINET 1701 else if (sa.sa.sa_family == AF_INET) 1702 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1703 hp, ntohs(sa.sin.sin_port)); 1704# endif /* NETINET */ 1705# if NETINET6 1706 else if (sa.sa.sa_family == AF_INET6) 1707 snprintf(p, SPACELEFT(buf, p), "%s/%d", 1708 hp, ntohs(sa.sin6.sin6_port)); 1709# endif /* NETINET6 */ 1710 else 1711 snprintf(p, SPACELEFT(buf, p), "%s", hp); 1712 } 1713 break; 1714#endif /* S_IFSOCK */ 1715 1716 case S_IFCHR: 1717 snprintf(p, SPACELEFT(buf, p), "CHR: "); 1718 p += strlen(p); 1719 goto defprint; 1720 1721 case S_IFBLK: 1722 snprintf(p, SPACELEFT(buf, p), "BLK: "); 1723 p += strlen(p); 1724 goto defprint; 1725 1726#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1727 case S_IFIFO: 1728 snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1729 p += strlen(p); 1730 goto defprint; 1731#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1732 1733#ifdef S_IFDIR 1734 case S_IFDIR: 1735 snprintf(p, SPACELEFT(buf, p), "DIR: "); 1736 p += strlen(p); 1737 goto defprint; 1738#endif /* S_IFDIR */ 1739 1740#ifdef S_IFLNK 1741 case S_IFLNK: 1742 snprintf(p, SPACELEFT(buf, p), "LNK: "); 1743 p += strlen(p); 1744 goto defprint; 1745#endif /* S_IFLNK */ 1746 1747 default: 1748defprint: 1749 /*CONSTCOND*/ 1750 if (sizeof st.st_ino > sizeof (long)) 1751 snprintf(p, SPACELEFT(buf, p), 1752 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", 1753 major(st.st_dev), minor(st.st_dev), 1754 quad_to_string(st.st_ino), 1755 (int) st.st_nlink, (int) st.st_uid, 1756 (int) st.st_gid); 1757 else 1758 snprintf(p, SPACELEFT(buf, p), 1759 "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", 1760 major(st.st_dev), minor(st.st_dev), 1761 (unsigned long) st.st_ino, 1762 (int) st.st_nlink, (int) st.st_uid, 1763 (int) st.st_gid); 1764 /*CONSTCOND*/ 1765 if (sizeof st.st_size > sizeof (long)) 1766 snprintf(p, SPACELEFT(buf, p), "size=%s", 1767 quad_to_string(st.st_size)); 1768 else 1769 snprintf(p, SPACELEFT(buf, p), "size=%lu", 1770 (unsigned long) st.st_size); 1771 break; 1772 } 1773 1774printit: 1775 if (logit) 1776 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1777 "%.800s", buf); 1778 else 1779 printf("%s\n", buf); 1780} 1781/* 1782** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1783** 1784** Parameters: 1785** host -- the host to shorten (stripped in place). 1786** 1787** Returns: 1788** none. 1789*/ 1790 1791void 1792shorten_hostname(host) 1793 char host[]; 1794{ 1795 register char *p; 1796 char *mydom; 1797 int i; 1798 bool canon = FALSE; 1799 1800 /* strip off final dot */ 1801 p = &host[strlen(host) - 1]; 1802 if (*p == '.') 1803 { 1804 *p = '\0'; 1805 canon = TRUE; 1806 } 1807 1808 /* see if there is any domain at all -- if not, we are done */ 1809 p = strchr(host, '.'); 1810 if (p == NULL) 1811 return; 1812 1813 /* yes, we have a domain -- see if it looks like us */ 1814 mydom = macvalue('m', CurEnv); 1815 if (mydom == NULL) 1816 mydom = ""; 1817 i = strlen(++p); 1818 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && 1819 (mydom[i] == '.' || mydom[i] == '\0')) 1820 *--p = '\0'; 1821} 1822/* 1823** PROG_OPEN -- open a program for reading 1824** 1825** Parameters: 1826** argv -- the argument list. 1827** pfd -- pointer to a place to store the file descriptor. 1828** e -- the current envelope. 1829** 1830** Returns: 1831** pid of the process -- -1 if it failed. 1832*/ 1833 1834int 1835prog_open(argv, pfd, e) 1836 char **argv; 1837 int *pfd; 1838 ENVELOPE *e; 1839{ 1840 int pid; 1841 int i; 1842 int save_errno; 1843 int fdv[2]; 1844 char *p, *q; 1845 char buf[MAXLINE + 1]; 1846 extern int DtableSize; 1847 1848 if (pipe(fdv) < 0) 1849 { 1850 syserr("%s: cannot create pipe for stdout", argv[0]); 1851 return -1; 1852 } 1853 pid = fork(); 1854 if (pid < 0) 1855 { 1856 syserr("%s: cannot fork", argv[0]); 1857 (void) close(fdv[0]); 1858 (void) close(fdv[1]); 1859 return -1; 1860 } 1861 if (pid > 0) 1862 { 1863 /* parent */ 1864 (void) close(fdv[1]); 1865 *pfd = fdv[0]; 1866 return pid; 1867 } 1868 1869 /* child -- close stdin */ 1870 (void) close(0); 1871 1872 /* stdout goes back to parent */ 1873 (void) close(fdv[0]); 1874 if (dup2(fdv[1], 1) < 0) 1875 { 1876 syserr("%s: cannot dup2 for stdout", argv[0]); 1877 _exit(EX_OSERR); 1878 } 1879 (void) close(fdv[1]); 1880 1881 /* stderr goes to transcript if available */ 1882 if (e->e_xfp != NULL) 1883 { 1884 int xfd; 1885 1886 xfd = fileno(e->e_xfp); 1887 if (xfd >= 0 && dup2(xfd, 2) < 0) 1888 { 1889 syserr("%s: cannot dup2 for stderr", argv[0]); 1890 _exit(EX_OSERR); 1891 } 1892 } 1893 1894 /* this process has no right to the queue file */ 1895 if (e->e_lockfp != NULL) 1896 (void) close(fileno(e->e_lockfp)); 1897 1898 /* chroot to the program mailer directory, if defined */ 1899 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 1900 { 1901 expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 1902 if (chroot(buf) < 0) 1903 { 1904 syserr("prog_open: cannot chroot(%s)", buf); 1905 exit(EX_TEMPFAIL); 1906 } 1907 if (chdir("/") < 0) 1908 { 1909 syserr("prog_open: cannot chdir(/)"); 1910 exit(EX_TEMPFAIL); 1911 } 1912 } 1913 1914 /* run as default user */ 1915 endpwent(); 1916 if (setgid(DefGid) < 0 && geteuid() == 0) 1917 { 1918 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 1919 exit(EX_TEMPFAIL); 1920 } 1921 if (setuid(DefUid) < 0 && geteuid() == 0) 1922 { 1923 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 1924 exit(EX_TEMPFAIL); 1925 } 1926 1927 /* run in some directory */ 1928 if (ProgMailer != NULL) 1929 p = ProgMailer->m_execdir; 1930 else 1931 p = NULL; 1932 for (; p != NULL; p = q) 1933 { 1934 q = strchr(p, ':'); 1935 if (q != NULL) 1936 *q = '\0'; 1937 expand(p, buf, sizeof buf, e); 1938 if (q != NULL) 1939 *q++ = ':'; 1940 if (buf[0] != '\0' && chdir(buf) >= 0) 1941 break; 1942 } 1943 if (p == NULL) 1944 { 1945 /* backup directories */ 1946 if (chdir("/tmp") < 0) 1947 (void) chdir("/"); 1948 } 1949 1950 /* arrange for all the files to be closed */ 1951 for (i = 3; i < DtableSize; i++) 1952 { 1953 register int j; 1954 1955 if ((j = fcntl(i, F_GETFD, 0)) != -1) 1956 (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 1957 } 1958 1959 /* now exec the process */ 1960 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 1961 1962 /* woops! failed */ 1963 save_errno = errno; 1964 syserr("%s: cannot exec", argv[0]); 1965 if (transienterror(save_errno)) 1966 _exit(EX_OSERR); 1967 _exit(EX_CONFIG); 1968 return -1; /* avoid compiler warning on IRIX */ 1969} 1970/* 1971** GET_COLUMN -- look up a Column in a line buffer 1972** 1973** Parameters: 1974** line -- the raw text line to search. 1975** col -- the column number to fetch. 1976** delim -- the delimiter between columns. If null, 1977** use white space. 1978** buf -- the output buffer. 1979** buflen -- the length of buf. 1980** 1981** Returns: 1982** buf if successful. 1983** NULL otherwise. 1984*/ 1985 1986char * 1987get_column(line, col, delim, buf, buflen) 1988 char line[]; 1989 int col; 1990 int delim; 1991 char buf[]; 1992 int buflen; 1993{ 1994 char *p; 1995 char *begin, *end; 1996 int i; 1997 char delimbuf[4]; 1998 1999 if ((char)delim == '\0') 2000 (void) strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 2001 else 2002 { 2003 delimbuf[0] = (char)delim; 2004 delimbuf[1] = '\0'; 2005 } 2006 2007 p = line; 2008 if (*p == '\0') 2009 return NULL; /* line empty */ 2010 if (*p == (char)delim && col == 0) 2011 return NULL; /* first column empty */ 2012 2013 begin = line; 2014 2015 if (col == 0 && (char)delim == '\0') 2016 { 2017 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2018 begin++; 2019 } 2020 2021 for (i = 0; i < col; i++) 2022 { 2023 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2024 return NULL; /* no such column */ 2025 begin++; 2026 if ((char)delim == '\0') 2027 { 2028 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2029 begin++; 2030 } 2031 } 2032 2033 end = strpbrk(begin, delimbuf); 2034 if (end == NULL) 2035 i = strlen(begin); 2036 else 2037 i = end - begin; 2038 if (i >= buflen) 2039 i = buflen - 1; 2040 (void) strlcpy(buf, begin, i + 1); 2041 return buf; 2042} 2043/* 2044** CLEANSTRCPY -- copy string keeping out bogus characters 2045** 2046** Parameters: 2047** t -- "to" string. 2048** f -- "from" string. 2049** l -- length of space available in "to" string. 2050** 2051** Returns: 2052** none. 2053*/ 2054 2055void 2056cleanstrcpy(t, f, l) 2057 register char *t; 2058 register char *f; 2059 int l; 2060{ 2061 /* check for newlines and log if necessary */ 2062 (void) denlstring(f, TRUE, TRUE); 2063 2064 if (l <= 0) 2065 syserr("!cleanstrcpy: length == 0"); 2066 2067 l--; 2068 while (l > 0 && *f != '\0') 2069 { 2070 if (isascii(*f) && 2071 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2072 { 2073 l--; 2074 *t++ = *f; 2075 } 2076 f++; 2077 } 2078 *t = '\0'; 2079} 2080 2081/* 2082** DENLSTRING -- convert newlines in a string to spaces 2083** 2084** Parameters: 2085** s -- the input string 2086** strict -- if set, don't permit continuation lines. 2087** logattacks -- if set, log attempted attacks. 2088** 2089** Returns: 2090** A pointer to a version of the string with newlines 2091** mapped to spaces. This should be copied. 2092*/ 2093 2094char * 2095denlstring(s, strict, logattacks) 2096 char *s; 2097 bool strict; 2098 bool logattacks; 2099{ 2100 register char *p; 2101 int l; 2102 static char *bp = NULL; 2103 static int bl = 0; 2104 2105 p = s; 2106 while ((p = strchr(p, '\n')) != NULL) 2107 if (strict || (*++p != ' ' && *p != '\t')) 2108 break; 2109 if (p == NULL) 2110 return s; 2111 2112 l = strlen(s) + 1; 2113 if (bl < l) 2114 { 2115 /* allocate more space */ 2116 if (bp != NULL) 2117 free(bp); 2118 bp = xalloc(l); 2119 bl = l; 2120 } 2121 (void) strlcpy(bp, s, l); 2122 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2123 *p++ = ' '; 2124 2125 if (logattacks) 2126 { 2127 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2128 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2129 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2130 shortenstring(bp, MAXSHORTSTR)); 2131 } 2132 2133 return bp; 2134} 2135/* 2136** PATH_IS_DIR -- check to see if file exists and is a directory. 2137** 2138** There are some additional checks for security violations in 2139** here. This routine is intended to be used for the host status 2140** support. 2141** 2142** Parameters: 2143** pathname -- pathname to check for directory-ness. 2144** createflag -- if set, create directory if needed. 2145** 2146** Returns: 2147** TRUE -- if the indicated pathname is a directory 2148** FALSE -- otherwise 2149*/ 2150 2151int 2152path_is_dir(pathname, createflag) 2153 char *pathname; 2154 bool createflag; 2155{ 2156 struct stat statbuf; 2157 2158#if HASLSTAT 2159 if (lstat(pathname, &statbuf) < 0) 2160#else /* HASLSTAT */ 2161 if (stat(pathname, &statbuf) < 0) 2162#endif /* HASLSTAT */ 2163 { 2164 if (errno != ENOENT || !createflag) 2165 return FALSE; 2166 if (mkdir(pathname, 0755) < 0) 2167 return FALSE; 2168 return TRUE; 2169 } 2170 if (!S_ISDIR(statbuf.st_mode)) 2171 { 2172 errno = ENOTDIR; 2173 return FALSE; 2174 } 2175 2176 /* security: don't allow writable directories */ 2177 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2178 { 2179 errno = EACCES; 2180 return FALSE; 2181 } 2182 2183 return TRUE; 2184} 2185/* 2186** PROC_LIST_ADD -- add process id to list of our children 2187** 2188** Parameters: 2189** pid -- pid to add to list. 2190** task -- task of pid. 2191** type -- type of process. 2192** 2193** Returns: 2194** none 2195*/ 2196 2197static struct procs *ProcListVec = NULL; 2198static int ProcListSize = 0; 2199 2200void 2201proc_list_add(pid, task, type) 2202 pid_t pid; 2203 char *task; 2204 int type; 2205{ 2206 int i; 2207 2208 for (i = 0; i < ProcListSize; i++) 2209 { 2210 if (ProcListVec[i].proc_pid == NO_PID) 2211 break; 2212 } 2213 if (i >= ProcListSize) 2214 { 2215 /* probe the existing vector to avoid growing infinitely */ 2216 proc_list_probe(); 2217 2218 /* now scan again */ 2219 for (i = 0; i < ProcListSize; i++) 2220 { 2221 if (ProcListVec[i].proc_pid == NO_PID) 2222 break; 2223 } 2224 } 2225 if (i >= ProcListSize) 2226 { 2227 /* grow process list */ 2228 struct procs *npv; 2229 2230 npv = (struct procs *) xalloc((sizeof *npv) * 2231 (ProcListSize + PROC_LIST_SEG)); 2232 if (ProcListSize > 0) 2233 { 2234 memmove(npv, ProcListVec, 2235 ProcListSize * sizeof (struct procs)); 2236 free(ProcListVec); 2237 } 2238 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2239 { 2240 npv[i].proc_pid = NO_PID; 2241 npv[i].proc_task = NULL; 2242 npv[i].proc_type = PROC_NONE; 2243 } 2244 i = ProcListSize; 2245 ProcListSize += PROC_LIST_SEG; 2246 ProcListVec = npv; 2247 } 2248 ProcListVec[i].proc_pid = pid; 2249 if (ProcListVec[i].proc_task != NULL) 2250 free(ProcListVec[i].proc_task); 2251 ProcListVec[i].proc_task = newstr(task); 2252 ProcListVec[i].proc_type = type; 2253 2254 /* if process adding itself, it's not a child */ 2255 if (pid != getpid()) 2256 CurChildren++; 2257} 2258/* 2259** PROC_LIST_SET -- set pid task in process list 2260** 2261** Parameters: 2262** pid -- pid to set 2263** task -- task of pid 2264** 2265** Returns: 2266** none. 2267*/ 2268 2269void 2270proc_list_set(pid, task) 2271 pid_t pid; 2272 char *task; 2273{ 2274 int i; 2275 2276 for (i = 0; i < ProcListSize; i++) 2277 { 2278 if (ProcListVec[i].proc_pid == pid) 2279 { 2280 if (ProcListVec[i].proc_task != NULL) 2281 free(ProcListVec[i].proc_task); 2282 ProcListVec[i].proc_task = newstr(task); 2283 break; 2284 } 2285 } 2286} 2287/* 2288** PROC_LIST_DROP -- drop pid from process list 2289** 2290** Parameters: 2291** pid -- pid to drop 2292** 2293** Returns: 2294** type of process 2295*/ 2296 2297int 2298proc_list_drop(pid) 2299 pid_t pid; 2300{ 2301 int i; 2302 int type = PROC_NONE; 2303 2304 for (i = 0; i < ProcListSize; i++) 2305 { 2306 if (ProcListVec[i].proc_pid == pid) 2307 { 2308 ProcListVec[i].proc_pid = NO_PID; 2309 type = ProcListVec[i].proc_type; 2310 break; 2311 } 2312 } 2313 if (CurChildren > 0) 2314 CurChildren--; 2315 2316 2317 return type; 2318} 2319/* 2320** PROC_LIST_CLEAR -- clear the process list 2321** 2322** Parameters: 2323** none. 2324** 2325** Returns: 2326** none. 2327*/ 2328 2329void 2330proc_list_clear() 2331{ 2332 int i; 2333 2334 /* start from 1 since 0 is the daemon itself */ 2335 for (i = 1; i < ProcListSize; i++) 2336 { 2337 ProcListVec[i].proc_pid = NO_PID; 2338 } 2339 CurChildren = 0; 2340} 2341/* 2342** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2343** 2344** Parameters: 2345** none 2346** 2347** Returns: 2348** none 2349*/ 2350 2351void 2352proc_list_probe() 2353{ 2354 int i; 2355 2356 /* start from 1 since 0 is the daemon itself */ 2357 for (i = 1; i < ProcListSize; i++) 2358 { 2359 if (ProcListVec[i].proc_pid == NO_PID) 2360 continue; 2361 if (kill(ProcListVec[i].proc_pid, 0) < 0) 2362 { 2363 if (LogLevel > 3) 2364 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2365 "proc_list_probe: lost pid %d", 2366 (int) ProcListVec[i].proc_pid); 2367 ProcListVec[i].proc_pid = NO_PID; 2368 CurChildren--; 2369 } 2370 } 2371 if (CurChildren < 0) 2372 CurChildren = 0; 2373} 2374/* 2375** PROC_LIST_DISPLAY -- display the process list 2376** 2377** Parameters: 2378** out -- output file pointer 2379** 2380** Returns: 2381** none. 2382*/ 2383 2384void 2385proc_list_display(out) 2386 FILE *out; 2387{ 2388 int i; 2389 2390 for (i = 0; i < ProcListSize; i++) 2391 { 2392 if (ProcListVec[i].proc_pid == NO_PID) 2393 continue; 2394 2395 fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid, 2396 ProcListVec[i].proc_task != NULL ? 2397 ProcListVec[i].proc_task : "(unknown)", 2398 (OpMode == MD_SMTP || 2399 OpMode == MD_DAEMON || 2400 OpMode == MD_ARPAFTP) ? "\r" : ""); 2401 } 2402} 2403/* 2404** SM_STRCASECMP -- 8-bit clean version of strcasecmp 2405** 2406** Thank you, vendors, for making this all necessary. 2407*/ 2408 2409/* 2410 * Copyright (c) 1987, 1993 2411 * The Regents of the University of California. All rights reserved. 2412 * 2413 * Redistribution and use in source and binary forms, with or without 2414 * modification, are permitted provided that the following conditions 2415 * are met: 2416 * 1. Redistributions of source code must retain the above copyright 2417 * notice, this list of conditions and the following disclaimer. 2418 * 2. Redistributions in binary form must reproduce the above copyright 2419 * notice, this list of conditions and the following disclaimer in the 2420 * documentation and/or other materials provided with the distribution. 2421 * 3. All advertising materials mentioning features or use of this software 2422 * must display the following acknowledgement: 2423 * This product includes software developed by the University of 2424 * California, Berkeley and its contributors. 2425 * 4. Neither the name of the University nor the names of its contributors 2426 * may be used to endorse or promote products derived from this software 2427 * without specific prior written permission. 2428 * 2429 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2430 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2431 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2432 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2433 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2434 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2435 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2436 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2437 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2438 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2439 * SUCH DAMAGE. 2440 */ 2441 2442#if defined(LIBC_SCCS) && !defined(lint) 2443static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; 2444#endif /* defined(LIBC_SCCS) && !defined(lint) */ 2445 2446/* 2447 * This array is designed for mapping upper and lower case letter 2448 * together for a case independent comparison. The mappings are 2449 * based upon ascii character sequences. 2450 */ 2451static const u_char charmap[] = { 2452 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 2453 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 2454 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 2455 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 2456 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 2457 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 2458 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 2459 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 2460 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 2461 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 2462 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 2463 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, 2464 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 2465 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 2466 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 2467 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 2468 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 2469 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 2470 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 2471 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 2472 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 2473 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 2474 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 2475 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 2476 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 2477 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, 2478 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 2479 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 2480 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 2481 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, 2482 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 2483 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, 2484}; 2485 2486int 2487sm_strcasecmp(s1, s2) 2488 const char *s1, *s2; 2489{ 2490 register const u_char *cm = charmap, 2491 *us1 = (const u_char *)s1, 2492 *us2 = (const u_char *)s2; 2493 2494 while (cm[*us1] == cm[*us2++]) 2495 if (*us1++ == '\0') 2496 return 0; 2497 return (cm[*us1] - cm[*--us2]); 2498} 2499 2500int 2501sm_strncasecmp(s1, s2, n) 2502 const char *s1, *s2; 2503 register size_t n; 2504{ 2505 if (n != 0) { 2506 register const u_char *cm = charmap, 2507 *us1 = (const u_char *)s1, 2508 *us2 = (const u_char *)s2; 2509 2510 do { 2511 if (cm[*us1] != cm[*us2++]) 2512 return (cm[*us1] - cm[*--us2]); 2513 if (*us1++ == '\0') 2514 break; 2515 } while (--n != 0); 2516 } 2517 return 0; 2518} 2519