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