1 /* 2 * Copyright (c) 1998-2007 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.414 2007/11/02 17:30:38 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 !bitset(MCIF_INLONGLINE, mci->mci_flags)) 1161 { 1162 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1163 SM_IO_EOF) 1164 { 1165 dead = true; 1166 break; 1167 } 1168 if (TrafficLogFile != NULL) 1169 (void) sm_io_putc(TrafficLogFile, 1170 SM_TIME_DEFAULT, '.'); 1171 } 1172 else if (l[0] == 'F' && slop == 0 && 1173 bitset(PXLF_MAPFROM, pxflags) && 1174 strncmp(l, "From ", 5) == 0 && 1175 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && 1176 !bitset(MCIF_INLONGLINE, mci->mci_flags)) 1177 { 1178 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1179 SM_IO_EOF) 1180 { 1181 dead = true; 1182 break; 1183 } 1184 if (TrafficLogFile != NULL) 1185 (void) sm_io_putc(TrafficLogFile, 1186 SM_TIME_DEFAULT, '>'); 1187 } 1188 PUTX(p); 1189 if (dead) 1190 break; 1191 1192 if (TrafficLogFile != NULL) 1193 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1194 '\n'); 1195 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol)) 1196 { 1197 mci->mci_flags &= ~MCIF_INLONGLINE; 1198 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1199 mci->mci_mailer->m_eol) == SM_IO_EOF) 1200 { 1201 dead = true; 1202 break; 1203 } 1204 } 1205 else 1206 mci->mci_flags |= MCIF_INLONGLINE; 1207 1208 if (l < end && *l == '\n') 1209 { 1210 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1211 bitset(PXLF_HEADER, pxflags)) 1212 { 1213 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1214 ' ') == SM_IO_EOF) 1215 { 1216 dead = true; 1217 break; 1218 } 1219 1220 if (TrafficLogFile != NULL) 1221 (void) sm_io_putc(TrafficLogFile, 1222 SM_TIME_DEFAULT, ' '); 1223 } 1224 } 1225 1226 } while (l < end); 1227 return !dead; 1228 } 1229 1230 /* 1231 ** XUNLINK -- unlink a file, doing logging as appropriate. 1232 ** 1233 ** Parameters: 1234 ** f -- name of file to unlink. 1235 ** 1236 ** Returns: 1237 ** return value of unlink() 1238 ** 1239 ** Side Effects: 1240 ** f is unlinked. 1241 */ 1242 1243 int 1244 xunlink(f) 1245 char *f; 1246 { 1247 register int i; 1248 int save_errno; 1249 1250 if (LogLevel > 98) 1251 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1252 1253 i = unlink(f); 1254 save_errno = errno; 1255 if (i < 0 && LogLevel > 97) 1256 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1257 f, errno); 1258 if (i >= 0) 1259 SYNC_DIR(f, false); 1260 errno = save_errno; 1261 return i; 1262 } 1263 1264 /* 1265 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1266 ** 1267 ** Parameters: 1268 ** buf -- place to put the input line. 1269 ** siz -- size of buf. 1270 ** fp -- file to read from. 1271 ** timeout -- the timeout before error occurs. 1272 ** during -- what we are trying to read (for error messages). 1273 ** 1274 ** Returns: 1275 ** NULL on error (including timeout). This may also leave 1276 ** buf containing a null string. 1277 ** buf otherwise. 1278 */ 1279 1280 1281 char * 1282 sfgets(buf, siz, fp, timeout, during) 1283 char *buf; 1284 int siz; 1285 SM_FILE_T *fp; 1286 time_t timeout; 1287 char *during; 1288 { 1289 register char *p; 1290 int save_errno; 1291 int io_timeout; 1292 1293 SM_REQUIRE(siz > 0); 1294 SM_REQUIRE(buf != NULL); 1295 1296 if (fp == NULL) 1297 { 1298 buf[0] = '\0'; 1299 errno = EBADF; 1300 return NULL; 1301 } 1302 1303 /* try to read */ 1304 p = NULL; 1305 errno = 0; 1306 1307 /* convert the timeout to sm_io notation */ 1308 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1309 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1310 { 1311 errno = 0; 1312 p = sm_io_fgets(fp, io_timeout, buf, siz); 1313 if (p == NULL && errno == EAGAIN) 1314 { 1315 /* The sm_io_fgets() call timedout */ 1316 if (LogLevel > 1) 1317 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1318 "timeout waiting for input from %.100s during %s", 1319 CURHOSTNAME, 1320 during); 1321 buf[0] = '\0'; 1322 #if XDEBUG 1323 checkfd012(during); 1324 #endif /* XDEBUG */ 1325 if (TrafficLogFile != NULL) 1326 (void) sm_io_fprintf(TrafficLogFile, 1327 SM_TIME_DEFAULT, 1328 "%05d <<< [TIMEOUT]\n", 1329 (int) CurrentPid); 1330 errno = ETIMEDOUT; 1331 return NULL; 1332 } 1333 if (p != NULL || errno != EINTR) 1334 break; 1335 (void) sm_io_clearerr(fp); 1336 } 1337 save_errno = errno; 1338 1339 /* clean up the books and exit */ 1340 LineNumber++; 1341 if (p == NULL) 1342 { 1343 buf[0] = '\0'; 1344 if (TrafficLogFile != NULL) 1345 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1346 "%05d <<< [EOF]\n", 1347 (int) CurrentPid); 1348 errno = save_errno; 1349 return NULL; 1350 } 1351 if (TrafficLogFile != NULL) 1352 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1353 "%05d <<< %s", (int) CurrentPid, buf); 1354 if (SevenBitInput) 1355 { 1356 for (p = buf; *p != '\0'; p++) 1357 *p &= ~0200; 1358 } 1359 else if (!HasEightBits) 1360 { 1361 for (p = buf; *p != '\0'; p++) 1362 { 1363 if (bitset(0200, *p)) 1364 { 1365 HasEightBits = true; 1366 break; 1367 } 1368 } 1369 } 1370 return buf; 1371 } 1372 1373 /* 1374 ** FGETFOLDED -- like fgets, but knows about folded lines. 1375 ** 1376 ** Parameters: 1377 ** buf -- place to put result. 1378 ** np -- pointer to bytes available; will be updated with 1379 ** the actual buffer size (not number of bytes filled) 1380 ** on return. 1381 ** f -- file to read from. 1382 ** 1383 ** Returns: 1384 ** input line(s) on success, NULL on error or SM_IO_EOF. 1385 ** This will normally be buf -- unless the line is too 1386 ** long, when it will be sm_malloc_x()ed. 1387 ** 1388 ** Side Effects: 1389 ** buf gets lines from f, with continuation lines (lines 1390 ** with leading white space) appended. CRLF's are mapped 1391 ** into single newlines. Any trailing NL is stripped. 1392 */ 1393 1394 char * 1395 fgetfolded(buf, np, f) 1396 char *buf; 1397 int *np; 1398 SM_FILE_T *f; 1399 { 1400 register char *p = buf; 1401 char *bp = buf; 1402 register int i; 1403 int n; 1404 1405 SM_REQUIRE(np != NULL); 1406 n = *np; 1407 SM_REQUIRE(n > 0); 1408 SM_REQUIRE(buf != NULL); 1409 if (f == NULL) 1410 { 1411 buf[0] = '\0'; 1412 errno = EBADF; 1413 return NULL; 1414 } 1415 1416 n--; 1417 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1418 { 1419 if (i == '\r') 1420 { 1421 i = sm_io_getc(f, SM_TIME_DEFAULT); 1422 if (i != '\n') 1423 { 1424 if (i != SM_IO_EOF) 1425 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1426 i); 1427 i = '\r'; 1428 } 1429 } 1430 if (--n <= 0) 1431 { 1432 /* allocate new space */ 1433 char *nbp; 1434 int nn; 1435 1436 nn = (p - bp); 1437 if (nn < MEMCHUNKSIZE) 1438 nn *= 2; 1439 else 1440 nn += MEMCHUNKSIZE; 1441 nbp = sm_malloc_x(nn); 1442 memmove(nbp, bp, p - bp); 1443 p = &nbp[p - bp]; 1444 if (bp != buf) 1445 sm_free(bp); 1446 bp = nbp; 1447 n = nn - (p - bp); 1448 *np = nn; 1449 } 1450 *p++ = i; 1451 if (i == '\n') 1452 { 1453 LineNumber++; 1454 i = sm_io_getc(f, SM_TIME_DEFAULT); 1455 if (i != SM_IO_EOF) 1456 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1457 if (i != ' ' && i != '\t') 1458 break; 1459 } 1460 } 1461 if (p == bp) 1462 return NULL; 1463 if (p[-1] == '\n') 1464 p--; 1465 *p = '\0'; 1466 return bp; 1467 } 1468 1469 /* 1470 ** CURTIME -- return current time. 1471 ** 1472 ** Parameters: 1473 ** none. 1474 ** 1475 ** Returns: 1476 ** the current time. 1477 */ 1478 1479 time_t 1480 curtime() 1481 { 1482 auto time_t t; 1483 1484 (void) time(&t); 1485 return t; 1486 } 1487 1488 /* 1489 ** ATOBOOL -- convert a string representation to boolean. 1490 ** 1491 ** Defaults to false 1492 ** 1493 ** Parameters: 1494 ** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1495 ** others as false. 1496 ** 1497 ** Returns: 1498 ** A boolean representation of the string. 1499 */ 1500 1501 bool 1502 atobool(s) 1503 register char *s; 1504 { 1505 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1506 return true; 1507 return false; 1508 } 1509 1510 /* 1511 ** ATOOCT -- convert a string representation to octal. 1512 ** 1513 ** Parameters: 1514 ** s -- string to convert. 1515 ** 1516 ** Returns: 1517 ** An integer representing the string interpreted as an 1518 ** octal number. 1519 */ 1520 1521 int 1522 atooct(s) 1523 register char *s; 1524 { 1525 register int i = 0; 1526 1527 while (*s >= '0' && *s <= '7') 1528 i = (i << 3) | (*s++ - '0'); 1529 return i; 1530 } 1531 1532 /* 1533 ** BITINTERSECT -- tell if two bitmaps intersect 1534 ** 1535 ** Parameters: 1536 ** a, b -- the bitmaps in question 1537 ** 1538 ** Returns: 1539 ** true if they have a non-null intersection 1540 ** false otherwise 1541 */ 1542 1543 bool 1544 bitintersect(a, b) 1545 BITMAP256 a; 1546 BITMAP256 b; 1547 { 1548 int i; 1549 1550 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1551 { 1552 if ((a[i] & b[i]) != 0) 1553 return true; 1554 } 1555 return false; 1556 } 1557 1558 /* 1559 ** BITZEROP -- tell if a bitmap is all zero 1560 ** 1561 ** Parameters: 1562 ** map -- the bit map to check 1563 ** 1564 ** Returns: 1565 ** true if map is all zero. 1566 ** false if there are any bits set in map. 1567 */ 1568 1569 bool 1570 bitzerop(map) 1571 BITMAP256 map; 1572 { 1573 int i; 1574 1575 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1576 { 1577 if (map[i] != 0) 1578 return false; 1579 } 1580 return true; 1581 } 1582 1583 /* 1584 ** STRCONTAINEDIN -- tell if one string is contained in another 1585 ** 1586 ** Parameters: 1587 ** icase -- ignore case? 1588 ** a -- possible substring. 1589 ** b -- possible superstring. 1590 ** 1591 ** Returns: 1592 ** true if a is contained in b (case insensitive). 1593 ** false otherwise. 1594 */ 1595 1596 bool 1597 strcontainedin(icase, a, b) 1598 bool icase; 1599 register char *a; 1600 register char *b; 1601 { 1602 int la; 1603 int lb; 1604 int c; 1605 1606 la = strlen(a); 1607 lb = strlen(b); 1608 c = *a; 1609 if (icase && isascii(c) && isupper(c)) 1610 c = tolower(c); 1611 for (; lb-- >= la; b++) 1612 { 1613 if (icase) 1614 { 1615 if (*b != c && 1616 isascii(*b) && isupper(*b) && tolower(*b) != c) 1617 continue; 1618 if (sm_strncasecmp(a, b, la) == 0) 1619 return true; 1620 } 1621 else 1622 { 1623 if (*b != c) 1624 continue; 1625 if (strncmp(a, b, la) == 0) 1626 return true; 1627 } 1628 } 1629 return false; 1630 } 1631 1632 /* 1633 ** CHECKFD012 -- check low numbered file descriptors 1634 ** 1635 ** File descriptors 0, 1, and 2 should be open at all times. 1636 ** This routine verifies that, and fixes it if not true. 1637 ** 1638 ** Parameters: 1639 ** where -- a tag printed if the assertion failed 1640 ** 1641 ** Returns: 1642 ** none 1643 */ 1644 1645 void 1646 checkfd012(where) 1647 char *where; 1648 { 1649 #if XDEBUG 1650 register int i; 1651 1652 for (i = 0; i < 3; i++) 1653 fill_fd(i, where); 1654 #endif /* XDEBUG */ 1655 } 1656 1657 /* 1658 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1659 ** 1660 ** Parameters: 1661 ** fd -- file descriptor to check. 1662 ** where -- tag to print on failure. 1663 ** 1664 ** Returns: 1665 ** none. 1666 */ 1667 1668 void 1669 checkfdopen(fd, where) 1670 int fd; 1671 char *where; 1672 { 1673 #if XDEBUG 1674 struct stat st; 1675 1676 if (fstat(fd, &st) < 0 && errno == EBADF) 1677 { 1678 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1679 printopenfds(true); 1680 } 1681 #endif /* XDEBUG */ 1682 } 1683 1684 /* 1685 ** CHECKFDS -- check for new or missing file descriptors 1686 ** 1687 ** Parameters: 1688 ** where -- tag for printing. If null, take a base line. 1689 ** 1690 ** Returns: 1691 ** none 1692 ** 1693 ** Side Effects: 1694 ** If where is set, shows changes since the last call. 1695 */ 1696 1697 void 1698 checkfds(where) 1699 char *where; 1700 { 1701 int maxfd; 1702 register int fd; 1703 bool printhdr = true; 1704 int save_errno = errno; 1705 static BITMAP256 baseline; 1706 extern int DtableSize; 1707 1708 if (DtableSize > BITMAPBITS) 1709 maxfd = BITMAPBITS; 1710 else 1711 maxfd = DtableSize; 1712 if (where == NULL) 1713 clrbitmap(baseline); 1714 1715 for (fd = 0; fd < maxfd; fd++) 1716 { 1717 struct stat stbuf; 1718 1719 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1720 { 1721 if (!bitnset(fd, baseline)) 1722 continue; 1723 clrbitn(fd, baseline); 1724 } 1725 else if (!bitnset(fd, baseline)) 1726 setbitn(fd, baseline); 1727 else 1728 continue; 1729 1730 /* file state has changed */ 1731 if (where == NULL) 1732 continue; 1733 if (printhdr) 1734 { 1735 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1736 "%s: changed fds:", 1737 where); 1738 printhdr = false; 1739 } 1740 dumpfd(fd, true, true); 1741 } 1742 errno = save_errno; 1743 } 1744 1745 /* 1746 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1747 ** 1748 ** Parameters: 1749 ** logit -- if set, send output to syslog; otherwise 1750 ** print for debugging. 1751 ** 1752 ** Returns: 1753 ** none. 1754 */ 1755 1756 #if NETINET || NETINET6 1757 # include <arpa/inet.h> 1758 #endif /* NETINET || NETINET6 */ 1759 1760 void 1761 printopenfds(logit) 1762 bool logit; 1763 { 1764 register int fd; 1765 extern int DtableSize; 1766 1767 for (fd = 0; fd < DtableSize; fd++) 1768 dumpfd(fd, false, logit); 1769 } 1770 1771 /* 1772 ** DUMPFD -- dump a file descriptor 1773 ** 1774 ** Parameters: 1775 ** fd -- the file descriptor to dump. 1776 ** printclosed -- if set, print a notification even if 1777 ** it is closed; otherwise print nothing. 1778 ** logit -- if set, use sm_syslog instead of sm_dprintf() 1779 ** 1780 ** Returns: 1781 ** none. 1782 */ 1783 1784 void 1785 dumpfd(fd, printclosed, logit) 1786 int fd; 1787 bool printclosed; 1788 bool logit; 1789 { 1790 register char *p; 1791 char *hp; 1792 #ifdef S_IFSOCK 1793 SOCKADDR sa; 1794 #endif /* S_IFSOCK */ 1795 auto SOCKADDR_LEN_T slen; 1796 int i; 1797 #if STAT64 > 0 1798 struct stat64 st; 1799 #else /* STAT64 > 0 */ 1800 struct stat st; 1801 #endif /* STAT64 > 0 */ 1802 char buf[200]; 1803 1804 p = buf; 1805 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1806 p += strlen(p); 1807 1808 if ( 1809 #if STAT64 > 0 1810 fstat64(fd, &st) 1811 #else /* STAT64 > 0 */ 1812 fstat(fd, &st) 1813 #endif /* STAT64 > 0 */ 1814 < 0) 1815 { 1816 if (errno != EBADF) 1817 { 1818 (void) sm_snprintf(p, SPACELEFT(buf, p), 1819 "CANNOT STAT (%s)", 1820 sm_errstring(errno)); 1821 goto printit; 1822 } 1823 else if (printclosed) 1824 { 1825 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1826 goto printit; 1827 } 1828 return; 1829 } 1830 1831 i = fcntl(fd, F_GETFL, 0); 1832 if (i != -1) 1833 { 1834 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1835 p += strlen(p); 1836 } 1837 1838 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1839 (int) st.st_mode); 1840 p += strlen(p); 1841 switch (st.st_mode & S_IFMT) 1842 { 1843 #ifdef S_IFSOCK 1844 case S_IFSOCK: 1845 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1846 p += strlen(p); 1847 memset(&sa, '\0', sizeof(sa)); 1848 slen = sizeof(sa); 1849 if (getsockname(fd, &sa.sa, &slen) < 0) 1850 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1851 sm_errstring(errno)); 1852 else 1853 { 1854 hp = hostnamebyanyaddr(&sa); 1855 if (hp == NULL) 1856 { 1857 /* EMPTY */ 1858 /* do nothing */ 1859 } 1860 # if NETINET 1861 else if (sa.sa.sa_family == AF_INET) 1862 (void) sm_snprintf(p, SPACELEFT(buf, p), 1863 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1864 # endif /* NETINET */ 1865 # if NETINET6 1866 else if (sa.sa.sa_family == AF_INET6) 1867 (void) sm_snprintf(p, SPACELEFT(buf, p), 1868 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1869 # endif /* NETINET6 */ 1870 else 1871 (void) sm_snprintf(p, SPACELEFT(buf, p), 1872 "%s", hp); 1873 } 1874 p += strlen(p); 1875 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 1876 p += strlen(p); 1877 slen = sizeof(sa); 1878 if (getpeername(fd, &sa.sa, &slen) < 0) 1879 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1880 sm_errstring(errno)); 1881 else 1882 { 1883 hp = hostnamebyanyaddr(&sa); 1884 if (hp == NULL) 1885 { 1886 /* EMPTY */ 1887 /* do nothing */ 1888 } 1889 # if NETINET 1890 else if (sa.sa.sa_family == AF_INET) 1891 (void) sm_snprintf(p, SPACELEFT(buf, p), 1892 "%s/%d", hp, ntohs(sa.sin.sin_port)); 1893 # endif /* NETINET */ 1894 # if NETINET6 1895 else if (sa.sa.sa_family == AF_INET6) 1896 (void) sm_snprintf(p, SPACELEFT(buf, p), 1897 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 1898 # endif /* NETINET6 */ 1899 else 1900 (void) sm_snprintf(p, SPACELEFT(buf, p), 1901 "%s", hp); 1902 } 1903 break; 1904 #endif /* S_IFSOCK */ 1905 1906 case S_IFCHR: 1907 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 1908 p += strlen(p); 1909 goto defprint; 1910 1911 #ifdef S_IFBLK 1912 case S_IFBLK: 1913 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 1914 p += strlen(p); 1915 goto defprint; 1916 #endif /* S_IFBLK */ 1917 1918 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1919 case S_IFIFO: 1920 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 1921 p += strlen(p); 1922 goto defprint; 1923 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */ 1924 1925 #ifdef S_IFDIR 1926 case S_IFDIR: 1927 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 1928 p += strlen(p); 1929 goto defprint; 1930 #endif /* S_IFDIR */ 1931 1932 #ifdef S_IFLNK 1933 case S_IFLNK: 1934 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 1935 p += strlen(p); 1936 goto defprint; 1937 #endif /* S_IFLNK */ 1938 1939 default: 1940 defprint: 1941 (void) sm_snprintf(p, SPACELEFT(buf, p), 1942 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ", 1943 major(st.st_dev), minor(st.st_dev), 1944 (ULONGLONG_T) st.st_ino, 1945 (int) st.st_nlink, (int) st.st_uid, 1946 (int) st.st_gid); 1947 p += strlen(p); 1948 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 1949 (ULONGLONG_T) st.st_size); 1950 break; 1951 } 1952 1953 printit: 1954 if (logit) 1955 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 1956 "%.800s", buf); 1957 else 1958 sm_dprintf("%s\n", buf); 1959 } 1960 1961 /* 1962 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1963 ** 1964 ** Parameters: 1965 ** host -- the host to shorten (stripped in place). 1966 ** 1967 ** Returns: 1968 ** place where string was truncated, NULL if not truncated. 1969 */ 1970 1971 char * 1972 shorten_hostname(host) 1973 char host[]; 1974 { 1975 register char *p; 1976 char *mydom; 1977 int i; 1978 bool canon = false; 1979 1980 /* strip off final dot */ 1981 i = strlen(host); 1982 p = &host[(i == 0) ? 0 : i - 1]; 1983 if (*p == '.') 1984 { 1985 *p = '\0'; 1986 canon = true; 1987 } 1988 1989 /* see if there is any domain at all -- if not, we are done */ 1990 p = strchr(host, '.'); 1991 if (p == NULL) 1992 return NULL; 1993 1994 /* yes, we have a domain -- see if it looks like us */ 1995 mydom = macvalue('m', CurEnv); 1996 if (mydom == NULL) 1997 mydom = ""; 1998 i = strlen(++p); 1999 if ((canon ? sm_strcasecmp(p, mydom) 2000 : sm_strncasecmp(p, mydom, i)) == 0 && 2001 (mydom[i] == '.' || mydom[i] == '\0')) 2002 { 2003 *--p = '\0'; 2004 return p; 2005 } 2006 return NULL; 2007 } 2008 2009 /* 2010 ** PROG_OPEN -- open a program for reading 2011 ** 2012 ** Parameters: 2013 ** argv -- the argument list. 2014 ** pfd -- pointer to a place to store the file descriptor. 2015 ** e -- the current envelope. 2016 ** 2017 ** Returns: 2018 ** pid of the process -- -1 if it failed. 2019 */ 2020 2021 pid_t 2022 prog_open(argv, pfd, e) 2023 char **argv; 2024 int *pfd; 2025 ENVELOPE *e; 2026 { 2027 pid_t pid; 2028 int save_errno; 2029 int sff; 2030 int ret; 2031 int fdv[2]; 2032 char *p, *q; 2033 char buf[MAXPATHLEN]; 2034 extern int DtableSize; 2035 2036 if (pipe(fdv) < 0) 2037 { 2038 syserr("%s: cannot create pipe for stdout", argv[0]); 2039 return -1; 2040 } 2041 pid = fork(); 2042 if (pid < 0) 2043 { 2044 syserr("%s: cannot fork", argv[0]); 2045 (void) close(fdv[0]); 2046 (void) close(fdv[1]); 2047 return -1; 2048 } 2049 if (pid > 0) 2050 { 2051 /* parent */ 2052 (void) close(fdv[1]); 2053 *pfd = fdv[0]; 2054 return pid; 2055 } 2056 2057 /* Reset global flags */ 2058 RestartRequest = NULL; 2059 RestartWorkGroup = false; 2060 ShutdownRequest = NULL; 2061 PendingSignal = 0; 2062 CurrentPid = getpid(); 2063 2064 /* 2065 ** Initialize exception stack and default exception 2066 ** handler for child process. 2067 */ 2068 2069 sm_exc_newthread(fatal_error); 2070 2071 /* child -- close stdin */ 2072 (void) close(0); 2073 2074 /* stdout goes back to parent */ 2075 (void) close(fdv[0]); 2076 if (dup2(fdv[1], 1) < 0) 2077 { 2078 syserr("%s: cannot dup2 for stdout", argv[0]); 2079 _exit(EX_OSERR); 2080 } 2081 (void) close(fdv[1]); 2082 2083 /* stderr goes to transcript if available */ 2084 if (e->e_xfp != NULL) 2085 { 2086 int xfd; 2087 2088 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 2089 if (xfd >= 0 && dup2(xfd, 2) < 0) 2090 { 2091 syserr("%s: cannot dup2 for stderr", argv[0]); 2092 _exit(EX_OSERR); 2093 } 2094 } 2095 2096 /* this process has no right to the queue file */ 2097 if (e->e_lockfp != NULL) 2098 { 2099 int fd; 2100 2101 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 2102 if (fd >= 0) 2103 (void) close(fd); 2104 else 2105 syserr("%s: lockfp does not have a fd", argv[0]); 2106 } 2107 2108 /* chroot to the program mailer directory, if defined */ 2109 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 2110 { 2111 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e); 2112 if (chroot(buf) < 0) 2113 { 2114 syserr("prog_open: cannot chroot(%s)", buf); 2115 exit(EX_TEMPFAIL); 2116 } 2117 if (chdir("/") < 0) 2118 { 2119 syserr("prog_open: cannot chdir(/)"); 2120 exit(EX_TEMPFAIL); 2121 } 2122 } 2123 2124 /* run as default user */ 2125 endpwent(); 2126 sm_mbdb_terminate(); 2127 #if _FFR_MEMSTAT 2128 (void) sm_memstat_close(); 2129 #endif /* _FFR_MEMSTAT */ 2130 if (setgid(DefGid) < 0 && geteuid() == 0) 2131 { 2132 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2133 exit(EX_TEMPFAIL); 2134 } 2135 if (setuid(DefUid) < 0 && geteuid() == 0) 2136 { 2137 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2138 exit(EX_TEMPFAIL); 2139 } 2140 2141 /* run in some directory */ 2142 if (ProgMailer != NULL) 2143 p = ProgMailer->m_execdir; 2144 else 2145 p = NULL; 2146 for (; p != NULL; p = q) 2147 { 2148 q = strchr(p, ':'); 2149 if (q != NULL) 2150 *q = '\0'; 2151 expand(p, buf, sizeof(buf), e); 2152 if (q != NULL) 2153 *q++ = ':'; 2154 if (buf[0] != '\0' && chdir(buf) >= 0) 2155 break; 2156 } 2157 if (p == NULL) 2158 { 2159 /* backup directories */ 2160 if (chdir("/tmp") < 0) 2161 (void) chdir("/"); 2162 } 2163 2164 /* Check safety of program to be run */ 2165 sff = SFF_ROOTOK|SFF_EXECOK; 2166 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2167 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2168 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2169 sff |= SFF_NOPATHCHECK; 2170 else 2171 sff |= SFF_SAFEDIRPATH; 2172 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2173 if (ret != 0) 2174 sm_syslog(LOG_INFO, e->e_id, 2175 "Warning: prog_open: program %s unsafe: %s", 2176 argv[0], sm_errstring(ret)); 2177 2178 /* arrange for all the files to be closed */ 2179 sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2180 2181 /* now exec the process */ 2182 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2183 2184 /* woops! failed */ 2185 save_errno = errno; 2186 syserr("%s: cannot exec", argv[0]); 2187 if (transienterror(save_errno)) 2188 _exit(EX_OSERR); 2189 _exit(EX_CONFIG); 2190 return -1; /* avoid compiler warning on IRIX */ 2191 } 2192 2193 /* 2194 ** GET_COLUMN -- look up a Column in a line buffer 2195 ** 2196 ** Parameters: 2197 ** line -- the raw text line to search. 2198 ** col -- the column number to fetch. 2199 ** delim -- the delimiter between columns. If null, 2200 ** use white space. 2201 ** buf -- the output buffer. 2202 ** buflen -- the length of buf. 2203 ** 2204 ** Returns: 2205 ** buf if successful. 2206 ** NULL otherwise. 2207 */ 2208 2209 char * 2210 get_column(line, col, delim, buf, buflen) 2211 char line[]; 2212 int col; 2213 int delim; 2214 char buf[]; 2215 int buflen; 2216 { 2217 char *p; 2218 char *begin, *end; 2219 int i; 2220 char delimbuf[4]; 2221 2222 if ((char) delim == '\0') 2223 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf)); 2224 else 2225 { 2226 delimbuf[0] = (char) delim; 2227 delimbuf[1] = '\0'; 2228 } 2229 2230 p = line; 2231 if (*p == '\0') 2232 return NULL; /* line empty */ 2233 if (*p == (char) delim && col == 0) 2234 return NULL; /* first column empty */ 2235 2236 begin = line; 2237 2238 if (col == 0 && (char) delim == '\0') 2239 { 2240 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2241 begin++; 2242 } 2243 2244 for (i = 0; i < col; i++) 2245 { 2246 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2247 return NULL; /* no such column */ 2248 begin++; 2249 if ((char) delim == '\0') 2250 { 2251 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2252 begin++; 2253 } 2254 } 2255 2256 end = strpbrk(begin, delimbuf); 2257 if (end == NULL) 2258 i = strlen(begin); 2259 else 2260 i = end - begin; 2261 if (i >= buflen) 2262 i = buflen - 1; 2263 (void) sm_strlcpy(buf, begin, i + 1); 2264 return buf; 2265 } 2266 2267 /* 2268 ** CLEANSTRCPY -- copy string keeping out bogus characters 2269 ** 2270 ** Parameters: 2271 ** t -- "to" string. 2272 ** f -- "from" string. 2273 ** l -- length of space available in "to" string. 2274 ** 2275 ** Returns: 2276 ** none. 2277 */ 2278 2279 void 2280 cleanstrcpy(t, f, l) 2281 register char *t; 2282 register char *f; 2283 int l; 2284 { 2285 /* check for newlines and log if necessary */ 2286 (void) denlstring(f, true, true); 2287 2288 if (l <= 0) 2289 syserr("!cleanstrcpy: length == 0"); 2290 2291 l--; 2292 while (l > 0 && *f != '\0') 2293 { 2294 if (isascii(*f) && 2295 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2296 { 2297 l--; 2298 *t++ = *f; 2299 } 2300 f++; 2301 } 2302 *t = '\0'; 2303 } 2304 2305 /* 2306 ** DENLSTRING -- convert newlines in a string to spaces 2307 ** 2308 ** Parameters: 2309 ** s -- the input string 2310 ** strict -- if set, don't permit continuation lines. 2311 ** logattacks -- if set, log attempted attacks. 2312 ** 2313 ** Returns: 2314 ** A pointer to a version of the string with newlines 2315 ** mapped to spaces. This should be copied. 2316 */ 2317 2318 char * 2319 denlstring(s, strict, logattacks) 2320 char *s; 2321 bool strict; 2322 bool logattacks; 2323 { 2324 register char *p; 2325 int l; 2326 static char *bp = NULL; 2327 static int bl = 0; 2328 2329 p = s; 2330 while ((p = strchr(p, '\n')) != NULL) 2331 if (strict || (*++p != ' ' && *p != '\t')) 2332 break; 2333 if (p == NULL) 2334 return s; 2335 2336 l = strlen(s) + 1; 2337 if (bl < l) 2338 { 2339 /* allocate more space */ 2340 char *nbp = sm_pmalloc_x(l); 2341 2342 if (bp != NULL) 2343 sm_free(bp); 2344 bp = nbp; 2345 bl = l; 2346 } 2347 (void) sm_strlcpy(bp, s, l); 2348 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2349 *p++ = ' '; 2350 2351 if (logattacks) 2352 { 2353 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL, 2354 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2355 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2356 shortenstring(bp, MAXSHORTSTR)); 2357 } 2358 2359 return bp; 2360 } 2361 2362 /* 2363 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 2364 ** 2365 ** Parameters: 2366 ** s -- string to manipulate (in place) 2367 ** subst -- character to use as replacement 2368 ** 2369 ** Returns: 2370 ** true iff string did not contain "unprintable" characters 2371 */ 2372 2373 bool 2374 strreplnonprt(s, c) 2375 char *s; 2376 int c; 2377 { 2378 bool ok; 2379 2380 ok = true; 2381 if (s == NULL) 2382 return ok; 2383 while (*s != '\0') 2384 { 2385 if (!(isascii(*s) && isprint(*s))) 2386 { 2387 *s = c; 2388 ok = false; 2389 } 2390 ++s; 2391 } 2392 return ok; 2393 } 2394 2395 /* 2396 ** PATH_IS_DIR -- check to see if file exists and is a directory. 2397 ** 2398 ** There are some additional checks for security violations in 2399 ** here. This routine is intended to be used for the host status 2400 ** support. 2401 ** 2402 ** Parameters: 2403 ** pathname -- pathname to check for directory-ness. 2404 ** createflag -- if set, create directory if needed. 2405 ** 2406 ** Returns: 2407 ** true -- if the indicated pathname is a directory 2408 ** false -- otherwise 2409 */ 2410 2411 bool 2412 path_is_dir(pathname, createflag) 2413 char *pathname; 2414 bool createflag; 2415 { 2416 struct stat statbuf; 2417 2418 #if HASLSTAT 2419 if (lstat(pathname, &statbuf) < 0) 2420 #else /* HASLSTAT */ 2421 if (stat(pathname, &statbuf) < 0) 2422 #endif /* HASLSTAT */ 2423 { 2424 if (errno != ENOENT || !createflag) 2425 return false; 2426 if (mkdir(pathname, 0755) < 0) 2427 return false; 2428 return true; 2429 } 2430 if (!S_ISDIR(statbuf.st_mode)) 2431 { 2432 errno = ENOTDIR; 2433 return false; 2434 } 2435 2436 /* security: don't allow writable directories */ 2437 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2438 { 2439 errno = EACCES; 2440 return false; 2441 } 2442 return true; 2443 } 2444 2445 /* 2446 ** PROC_LIST_ADD -- add process id to list of our children 2447 ** 2448 ** Parameters: 2449 ** pid -- pid to add to list. 2450 ** task -- task of pid. 2451 ** type -- type of process. 2452 ** count -- number of processes. 2453 ** other -- other information for this type. 2454 ** 2455 ** Returns: 2456 ** none 2457 ** 2458 ** Side Effects: 2459 ** May increase CurChildren. May grow ProcList. 2460 */ 2461 2462 typedef struct procs PROCS_T; 2463 2464 struct procs 2465 { 2466 pid_t proc_pid; 2467 char *proc_task; 2468 int proc_type; 2469 int proc_count; 2470 int proc_other; 2471 SOCKADDR proc_hostaddr; 2472 }; 2473 2474 static PROCS_T *volatile ProcListVec = NULL; 2475 static int ProcListSize = 0; 2476 2477 void 2478 proc_list_add(pid, task, type, count, other, hostaddr) 2479 pid_t pid; 2480 char *task; 2481 int type; 2482 int count; 2483 int other; 2484 SOCKADDR *hostaddr; 2485 { 2486 int i; 2487 2488 for (i = 0; i < ProcListSize; i++) 2489 { 2490 if (ProcListVec[i].proc_pid == NO_PID) 2491 break; 2492 } 2493 if (i >= ProcListSize) 2494 { 2495 /* probe the existing vector to avoid growing infinitely */ 2496 proc_list_probe(); 2497 2498 /* now scan again */ 2499 for (i = 0; i < ProcListSize; i++) 2500 { 2501 if (ProcListVec[i].proc_pid == NO_PID) 2502 break; 2503 } 2504 } 2505 if (i >= ProcListSize) 2506 { 2507 /* grow process list */ 2508 int chldwasblocked; 2509 PROCS_T *npv; 2510 2511 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2512 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) * 2513 (ProcListSize + PROC_LIST_SEG)); 2514 2515 /* Block SIGCHLD so reapchild() doesn't mess with us */ 2516 chldwasblocked = sm_blocksignal(SIGCHLD); 2517 if (ProcListSize > 0) 2518 { 2519 memmove(npv, ProcListVec, 2520 ProcListSize * sizeof(PROCS_T)); 2521 sm_free(ProcListVec); 2522 } 2523 2524 /* XXX just use memset() to initialize this part? */ 2525 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2526 { 2527 npv[i].proc_pid = NO_PID; 2528 npv[i].proc_task = NULL; 2529 npv[i].proc_type = PROC_NONE; 2530 } 2531 i = ProcListSize; 2532 ProcListSize += PROC_LIST_SEG; 2533 ProcListVec = npv; 2534 if (chldwasblocked == 0) 2535 (void) sm_releasesignal(SIGCHLD); 2536 } 2537 ProcListVec[i].proc_pid = pid; 2538 PSTRSET(ProcListVec[i].proc_task, task); 2539 ProcListVec[i].proc_type = type; 2540 ProcListVec[i].proc_count = count; 2541 ProcListVec[i].proc_other = other; 2542 if (hostaddr != NULL) 2543 ProcListVec[i].proc_hostaddr = *hostaddr; 2544 else 2545 memset(&ProcListVec[i].proc_hostaddr, 0, 2546 sizeof(ProcListVec[i].proc_hostaddr)); 2547 2548 /* if process adding itself, it's not a child */ 2549 if (pid != CurrentPid) 2550 { 2551 SM_ASSERT(CurChildren < INT_MAX); 2552 CurChildren++; 2553 } 2554 } 2555 2556 /* 2557 ** PROC_LIST_SET -- set pid task in process list 2558 ** 2559 ** Parameters: 2560 ** pid -- pid to set 2561 ** task -- task of pid 2562 ** 2563 ** Returns: 2564 ** none. 2565 */ 2566 2567 void 2568 proc_list_set(pid, task) 2569 pid_t pid; 2570 char *task; 2571 { 2572 int i; 2573 2574 for (i = 0; i < ProcListSize; i++) 2575 { 2576 if (ProcListVec[i].proc_pid == pid) 2577 { 2578 PSTRSET(ProcListVec[i].proc_task, task); 2579 break; 2580 } 2581 } 2582 } 2583 2584 /* 2585 ** PROC_LIST_DROP -- drop pid from process list 2586 ** 2587 ** Parameters: 2588 ** pid -- pid to drop 2589 ** st -- process status 2590 ** other -- storage for proc_other (return). 2591 ** 2592 ** Returns: 2593 ** none. 2594 ** 2595 ** Side Effects: 2596 ** May decrease CurChildren, CurRunners, or 2597 ** set RestartRequest or ShutdownRequest. 2598 ** 2599 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2600 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2601 ** DOING. 2602 */ 2603 2604 void 2605 proc_list_drop(pid, st, other) 2606 pid_t pid; 2607 int st; 2608 int *other; 2609 { 2610 int i; 2611 int type = PROC_NONE; 2612 2613 for (i = 0; i < ProcListSize; i++) 2614 { 2615 if (ProcListVec[i].proc_pid == pid) 2616 { 2617 ProcListVec[i].proc_pid = NO_PID; 2618 type = ProcListVec[i].proc_type; 2619 if (other != NULL) 2620 *other = ProcListVec[i].proc_other; 2621 if (CurChildren > 0) 2622 CurChildren--; 2623 break; 2624 } 2625 } 2626 2627 2628 if (type == PROC_CONTROL && WIFEXITED(st)) 2629 { 2630 /* if so, see if we need to restart or shutdown */ 2631 if (WEXITSTATUS(st) == EX_RESTART) 2632 RestartRequest = "control socket"; 2633 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2634 ShutdownRequest = "control socket"; 2635 } 2636 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2637 ProcListVec[i].proc_other > -1) 2638 { 2639 /* restart this persistent runner */ 2640 mark_work_group_restart(ProcListVec[i].proc_other, st); 2641 } 2642 else if (type == PROC_QUEUE) 2643 CurRunners -= ProcListVec[i].proc_count; 2644 } 2645 2646 /* 2647 ** PROC_LIST_CLEAR -- clear the process list 2648 ** 2649 ** Parameters: 2650 ** none. 2651 ** 2652 ** Returns: 2653 ** none. 2654 ** 2655 ** Side Effects: 2656 ** Sets CurChildren to zero. 2657 */ 2658 2659 void 2660 proc_list_clear() 2661 { 2662 int i; 2663 2664 /* start from 1 since 0 is the daemon itself */ 2665 for (i = 1; i < ProcListSize; i++) 2666 ProcListVec[i].proc_pid = NO_PID; 2667 CurChildren = 0; 2668 } 2669 2670 /* 2671 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2672 ** 2673 ** Parameters: 2674 ** none 2675 ** 2676 ** Returns: 2677 ** none 2678 ** 2679 ** Side Effects: 2680 ** May decrease CurChildren. 2681 */ 2682 2683 void 2684 proc_list_probe() 2685 { 2686 int i, children; 2687 int chldwasblocked; 2688 pid_t pid; 2689 2690 children = 0; 2691 chldwasblocked = sm_blocksignal(SIGCHLD); 2692 2693 /* start from 1 since 0 is the daemon itself */ 2694 for (i = 1; i < ProcListSize; i++) 2695 { 2696 pid = ProcListVec[i].proc_pid; 2697 if (pid == NO_PID || pid == CurrentPid) 2698 continue; 2699 if (kill(pid, 0) < 0) 2700 { 2701 if (LogLevel > 3) 2702 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2703 "proc_list_probe: lost pid %d", 2704 (int) ProcListVec[i].proc_pid); 2705 ProcListVec[i].proc_pid = NO_PID; 2706 SM_FREE_CLR(ProcListVec[i].proc_task); 2707 CurChildren--; 2708 } 2709 else 2710 { 2711 ++children; 2712 } 2713 } 2714 if (CurChildren < 0) 2715 CurChildren = 0; 2716 if (chldwasblocked == 0) 2717 (void) sm_releasesignal(SIGCHLD); 2718 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid) 2719 { 2720 sm_syslog(LOG_ERR, NOQID, 2721 "proc_list_probe: found %d children, expected %d", 2722 children, CurChildren); 2723 } 2724 } 2725 2726 /* 2727 ** PROC_LIST_DISPLAY -- display the process list 2728 ** 2729 ** Parameters: 2730 ** out -- output file pointer 2731 ** prefix -- string to output in front of each line. 2732 ** 2733 ** Returns: 2734 ** none. 2735 */ 2736 2737 void 2738 proc_list_display(out, prefix) 2739 SM_FILE_T *out; 2740 char *prefix; 2741 { 2742 int i; 2743 2744 for (i = 0; i < ProcListSize; i++) 2745 { 2746 if (ProcListVec[i].proc_pid == NO_PID) 2747 continue; 2748 2749 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2750 prefix, 2751 (int) ProcListVec[i].proc_pid, 2752 ProcListVec[i].proc_task != NULL ? 2753 ProcListVec[i].proc_task : "(unknown)", 2754 (OpMode == MD_SMTP || 2755 OpMode == MD_DAEMON || 2756 OpMode == MD_ARPAFTP) ? "\r" : ""); 2757 } 2758 } 2759 2760 /* 2761 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2762 ** 2763 ** Parameters: 2764 ** type -- type of process to signal 2765 ** signal -- the type of signal to send 2766 ** 2767 ** Results: 2768 ** none. 2769 ** 2770 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2771 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2772 ** DOING. 2773 */ 2774 2775 void 2776 proc_list_signal(type, signal) 2777 int type; 2778 int signal; 2779 { 2780 int chldwasblocked; 2781 int alrmwasblocked; 2782 int i; 2783 pid_t mypid = getpid(); 2784 2785 /* block these signals so that we may signal cleanly */ 2786 chldwasblocked = sm_blocksignal(SIGCHLD); 2787 alrmwasblocked = sm_blocksignal(SIGALRM); 2788 2789 /* Find all processes of type and send signal */ 2790 for (i = 0; i < ProcListSize; i++) 2791 { 2792 if (ProcListVec[i].proc_pid == NO_PID || 2793 ProcListVec[i].proc_pid == mypid) 2794 continue; 2795 if (ProcListVec[i].proc_type != type) 2796 continue; 2797 (void) kill(ProcListVec[i].proc_pid, signal); 2798 } 2799 2800 /* restore the signals */ 2801 if (alrmwasblocked == 0) 2802 (void) sm_releasesignal(SIGALRM); 2803 if (chldwasblocked == 0) 2804 (void) sm_releasesignal(SIGCHLD); 2805 } 2806 2807 /* 2808 ** COUNT_OPEN_CONNECTIONS 2809 ** 2810 ** Parameters: 2811 ** hostaddr - ClientAddress 2812 ** 2813 ** Returns: 2814 ** the number of open connections for this client 2815 ** 2816 */ 2817 2818 int 2819 count_open_connections(hostaddr) 2820 SOCKADDR *hostaddr; 2821 { 2822 int i, n; 2823 2824 if (hostaddr == NULL) 2825 return 0; 2826 2827 /* 2828 ** This code gets called before proc_list_add() gets called, 2829 ** so we (the daemon child for this connection) have not yet 2830 ** counted ourselves. Hence initialize the counter to 1 2831 ** instead of 0 to compensate. 2832 */ 2833 2834 n = 1; 2835 for (i = 0; i < ProcListSize; i++) 2836 { 2837 if (ProcListVec[i].proc_pid == NO_PID) 2838 continue; 2839 if (hostaddr->sa.sa_family != 2840 ProcListVec[i].proc_hostaddr.sa.sa_family) 2841 continue; 2842 #if NETINET 2843 if (hostaddr->sa.sa_family == AF_INET && 2844 (hostaddr->sin.sin_addr.s_addr == 2845 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 2846 n++; 2847 #endif /* NETINET */ 2848 #if NETINET6 2849 if (hostaddr->sa.sa_family == AF_INET6 && 2850 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 2851 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 2852 n++; 2853 #endif /* NETINET6 */ 2854 } 2855 return n; 2856 } 2857