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.394 2006/05/03 23:55:29 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 { 2048 int fd; 2049 2050 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 2051 if (fd >= 0) 2052 (void) close(fd); 2053 else 2054 syserr("%s: lockfp does not have a fd", argv[0]); 2055 } 2056 2057 /* chroot to the program mailer directory, if defined */ 2058 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 2059 { 2060 expand(ProgMailer->m_rootdir, buf, sizeof buf, e); 2061 if (chroot(buf) < 0) 2062 { 2063 syserr("prog_open: cannot chroot(%s)", buf); 2064 exit(EX_TEMPFAIL); 2065 } 2066 if (chdir("/") < 0) 2067 { 2068 syserr("prog_open: cannot chdir(/)"); 2069 exit(EX_TEMPFAIL); 2070 } 2071 } 2072 2073 /* run as default user */ 2074 endpwent(); 2075 sm_mbdb_terminate(); 2076 #if _FFR_MEMSTAT 2077 (void) sm_memstat_close(); 2078 #endif /* _FFR_MEMSTAT */ 2079 if (setgid(DefGid) < 0 && geteuid() == 0) 2080 { 2081 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2082 exit(EX_TEMPFAIL); 2083 } 2084 if (setuid(DefUid) < 0 && geteuid() == 0) 2085 { 2086 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2087 exit(EX_TEMPFAIL); 2088 } 2089 2090 /* run in some directory */ 2091 if (ProgMailer != NULL) 2092 p = ProgMailer->m_execdir; 2093 else 2094 p = NULL; 2095 for (; p != NULL; p = q) 2096 { 2097 q = strchr(p, ':'); 2098 if (q != NULL) 2099 *q = '\0'; 2100 expand(p, buf, sizeof buf, e); 2101 if (q != NULL) 2102 *q++ = ':'; 2103 if (buf[0] != '\0' && chdir(buf) >= 0) 2104 break; 2105 } 2106 if (p == NULL) 2107 { 2108 /* backup directories */ 2109 if (chdir("/tmp") < 0) 2110 (void) chdir("/"); 2111 } 2112 2113 /* Check safety of program to be run */ 2114 sff = SFF_ROOTOK|SFF_EXECOK; 2115 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2116 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2117 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2118 sff |= SFF_NOPATHCHECK; 2119 else 2120 sff |= SFF_SAFEDIRPATH; 2121 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2122 if (ret != 0) 2123 sm_syslog(LOG_INFO, e->e_id, 2124 "Warning: prog_open: program %s unsafe: %s", 2125 argv[0], sm_errstring(ret)); 2126 2127 /* arrange for all the files to be closed */ 2128 sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2129 2130 /* now exec the process */ 2131 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2132 2133 /* woops! failed */ 2134 save_errno = errno; 2135 syserr("%s: cannot exec", argv[0]); 2136 if (transienterror(save_errno)) 2137 _exit(EX_OSERR); 2138 _exit(EX_CONFIG); 2139 return -1; /* avoid compiler warning on IRIX */ 2140 } 2141 /* 2142 ** GET_COLUMN -- look up a Column in a line buffer 2143 ** 2144 ** Parameters: 2145 ** line -- the raw text line to search. 2146 ** col -- the column number to fetch. 2147 ** delim -- the delimiter between columns. If null, 2148 ** use white space. 2149 ** buf -- the output buffer. 2150 ** buflen -- the length of buf. 2151 ** 2152 ** Returns: 2153 ** buf if successful. 2154 ** NULL otherwise. 2155 */ 2156 2157 char * 2158 get_column(line, col, delim, buf, buflen) 2159 char line[]; 2160 int col; 2161 int delim; 2162 char buf[]; 2163 int buflen; 2164 { 2165 char *p; 2166 char *begin, *end; 2167 int i; 2168 char delimbuf[4]; 2169 2170 if ((char) delim == '\0') 2171 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf); 2172 else 2173 { 2174 delimbuf[0] = (char) delim; 2175 delimbuf[1] = '\0'; 2176 } 2177 2178 p = line; 2179 if (*p == '\0') 2180 return NULL; /* line empty */ 2181 if (*p == (char) delim && col == 0) 2182 return NULL; /* first column empty */ 2183 2184 begin = line; 2185 2186 if (col == 0 && (char) delim == '\0') 2187 { 2188 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2189 begin++; 2190 } 2191 2192 for (i = 0; i < col; i++) 2193 { 2194 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2195 return NULL; /* no such column */ 2196 begin++; 2197 if ((char) delim == '\0') 2198 { 2199 while (*begin != '\0' && isascii(*begin) && isspace(*begin)) 2200 begin++; 2201 } 2202 } 2203 2204 end = strpbrk(begin, delimbuf); 2205 if (end == NULL) 2206 i = strlen(begin); 2207 else 2208 i = end - begin; 2209 if (i >= buflen) 2210 i = buflen - 1; 2211 (void) sm_strlcpy(buf, begin, i + 1); 2212 return buf; 2213 } 2214 /* 2215 ** CLEANSTRCPY -- copy string keeping out bogus characters 2216 ** 2217 ** Parameters: 2218 ** t -- "to" string. 2219 ** f -- "from" string. 2220 ** l -- length of space available in "to" string. 2221 ** 2222 ** Returns: 2223 ** none. 2224 */ 2225 2226 void 2227 cleanstrcpy(t, f, l) 2228 register char *t; 2229 register char *f; 2230 int l; 2231 { 2232 /* check for newlines and log if necessary */ 2233 (void) denlstring(f, true, true); 2234 2235 if (l <= 0) 2236 syserr("!cleanstrcpy: length == 0"); 2237 2238 l--; 2239 while (l > 0 && *f != '\0') 2240 { 2241 if (isascii(*f) && 2242 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2243 { 2244 l--; 2245 *t++ = *f; 2246 } 2247 f++; 2248 } 2249 *t = '\0'; 2250 } 2251 /* 2252 ** DENLSTRING -- convert newlines in a string to spaces 2253 ** 2254 ** Parameters: 2255 ** s -- the input string 2256 ** strict -- if set, don't permit continuation lines. 2257 ** logattacks -- if set, log attempted attacks. 2258 ** 2259 ** Returns: 2260 ** A pointer to a version of the string with newlines 2261 ** mapped to spaces. This should be copied. 2262 */ 2263 2264 char * 2265 denlstring(s, strict, logattacks) 2266 char *s; 2267 bool strict; 2268 bool logattacks; 2269 { 2270 register char *p; 2271 int l; 2272 static char *bp = NULL; 2273 static int bl = 0; 2274 2275 p = s; 2276 while ((p = strchr(p, '\n')) != NULL) 2277 if (strict || (*++p != ' ' && *p != '\t')) 2278 break; 2279 if (p == NULL) 2280 return s; 2281 2282 l = strlen(s) + 1; 2283 if (bl < l) 2284 { 2285 /* allocate more space */ 2286 char *nbp = sm_pmalloc_x(l); 2287 2288 if (bp != NULL) 2289 sm_free(bp); 2290 bp = nbp; 2291 bl = l; 2292 } 2293 (void) sm_strlcpy(bp, s, l); 2294 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2295 *p++ = ' '; 2296 2297 if (logattacks) 2298 { 2299 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2300 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2301 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2302 shortenstring(bp, MAXSHORTSTR)); 2303 } 2304 2305 return bp; 2306 } 2307 2308 /* 2309 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 2310 ** 2311 ** Parameters: 2312 ** s -- string to manipulate (in place) 2313 ** subst -- character to use as replacement 2314 ** 2315 ** Returns: 2316 ** true iff string did not contain "unprintable" characters 2317 */ 2318 2319 bool 2320 strreplnonprt(s, c) 2321 char *s; 2322 int c; 2323 { 2324 bool ok; 2325 2326 ok = true; 2327 if (s == NULL) 2328 return ok; 2329 while (*s != '\0') 2330 { 2331 if (!(isascii(*s) && isprint(*s))) 2332 { 2333 *s = c; 2334 ok = false; 2335 } 2336 ++s; 2337 } 2338 return ok; 2339 } 2340 2341 /* 2342 ** STR2PRT -- convert "unprintable" characters in a string to \oct 2343 ** 2344 ** Parameters: 2345 ** s -- string to convert 2346 ** 2347 ** Returns: 2348 ** converted string. 2349 ** This is a static local buffer, string must be copied 2350 ** before this function is called again! 2351 */ 2352 2353 char * 2354 str2prt(s) 2355 char *s; 2356 { 2357 int l; 2358 char c, *h; 2359 bool ok; 2360 static int len = 0; 2361 static char *buf = NULL; 2362 2363 if (s == NULL) 2364 return NULL; 2365 ok = true; 2366 for (h = s, l = 1; *h != '\0'; h++, l++) 2367 { 2368 if (*h == '\\') 2369 { 2370 ++l; 2371 ok = false; 2372 } 2373 else if (!(isascii(*h) && isprint(*h))) 2374 { 2375 l += 3; 2376 ok = false; 2377 } 2378 } 2379 if (ok) 2380 return s; 2381 if (l > len) 2382 { 2383 char *nbuf = sm_pmalloc_x(l); 2384 2385 if (buf != NULL) 2386 sm_free(buf); 2387 len = l; 2388 buf = nbuf; 2389 } 2390 for (h = buf; *s != '\0' && l > 0; s++, l--) 2391 { 2392 c = *s; 2393 if (isascii(c) && isprint(c) && c != '\\') 2394 { 2395 *h++ = c; 2396 } 2397 else 2398 { 2399 *h++ = '\\'; 2400 --l; 2401 switch (c) 2402 { 2403 case '\\': 2404 *h++ = '\\'; 2405 break; 2406 case '\t': 2407 *h++ = 't'; 2408 break; 2409 case '\n': 2410 *h++ = 'n'; 2411 break; 2412 case '\r': 2413 *h++ = 'r'; 2414 break; 2415 default: 2416 SM_ASSERT(l >= 2); 2417 (void) sm_snprintf(h, l, "%03o", 2418 (unsigned int)((unsigned char) c)); 2419 2420 /* 2421 ** XXX since l is unsigned this may 2422 ** wrap around if the calculation is screwed 2423 ** up... 2424 */ 2425 2426 l -= 2; 2427 h += 3; 2428 break; 2429 } 2430 } 2431 } 2432 *h = '\0'; 2433 buf[len - 1] = '\0'; 2434 return buf; 2435 } 2436 /* 2437 ** PATH_IS_DIR -- check to see if file exists and is a directory. 2438 ** 2439 ** There are some additional checks for security violations in 2440 ** here. This routine is intended to be used for the host status 2441 ** support. 2442 ** 2443 ** Parameters: 2444 ** pathname -- pathname to check for directory-ness. 2445 ** createflag -- if set, create directory if needed. 2446 ** 2447 ** Returns: 2448 ** true -- if the indicated pathname is a directory 2449 ** false -- otherwise 2450 */ 2451 2452 bool 2453 path_is_dir(pathname, createflag) 2454 char *pathname; 2455 bool createflag; 2456 { 2457 struct stat statbuf; 2458 2459 #if HASLSTAT 2460 if (lstat(pathname, &statbuf) < 0) 2461 #else /* HASLSTAT */ 2462 if (stat(pathname, &statbuf) < 0) 2463 #endif /* HASLSTAT */ 2464 { 2465 if (errno != ENOENT || !createflag) 2466 return false; 2467 if (mkdir(pathname, 0755) < 0) 2468 return false; 2469 return true; 2470 } 2471 if (!S_ISDIR(statbuf.st_mode)) 2472 { 2473 errno = ENOTDIR; 2474 return false; 2475 } 2476 2477 /* security: don't allow writable directories */ 2478 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2479 { 2480 errno = EACCES; 2481 return false; 2482 } 2483 return true; 2484 } 2485 /* 2486 ** PROC_LIST_ADD -- add process id to list of our children 2487 ** 2488 ** Parameters: 2489 ** pid -- pid to add to list. 2490 ** task -- task of pid. 2491 ** type -- type of process. 2492 ** count -- number of processes. 2493 ** other -- other information for this type. 2494 ** 2495 ** Returns: 2496 ** none 2497 ** 2498 ** Side Effects: 2499 ** May increase CurChildren. May grow ProcList. 2500 */ 2501 2502 typedef struct procs PROCS_T; 2503 2504 struct procs 2505 { 2506 pid_t proc_pid; 2507 char *proc_task; 2508 int proc_type; 2509 int proc_count; 2510 int proc_other; 2511 SOCKADDR proc_hostaddr; 2512 }; 2513 2514 static PROCS_T *volatile ProcListVec = NULL; 2515 static int ProcListSize = 0; 2516 2517 void 2518 proc_list_add(pid, task, type, count, other, hostaddr) 2519 pid_t pid; 2520 char *task; 2521 int type; 2522 int count; 2523 int other; 2524 SOCKADDR *hostaddr; 2525 { 2526 int i; 2527 2528 for (i = 0; i < ProcListSize; i++) 2529 { 2530 if (ProcListVec[i].proc_pid == NO_PID) 2531 break; 2532 } 2533 if (i >= ProcListSize) 2534 { 2535 /* probe the existing vector to avoid growing infinitely */ 2536 proc_list_probe(); 2537 2538 /* now scan again */ 2539 for (i = 0; i < ProcListSize; i++) 2540 { 2541 if (ProcListVec[i].proc_pid == NO_PID) 2542 break; 2543 } 2544 } 2545 if (i >= ProcListSize) 2546 { 2547 /* grow process list */ 2548 PROCS_T *npv; 2549 2550 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2551 npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) * 2552 (ProcListSize + PROC_LIST_SEG)); 2553 if (ProcListSize > 0) 2554 { 2555 memmove(npv, ProcListVec, 2556 ProcListSize * sizeof (PROCS_T)); 2557 sm_free(ProcListVec); 2558 } 2559 2560 /* XXX just use memset() to initialize this part? */ 2561 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2562 { 2563 npv[i].proc_pid = NO_PID; 2564 npv[i].proc_task = NULL; 2565 npv[i].proc_type = PROC_NONE; 2566 } 2567 i = ProcListSize; 2568 ProcListSize += PROC_LIST_SEG; 2569 ProcListVec = npv; 2570 } 2571 ProcListVec[i].proc_pid = pid; 2572 PSTRSET(ProcListVec[i].proc_task, task); 2573 ProcListVec[i].proc_type = type; 2574 ProcListVec[i].proc_count = count; 2575 ProcListVec[i].proc_other = other; 2576 if (hostaddr != NULL) 2577 ProcListVec[i].proc_hostaddr = *hostaddr; 2578 else 2579 memset(&ProcListVec[i].proc_hostaddr, 0, 2580 sizeof(ProcListVec[i].proc_hostaddr)); 2581 2582 /* if process adding itself, it's not a child */ 2583 if (pid != CurrentPid) 2584 { 2585 SM_ASSERT(CurChildren < INT_MAX); 2586 CurChildren++; 2587 } 2588 } 2589 /* 2590 ** PROC_LIST_SET -- set pid task in process list 2591 ** 2592 ** Parameters: 2593 ** pid -- pid to set 2594 ** task -- task of pid 2595 ** 2596 ** Returns: 2597 ** none. 2598 */ 2599 2600 void 2601 proc_list_set(pid, task) 2602 pid_t pid; 2603 char *task; 2604 { 2605 int i; 2606 2607 for (i = 0; i < ProcListSize; i++) 2608 { 2609 if (ProcListVec[i].proc_pid == pid) 2610 { 2611 PSTRSET(ProcListVec[i].proc_task, task); 2612 break; 2613 } 2614 } 2615 } 2616 /* 2617 ** PROC_LIST_DROP -- drop pid from process list 2618 ** 2619 ** Parameters: 2620 ** pid -- pid to drop 2621 ** st -- process status 2622 ** other -- storage for proc_other (return). 2623 ** 2624 ** Returns: 2625 ** none. 2626 ** 2627 ** Side Effects: 2628 ** May decrease CurChildren, CurRunners, or 2629 ** set RestartRequest or ShutdownRequest. 2630 ** 2631 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2632 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2633 ** DOING. 2634 */ 2635 2636 void 2637 proc_list_drop(pid, st, other) 2638 pid_t pid; 2639 int st; 2640 int *other; 2641 { 2642 int i; 2643 int type = PROC_NONE; 2644 2645 for (i = 0; i < ProcListSize; i++) 2646 { 2647 if (ProcListVec[i].proc_pid == pid) 2648 { 2649 ProcListVec[i].proc_pid = NO_PID; 2650 type = ProcListVec[i].proc_type; 2651 if (other != NULL) 2652 *other = ProcListVec[i].proc_other; 2653 if (CurChildren > 0) 2654 CurChildren--; 2655 break; 2656 } 2657 } 2658 2659 2660 if (type == PROC_CONTROL && WIFEXITED(st)) 2661 { 2662 /* if so, see if we need to restart or shutdown */ 2663 if (WEXITSTATUS(st) == EX_RESTART) 2664 RestartRequest = "control socket"; 2665 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2666 ShutdownRequest = "control socket"; 2667 } 2668 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2669 ProcListVec[i].proc_other > -1) 2670 { 2671 /* restart this persistent runner */ 2672 mark_work_group_restart(ProcListVec[i].proc_other, st); 2673 } 2674 else if (type == PROC_QUEUE) 2675 CurRunners -= ProcListVec[i].proc_count; 2676 } 2677 /* 2678 ** PROC_LIST_CLEAR -- clear the process list 2679 ** 2680 ** Parameters: 2681 ** none. 2682 ** 2683 ** Returns: 2684 ** none. 2685 ** 2686 ** Side Effects: 2687 ** Sets CurChildren to zero. 2688 */ 2689 2690 void 2691 proc_list_clear() 2692 { 2693 int i; 2694 2695 /* start from 1 since 0 is the daemon itself */ 2696 for (i = 1; i < ProcListSize; i++) 2697 ProcListVec[i].proc_pid = NO_PID; 2698 CurChildren = 0; 2699 } 2700 /* 2701 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2702 ** 2703 ** Parameters: 2704 ** none 2705 ** 2706 ** Returns: 2707 ** none 2708 ** 2709 ** Side Effects: 2710 ** May decrease CurChildren. 2711 */ 2712 2713 void 2714 proc_list_probe() 2715 { 2716 int i, children; 2717 int chldwasblocked; 2718 pid_t pid; 2719 2720 children = 0; 2721 chldwasblocked = sm_blocksignal(SIGCHLD); 2722 2723 /* start from 1 since 0 is the daemon itself */ 2724 for (i = 1; i < ProcListSize; i++) 2725 { 2726 pid = ProcListVec[i].proc_pid; 2727 if (pid == NO_PID || pid == CurrentPid) 2728 continue; 2729 if (kill(pid, 0) < 0) 2730 { 2731 if (LogLevel > 3) 2732 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2733 "proc_list_probe: lost pid %d", 2734 (int) ProcListVec[i].proc_pid); 2735 ProcListVec[i].proc_pid = NO_PID; 2736 SM_FREE_CLR(ProcListVec[i].proc_task); 2737 CurChildren--; 2738 } 2739 else 2740 { 2741 ++children; 2742 } 2743 } 2744 if (CurChildren < 0) 2745 CurChildren = 0; 2746 if (chldwasblocked == 0) 2747 (void) sm_releasesignal(SIGCHLD); 2748 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid) 2749 { 2750 sm_syslog(LOG_ERR, NOQID, 2751 "proc_list_probe: found %d children, expected %d", 2752 children, CurChildren); 2753 } 2754 } 2755 2756 /* 2757 ** PROC_LIST_DISPLAY -- display the process list 2758 ** 2759 ** Parameters: 2760 ** out -- output file pointer 2761 ** prefix -- string to output in front of each line. 2762 ** 2763 ** Returns: 2764 ** none. 2765 */ 2766 2767 void 2768 proc_list_display(out, prefix) 2769 SM_FILE_T *out; 2770 char *prefix; 2771 { 2772 int i; 2773 2774 for (i = 0; i < ProcListSize; i++) 2775 { 2776 if (ProcListVec[i].proc_pid == NO_PID) 2777 continue; 2778 2779 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2780 prefix, 2781 (int) ProcListVec[i].proc_pid, 2782 ProcListVec[i].proc_task != NULL ? 2783 ProcListVec[i].proc_task : "(unknown)", 2784 (OpMode == MD_SMTP || 2785 OpMode == MD_DAEMON || 2786 OpMode == MD_ARPAFTP) ? "\r" : ""); 2787 } 2788 } 2789 2790 /* 2791 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2792 ** 2793 ** Parameters: 2794 ** type -- type of process to signal 2795 ** signal -- the type of signal to send 2796 ** 2797 ** Results: 2798 ** none. 2799 ** 2800 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2801 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2802 ** DOING. 2803 */ 2804 2805 void 2806 proc_list_signal(type, signal) 2807 int type; 2808 int signal; 2809 { 2810 int chldwasblocked; 2811 int alrmwasblocked; 2812 int i; 2813 pid_t mypid = getpid(); 2814 2815 /* block these signals so that we may signal cleanly */ 2816 chldwasblocked = sm_blocksignal(SIGCHLD); 2817 alrmwasblocked = sm_blocksignal(SIGALRM); 2818 2819 /* Find all processes of type and send signal */ 2820 for (i = 0; i < ProcListSize; i++) 2821 { 2822 if (ProcListVec[i].proc_pid == NO_PID || 2823 ProcListVec[i].proc_pid == mypid) 2824 continue; 2825 if (ProcListVec[i].proc_type != type) 2826 continue; 2827 (void) kill(ProcListVec[i].proc_pid, signal); 2828 } 2829 2830 /* restore the signals */ 2831 if (alrmwasblocked == 0) 2832 (void) sm_releasesignal(SIGALRM); 2833 if (chldwasblocked == 0) 2834 (void) sm_releasesignal(SIGCHLD); 2835 } 2836 2837 /* 2838 ** COUNT_OPEN_CONNECTIONS 2839 ** 2840 ** Parameters: 2841 ** hostaddr - ClientAddress 2842 ** 2843 ** Returns: 2844 ** the number of open connections for this client 2845 ** 2846 */ 2847 2848 int 2849 count_open_connections(hostaddr) 2850 SOCKADDR *hostaddr; 2851 { 2852 int i, n; 2853 2854 if (hostaddr == NULL) 2855 return 0; 2856 n = 0; 2857 for (i = 0; i < ProcListSize; i++) 2858 { 2859 if (ProcListVec[i].proc_pid == NO_PID) 2860 continue; 2861 if (hostaddr->sa.sa_family != 2862 ProcListVec[i].proc_hostaddr.sa.sa_family) 2863 continue; 2864 #if NETINET 2865 if (hostaddr->sa.sa_family == AF_INET && 2866 (hostaddr->sin.sin_addr.s_addr == 2867 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 2868 n++; 2869 #endif /* NETINET */ 2870 #if NETINET6 2871 if (hostaddr->sa.sa_family == AF_INET6 && 2872 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 2873 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 2874 n++; 2875 #endif /* NETINET6 */ 2876 } 2877 return n; 2878 } 2879