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