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