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