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