1 /* 2 * Copyright (c) 1998-2006 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 #pragma ident "%Z%%M% %I% %E% SMI" 15 16 #include <sendmail.h> 17 18 SM_RCSID("@(#)$Id: util.c,v 8.410 2006/12/18 18:36:44 ca Exp $") 19 20 #include <sm/sendmail.h> 21 #include <sysexits.h> 22 #include <sm/xtrap.h> 23 24 /* 25 ** NEWSTR -- Create a copy of a C string 26 ** 27 ** Parameters: 28 ** s -- the string to copy. 29 ** 30 ** Returns: 31 ** pointer to newly allocated string. 32 */ 33 34 char * 35 newstr(s) 36 const char *s; 37 { 38 size_t l; 39 char *n; 40 41 l = strlen(s); 42 SM_ASSERT(l + 1 > l); 43 n = xalloc(l + 1); 44 sm_strlcpy(n, s, l + 1); 45 return n; 46 } 47 48 /* 49 ** ADDQUOTES -- Adds quotes & quote bits to a string. 50 ** 51 ** Runs through a string and adds backslashes and quote bits. 52 ** 53 ** Parameters: 54 ** s -- the string to modify. 55 ** rpool -- resource pool from which to allocate result 56 ** 57 ** Returns: 58 ** pointer to quoted string. 59 */ 60 61 char * 62 addquotes(s, rpool) 63 char *s; 64 SM_RPOOL_T *rpool; 65 { 66 int len = 0; 67 char c; 68 char *p = s, *q, *r; 69 70 if (s == NULL) 71 return NULL; 72 73 /* Find length of quoted string */ 74 while ((c = *p++) != '\0') 75 { 76 len++; 77 if (c == '\\' || c == '"') 78 len++; 79 } 80 81 q = r = sm_rpool_malloc_x(rpool, len + 3); 82 p = s; 83 84 /* add leading quote */ 85 *q++ = '"'; 86 while ((c = *p++) != '\0') 87 { 88 /* quote \ or " */ 89 if (c == '\\' || c == '"') 90 *q++ = '\\'; 91 *q++ = c; 92 } 93 *q++ = '"'; 94 *q = '\0'; 95 return r; 96 } 97 98 /* 99 ** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided 100 ** the following character is alpha-numerical. 101 ** 102 ** This is done in place. 103 ** 104 ** Parameters: 105 ** s -- the string to strip. 106 ** 107 ** Returns: 108 ** none. 109 */ 110 111 void 112 stripbackslash(s) 113 char *s; 114 { 115 char *p, *q, c; 116 117 if (s == NULL || *s == '\0') 118 return; 119 p = q = s; 120 while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1])))) 121 p++; 122 do 123 { 124 c = *q++ = *p++; 125 } while (c != '\0'); 126 } 127 128 /* 129 ** RFC822_STRING -- Checks string for proper RFC822 string quoting. 130 ** 131 ** Runs through a string and verifies RFC822 special characters 132 ** are only found inside comments, quoted strings, or backslash 133 ** escaped. Also verified balanced quotes and parenthesis. 134 ** 135 ** Parameters: 136 ** s -- the string to modify. 137 ** 138 ** Returns: 139 ** true iff the string is RFC822 compliant, false otherwise. 140 */ 141 142 bool 143 rfc822_string(s) 144 char *s; 145 { 146 bool quoted = false; 147 int commentlev = 0; 148 char *c = s; 149 150 if (s == NULL) 151 return false; 152 153 while (*c != '\0') 154 { 155 /* escaped character */ 156 if (*c == '\\') 157 { 158 c++; 159 if (*c == '\0') 160 return false; 161 } 162 else if (commentlev == 0 && *c == '"') 163 quoted = !quoted; 164 else if (!quoted) 165 { 166 if (*c == ')') 167 { 168 /* unbalanced ')' */ 169 if (commentlev == 0) 170 return false; 171 else 172 commentlev--; 173 } 174 else if (*c == '(') 175 commentlev++; 176 else if (commentlev == 0 && 177 strchr(MustQuoteChars, *c) != NULL) 178 return false; 179 } 180 c++; 181 } 182 183 /* unbalanced '"' or '(' */ 184 return !quoted && commentlev == 0; 185 } 186 187 /* 188 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string 189 ** 190 ** Arbitrarily shorten (in place) an RFC822 string and rebalance 191 ** comments and quotes. 192 ** 193 ** Parameters: 194 ** string -- the string to shorten 195 ** length -- the maximum size, 0 if no maximum 196 ** 197 ** Returns: 198 ** true if string is changed, false otherwise 199 ** 200 ** Side Effects: 201 ** Changes string in place, possibly resulting 202 ** in a shorter string. 203 */ 204 205 bool 206 shorten_rfc822_string(string, length) 207 char *string; 208 size_t length; 209 { 210 bool backslash = false; 211 bool modified = false; 212 bool quoted = false; 213 size_t slen; 214 int parencount = 0; 215 char *ptr = string; 216 217 /* 218 ** If have to rebalance an already short enough string, 219 ** need to do it within allocated space. 220 */ 221 222 slen = strlen(string); 223 if (length == 0 || slen < length) 224 length = slen; 225 226 while (*ptr != '\0') 227 { 228 if (backslash) 229 { 230 backslash = false; 231 goto increment; 232 } 233 234 if (*ptr == '\\') 235 backslash = true; 236 else if (*ptr == '(') 237 { 238 if (!quoted) 239 parencount++; 240 } 241 else if (*ptr == ')') 242 { 243 if (--parencount < 0) 244 parencount = 0; 245 } 246 247 /* Inside a comment, quotes don't matter */ 248 if (parencount <= 0 && *ptr == '"') 249 quoted = !quoted; 250 251 increment: 252 /* Check for sufficient space for next character */ 253 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) + 254 parencount + 255 (quoted ? 1 : 0))) 256 { 257 /* Not enough, backtrack */ 258 if (*ptr == '\\') 259 backslash = false; 260 else if (*ptr == '(' && !quoted) 261 parencount--; 262 else if (*ptr == '"' && parencount == 0) 263 quoted = false; 264 break; 265 } 266 ptr++; 267 } 268 269 /* Rebalance */ 270 while (parencount-- > 0) 271 { 272 if (*ptr != ')') 273 { 274 modified = true; 275 *ptr = ')'; 276 } 277 ptr++; 278 } 279 if (quoted) 280 { 281 if (*ptr != '"') 282 { 283 modified = true; 284 *ptr = '"'; 285 } 286 ptr++; 287 } 288 if (*ptr != '\0') 289 { 290 modified = true; 291 *ptr = '\0'; 292 } 293 return modified; 294 } 295 296 /* 297 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string 298 ** 299 ** Find an unquoted, non-commented character in an RFC822 300 ** string and return a pointer to its location in the 301 ** string. 302 ** 303 ** Parameters: 304 ** string -- the string to search 305 ** character -- the character to find 306 ** 307 ** Returns: 308 ** pointer to the character, or 309 ** a pointer to the end of the line if character is not found 310 */ 311 312 char * 313 find_character(string, character) 314 char *string; 315 int character; 316 { 317 bool backslash = false; 318 bool quoted = false; 319 int parencount = 0; 320 321 while (string != NULL && *string != '\0') 322 { 323 if (backslash) 324 { 325 backslash = false; 326 if (!quoted && character == '\\' && *string == '\\') 327 break; 328 string++; 329 continue; 330 } 331 switch (*string) 332 { 333 case '\\': 334 backslash = true; 335 break; 336 337 case '(': 338 if (!quoted) 339 parencount++; 340 break; 341 342 case ')': 343 if (--parencount < 0) 344 parencount = 0; 345 break; 346 } 347 348 /* Inside a comment, nothing matters */ 349 if (parencount > 0) 350 { 351 string++; 352 continue; 353 } 354 355 if (*string == '"') 356 quoted = !quoted; 357 else if (*string == character && !quoted) 358 break; 359 string++; 360 } 361 362 /* Return pointer to the character */ 363 return string; 364 } 365 366 /* 367 ** CHECK_BODYTYPE -- check bodytype parameter 368 ** 369 ** Parameters: 370 ** bodytype -- bodytype parameter 371 ** 372 ** Returns: 373 ** BODYTYPE_* according to parameter 374 ** 375 */ 376 377 int 378 check_bodytype(bodytype) 379 char *bodytype; 380 { 381 /* check body type for legality */ 382 if (bodytype == NULL) 383 return BODYTYPE_NONE; 384 if (sm_strcasecmp(bodytype, "7BIT") == 0) 385 return BODYTYPE_7BIT; 386 if (sm_strcasecmp(bodytype, "8BITMIME") == 0) 387 return BODYTYPE_8BITMIME; 388 return BODYTYPE_ILLEGAL; 389 } 390 391 /* 392 ** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..." 393 ** 394 ** Parameters: 395 ** str -- string to truncate 396 ** len -- maximum length (including '\0') (0 for unlimited) 397 ** delim -- delimiter character 398 ** 399 ** Returns: 400 ** None. 401 */ 402 403 void 404 truncate_at_delim(str, len, delim) 405 char *str; 406 size_t len; 407 int delim; 408 { 409 char *p; 410 411 if (str == NULL || len == 0 || strlen(str) < len) 412 return; 413 414 *(str + len - 1) = '\0'; 415 while ((p = strrchr(str, delim)) != NULL) 416 { 417 *p = '\0'; 418 if (p - str + 4 < len) 419 { 420 *p++ = (char) delim; 421 *p = '\0'; 422 (void) sm_strlcat(str, "...", len); 423 return; 424 } 425 } 426 427 /* Couldn't find a place to append "..." */ 428 if (len > 3) 429 (void) sm_strlcpy(str, "...", len); 430 else 431 str[0] = '\0'; 432 } 433 434 /* 435 ** XALLOC -- Allocate memory, raise an exception on error 436 ** 437 ** Parameters: 438 ** sz -- size of area to allocate. 439 ** 440 ** Returns: 441 ** pointer to data region. 442 ** 443 ** Exceptions: 444 ** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory 445 ** 446 ** Side Effects: 447 ** Memory is allocated. 448 */ 449 450 char * 451 #if SM_HEAP_CHECK 452 xalloc_tagged(sz, file, line) 453 register int sz; 454 char *file; 455 int line; 456 #else /* SM_HEAP_CHECK */ 457 xalloc(sz) 458 register int sz; 459 #endif /* SM_HEAP_CHECK */ 460 { 461 register char *p; 462 463 SM_REQUIRE(sz >= 0); 464 465 /* some systems can't handle size zero mallocs */ 466 if (sz <= 0) 467 sz = 1; 468 469 /* scaffolding for testing error handling code */ 470 sm_xtrap_raise_x(&SmHeapOutOfMemory); 471 472 p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group()); 473 if (p == NULL) 474 { 475 sm_exc_raise_x(&SmHeapOutOfMemory); 476 } 477 return p; 478 } 479 480 /* 481 ** COPYPLIST -- copy list of pointers. 482 ** 483 ** This routine is the equivalent of strdup for lists of 484 ** pointers. 485 ** 486 ** Parameters: 487 ** list -- list of pointers to copy. 488 ** Must be NULL terminated. 489 ** copycont -- if true, copy the contents of the vector 490 ** (which must be a string) also. 491 ** rpool -- resource pool from which to allocate storage, 492 ** or NULL 493 ** 494 ** Returns: 495 ** a copy of 'list'. 496 */ 497 498 char ** 499 copyplist(list, copycont, rpool) 500 char **list; 501 bool copycont; 502 SM_RPOOL_T *rpool; 503 { 504 register char **vp; 505 register char **newvp; 506 507 for (vp = list; *vp != NULL; vp++) 508 continue; 509 510 vp++; 511 512 newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp)); 513 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp)); 514 515 if (copycont) 516 { 517 for (vp = newvp; *vp != NULL; vp++) 518 *vp = sm_rpool_strdup_x(rpool, *vp); 519 } 520 521 return newvp; 522 } 523 524 /* 525 ** COPYQUEUE -- copy address queue. 526 ** 527 ** This routine is the equivalent of strdup for address queues; 528 ** addresses marked as QS_IS_DEAD() aren't copied 529 ** 530 ** Parameters: 531 ** addr -- list of address structures to copy. 532 ** rpool -- resource pool from which to allocate storage 533 ** 534 ** Returns: 535 ** a copy of 'addr'. 536 */ 537 538 ADDRESS * 539 copyqueue(addr, rpool) 540 ADDRESS *addr; 541 SM_RPOOL_T *rpool; 542 { 543 register ADDRESS *newaddr; 544 ADDRESS *ret; 545 register ADDRESS **tail = &ret; 546 547 while (addr != NULL) 548 { 549 if (!QS_IS_DEAD(addr->q_state)) 550 { 551 newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool, 552 sizeof(*newaddr)); 553 STRUCTCOPY(*addr, *newaddr); 554 *tail = newaddr; 555 tail = &newaddr->q_next; 556 } 557 addr = addr->q_next; 558 } 559 *tail = NULL; 560 561 return ret; 562 } 563 564 /* 565 ** LOG_SENDMAIL_PID -- record sendmail pid and command line. 566 ** 567 ** Parameters: 568 ** e -- the current envelope. 569 ** 570 ** Returns: 571 ** none. 572 ** 573 ** Side Effects: 574 ** writes pidfile, logs command line. 575 ** keeps file open and locked to prevent overwrite of active file 576 */ 577 578 static SM_FILE_T *Pidf = NULL; 579 580 void 581 log_sendmail_pid(e) 582 ENVELOPE *e; 583 { 584 long sff; 585 char pidpath[MAXPATHLEN]; 586 extern char *CommandLineArgs; 587 588 /* write the pid to the log file for posterity */ 589 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK; 590 if (TrustedUid != 0 && RealUid == TrustedUid) 591 sff |= SFF_OPENASROOT; 592 expand(PidFile, pidpath, sizeof(pidpath), e); 593 Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff); 594 if (Pidf == NULL) 595 { 596 if (errno == EWOULDBLOCK) 597 sm_syslog(LOG_ERR, NOQID, 598 "unable to write pid to %s: file in use by another process", 599 pidpath); 600 else 601 sm_syslog(LOG_ERR, NOQID, 602 "unable to write pid to %s: %s", 603 pidpath, sm_errstring(errno)); 604 } 605 else 606 { 607 PidFilePid = getpid(); 608 609 /* write the process id on line 1 */ 610 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n", 611 (long) PidFilePid); 612 613 /* line 2 contains all command line flags */ 614 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n", 615 CommandLineArgs); 616 617 /* flush */ 618 (void) sm_io_flush(Pidf, SM_TIME_DEFAULT); 619 620 /* 621 ** Leave pid file open until process ends 622 ** so it's not overwritten by another 623 ** process. 624 */ 625 } 626 if (LogLevel > 9) 627 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs); 628 } 629 630 /* 631 ** CLOSE_SENDMAIL_PID -- close sendmail pid file 632 ** 633 ** Parameters: 634 ** none. 635 ** 636 ** Returns: 637 ** none. 638 */ 639 640 void 641 close_sendmail_pid() 642 { 643 if (Pidf == NULL) 644 return; 645 646 (void) sm_io_close(Pidf, SM_TIME_DEFAULT); 647 Pidf = NULL; 648 } 649 650 /* 651 ** SET_DELIVERY_MODE -- set and record the delivery mode 652 ** 653 ** Parameters: 654 ** mode -- delivery mode 655 ** e -- the current envelope. 656 ** 657 ** Returns: 658 ** none. 659 ** 660 ** Side Effects: 661 ** sets {deliveryMode} macro 662 */ 663 664 void 665 set_delivery_mode(mode, e) 666 int mode; 667 ENVELOPE *e; 668 { 669 char buf[2]; 670 671 e->e_sendmode = (char) mode; 672 buf[0] = (char) mode; 673 buf[1] = '\0'; 674 macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf); 675 } 676 677 /* 678 ** SET_OP_MODE -- set and record the op mode 679 ** 680 ** Parameters: 681 ** mode -- op mode 682 ** e -- the current envelope. 683 ** 684 ** Returns: 685 ** none. 686 ** 687 ** Side Effects: 688 ** sets {opMode} macro 689 */ 690 691 void 692 set_op_mode(mode) 693 int mode; 694 { 695 char buf[2]; 696 extern ENVELOPE BlankEnvelope; 697 698 OpMode = (char) mode; 699 buf[0] = (char) mode; 700 buf[1] = '\0'; 701 macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf); 702 } 703 704 /* 705 ** PRINTAV -- print argument vector. 706 ** 707 ** Parameters: 708 ** fp -- output file pointer. 709 ** av -- argument vector. 710 ** 711 ** Returns: 712 ** none. 713 ** 714 ** Side Effects: 715 ** prints av. 716 */ 717 718 void 719 printav(fp, av) 720 SM_FILE_T *fp; 721 char **av; 722 { 723 while (*av != NULL) 724 { 725 if (tTd(0, 44)) 726 sm_dprintf("\n\t%08lx=", (unsigned long) *av); 727 else 728 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' '); 729 if (tTd(0, 99)) 730 sm_dprintf("%s", str2prt(*av++)); 731 else 732 xputs(fp, *av++); 733 } 734 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n'); 735 } 736 737 /* 738 ** XPUTS -- put string doing control escapes. 739 ** 740 ** Parameters: 741 ** fp -- output file pointer. 742 ** s -- string to put. 743 ** 744 ** Returns: 745 ** none. 746 ** 747 ** Side Effects: 748 ** output to stdout 749 */ 750 751 void 752 xputs(fp, s) 753 SM_FILE_T *fp; 754 const char *s; 755 { 756 int c; 757 struct metamac *mp; 758 bool shiftout = false; 759 extern struct metamac MetaMacros[]; 760 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 761 "@(#)$Debug: ANSI - enable reverse video in debug output $"); 762 763 /* 764 ** TermEscape is set here, rather than in main(), 765 ** because ANSI mode can be turned on or off at any time 766 ** if we are in -bt rule testing mode. 767 */ 768 769 if (sm_debug_unknown(&DebugANSI)) 770 { 771 if (sm_debug_active(&DebugANSI, 1)) 772 { 773 TermEscape.te_rv_on = "\033[7m"; 774 TermEscape.te_normal = "\033[0m"; 775 } 776 else 777 { 778 TermEscape.te_rv_on = ""; 779 TermEscape.te_normal = ""; 780 } 781 } 782 783 if (s == NULL) 784 { 785 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s", 786 TermEscape.te_rv_on, TermEscape.te_normal); 787 return; 788 } 789 while ((c = (*s++ & 0377)) != '\0') 790 { 791 if (shiftout) 792 { 793 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 794 TermEscape.te_normal); 795 shiftout = false; 796 } 797 if (!isascii(c) && !tTd(84, 1)) 798 { 799 if (c == MATCHREPL) 800 { 801 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 802 "%s$", 803 TermEscape.te_rv_on); 804 shiftout = true; 805 if (*s == '\0') 806 continue; 807 c = *s++ & 0377; 808 goto printchar; 809 } 810 if (c == MACROEXPAND || c == MACRODEXPAND) 811 { 812 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 813 "%s$", 814 TermEscape.te_rv_on); 815 if (c == MACRODEXPAND) 816 (void) sm_io_putc(fp, 817 SM_TIME_DEFAULT, '&'); 818 shiftout = true; 819 if (*s == '\0') 820 continue; 821 if (strchr("=~&?", *s) != NULL) 822 (void) sm_io_putc(fp, 823 SM_TIME_DEFAULT, 824 *s++); 825 if (bitset(0200, *s)) 826 (void) sm_io_fprintf(fp, 827 SM_TIME_DEFAULT, 828 "{%s}", 829 macname(bitidx(*s++))); 830 else 831 (void) sm_io_fprintf(fp, 832 SM_TIME_DEFAULT, 833 "%c", 834 *s++); 835 continue; 836 } 837 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 838 { 839 if (bitidx(mp->metaval) == c) 840 { 841 (void) sm_io_fprintf(fp, 842 SM_TIME_DEFAULT, 843 "%s$%c", 844 TermEscape.te_rv_on, 845 mp->metaname); 846 shiftout = true; 847 break; 848 } 849 } 850 if (c == MATCHCLASS || c == MATCHNCLASS) 851 { 852 if (bitset(0200, *s)) 853 (void) sm_io_fprintf(fp, 854 SM_TIME_DEFAULT, 855 "{%s}", 856 macname(bitidx(*s++))); 857 else if (*s != '\0') 858 (void) sm_io_fprintf(fp, 859 SM_TIME_DEFAULT, 860 "%c", 861 *s++); 862 } 863 if (mp->metaname != '\0') 864 continue; 865 866 /* unrecognized meta character */ 867 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-", 868 TermEscape.te_rv_on); 869 shiftout = true; 870 c &= 0177; 871 } 872 printchar: 873 if (isprint(c)) 874 { 875 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 876 continue; 877 } 878 879 /* wasn't a meta-macro -- find another way to print it */ 880 switch (c) 881 { 882 case '\n': 883 c = 'n'; 884 break; 885 886 case '\r': 887 c = 'r'; 888 break; 889 890 case '\t': 891 c = 't'; 892 break; 893 } 894 if (!shiftout) 895 { 896 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 897 TermEscape.te_rv_on); 898 shiftout = true; 899 } 900 if (isprint(c)) 901 { 902 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\'); 903 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 904 } 905 else if (tTd(84, 2)) 906 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c); 907 else if (tTd(84, 1)) 908 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c); 909 else if (!isascii(c) && !tTd(84, 1)) 910 { 911 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^'); 912 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100); 913 } 914 } 915 if (shiftout) 916 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 917 TermEscape.te_normal); 918 (void) sm_io_flush(fp, SM_TIME_DEFAULT); 919 } 920 921 /* 922 ** MAKELOWER -- Translate a line into lower case 923 ** 924 ** Parameters: 925 ** p -- the string to translate. If NULL, return is 926 ** immediate. 927 ** 928 ** Returns: 929 ** none. 930 ** 931 ** Side Effects: 932 ** String pointed to by p is translated to lower case. 933 */ 934 935 void 936 makelower(p) 937 register char *p; 938 { 939 register char c; 940 941 if (p == NULL) 942 return; 943 for (; (c = *p) != '\0'; p++) 944 if (isascii(c) && isupper(c)) 945 *p = tolower(c); 946 } 947 948 /* 949 ** FIXCRLF -- fix <CR><LF> in line. 950 ** 951 ** Looks for the <CR><LF> combination and turns it into the 952 ** UNIX canonical <NL> character. It only takes one line, 953 ** i.e., it is assumed that the first <NL> found is the end 954 ** of the line. 955 ** 956 ** Parameters: 957 ** line -- the line to fix. 958 ** stripnl -- if true, strip the newline also. 959 ** 960 ** Returns: 961 ** none. 962 ** 963 ** Side Effects: 964 ** line is changed in place. 965 */ 966 967 void 968 fixcrlf(line, stripnl) 969 char *line; 970 bool stripnl; 971 { 972 register char *p; 973 974 p = strchr(line, '\n'); 975 if (p == NULL) 976 return; 977 if (p > line && p[-1] == '\r') 978 p--; 979 if (!stripnl) 980 *p++ = '\n'; 981 *p = '\0'; 982 } 983 984 /* 985 ** PUTLINE -- put a line like fputs obeying SMTP conventions 986 ** 987 ** This routine always guarantees outputing a newline (or CRLF, 988 ** as appropriate) at the end of the string. 989 ** 990 ** Parameters: 991 ** l -- line to put. 992 ** mci -- the mailer connection information. 993 ** 994 ** Returns: 995 ** true iff line was written successfully 996 ** 997 ** Side Effects: 998 ** output of l to mci->mci_out. 999 */ 1000 1001 bool 1002 putline(l, mci) 1003 register char *l; 1004 register MCI *mci; 1005 { 1006 return putxline(l, strlen(l), mci, PXLF_MAPFROM); 1007 } 1008 1009 /* 1010 ** PUTXLINE -- putline with flags bits. 1011 ** 1012 ** This routine always guarantees outputing a newline (or CRLF, 1013 ** as appropriate) at the end of the string. 1014 ** 1015 ** Parameters: 1016 ** l -- line to put. 1017 ** len -- the length of the line. 1018 ** mci -- the mailer connection information. 1019 ** pxflags -- flag bits: 1020 ** PXLF_MAPFROM -- map From_ to >From_. 1021 ** PXLF_STRIP8BIT -- strip 8th bit. 1022 ** PXLF_HEADER -- map bare newline in header to newline space. 1023 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 1024 ** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes. 1025 ** 1026 ** Returns: 1027 ** true iff line was written successfully 1028 ** 1029 ** Side Effects: 1030 ** output of l to mci->mci_out. 1031 */ 1032 1033 1034 #define PUTX(limit) \ 1035 do \ 1036 { \ 1037 quotenext = false; \ 1038 while (l < limit) \ 1039 { \ 1040 unsigned char c = (unsigned char) *l++; \ 1041 \ 1042 if (bitset(PXLF_STRIPMQUOTE, pxflags) && \ 1043 !quotenext && c == METAQUOTE) \ 1044 { \ 1045 quotenext = true; \ 1046 continue; \ 1047 } \ 1048 quotenext = false; \ 1049 if (strip8bit) \ 1050 c &= 0177; \ 1051 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \ 1052 c) == SM_IO_EOF) \ 1053 { \ 1054 dead = true; \ 1055 break; \ 1056 } \ 1057 if (TrafficLogFile != NULL) \ 1058 (void) sm_io_putc(TrafficLogFile, \ 1059 SM_TIME_DEFAULT, \ 1060 c); \ 1061 } \ 1062 } while (0) 1063 1064 bool 1065 putxline(l, len, mci, pxflags) 1066 register char *l; 1067 size_t len; 1068 register MCI *mci; 1069 int pxflags; 1070 { 1071 register char *p, *end; 1072 int slop; 1073 bool dead, quotenext, strip8bit; 1074 1075 /* strip out 0200 bits -- these can look like TELNET protocol */ 1076 strip8bit = bitset(MCIF_7BIT, mci->mci_flags) || 1077 bitset(PXLF_STRIP8BIT, pxflags); 1078 dead = false; 1079 slop = 0; 1080 1081 end = l + len; 1082 do 1083 { 1084 bool noeol = false; 1085 1086 /* find the end of the line */ 1087 p = memchr(l, '\n', end - l); 1088 if (p == NULL) 1089 { 1090 p = end; 1091 noeol = true; 1092 } 1093 1094 if (TrafficLogFile != NULL) 1095 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1096 "%05d >>> ", (int) CurrentPid); 1097 1098 /* check for line overflow */ 1099 while (mci->mci_mailer->m_linelimit > 0 && 1100 (p - l + slop) > mci->mci_mailer->m_linelimit) 1101 { 1102 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 1103 1104 if (l[0] == '.' && slop == 0 && 1105 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1106 { 1107 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1108 '.') == SM_IO_EOF) 1109 dead = true; 1110 if (TrafficLogFile != NULL) 1111 (void) sm_io_putc(TrafficLogFile, 1112 SM_TIME_DEFAULT, '.'); 1113 } 1114 else if (l[0] == 'F' && slop == 0 && 1115 bitset(PXLF_MAPFROM, pxflags) && 1116 strncmp(l, "From ", 5) == 0 && 1117 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1118 { 1119 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1120 '>') == SM_IO_EOF) 1121 dead = true; 1122 if (TrafficLogFile != NULL) 1123 (void) sm_io_putc(TrafficLogFile, 1124 SM_TIME_DEFAULT, 1125 '>'); 1126 } 1127 if (dead) 1128 break; 1129 1130 PUTX(q); 1131 if (dead) 1132 break; 1133 1134 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1135 '!') == SM_IO_EOF || 1136 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1137 mci->mci_mailer->m_eol) == SM_IO_EOF || 1138 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1139 ' ') == SM_IO_EOF) 1140 { 1141 dead = true; 1142 break; 1143 } 1144 if (TrafficLogFile != NULL) 1145 { 1146 (void) sm_io_fprintf(TrafficLogFile, 1147 SM_TIME_DEFAULT, 1148 "!\n%05d >>> ", 1149 (int) CurrentPid); 1150 } 1151 slop = 1; 1152 } 1153 1154 if (dead) 1155 break; 1156 1157 /* output last part */ 1158 if (l[0] == '.' && slop == 0 && 1159 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1160 { 1161 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1162 SM_IO_EOF) 1163 { 1164 dead = true; 1165 break; 1166 } 1167 if (TrafficLogFile != NULL) 1168 (void) sm_io_putc(TrafficLogFile, 1169 SM_TIME_DEFAULT, '.'); 1170 } 1171 else if (l[0] == 'F' && slop == 0 && 1172 bitset(PXLF_MAPFROM, pxflags) && 1173 strncmp(l, "From ", 5) == 0 && 1174 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1175 { 1176 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1177 SM_IO_EOF) 1178 { 1179 dead = true; 1180 break; 1181 } 1182 if (TrafficLogFile != NULL) 1183 (void) sm_io_putc(TrafficLogFile, 1184 SM_TIME_DEFAULT, '>'); 1185 } 1186 PUTX(p); 1187 if (dead) 1188 break; 1189 1190 if (TrafficLogFile != NULL) 1191 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1192 '\n'); 1193 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) && 1194 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1195 mci->mci_mailer->m_eol) == SM_IO_EOF) 1196 { 1197 dead = true; 1198 break; 1199 } 1200 if (l < end && *l == '\n') 1201 { 1202 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1203 bitset(PXLF_HEADER, pxflags)) 1204 { 1205 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1206 ' ') == SM_IO_EOF) 1207 { 1208 dead = true; 1209 break; 1210 } 1211 1212 if (TrafficLogFile != NULL) 1213 (void) sm_io_putc(TrafficLogFile, 1214 SM_TIME_DEFAULT, ' '); 1215 } 1216 } 1217 1218 } while (l < end); 1219 return !dead; 1220 } 1221 1222 /* 1223 ** XUNLINK -- unlink a file, doing logging as appropriate. 1224 ** 1225 ** Parameters: 1226 ** f -- name of file to unlink. 1227 ** 1228 ** Returns: 1229 ** return value of unlink() 1230 ** 1231 ** Side Effects: 1232 ** f is unlinked. 1233 */ 1234 1235 int 1236 xunlink(f) 1237 char *f; 1238 { 1239 register int i; 1240 int save_errno; 1241 1242 if (LogLevel > 98) 1243 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1244 1245 i = unlink(f); 1246 save_errno = errno; 1247 if (i < 0 && LogLevel > 97) 1248 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1249 f, errno); 1250 if (i >= 0) 1251 SYNC_DIR(f, false); 1252 errno = save_errno; 1253 return i; 1254 } 1255 1256 /* 1257 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1258 ** 1259 ** Parameters: 1260 ** buf -- place to put the input line. 1261 ** siz -- size of buf. 1262 ** fp -- file to read from. 1263 ** timeout -- the timeout before error occurs. 1264 ** during -- what we are trying to read (for error messages). 1265 ** 1266 ** Returns: 1267 ** NULL on error (including timeout). This may also leave 1268 ** buf containing a null string. 1269 ** buf otherwise. 1270 */ 1271 1272 1273 char * 1274 sfgets(buf, siz, fp, timeout, during) 1275 char *buf; 1276 int siz; 1277 SM_FILE_T *fp; 1278 time_t timeout; 1279 char *during; 1280 { 1281 register char *p; 1282 int save_errno; 1283 int io_timeout; 1284 1285 SM_REQUIRE(siz > 0); 1286 SM_REQUIRE(buf != NULL); 1287 1288 if (fp == NULL) 1289 { 1290 buf[0] = '\0'; 1291 errno = EBADF; 1292 return NULL; 1293 } 1294 1295 /* try to read */ 1296 p = NULL; 1297 errno = 0; 1298 1299 /* convert the timeout to sm_io notation */ 1300 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1301 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1302 { 1303 errno = 0; 1304 p = sm_io_fgets(fp, io_timeout, buf, siz); 1305 if (p == NULL && errno == EAGAIN) 1306 { 1307 /* The sm_io_fgets() call timedout */ 1308 if (LogLevel > 1) 1309 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1310 "timeout waiting for input from %.100s during %s", 1311 CURHOSTNAME, 1312 during); 1313 buf[0] = '\0'; 1314 #if XDEBUG 1315 checkfd012(during); 1316 #endif /* XDEBUG */ 1317 if (TrafficLogFile != NULL) 1318 (void) sm_io_fprintf(TrafficLogFile, 1319 SM_TIME_DEFAULT, 1320 "%05d <<< [TIMEOUT]\n", 1321 (int) CurrentPid); 1322 errno = ETIMEDOUT; 1323 return NULL; 1324 } 1325 if (p != NULL || errno != EINTR) 1326 break; 1327 (void) sm_io_clearerr(fp); 1328 } 1329 save_errno = errno; 1330 1331 /* clean up the books and exit */ 1332 LineNumber++; 1333 if (p == NULL) 1334 { 1335 buf[0] = '\0'; 1336 if (TrafficLogFile != NULL) 1337 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1338 "%05d <<< [EOF]\n", 1339 (int) CurrentPid); 1340 errno = save_errno; 1341 return NULL; 1342 } 1343 if (TrafficLogFile != NULL) 1344 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1345 "%05d <<< %s", (int) CurrentPid, buf); 1346 if (SevenBitInput) 1347 { 1348 for (p = buf; *p != '\0'; p++) 1349 *p &= ~0200; 1350 } 1351 else if (!HasEightBits) 1352 { 1353 for (p = buf; *p != '\0'; p++) 1354 { 1355 if (bitset(0200, *p)) 1356 { 1357 HasEightBits = true; 1358 break; 1359 } 1360 } 1361 } 1362 return buf; 1363 } 1364 1365 /* 1366 ** FGETFOLDED -- like fgets, but knows about folded lines. 1367 ** 1368 ** Parameters: 1369 ** buf -- place to put result. 1370 ** np -- pointer to bytes available; will be updated with 1371 ** the actual buffer size (not number of bytes filled) 1372 ** on return. 1373 ** f -- file to read from. 1374 ** 1375 ** Returns: 1376 ** input line(s) on success, NULL on error or SM_IO_EOF. 1377 ** This will normally be buf -- unless the line is too 1378 ** long, when it will be sm_malloc_x()ed. 1379 ** 1380 ** Side Effects: 1381 ** buf gets lines from f, with continuation lines (lines 1382 ** with leading white space) appended. CRLF's are mapped 1383 ** into single newlines. Any trailing NL is stripped. 1384 */ 1385 1386 char * 1387 fgetfolded(buf, np, f) 1388 char *buf; 1389 int *np; 1390 SM_FILE_T *f; 1391 { 1392 register char *p = buf; 1393 char *bp = buf; 1394 register int i; 1395 int n; 1396 1397 SM_REQUIRE(np != NULL); 1398 n = *np; 1399 SM_REQUIRE(n > 0); 1400 SM_REQUIRE(buf != NULL); 1401 if (f == NULL) 1402 { 1403 buf[0] = '\0'; 1404 errno = EBADF; 1405 return NULL; 1406 } 1407 1408 n--; 1409 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1410 { 1411 if (i == '\r') 1412 { 1413 i = sm_io_getc(f, SM_TIME_DEFAULT); 1414 if (i != '\n') 1415 { 1416 if (i != SM_IO_EOF) 1417 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1418 i); 1419 i = '\r'; 1420 } 1421 } 1422 if (--n <= 0) 1423 { 1424 /* allocate new space */ 1425 char *nbp; 1426 int nn; 1427 1428 nn = (p - bp); 1429 if (nn < MEMCHUNKSIZE) 1430 nn *= 2; 1431 else 1432 nn += MEMCHUNKSIZE; 1433 nbp = sm_malloc_x(nn); 1434 memmove(nbp, bp, p - bp); 1435 p = &nbp[p - bp]; 1436 if (bp != buf) 1437 sm_free(bp); 1438 bp = nbp; 1439 n = nn - (p - bp); 1440 *np = nn; 1441 } 1442 *p++ = i; 1443 if (i == '\n') 1444 { 1445 LineNumber++; 1446 i = sm_io_getc(f, SM_TIME_DEFAULT); 1447 if (i != SM_IO_EOF) 1448 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1449 if (i != ' ' && i != '\t') 1450 break; 1451 } 1452 } 1453 if (p == bp) 1454 return NULL; 1455 if (p[-1] == '\n') 1456 p--; 1457 *p = '\0'; 1458 return bp; 1459 } 1460 1461 /* 1462 ** CURTIME -- return current time. 1463 ** 1464 ** Parameters: 1465 ** none. 1466 ** 1467 ** Returns: 1468 ** the current time. 1469 */ 1470 1471 time_t 1472 curtime() 1473 { 1474 auto time_t t; 1475 1476 (void) time(&t); 1477 return t; 1478 } 1479 1480 /* 1481 ** ATOBOOL -- convert a string representation to boolean. 1482 ** 1483 ** Defaults to false 1484 ** 1485 ** Parameters: 1486 ** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1487 ** others as false. 1488 ** 1489 ** Returns: 1490 ** A boolean representation of the string. 1491 */ 1492 1493 bool 1494 atobool(s) 1495 register char *s; 1496 { 1497 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1498 return true; 1499 return false; 1500 } 1501 1502 /* 1503 ** ATOOCT -- convert a string representation to octal. 1504 ** 1505 ** Parameters: 1506 ** s -- string to convert. 1507 ** 1508 ** Returns: 1509 ** An integer representing the string interpreted as an 1510 ** octal number. 1511 */ 1512 1513 int 1514 atooct(s) 1515 register char *s; 1516 { 1517 register int i = 0; 1518 1519 while (*s >= '0' && *s <= '7') 1520 i = (i << 3) | (*s++ - '0'); 1521 return i; 1522 } 1523 1524 /* 1525 ** BITINTERSECT -- tell if two bitmaps intersect 1526 ** 1527 ** Parameters: 1528 ** a, b -- the bitmaps in question 1529 ** 1530 ** Returns: 1531 ** true if they have a non-null intersection 1532 ** false otherwise 1533 */ 1534 1535 bool 1536 bitintersect(a, b) 1537 BITMAP256 a; 1538 BITMAP256 b; 1539 { 1540 int i; 1541 1542 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1543 { 1544 if ((a[i] & b[i]) != 0) 1545 return true; 1546 } 1547 return false; 1548 } 1549 1550 /* 1551 ** BITZEROP -- tell if a bitmap is all zero 1552 ** 1553 ** Parameters: 1554 ** map -- the bit map to check 1555 ** 1556 ** Returns: 1557 ** true if map is all zero. 1558 ** false if there are any bits set in map. 1559 */ 1560 1561 bool 1562 bitzerop(map) 1563 BITMAP256 map; 1564 { 1565 int i; 1566 1567 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1568 { 1569 if (map[i] != 0) 1570 return false; 1571 } 1572 return true; 1573 } 1574 1575 /* 1576 ** STRCONTAINEDIN -- tell if one string is contained in another 1577 ** 1578 ** Parameters: 1579 ** icase -- ignore case? 1580 ** a -- possible substring. 1581 ** b -- possible superstring. 1582 ** 1583 ** Returns: 1584 ** true if a is contained in b (case insensitive). 1585 ** false otherwise. 1586 */ 1587 1588 bool 1589 strcontainedin(icase, a, b) 1590 bool icase; 1591 register char *a; 1592 register char *b; 1593 { 1594 int la; 1595 int lb; 1596 int c; 1597 1598 la = strlen(a); 1599 lb = strlen(b); 1600 c = *a; 1601 if (icase && isascii(c) && isupper(c)) 1602 c = tolower(c); 1603 for (; lb-- >= la; b++) 1604 { 1605 if (icase) 1606 { 1607 if (*b != c && 1608 isascii(*b) && isupper(*b) && tolower(*b) != c) 1609 continue; 1610 if (sm_strncasecmp(a, b, la) == 0) 1611 return true; 1612 } 1613 else 1614 { 1615 if (*b != c) 1616 continue; 1617 if (strncmp(a, b, la) == 0) 1618 return true; 1619 } 1620 } 1621 return false; 1622 } 1623 1624 /* 1625 ** CHECKFD012 -- check low numbered file descriptors 1626 ** 1627 ** File descriptors 0, 1, and 2 should be open at all times. 1628 ** This routine verifies that, and fixes it if not true. 1629 ** 1630 ** Parameters: 1631 ** where -- a tag printed if the assertion failed 1632 ** 1633 ** Returns: 1634 ** none 1635 */ 1636 1637 void 1638 checkfd012(where) 1639 char *where; 1640 { 1641 #if XDEBUG 1642 register int i; 1643 1644 for (i = 0; i < 3; i++) 1645 fill_fd(i, where); 1646 #endif /* XDEBUG */ 1647 } 1648 1649 /* 1650 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1651 ** 1652 ** Parameters: 1653 ** fd -- file descriptor to check. 1654 ** where -- tag to print on failure. 1655 ** 1656 ** Returns: 1657 ** none. 1658 */ 1659 1660 void 1661 checkfdopen(fd, where) 1662 int fd; 1663 char *where; 1664 { 1665 #if XDEBUG 1666 struct stat st; 1667 1668 if (fstat(fd, &st) < 0 && errno == EBADF) 1669 { 1670 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1671 printopenfds(true); 1672 } 1673 #endif /* XDEBUG */ 1674 } 1675 1676 /* 1677 ** CHECKFDS -- check for new or missing file descriptors 1678 ** 1679 ** Parameters: 1680 ** where -- tag for printing. If null, take a base line. 1681 ** 1682 ** Returns: 1683 ** none 1684 ** 1685 ** Side Effects: 1686 ** If where is set, shows changes since the last call. 1687 */ 1688 1689 void 1690 checkfds(where) 1691 char *where; 1692 { 1693 int maxfd; 1694 register int fd; 1695 bool printhdr = true; 1696 int save_errno = errno; 1697 static BITMAP256 baseline; 1698 extern int DtableSize; 1699 1700 if (DtableSize > BITMAPBITS) 1701 maxfd = BITMAPBITS; 1702 else 1703 maxfd = DtableSize; 1704 if (where == NULL) 1705 clrbitmap(baseline); 1706 1707 for (fd = 0; fd < maxfd; fd++) 1708 { 1709 struct stat stbuf; 1710 1711 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1712 { 1713 if (!bitnset(fd, baseline)) 1714 continue; 1715 clrbitn(fd, baseline); 1716 } 1717 else if (!bitnset(fd, baseline)) 1718 setbitn(fd, baseline); 1719 else 1720 continue; 1721 1722 /* file state has changed */ 1723 if (where == NULL) 1724 continue; 1725 if (printhdr) 1726 { 1727 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1728 "%s: changed fds:", 1729 where); 1730 printhdr = false; 1731 } 1732 dumpfd(fd, true, true); 1733 } 1734 errno = save_errno; 1735 } 1736 1737 /* 1738 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1739 ** 1740 ** Parameters: 1741 ** logit -- if set, send output to syslog; otherwise 1742 ** print for debugging. 1743 ** 1744 ** Returns: 1745 ** none. 1746 */ 1747 1748 #if NETINET || NETINET6 1749 # include <arpa/inet.h> 1750 #endif /* NETINET || NETINET6 */ 1751 1752 void 1753 printopenfds(logit) 1754 bool logit; 1755 { 1756 register int fd; 1757 extern int DtableSize; 1758 1759 for (fd = 0; fd < DtableSize; fd++) 1760 dumpfd(fd, false, logit); 1761 } 1762 1763 /* 1764 ** DUMPFD -- dump a file descriptor 1765 ** 1766 ** Parameters: 1767 ** fd -- the file descriptor to dump. 1768 ** printclosed -- if set, print a notification even if 1769 ** it is closed; otherwise print nothing. 1770 ** logit -- if set, use sm_syslog instead of sm_dprintf() 1771 ** 1772 ** Returns: 1773 ** none. 1774 */ 1775 1776 void 1777 dumpfd(fd, printclosed, logit) 1778 int fd; 1779 bool printclosed; 1780 bool logit; 1781 { 1782 register char *p; 1783 char *hp; 1784 #ifdef S_IFSOCK 1785 SOCKADDR sa; 1786 #endif /* S_IFSOCK */ 1787 auto SOCKADDR_LEN_T slen; 1788 int i; 1789 #if STAT64 > 0 1790 struct stat64 st; 1791 #else /* STAT64 > 0 */ 1792 struct stat st; 1793 #endif /* STAT64 > 0 */ 1794 char buf[200]; 1795 1796 p = buf; 1797 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1798 p += strlen(p); 1799 1800 if ( 1801 #if STAT64 > 0 1802 fstat64(fd, &st) 1803 #else /* STAT64 > 0 */ 1804 fstat(fd, &st) 1805 #endif /* STAT64 > 0 */ 1806 < 0) 1807 { 1808 if (errno != EBADF) 1809 { 1810 (void) sm_snprintf(p, SPACELEFT(buf, p), 1811 "CANNOT STAT (%s)", 1812 sm_errstring(errno)); 1813 goto printit; 1814 } 1815 else if (printclosed) 1816 { 1817 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1818 goto printit; 1819 } 1820 return; 1821 } 1822 1823 i = fcntl(fd, F_GETFL, 0); 1824 if (i != -1) 1825 { 1826 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1827 p += strlen(p); 1828 } 1829 1830 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1831 (int) st.st_mode); 1832 p += strlen(p); 1833 switch (st.st_mode & S_IFMT) 1834 { 1835 #ifdef S_IFSOCK 1836 case S_IFSOCK: 1837 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1838 p += strlen(p); 1839 memset(&sa, '\0', sizeof(sa)); 1840 slen = sizeof(sa); 1841 if (getsockname(fd, &sa.sa, &slen) < 0) 1842 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1843 sm_errstring(errno)); 1844 else 1845 { 1846 hp = hostnamebyanyaddr(&sa); 1847 if (hp == NULL) 1848 { 1849 /* EMPTY */ 1850 /* do nothing */ 1851 } 1852 # if NETINET 1853 else if (sa.sa.sa_family == AF_INET) 1854 (void) sm_snprintf(p, SPACELEFT(buf, p), 1855 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1856 # endif /* NETINET */ 1857 # if NETINET6 1858 else if (sa.sa.sa_family == AF_INET6) 1859 (void) sm_snprintf(p, SPACELEFT(buf, p), 1860 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1861 # endif /* NETINET6 */ 1862 else 1863 (void) sm_snprintf(p, SPACELEFT(buf, p), 1864 "%s", hp); 1865 } 1866 p += strlen(p); 1867 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 1868 p += strlen(p); 1869 slen = sizeof(sa); 1870 if (getpeername(fd, &sa.sa, &slen) < 0) 1871 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1872 sm_errstring(errno)); 1873 else 1874 { 1875 hp = hostnamebyanyaddr(&sa); 1876 if (hp == NULL) 1877 { 1878 /* EMPTY */ 1879 /* do nothing */ 1880 } 1881 # if NETINET 1882 else if (sa.sa.sa_family == AF_INET) 1883 (void) sm_snprintf(p, SPACELEFT(buf, p), 1884 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1885 # endif /* NETINET */ 1886 # if NETINET6 1887 else if (sa.sa.sa_family == AF_INET6) 1888 (void) sm_snprintf(p, SPACELEFT(buf, p), 1889 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1890 # endif /* NETINET6 */ 1891 else 1892 (void) sm_snprintf(p, SPACELEFT(buf, p), 1893 "%s", hp); 1894 } 1895 break; 1896 #endif /* S_IFSOCK */ 1897 1898 case S_IFCHR: 1899 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 1900 p += strlen(p); 1901 goto defprint; 1902 1903 #ifdef S_IFBLK 1904 case S_IFBLK: 1905 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 1906 p += strlen(p); 1907 goto defprint; 1908 #endif /* S_IFBLK */ 1909 1910 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1911 case S_IFIFO: 1912 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1913 p += strlen(p); 1914 goto defprint; 1915 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1916 1917 #ifdef S_IFDIR 1918 case S_IFDIR: 1919 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 1920 p += strlen(p); 1921 goto defprint; 1922 #endif /* S_IFDIR */ 1923 1924 #ifdef S_IFLNK 1925 case S_IFLNK: 1926 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 1927 p += strlen(p); 1928 goto defprint; 1929 #endif /* S_IFLNK */ 1930 1931 default: 1932 defprint: 1933 (void) sm_snprintf(p, SPACELEFT(buf, p), 1934 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 1935 major(st.st_dev), minor(st.st_dev), 1936 (ULONGLONG_T) st.st_ino, 1937 (int) st.st_nlink, (int) st.st_uid, 1938 (int) st.st_gid); 1939 p += strlen(p); 1940 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 1941 (ULONGLONG_T) st.st_size); 1942 break; 1943 } 1944 1945 printit: 1946 if (logit) 1947 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1948 "%.800s", buf); 1949 else 1950 sm_dprintf("%s\n", buf); 1951 } 1952 1953 /* 1954 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1955 ** 1956 ** Parameters: 1957 ** host -- the host to shorten (stripped in place). 1958 ** 1959 ** Returns: 1960 ** place where string was truncated, NULL if not truncated. 1961 */ 1962 1963 char * 1964 shorten_hostname(host) 1965 char host[]; 1966 { 1967 register char *p; 1968 char *mydom; 1969 int i; 1970 bool canon = false; 1971 1972 /* strip off final dot */ 1973 i = strlen(host); 1974 p = &host[(i == 0) ? 0 : i - 1]; 1975 if (*p == '.') 1976 { 1977 *p = '\0'; 1978 canon = true; 1979 } 1980 1981 /* see if there is any domain at all -- if not, we are done */ 1982 p = strchr(host, '.'); 1983 if (p == NULL) 1984 return NULL; 1985 1986 /* yes, we have a domain -- see if it looks like us */ 1987 mydom = macvalue('m', CurEnv); 1988 if (mydom == NULL) 1989 mydom = ""; 1990 i = strlen(++p); 1991 if ((canon ? sm_strcasecmp(p, mydom) 1992 : sm_strncasecmp(p, mydom, i)) == 0 && 1993 (mydom[i] == '.' || mydom[i] == '\0')) 1994 { 1995 *--p = '\0'; 1996 return p; 1997 } 1998 return NULL; 1999 } 2000 2001 /* 2002 ** PROG_OPEN -- open a program for reading 2003 ** 2004 ** Parameters: 2005 ** argv -- the argument list. 2006 ** pfd -- pointer to a place to store the file descriptor. 2007 ** e -- the current envelope. 2008 ** 2009 ** Returns: 2010 ** pid of the process -- -1 if it failed. 2011 */ 2012 2013 pid_t 2014 prog_open(argv, pfd, e) 2015 char **argv; 2016 int *pfd; 2017 ENVELOPE *e; 2018 { 2019 pid_t pid; 2020 int save_errno; 2021 int sff; 2022 int ret; 2023 int fdv[2]; 2024 char *p, *q; 2025 char buf[MAXPATHLEN]; 2026 extern int DtableSize; 2027 2028 if (pipe(fdv) < 0) 2029 { 2030 syserr("%s: cannot create pipe for stdout", argv[0]); 2031 return -1; 2032 } 2033 pid = fork(); 2034 if (pid < 0) 2035 { 2036 syserr("%s: cannot fork", argv[0]); 2037 (void) close(fdv[0]); 2038 (void) close(fdv[1]); 2039 return -1; 2040 } 2041 if (pid > 0) 2042 { 2043 /* parent */ 2044 (void) close(fdv[1]); 2045 *pfd = fdv[0]; 2046 return pid; 2047 } 2048 2049 /* Reset global flags */ 2050 RestartRequest = NULL; 2051 RestartWorkGroup = false; 2052 ShutdownRequest = NULL; 2053 PendingSignal = 0; 2054 CurrentPid = getpid(); 2055 2056 /* 2057 ** Initialize exception stack and default exception 2058 ** handler for child process. 2059 */ 2060 2061 sm_exc_newthread(fatal_error); 2062 2063 /* child -- close stdin */ 2064 (void) close(0); 2065 2066 /* stdout goes back to parent */ 2067 (void) close(fdv[0]); 2068 if (dup2(fdv[1], 1) < 0) 2069 { 2070 syserr("%s: cannot dup2 for stdout", argv[0]); 2071 _exit(EX_OSERR); 2072 } 2073 (void) close(fdv[1]); 2074 2075 /* stderr goes to transcript if available */ 2076 if (e->e_xfp != NULL) 2077 { 2078 int xfd; 2079 2080 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 2081 if (xfd >= 0 && dup2(xfd, 2) < 0) 2082 { 2083 syserr("%s: cannot dup2 for stderr", argv[0]); 2084 _exit(EX_OSERR); 2085 } 2086 } 2087 2088 /* this process has no right to the queue file */ 2089 if (e->e_lockfp != NULL) 2090 { 2091 int fd; 2092 2093 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 2094 if (fd >= 0) 2095 (void) close(fd); 2096 else 2097 syserr("%s: lockfp does not have a fd", argv[0]); 2098 } 2099 2100 /* chroot to the program mailer directory, if defined */ 2101 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 2102 { 2103 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e); 2104 if (chroot(buf) < 0) 2105 { 2106 syserr("prog_open: cannot chroot(%s)", buf); 2107 exit(EX_TEMPFAIL); 2108 } 2109 if (chdir("/") < 0) 2110 { 2111 syserr("prog_open: cannot chdir(/)"); 2112 exit(EX_TEMPFAIL); 2113 } 2114 } 2115 2116 /* run as default user */ 2117 endpwent(); 2118 sm_mbdb_terminate(); 2119 #if _FFR_MEMSTAT 2120 (void) sm_memstat_close(); 2121 #endif /* _FFR_MEMSTAT */ 2122 if (setgid(DefGid) < 0 && geteuid() == 0) 2123 { 2124 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2125 exit(EX_TEMPFAIL); 2126 } 2127 if (setuid(DefUid) < 0 && geteuid() == 0) 2128 { 2129 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2130 exit(EX_TEMPFAIL); 2131 } 2132 2133 /* run in some directory */ 2134 if (ProgMailer != NULL) 2135 p = ProgMailer->m_execdir; 2136 else 2137 p = NULL; 2138 for (; p != NULL; p = q) 2139 { 2140 q = strchr(p, ':'); 2141 if (q != NULL) 2142 *q = '\0'; 2143 expand(p, buf, sizeof(buf), e); 2144 if (q != NULL) 2145 *q++ = ':'; 2146 if (buf[0] != '\0' && chdir(buf) >= 0) 2147 break; 2148 } 2149 if (p == NULL) 2150 { 2151 /* backup directories */ 2152 if (chdir("/tmp") < 0) 2153 (void) chdir("/"); 2154 } 2155 2156 /* Check safety of program to be run */ 2157 sff = SFF_ROOTOK|SFF_EXECOK; 2158 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2159 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2160 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2161 sff |= SFF_NOPATHCHECK; 2162 else 2163 sff |= SFF_SAFEDIRPATH; 2164 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2165 if (ret != 0) 2166 sm_syslog(LOG_INFO, e->e_id, 2167 "Warning: prog_open: program %s unsafe: %s", 2168 argv[0], sm_errstring(ret)); 2169 2170 /* arrange for all the files to be closed */ 2171 sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2172 2173 /* now exec the process */ 2174 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2175 2176 /* woops! failed */ 2177 save_errno = errno; 2178 syserr("%s: cannot exec", argv[0]); 2179 if (transienterror(save_errno)) 2180 _exit(EX_OSERR); 2181 _exit(EX_CONFIG); 2182 return -1; /* avoid compiler warning on IRIX */ 2183 } 2184 2185 /* 2186 ** GET_COLUMN -- look up a Column in a line buffer 2187 ** 2188 ** Parameters: 2189 ** line -- the raw text line to search. 2190 ** col -- the column number to fetch. 2191 ** delim -- the delimiter between columns. If null, 2192 ** use white space. 2193 ** buf -- the output buffer. 2194 ** buflen -- the length of buf. 2195 ** 2196 ** Returns: 2197 ** buf if successful. 2198 ** NULL otherwise. 2199 */ 2200 2201 char * 2202 get_column(line, col, delim, buf, buflen) 2203 char line[]; 2204 int col; 2205 int delim; 2206 char buf[]; 2207 int buflen; 2208 { 2209 char *p; 2210 char *begin, *end; 2211 int i; 2212 char delimbuf[4]; 2213 2214 if ((char) delim == '\0') 2215 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf)); 2216 else 2217 { 2218 delimbuf[0] = (char) delim; 2219 delimbuf[1] = '\0'; 2220 } 2221 2222 p = line; 2223 if (*p == '\0') 2224 return NULL; /* line empty */ 2225 if (*p == (char) delim && col == 0) 2226 return NULL; /* first column empty */ 2227 2228 begin = line; 2229 2230 if (col == 0 && (char) delim == '\0') 2231 { 2232 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2233 begin++; 2234 } 2235 2236 for (i = 0; i < col; i++) 2237 { 2238 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2239 return NULL; /* no such column */ 2240 begin++; 2241 if ((char) delim == '\0') 2242 { 2243 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2244 begin++; 2245 } 2246 } 2247 2248 end = strpbrk(begin, delimbuf); 2249 if (end == NULL) 2250 i = strlen(begin); 2251 else 2252 i = end - begin; 2253 if (i >= buflen) 2254 i = buflen - 1; 2255 (void) sm_strlcpy(buf, begin, i + 1); 2256 return buf; 2257 } 2258 2259 /* 2260 ** CLEANSTRCPY -- copy string keeping out bogus characters 2261 ** 2262 ** Parameters: 2263 ** t -- "to" string. 2264 ** f -- "from" string. 2265 ** l -- length of space available in "to" string. 2266 ** 2267 ** Returns: 2268 ** none. 2269 */ 2270 2271 void 2272 cleanstrcpy(t, f, l) 2273 register char *t; 2274 register char *f; 2275 int l; 2276 { 2277 /* check for newlines and log if necessary */ 2278 (void) denlstring(f, true, true); 2279 2280 if (l <= 0) 2281 syserr("!cleanstrcpy: length == 0"); 2282 2283 l--; 2284 while (l > 0 && *f != '\0') 2285 { 2286 if (isascii(*f) && 2287 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2288 { 2289 l--; 2290 *t++ = *f; 2291 } 2292 f++; 2293 } 2294 *t = '\0'; 2295 } 2296 2297 /* 2298 ** DENLSTRING -- convert newlines in a string to spaces 2299 ** 2300 ** Parameters: 2301 ** s -- the input string 2302 ** strict -- if set, don't permit continuation lines. 2303 ** logattacks -- if set, log attempted attacks. 2304 ** 2305 ** Returns: 2306 ** A pointer to a version of the string with newlines 2307 ** mapped to spaces. This should be copied. 2308 */ 2309 2310 char * 2311 denlstring(s, strict, logattacks) 2312 char *s; 2313 bool strict; 2314 bool logattacks; 2315 { 2316 register char *p; 2317 int l; 2318 static char *bp = NULL; 2319 static int bl = 0; 2320 2321 p = s; 2322 while ((p = strchr(p, '\n')) != NULL) 2323 if (strict || (*++p != ' ' && *p != '\t')) 2324 break; 2325 if (p == NULL) 2326 return s; 2327 2328 l = strlen(s) + 1; 2329 if (bl < l) 2330 { 2331 /* allocate more space */ 2332 char *nbp = sm_pmalloc_x(l); 2333 2334 if (bp != NULL) 2335 sm_free(bp); 2336 bp = nbp; 2337 bl = l; 2338 } 2339 (void) sm_strlcpy(bp, s, l); 2340 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2341 *p++ = ' '; 2342 2343 if (logattacks) 2344 { 2345 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL, 2346 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2347 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2348 shortenstring(bp, MAXSHORTSTR)); 2349 } 2350 2351 return bp; 2352 } 2353 2354 /* 2355 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 2356 ** 2357 ** Parameters: 2358 ** s -- string to manipulate (in place) 2359 ** subst -- character to use as replacement 2360 ** 2361 ** Returns: 2362 ** true iff string did not contain "unprintable" characters 2363 */ 2364 2365 bool 2366 strreplnonprt(s, c) 2367 char *s; 2368 int c; 2369 { 2370 bool ok; 2371 2372 ok = true; 2373 if (s == NULL) 2374 return ok; 2375 while (*s != '\0') 2376 { 2377 if (!(isascii(*s) && isprint(*s))) 2378 { 2379 *s = c; 2380 ok = false; 2381 } 2382 ++s; 2383 } 2384 return ok; 2385 } 2386 2387 /* 2388 ** PATH_IS_DIR -- check to see if file exists and is a directory. 2389 ** 2390 ** There are some additional checks for security violations in 2391 ** here. This routine is intended to be used for the host status 2392 ** support. 2393 ** 2394 ** Parameters: 2395 ** pathname -- pathname to check for directory-ness. 2396 ** createflag -- if set, create directory if needed. 2397 ** 2398 ** Returns: 2399 ** true -- if the indicated pathname is a directory 2400 ** false -- otherwise 2401 */ 2402 2403 bool 2404 path_is_dir(pathname, createflag) 2405 char *pathname; 2406 bool createflag; 2407 { 2408 struct stat statbuf; 2409 2410 #if HASLSTAT 2411 if (lstat(pathname, &statbuf) < 0) 2412 #else /* HASLSTAT */ 2413 if (stat(pathname, &statbuf) < 0) 2414 #endif /* HASLSTAT */ 2415 { 2416 if (errno != ENOENT || !createflag) 2417 return false; 2418 if (mkdir(pathname, 0755) < 0) 2419 return false; 2420 return true; 2421 } 2422 if (!S_ISDIR(statbuf.st_mode)) 2423 { 2424 errno = ENOTDIR; 2425 return false; 2426 } 2427 2428 /* security: don't allow writable directories */ 2429 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2430 { 2431 errno = EACCES; 2432 return false; 2433 } 2434 return true; 2435 } 2436 2437 /* 2438 ** PROC_LIST_ADD -- add process id to list of our children 2439 ** 2440 ** Parameters: 2441 ** pid -- pid to add to list. 2442 ** task -- task of pid. 2443 ** type -- type of process. 2444 ** count -- number of processes. 2445 ** other -- other information for this type. 2446 ** 2447 ** Returns: 2448 ** none 2449 ** 2450 ** Side Effects: 2451 ** May increase CurChildren. May grow ProcList. 2452 */ 2453 2454 typedef struct procs PROCS_T; 2455 2456 struct procs 2457 { 2458 pid_t proc_pid; 2459 char *proc_task; 2460 int proc_type; 2461 int proc_count; 2462 int proc_other; 2463 SOCKADDR proc_hostaddr; 2464 }; 2465 2466 static PROCS_T *volatile ProcListVec = NULL; 2467 static int ProcListSize = 0; 2468 2469 void 2470 proc_list_add(pid, task, type, count, other, hostaddr) 2471 pid_t pid; 2472 char *task; 2473 int type; 2474 int count; 2475 int other; 2476 SOCKADDR *hostaddr; 2477 { 2478 int i; 2479 2480 for (i = 0; i < ProcListSize; i++) 2481 { 2482 if (ProcListVec[i].proc_pid == NO_PID) 2483 break; 2484 } 2485 if (i >= ProcListSize) 2486 { 2487 /* probe the existing vector to avoid growing infinitely */ 2488 proc_list_probe(); 2489 2490 /* now scan again */ 2491 for (i = 0; i < ProcListSize; i++) 2492 { 2493 if (ProcListVec[i].proc_pid == NO_PID) 2494 break; 2495 } 2496 } 2497 if (i >= ProcListSize) 2498 { 2499 /* grow process list */ 2500 int chldwasblocked; 2501 PROCS_T *npv; 2502 2503 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2504 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) * 2505 (ProcListSize + PROC_LIST_SEG)); 2506 2507 /* Block SIGCHLD so reapchild() doesn't mess with us */ 2508 chldwasblocked = sm_blocksignal(SIGCHLD); 2509 if (ProcListSize > 0) 2510 { 2511 memmove(npv, ProcListVec, 2512 ProcListSize * sizeof(PROCS_T)); 2513 sm_free(ProcListVec); 2514 } 2515 2516 /* XXX just use memset() to initialize this part? */ 2517 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2518 { 2519 npv[i].proc_pid = NO_PID; 2520 npv[i].proc_task = NULL; 2521 npv[i].proc_type = PROC_NONE; 2522 } 2523 i = ProcListSize; 2524 ProcListSize += PROC_LIST_SEG; 2525 ProcListVec = npv; 2526 if (chldwasblocked == 0) 2527 (void) sm_releasesignal(SIGCHLD); 2528 } 2529 ProcListVec[i].proc_pid = pid; 2530 PSTRSET(ProcListVec[i].proc_task, task); 2531 ProcListVec[i].proc_type = type; 2532 ProcListVec[i].proc_count = count; 2533 ProcListVec[i].proc_other = other; 2534 if (hostaddr != NULL) 2535 ProcListVec[i].proc_hostaddr = *hostaddr; 2536 else 2537 memset(&ProcListVec[i].proc_hostaddr, 0, 2538 sizeof(ProcListVec[i].proc_hostaddr)); 2539 2540 /* if process adding itself, it's not a child */ 2541 if (pid != CurrentPid) 2542 { 2543 SM_ASSERT(CurChildren < INT_MAX); 2544 CurChildren++; 2545 } 2546 } 2547 2548 /* 2549 ** PROC_LIST_SET -- set pid task in process list 2550 ** 2551 ** Parameters: 2552 ** pid -- pid to set 2553 ** task -- task of pid 2554 ** 2555 ** Returns: 2556 ** none. 2557 */ 2558 2559 void 2560 proc_list_set(pid, task) 2561 pid_t pid; 2562 char *task; 2563 { 2564 int i; 2565 2566 for (i = 0; i < ProcListSize; i++) 2567 { 2568 if (ProcListVec[i].proc_pid == pid) 2569 { 2570 PSTRSET(ProcListVec[i].proc_task, task); 2571 break; 2572 } 2573 } 2574 } 2575 2576 /* 2577 ** PROC_LIST_DROP -- drop pid from process list 2578 ** 2579 ** Parameters: 2580 ** pid -- pid to drop 2581 ** st -- process status 2582 ** other -- storage for proc_other (return). 2583 ** 2584 ** Returns: 2585 ** none. 2586 ** 2587 ** Side Effects: 2588 ** May decrease CurChildren, CurRunners, or 2589 ** set RestartRequest or ShutdownRequest. 2590 ** 2591 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2592 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2593 ** DOING. 2594 */ 2595 2596 void 2597 proc_list_drop(pid, st, other) 2598 pid_t pid; 2599 int st; 2600 int *other; 2601 { 2602 int i; 2603 int type = PROC_NONE; 2604 2605 for (i = 0; i < ProcListSize; i++) 2606 { 2607 if (ProcListVec[i].proc_pid == pid) 2608 { 2609 ProcListVec[i].proc_pid = NO_PID; 2610 type = ProcListVec[i].proc_type; 2611 if (other != NULL) 2612 *other = ProcListVec[i].proc_other; 2613 if (CurChildren > 0) 2614 CurChildren--; 2615 break; 2616 } 2617 } 2618 2619 2620 if (type == PROC_CONTROL && WIFEXITED(st)) 2621 { 2622 /* if so, see if we need to restart or shutdown */ 2623 if (WEXITSTATUS(st) == EX_RESTART) 2624 RestartRequest = "control socket"; 2625 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2626 ShutdownRequest = "control socket"; 2627 } 2628 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2629 ProcListVec[i].proc_other > -1) 2630 { 2631 /* restart this persistent runner */ 2632 mark_work_group_restart(ProcListVec[i].proc_other, st); 2633 } 2634 else if (type == PROC_QUEUE) 2635 CurRunners -= ProcListVec[i].proc_count; 2636 } 2637 2638 /* 2639 ** PROC_LIST_CLEAR -- clear the process list 2640 ** 2641 ** Parameters: 2642 ** none. 2643 ** 2644 ** Returns: 2645 ** none. 2646 ** 2647 ** Side Effects: 2648 ** Sets CurChildren to zero. 2649 */ 2650 2651 void 2652 proc_list_clear() 2653 { 2654 int i; 2655 2656 /* start from 1 since 0 is the daemon itself */ 2657 for (i = 1; i < ProcListSize; i++) 2658 ProcListVec[i].proc_pid = NO_PID; 2659 CurChildren = 0; 2660 } 2661 2662 /* 2663 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2664 ** 2665 ** Parameters: 2666 ** none 2667 ** 2668 ** Returns: 2669 ** none 2670 ** 2671 ** Side Effects: 2672 ** May decrease CurChildren. 2673 */ 2674 2675 void 2676 proc_list_probe() 2677 { 2678 int i, children; 2679 int chldwasblocked; 2680 pid_t pid; 2681 2682 children = 0; 2683 chldwasblocked = sm_blocksignal(SIGCHLD); 2684 2685 /* start from 1 since 0 is the daemon itself */ 2686 for (i = 1; i < ProcListSize; i++) 2687 { 2688 pid = ProcListVec[i].proc_pid; 2689 if (pid == NO_PID || pid == CurrentPid) 2690 continue; 2691 if (kill(pid, 0) < 0) 2692 { 2693 if (LogLevel > 3) 2694 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2695 "proc_list_probe: lost pid %d", 2696 (int) ProcListVec[i].proc_pid); 2697 ProcListVec[i].proc_pid = NO_PID; 2698 SM_FREE_CLR(ProcListVec[i].proc_task); 2699 CurChildren--; 2700 } 2701 else 2702 { 2703 ++children; 2704 } 2705 } 2706 if (CurChildren < 0) 2707 CurChildren = 0; 2708 if (chldwasblocked == 0) 2709 (void) sm_releasesignal(SIGCHLD); 2710 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid) 2711 { 2712 sm_syslog(LOG_ERR, NOQID, 2713 "proc_list_probe: found %d children, expected %d", 2714 children, CurChildren); 2715 } 2716 } 2717 2718 /* 2719 ** PROC_LIST_DISPLAY -- display the process list 2720 ** 2721 ** Parameters: 2722 ** out -- output file pointer 2723 ** prefix -- string to output in front of each line. 2724 ** 2725 ** Returns: 2726 ** none. 2727 */ 2728 2729 void 2730 proc_list_display(out, prefix) 2731 SM_FILE_T *out; 2732 char *prefix; 2733 { 2734 int i; 2735 2736 for (i = 0; i < ProcListSize; i++) 2737 { 2738 if (ProcListVec[i].proc_pid == NO_PID) 2739 continue; 2740 2741 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2742 prefix, 2743 (int) ProcListVec[i].proc_pid, 2744 ProcListVec[i].proc_task != NULL ? 2745 ProcListVec[i].proc_task : "(unknown)", 2746 (OpMode == MD_SMTP || 2747 OpMode == MD_DAEMON || 2748 OpMode == MD_ARPAFTP) ? "\r" : ""); 2749 } 2750 } 2751 2752 /* 2753 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2754 ** 2755 ** Parameters: 2756 ** type -- type of process to signal 2757 ** signal -- the type of signal to send 2758 ** 2759 ** Results: 2760 ** none. 2761 ** 2762 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2763 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2764 ** DOING. 2765 */ 2766 2767 void 2768 proc_list_signal(type, signal) 2769 int type; 2770 int signal; 2771 { 2772 int chldwasblocked; 2773 int alrmwasblocked; 2774 int i; 2775 pid_t mypid = getpid(); 2776 2777 /* block these signals so that we may signal cleanly */ 2778 chldwasblocked = sm_blocksignal(SIGCHLD); 2779 alrmwasblocked = sm_blocksignal(SIGALRM); 2780 2781 /* Find all processes of type and send signal */ 2782 for (i = 0; i < ProcListSize; i++) 2783 { 2784 if (ProcListVec[i].proc_pid == NO_PID || 2785 ProcListVec[i].proc_pid == mypid) 2786 continue; 2787 if (ProcListVec[i].proc_type != type) 2788 continue; 2789 (void) kill(ProcListVec[i].proc_pid, signal); 2790 } 2791 2792 /* restore the signals */ 2793 if (alrmwasblocked == 0) 2794 (void) sm_releasesignal(SIGALRM); 2795 if (chldwasblocked == 0) 2796 (void) sm_releasesignal(SIGCHLD); 2797 } 2798 2799 /* 2800 ** COUNT_OPEN_CONNECTIONS 2801 ** 2802 ** Parameters: 2803 ** hostaddr - ClientAddress 2804 ** 2805 ** Returns: 2806 ** the number of open connections for this client 2807 ** 2808 */ 2809 2810 int 2811 count_open_connections(hostaddr) 2812 SOCKADDR *hostaddr; 2813 { 2814 int i, n; 2815 2816 if (hostaddr == NULL) 2817 return 0; 2818 n = 0; 2819 for (i = 0; i < ProcListSize; i++) 2820 { 2821 if (ProcListVec[i].proc_pid == NO_PID) 2822 continue; 2823 if (hostaddr->sa.sa_family != 2824 ProcListVec[i].proc_hostaddr.sa.sa_family) 2825 continue; 2826 #if NETINET 2827 if (hostaddr->sa.sa_family == AF_INET && 2828 (hostaddr->sin.sin_addr.s_addr == 2829 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 2830 n++; 2831 #endif /* NETINET */ 2832 #if NETINET6 2833 if (hostaddr->sa.sa_family == AF_INET6 && 2834 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 2835 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 2836 n++; 2837 #endif /* NETINET6 */ 2838 } 2839 return n; 2840 } 2841