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