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