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