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