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