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 (void) sm_io_putc(fp, SM_TIME_DEFAULT, v); 761 # if 0 762 if (isascii(v) && isprint(v)) 763 (void) sm_io_putc(fp, SM_TIME_DEFAULT, 764 v); 765 else 766 (void) sm_io_fprintf(fp, 767 SM_TIME_DEFAULT, "\\x%hhx", v); 768 # endif 769 } 770 if (*av != NULL) 771 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' '); 772 continue; 773 } 774 #endif /* _FFR_8BITENVADDR */ 775 if (tTd(0, 44)) 776 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 777 "\n\t%08lx=", (unsigned long) *av); 778 else 779 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' '); 780 if (tTd(0, 99)) 781 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 782 "%s", str2prt(*av++)); 783 else 784 xputs(fp, *av++); 785 } 786 787 /* don't print this if invoked directly (not via xputs())? */ 788 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n'); 789 } 790 791 /* 792 ** XPUTS -- put string doing control escapes. 793 ** 794 ** Parameters: 795 ** fp -- output file pointer. 796 ** s -- string to put. [A] 797 ** 798 ** Returns: 799 ** none. 800 ** 801 ** Side Effects: 802 ** output to stdout 803 */ 804 805 void 806 xputs(fp, s) 807 SM_FILE_T *fp; 808 const char *s; 809 { 810 int c; 811 struct metamac *mp; 812 bool shiftout = false; 813 extern struct metamac MetaMacros[]; 814 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI", 815 "@(#)$Debug: ANSI - enable reverse video in debug output $"); 816 #if _FFR_8BITENVADDR 817 if (tTd(0, 9)) 818 { 819 char *av[2]; 820 821 av[0] = (char *) s; 822 av[1] = NULL; 823 printav(fp, av); 824 return; 825 } 826 #endif 827 828 /* 829 ** TermEscape is set here, rather than in main(), 830 ** because ANSI mode can be turned on or off at any time 831 ** if we are in -bt rule testing mode. 832 */ 833 834 if (sm_debug_unknown(&DebugANSI)) 835 { 836 if (sm_debug_active(&DebugANSI, 1)) 837 { 838 TermEscape.te_rv_on = "\033[7m"; 839 TermEscape.te_normal = "\033[0m"; 840 } 841 else 842 { 843 TermEscape.te_rv_on = ""; 844 TermEscape.te_normal = ""; 845 } 846 } 847 848 if (s == NULL) 849 { 850 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s", 851 TermEscape.te_rv_on, TermEscape.te_normal); 852 return; 853 } 854 while ((c = (*s++ & 0377)) != '\0') 855 { 856 if (shiftout) 857 { 858 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 859 TermEscape.te_normal); 860 shiftout = false; 861 } 862 if (!isascii(c) && !tTd(84, 1)) 863 { 864 if (c == MATCHREPL) 865 { 866 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 867 "%s$", 868 TermEscape.te_rv_on); 869 shiftout = true; 870 if (*s == '\0') 871 continue; 872 c = *s++ & 0377; 873 goto printchar; 874 } 875 if (c == MACROEXPAND || c == MACRODEXPAND) 876 { 877 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 878 "%s$", 879 TermEscape.te_rv_on); 880 if (c == MACRODEXPAND) 881 (void) sm_io_putc(fp, 882 SM_TIME_DEFAULT, '&'); 883 shiftout = true; 884 if (*s == '\0') 885 continue; 886 if (strchr("=~&?", *s) != NULL) 887 (void) sm_io_putc(fp, 888 SM_TIME_DEFAULT, 889 *s++); 890 if (bitset(0200, *s)) 891 (void) sm_io_fprintf(fp, 892 SM_TIME_DEFAULT, 893 "{%s}", 894 macname(bitidx(*s++))); 895 else 896 (void) sm_io_fprintf(fp, 897 SM_TIME_DEFAULT, 898 "%c", 899 *s++); 900 continue; 901 } 902 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 903 { 904 if (bitidx(mp->metaval) == c) 905 { 906 (void) sm_io_fprintf(fp, 907 SM_TIME_DEFAULT, 908 "%s$%c", 909 TermEscape.te_rv_on, 910 mp->metaname); 911 shiftout = true; 912 break; 913 } 914 } 915 if (c == MATCHCLASS || c == MATCHNCLASS) 916 { 917 if (bitset(0200, *s)) 918 (void) sm_io_fprintf(fp, 919 SM_TIME_DEFAULT, 920 "{%s}", 921 macname(bitidx(*s++))); 922 else if (*s != '\0') 923 (void) sm_io_fprintf(fp, 924 SM_TIME_DEFAULT, 925 "%c", 926 *s++); 927 } 928 if (mp->metaname != '\0') 929 continue; 930 931 /* unrecognized meta character */ 932 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-", 933 TermEscape.te_rv_on); 934 shiftout = true; 935 c &= 0177; 936 } 937 printchar: 938 if (isascii(c) && isprint(c)) 939 { 940 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 941 continue; 942 } 943 944 /* wasn't a meta-macro -- find another way to print it */ 945 switch (c) 946 { 947 case '\n': 948 c = 'n'; 949 break; 950 951 case '\r': 952 c = 'r'; 953 break; 954 955 case '\t': 956 c = 't'; 957 break; 958 } 959 if (!shiftout) 960 { 961 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 962 TermEscape.te_rv_on); 963 shiftout = true; 964 } 965 if (isascii(c) && isprint(c)) 966 { 967 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\'); 968 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c); 969 } 970 else if (tTd(84, 2)) 971 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c); 972 else if (tTd(84, 1)) 973 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c); 974 else if (!isascii(c) && !tTd(84, 1)) 975 { 976 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^'); 977 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100); 978 } 979 } 980 if (shiftout) 981 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 982 TermEscape.te_normal); 983 (void) sm_io_flush(fp, SM_TIME_DEFAULT); 984 } 985 986 /* 987 ** MAKELOWER_A -- Translate a line into lower case 988 ** 989 ** Parameters: 990 ** pp -- pointer to the string to translate (modified in place if possible). [A] 991 ** rpool -- rpool to use to reallocate string if needed 992 ** (if NULL: uses sm_malloc() internally) 993 ** 994 ** Returns: 995 ** lower cased string 996 ** 997 ** Side Effects: 998 ** String pointed to by pp is translated to lower case if possible. 999 */ 1000 1001 char * 1002 makelower_a(pp, rpool) 1003 char **pp; 1004 SM_RPOOL_T *rpool; 1005 { 1006 char c; 1007 char *orig, *p; 1008 1009 SM_REQUIRE(pp != NULL); 1010 p = *pp; 1011 if (p == NULL) 1012 return p; 1013 orig = p; 1014 1015 #if USE_EAI 1016 if (!addr_is_ascii(p)) 1017 { 1018 char *new; 1019 1020 new = sm_lowercase(p); 1021 if (new == p) 1022 return p; 1023 *pp = sm_rpool_strdup_tagged_x(rpool, new, "makelower", 0, 1); 1024 return *pp; 1025 } 1026 #endif /* USE_EAI */ 1027 for (; (c = *p) != '\0'; p++) 1028 if (isascii(c) && isupper(c)) 1029 *p = tolower(c); 1030 return orig; 1031 } 1032 1033 #if 0 1034 makelower: Optimization for EAI case? 1035 1036 unsigned char ch; 1037 1038 while ((ch = (unsigned char)*str) != '\0' && ch < 127) 1039 { 1040 if (isascii(c) && isupper(c)) 1041 *str = tolower(ch); 1042 ++str; 1043 } 1044 if ('\0' == ch) 1045 return orig; 1046 handle UTF-8 case: invoke sm_lowercase() etc 1047 #endif /* 0 */ 1048 1049 /* 1050 ** MAKELOWER_BUF -- Translate a line into lower case 1051 ** 1052 ** Parameters: 1053 ** str -- string to translate. [A] 1054 ** buf -- where to place lower case version. 1055 ** buflen -- size of buf 1056 ** 1057 ** Returns: 1058 ** nothing 1059 ** 1060 ** Side Effects: 1061 ** String pointed to by str is translated to lower case if possible. 1062 ** 1063 ** Note: 1064 ** if str is lower cased in place and str == buf (same pointer), 1065 ** then it is not explicitly copied. 1066 */ 1067 1068 void 1069 makelower_buf(str, buf, buflen) 1070 char *str; 1071 char *buf; 1072 int buflen; 1073 { 1074 char *lower; 1075 1076 SM_REQUIRE(buf != NULL); 1077 if (str == NULL) 1078 return; 1079 lower = makelower_a(&str, NULL); 1080 if (lower != str || str != buf) 1081 { 1082 sm_strlcpy(buf, lower, buflen); 1083 if (lower != str) 1084 SM_FREE(lower); 1085 } 1086 return; 1087 } 1088 1089 /* 1090 ** FIXCRLF -- fix CRLF in line. 1091 ** 1092 ** XXX: Could this be a problem for EAI? That is, can there 1093 ** be a string with \n and the previous octet is \n 1094 ** but is part of a UTF8 "char"? 1095 ** 1096 ** Looks for the CRLF combination and turns it into the 1097 ** UNIX canonical LF character. It only takes one line, 1098 ** i.e., it is assumed that the first LF found is the end 1099 ** of the line. 1100 ** 1101 ** Parameters: 1102 ** line -- the line to fix. [A] 1103 ** stripnl -- if true, strip the newline also. 1104 ** 1105 ** Returns: 1106 ** none. 1107 ** 1108 ** Side Effects: 1109 ** line is changed in place. 1110 */ 1111 1112 void 1113 fixcrlf(line, stripnl) 1114 char *line; 1115 bool stripnl; 1116 { 1117 register char *p; 1118 1119 p = strchr(line, '\n'); 1120 if (p == NULL) 1121 return; 1122 if (p > line && p[-1] == '\r') 1123 p--; 1124 if (!stripnl) 1125 *p++ = '\n'; 1126 *p = '\0'; 1127 } 1128 1129 /* 1130 ** PUTLINE -- put a line like fputs obeying SMTP conventions 1131 ** 1132 ** This routine always guarantees outputting a newline (or CRLF, 1133 ** as appropriate) at the end of the string. 1134 ** 1135 ** Parameters: 1136 ** l -- line to put. (should be [x]) 1137 ** mci -- the mailer connection information. 1138 ** 1139 ** Returns: 1140 ** true iff line was written successfully 1141 ** 1142 ** Side Effects: 1143 ** output of l to mci->mci_out. 1144 */ 1145 1146 bool 1147 putline(l, mci) 1148 register char *l; 1149 register MCI *mci; 1150 { 1151 return putxline(l, strlen(l), mci, PXLF_MAPFROM); 1152 } 1153 1154 /* 1155 ** PUTXLINE -- putline with flags bits. 1156 ** 1157 ** This routine always guarantees outputting a newline (or CRLF, 1158 ** as appropriate) at the end of the string. 1159 ** 1160 ** Parameters: 1161 ** l -- line to put. (should be [x]) 1162 ** len -- the length of the line. 1163 ** mci -- the mailer connection information. 1164 ** pxflags -- flag bits: 1165 ** PXLF_MAPFROM -- map From_ to >From_. 1166 ** PXLF_STRIP8BIT -- strip 8th bit. 1167 ** PXLF_HEADER -- map bare newline in header to newline space. 1168 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present. 1169 ** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes. 1170 ** 1171 ** Returns: 1172 ** true iff line was written successfully 1173 ** 1174 ** Side Effects: 1175 ** output of l to mci->mci_out. 1176 */ 1177 1178 #define PUTX(limit) \ 1179 do \ 1180 { \ 1181 quotenext = false; \ 1182 while (l < limit) \ 1183 { \ 1184 unsigned char c = (unsigned char) *l++; \ 1185 \ 1186 if (bitset(PXLF_STRIPMQUOTE, pxflags) && \ 1187 !quotenext && c == METAQUOTE) \ 1188 { \ 1189 quotenext = true; \ 1190 continue; \ 1191 } \ 1192 quotenext = false; \ 1193 if (strip8bit) \ 1194 c &= 0177; \ 1195 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \ 1196 c) == SM_IO_EOF) \ 1197 { \ 1198 dead = true; \ 1199 break; \ 1200 } \ 1201 if (TrafficLogFile != NULL) \ 1202 (void) sm_io_putc(TrafficLogFile, \ 1203 SM_TIME_DEFAULT, \ 1204 c); \ 1205 } \ 1206 } while (0) 1207 1208 bool 1209 putxline(l, len, mci, pxflags) 1210 register char *l; 1211 size_t len; 1212 register MCI *mci; 1213 int pxflags; 1214 { 1215 register char *p, *end; 1216 int slop; 1217 bool dead, quotenext, strip8bit; 1218 1219 /* strip out 0200 bits -- these can look like TELNET protocol */ 1220 strip8bit = bitset(MCIF_7BIT, mci->mci_flags) || 1221 bitset(PXLF_STRIP8BIT, pxflags); 1222 dead = false; 1223 slop = 0; 1224 1225 end = l + len; 1226 do 1227 { 1228 bool noeol = false; 1229 1230 /* find the end of the line */ 1231 p = memchr(l, '\n', end - l); 1232 if (p == NULL) 1233 { 1234 p = end; 1235 noeol = true; 1236 } 1237 1238 if (TrafficLogFile != NULL) 1239 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1240 "%05d >>> ", (int) CurrentPid); 1241 1242 /* check for line overflow */ 1243 while (mci->mci_mailer->m_linelimit > 0 && 1244 (p - l + slop) > mci->mci_mailer->m_linelimit) 1245 { 1246 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 1247 1248 if (l[0] == '.' && slop == 0 && 1249 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 1250 { 1251 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1252 '.') == SM_IO_EOF) 1253 dead = true; 1254 if (TrafficLogFile != NULL) 1255 (void) sm_io_putc(TrafficLogFile, 1256 SM_TIME_DEFAULT, '.'); 1257 } 1258 else if (l[0] == 'F' && slop == 0 && 1259 bitset(PXLF_MAPFROM, pxflags) && 1260 strncmp(l, "From ", 5) == 0 && 1261 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 1262 { 1263 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1264 '>') == SM_IO_EOF) 1265 dead = true; 1266 if (TrafficLogFile != NULL) 1267 (void) sm_io_putc(TrafficLogFile, 1268 SM_TIME_DEFAULT, 1269 '>'); 1270 } 1271 if (dead) 1272 break; 1273 1274 PUTX(q); 1275 if (dead) 1276 break; 1277 1278 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1279 '!') == SM_IO_EOF || 1280 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1281 mci->mci_mailer->m_eol) == SM_IO_EOF || 1282 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1283 ' ') == SM_IO_EOF) 1284 { 1285 dead = true; 1286 break; 1287 } 1288 if (TrafficLogFile != NULL) 1289 { 1290 (void) sm_io_fprintf(TrafficLogFile, 1291 SM_TIME_DEFAULT, 1292 "!\n%05d >>> ", 1293 (int) CurrentPid); 1294 } 1295 slop = 1; 1296 } 1297 1298 if (dead) 1299 break; 1300 1301 /* output last part */ 1302 if (l[0] == '.' && slop == 0 && 1303 bitnset(M_XDOT, mci->mci_mailer->m_flags) && 1304 !bitset(MCIF_INLONGLINE, mci->mci_flags)) 1305 { 1306 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') == 1307 SM_IO_EOF) 1308 { 1309 dead = true; 1310 break; 1311 } 1312 if (TrafficLogFile != NULL) 1313 (void) sm_io_putc(TrafficLogFile, 1314 SM_TIME_DEFAULT, '.'); 1315 } 1316 else if (l[0] == 'F' && slop == 0 && 1317 bitset(PXLF_MAPFROM, pxflags) && 1318 strncmp(l, "From ", 5) == 0 && 1319 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && 1320 !bitset(MCIF_INLONGLINE, mci->mci_flags)) 1321 { 1322 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') == 1323 SM_IO_EOF) 1324 { 1325 dead = true; 1326 break; 1327 } 1328 if (TrafficLogFile != NULL) 1329 (void) sm_io_putc(TrafficLogFile, 1330 SM_TIME_DEFAULT, '>'); 1331 } 1332 PUTX(p); 1333 if (dead) 1334 break; 1335 1336 if (TrafficLogFile != NULL) 1337 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, 1338 '\n'); 1339 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol)) 1340 { 1341 mci->mci_flags &= ~MCIF_INLONGLINE; 1342 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 1343 mci->mci_mailer->m_eol) == SM_IO_EOF) 1344 { 1345 dead = true; 1346 break; 1347 } 1348 } 1349 else 1350 mci->mci_flags |= MCIF_INLONGLINE; 1351 1352 if (l < end && *l == '\n') 1353 { 1354 if (*++l != ' ' && *l != '\t' && *l != '\0' && 1355 bitset(PXLF_HEADER, pxflags)) 1356 { 1357 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 1358 ' ') == SM_IO_EOF) 1359 { 1360 dead = true; 1361 break; 1362 } 1363 1364 if (TrafficLogFile != NULL) 1365 (void) sm_io_putc(TrafficLogFile, 1366 SM_TIME_DEFAULT, ' '); 1367 } 1368 } 1369 1370 } while (l < end); 1371 return !dead; 1372 } 1373 1374 /* 1375 ** XUNLINK -- unlink a file, doing logging as appropriate. 1376 ** 1377 ** Parameters: 1378 ** f -- name of file to unlink. 1379 ** 1380 ** Returns: 1381 ** return value of unlink() 1382 ** 1383 ** Side Effects: 1384 ** f is unlinked. 1385 */ 1386 1387 int 1388 xunlink(f) 1389 char *f; 1390 { 1391 register int i; 1392 int save_errno; 1393 1394 if (LogLevel > 98) 1395 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f); 1396 1397 i = unlink(f); 1398 save_errno = errno; 1399 if (i < 0 && LogLevel > 97) 1400 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d", 1401 f, errno); 1402 if (i >= 0) 1403 SYNC_DIR(f, false); 1404 errno = save_errno; 1405 return i; 1406 } 1407 1408 /* 1409 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1410 ** 1411 ** Parameters: 1412 ** buf -- place to put the input line. 1413 ** (can be [A], but should be [x]) 1414 ** siz -- size of buf. 1415 ** fp -- file to read from. 1416 ** timeout -- the timeout before error occurs. 1417 ** during -- what we are trying to read (for error messages). 1418 ** 1419 ** Returns: 1420 ** NULL on error (including timeout). This may also leave 1421 ** buf containing a null string. 1422 ** buf otherwise. 1423 */ 1424 1425 char * 1426 sfgets(buf, siz, fp, timeout, during) 1427 char *buf; 1428 int siz; 1429 SM_FILE_T *fp; 1430 time_t timeout; 1431 char *during; 1432 { 1433 register char *p; 1434 int save_errno, io_timeout, l; 1435 1436 SM_REQUIRE(siz > 0); 1437 SM_REQUIRE(buf != NULL); 1438 1439 if (fp == NULL) 1440 { 1441 buf[0] = '\0'; 1442 errno = EBADF; 1443 return NULL; 1444 } 1445 1446 /* try to read */ 1447 l = -1; 1448 errno = 0; 1449 1450 /* convert the timeout to sm_io notation */ 1451 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000; 1452 while (!sm_io_eof(fp) && !sm_io_error(fp)) 1453 { 1454 errno = 0; 1455 l = sm_io_fgets(fp, io_timeout, buf, siz); 1456 if (l < 0 && errno == EAGAIN) 1457 { 1458 /* The sm_io_fgets() call timedout */ 1459 if (LogLevel > 1) 1460 sm_syslog(LOG_NOTICE, CurEnv->e_id, 1461 "timeout waiting for input from %.100s during %s", 1462 CURHOSTNAME, 1463 during); 1464 buf[0] = '\0'; 1465 checkfd012(during); 1466 if (TrafficLogFile != NULL) 1467 (void) sm_io_fprintf(TrafficLogFile, 1468 SM_TIME_DEFAULT, 1469 "%05d <<< [TIMEOUT]\n", 1470 (int) CurrentPid); 1471 errno = ETIMEDOUT; 1472 return NULL; 1473 } 1474 if (l >= 0 || errno != EINTR) 1475 break; 1476 (void) sm_io_clearerr(fp); 1477 } 1478 save_errno = errno; 1479 1480 /* clean up the books and exit */ 1481 LineNumber++; 1482 if (l < 0) 1483 { 1484 buf[0] = '\0'; 1485 if (TrafficLogFile != NULL) 1486 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1487 "%05d <<< [EOF]\n", 1488 (int) CurrentPid); 1489 errno = save_errno; 1490 return NULL; 1491 } 1492 if (TrafficLogFile != NULL) 1493 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 1494 "%05d <<< %s", (int) CurrentPid, buf); 1495 if (SevenBitInput) 1496 { 1497 for (p = buf; *p != '\0'; p++) 1498 *p &= ~0200; 1499 } 1500 else if (!HasEightBits) 1501 { 1502 for (p = buf; *p != '\0'; p++) 1503 { 1504 if (bitset(0200, *p)) 1505 { 1506 HasEightBits = true; 1507 break; 1508 } 1509 } 1510 } 1511 return buf; 1512 } 1513 1514 /* 1515 ** FGETFOLDED -- like fgets, but knows about folded lines. 1516 ** 1517 ** Parameters: 1518 ** buf -- place to put result. 1519 ** (can be [A], but should be [x]) 1520 ** np -- pointer to bytes available; will be updated with 1521 ** the actual buffer size (not number of bytes filled) 1522 ** on return. 1523 ** f -- file to read from. 1524 ** 1525 ** Returns: 1526 ** input line(s) on success, NULL on error or SM_IO_EOF. 1527 ** This will normally be buf -- unless the line is too 1528 ** long, when it will be sm_malloc_x()ed. 1529 ** 1530 ** Side Effects: 1531 ** buf gets lines from f, with continuation lines (lines 1532 ** with leading white space) appended. CRLF's are mapped 1533 ** into single newlines. Any trailing LF is stripped. 1534 ** Increases LineNumber for each line. 1535 */ 1536 1537 char * 1538 fgetfolded(buf, np, f) 1539 char *buf; 1540 int *np; 1541 SM_FILE_T *f; 1542 { 1543 register char *p = buf; 1544 char *bp = buf; 1545 register int i; 1546 int n; 1547 1548 SM_REQUIRE(np != NULL); 1549 n = *np; 1550 SM_REQUIRE(n > 0); 1551 SM_REQUIRE(buf != NULL); 1552 if (f == NULL) 1553 { 1554 buf[0] = '\0'; 1555 errno = EBADF; 1556 return NULL; 1557 } 1558 1559 n--; 1560 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF) 1561 { 1562 if (i == '\r') 1563 { 1564 i = sm_io_getc(f, SM_TIME_DEFAULT); 1565 if (i != '\n') 1566 { 1567 if (i != SM_IO_EOF) 1568 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, 1569 i); 1570 i = '\r'; 1571 } 1572 } 1573 if (--n <= 0) 1574 { 1575 /* allocate new space */ 1576 char *nbp; 1577 int nn; 1578 1579 nn = (p - bp); 1580 if (nn < MEMCHUNKSIZE) 1581 nn *= 2; 1582 else 1583 nn += MEMCHUNKSIZE; 1584 nbp = sm_malloc_x(nn); 1585 memmove(nbp, bp, p - bp); 1586 p = &nbp[p - bp]; 1587 if (bp != buf) 1588 sm_free(bp); 1589 bp = nbp; 1590 n = nn - (p - bp); 1591 *np = nn; 1592 } 1593 *p++ = i; 1594 if (i == '\n') 1595 { 1596 LineNumber++; 1597 i = sm_io_getc(f, SM_TIME_DEFAULT); 1598 if (i != SM_IO_EOF) 1599 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i); 1600 if (i != ' ' && i != '\t') 1601 break; 1602 } 1603 } 1604 if (p == bp) 1605 return NULL; 1606 if (p[-1] == '\n') 1607 p--; 1608 *p = '\0'; 1609 return bp; 1610 } 1611 1612 /* 1613 ** CURTIME -- return current time. 1614 ** 1615 ** Parameters: 1616 ** none. 1617 ** 1618 ** Returns: 1619 ** the current time. 1620 */ 1621 1622 time_t 1623 curtime() 1624 { 1625 auto time_t t; 1626 1627 (void) time(&t); 1628 return t; 1629 } 1630 1631 /* 1632 ** ATOBOOL -- convert a string representation to boolean. 1633 ** 1634 ** Defaults to false 1635 ** 1636 ** Parameters: 1637 ** s -- string to convert. Takes "tTyY", empty, and NULL as true, 1638 ** others as false. 1639 ** 1640 ** Returns: 1641 ** A boolean representation of the string. 1642 */ 1643 1644 bool 1645 atobool(s) 1646 register char *s; 1647 { 1648 if (SM_IS_EMPTY(s) || strchr("tTyY", *s) != NULL) 1649 return true; 1650 return false; 1651 } 1652 1653 /* 1654 ** ATOOCT -- convert a string representation to octal. 1655 ** 1656 ** Parameters: 1657 ** s -- string to convert. 1658 ** 1659 ** Returns: 1660 ** An integer representing the string interpreted as an 1661 ** octal number. 1662 */ 1663 1664 int 1665 atooct(s) 1666 register char *s; 1667 { 1668 register int i = 0; 1669 1670 while (*s >= '0' && *s <= '7') 1671 i = (i << 3) | (*s++ - '0'); 1672 return i; 1673 } 1674 1675 /* 1676 ** BITINTERSECT -- tell if two bitmaps intersect 1677 ** 1678 ** Parameters: 1679 ** a, b -- the bitmaps in question 1680 ** 1681 ** Returns: 1682 ** true if they have a non-null intersection 1683 ** false otherwise 1684 */ 1685 1686 bool 1687 bitintersect(a, b) 1688 BITMAP256 a; 1689 BITMAP256 b; 1690 { 1691 int i; 1692 1693 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1694 { 1695 if ((a[i] & b[i]) != 0) 1696 return true; 1697 } 1698 return false; 1699 } 1700 1701 /* 1702 ** BITZEROP -- tell if a bitmap is all zero 1703 ** 1704 ** Parameters: 1705 ** map -- the bit map to check 1706 ** 1707 ** Returns: 1708 ** true if map is all zero. 1709 ** false if there are any bits set in map. 1710 */ 1711 1712 bool 1713 bitzerop(map) 1714 BITMAP256 map; 1715 { 1716 int i; 1717 1718 for (i = BITMAPBYTES / sizeof(int); --i >= 0; ) 1719 { 1720 if (map[i] != 0) 1721 return false; 1722 } 1723 return true; 1724 } 1725 1726 /* 1727 ** STRCONTAINEDIN -- tell if one string is contained in another 1728 ** 1729 ** Parameters: 1730 ** icase -- ignore case? 1731 ** a -- possible substring. [A] 1732 ** b -- possible superstring. [A] 1733 ** (both must be the same format: X or Q) 1734 ** 1735 ** Returns: 1736 ** true if a is contained in b (case insensitive). 1737 ** false otherwise. 1738 */ 1739 1740 bool 1741 strcontainedin(icase, a, b) 1742 bool icase; 1743 register char *a; 1744 register char *b; 1745 { 1746 int la; 1747 int lb; 1748 int c; 1749 1750 la = strlen(a); 1751 lb = strlen(b); 1752 c = *a; 1753 if (icase && isascii(c) && isupper(c)) 1754 c = tolower(c); 1755 for (; lb-- >= la; b++) 1756 { 1757 if (icase) 1758 { 1759 if (*b != c && 1760 isascii(*b) && isupper(*b) && tolower(*b) != c) 1761 continue; 1762 if (sm_strncasecmp(a, b, la) == 0) 1763 return true; 1764 } 1765 else 1766 { 1767 if (*b != c) 1768 continue; 1769 if (strncmp(a, b, la) == 0) 1770 return true; 1771 } 1772 } 1773 return false; 1774 } 1775 1776 #if XDEBUG 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 register int i; 1795 1796 for (i = 0; i < 3; i++) 1797 fill_fd(i, where); 1798 } 1799 1800 /* 1801 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging 1802 ** 1803 ** Parameters: 1804 ** fd -- file descriptor to check. 1805 ** where -- tag to print on failure. 1806 ** 1807 ** Returns: 1808 ** none. 1809 */ 1810 1811 void 1812 checkfdopen(fd, where) 1813 int fd; 1814 char *where; 1815 { 1816 struct stat st; 1817 1818 if (fstat(fd, &st) < 0 && errno == EBADF) 1819 { 1820 syserr("checkfdopen(%d): %s not open as expected!", fd, where); 1821 printopenfds(true); 1822 } 1823 } 1824 #endif /* XDEBUG */ 1825 1826 /* 1827 ** CHECKFDS -- check for new or missing file descriptors 1828 ** 1829 ** Parameters: 1830 ** where -- tag for printing. If null, take a base line. 1831 ** 1832 ** Returns: 1833 ** none 1834 ** 1835 ** Side Effects: 1836 ** If where is set, shows changes since the last call. 1837 */ 1838 1839 void 1840 checkfds(where) 1841 char *where; 1842 { 1843 int maxfd; 1844 register int fd; 1845 bool printhdr = true; 1846 int save_errno = errno; 1847 static BITMAP256 baseline; 1848 extern int DtableSize; 1849 1850 if (DtableSize > BITMAPBITS) 1851 maxfd = BITMAPBITS; 1852 else 1853 maxfd = DtableSize; 1854 if (where == NULL) 1855 clrbitmap(baseline); 1856 1857 for (fd = 0; fd < maxfd; fd++) 1858 { 1859 struct stat stbuf; 1860 1861 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) 1862 { 1863 if (!bitnset(fd, baseline)) 1864 continue; 1865 clrbitn(fd, baseline); 1866 } 1867 else if (!bitnset(fd, baseline)) 1868 setbitn(fd, baseline); 1869 else 1870 continue; 1871 1872 /* file state has changed */ 1873 if (where == NULL) 1874 continue; 1875 if (printhdr) 1876 { 1877 sm_syslog(LOG_DEBUG, CurEnv->e_id, 1878 "%s: changed fds:", 1879 where); 1880 printhdr = false; 1881 } 1882 dumpfd(fd, true, true); 1883 } 1884 errno = save_errno; 1885 } 1886 1887 /* 1888 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1889 ** 1890 ** Parameters: 1891 ** logit -- if set, send output to syslog; otherwise 1892 ** print for debugging. 1893 ** 1894 ** Returns: 1895 ** none. 1896 */ 1897 1898 #if NETINET || NETINET6 1899 # include <arpa/inet.h> 1900 #endif 1901 1902 void 1903 printopenfds(logit) 1904 bool logit; 1905 { 1906 register int fd; 1907 extern int DtableSize; 1908 1909 for (fd = 0; fd < DtableSize; fd++) 1910 dumpfd(fd, false, logit); 1911 } 1912 1913 /* 1914 ** DUMPFD -- dump a file descriptor 1915 ** 1916 ** Parameters: 1917 ** fd -- the file descriptor to dump. 1918 ** printclosed -- if set, print a notification even if 1919 ** it is closed; otherwise print nothing. 1920 ** logit -- if set, use sm_syslog instead of sm_dprintf() 1921 ** 1922 ** Returns: 1923 ** none. 1924 */ 1925 1926 void 1927 dumpfd(fd, printclosed, logit) 1928 int fd; 1929 bool printclosed; 1930 bool logit; 1931 { 1932 register char *p; 1933 char *hp; 1934 #ifdef S_IFSOCK 1935 SOCKADDR sa; 1936 #endif 1937 auto SOCKADDR_LEN_T slen; 1938 int i; 1939 #if STAT64 > 0 1940 struct stat64 st; 1941 #else 1942 struct stat st; 1943 #endif 1944 char buf[200]; 1945 1946 p = buf; 1947 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); 1948 p += strlen(p); 1949 1950 if ( 1951 #if STAT64 > 0 1952 fstat64(fd, &st) 1953 #else 1954 fstat(fd, &st) 1955 #endif 1956 < 0) 1957 { 1958 if (errno != EBADF) 1959 { 1960 (void) sm_snprintf(p, SPACELEFT(buf, p), 1961 "CANNOT STAT (%s)", 1962 sm_errstring(errno)); 1963 goto printit; 1964 } 1965 else if (printclosed) 1966 { 1967 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED"); 1968 goto printit; 1969 } 1970 return; 1971 } 1972 1973 i = fcntl(fd, F_GETFL, 0); 1974 if (i != -1) 1975 { 1976 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); 1977 p += strlen(p); 1978 } 1979 1980 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ", 1981 (unsigned int) st.st_mode); 1982 p += strlen(p); 1983 switch (st.st_mode & S_IFMT) 1984 { 1985 #ifdef S_IFSOCK 1986 case S_IFSOCK: 1987 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK "); 1988 p += strlen(p); 1989 memset(&sa, '\0', sizeof(sa)); 1990 slen = sizeof(sa); 1991 if (getsockname(fd, &sa.sa, &slen) < 0) 1992 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 1993 sm_errstring(errno)); 1994 else 1995 { 1996 hp = hostnamebyanyaddr(&sa); 1997 if (hp == NULL) 1998 { 1999 /* EMPTY */ 2000 /* do nothing */ 2001 } 2002 # if NETINET 2003 else if (sa.sa.sa_family == AF_INET) 2004 (void) sm_snprintf(p, SPACELEFT(buf, p), 2005 "%s/%d", hp, ntohs(sa.sin.sin_port)); 2006 # endif 2007 # if NETINET6 2008 else if (sa.sa.sa_family == AF_INET6) 2009 (void) sm_snprintf(p, SPACELEFT(buf, p), 2010 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 2011 # endif 2012 else 2013 (void) sm_snprintf(p, SPACELEFT(buf, p), 2014 "%s", hp); 2015 } 2016 p += strlen(p); 2017 (void) sm_snprintf(p, SPACELEFT(buf, p), "->"); 2018 p += strlen(p); 2019 slen = sizeof(sa); 2020 if (getpeername(fd, &sa.sa, &slen) < 0) 2021 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)", 2022 sm_errstring(errno)); 2023 else 2024 { 2025 hp = hostnamebyanyaddr(&sa); 2026 if (hp == NULL) 2027 { 2028 /* EMPTY */ 2029 /* do nothing */ 2030 } 2031 # if NETINET 2032 else if (sa.sa.sa_family == AF_INET) 2033 (void) sm_snprintf(p, SPACELEFT(buf, p), 2034 "%s/%d", hp, ntohs(sa.sin.sin_port)); 2035 # endif 2036 # if NETINET6 2037 else if (sa.sa.sa_family == AF_INET6) 2038 (void) sm_snprintf(p, SPACELEFT(buf, p), 2039 "%s/%d", hp, ntohs(sa.sin6.sin6_port)); 2040 # endif 2041 else 2042 (void) sm_snprintf(p, SPACELEFT(buf, p), 2043 "%s", hp); 2044 } 2045 break; 2046 #endif /* S_IFSOCK */ 2047 2048 case S_IFCHR: 2049 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: "); 2050 p += strlen(p); 2051 goto defprint; 2052 2053 #ifdef S_IFBLK 2054 case S_IFBLK: 2055 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: "); 2056 p += strlen(p); 2057 goto defprint; 2058 #endif 2059 2060 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 2061 case S_IFIFO: 2062 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: "); 2063 p += strlen(p); 2064 goto defprint; 2065 #endif 2066 2067 #ifdef S_IFDIR 2068 case S_IFDIR: 2069 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: "); 2070 p += strlen(p); 2071 goto defprint; 2072 #endif 2073 2074 #ifdef S_IFLNK 2075 case S_IFLNK: 2076 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: "); 2077 p += strlen(p); 2078 goto defprint; 2079 #endif 2080 2081 default: 2082 defprint: 2083 (void) sm_snprintf(p, SPACELEFT(buf, p), 2084 "dev=%ld/%ld, ino=%llu, nlink=%d, u/gid=%ld/%ld, ", 2085 (long) major(st.st_dev), (long) minor(st.st_dev), 2086 (ULONGLONG_T) st.st_ino, 2087 (int) st.st_nlink, (long) st.st_uid, 2088 (long) st.st_gid); 2089 p += strlen(p); 2090 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu", 2091 (ULONGLONG_T) st.st_size); 2092 break; 2093 } 2094 2095 printit: 2096 if (logit) 2097 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, 2098 "%.800s", buf); 2099 else 2100 sm_dprintf("%s\n", buf); 2101 } 2102 2103 /* 2104 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 2105 ** 2106 ** Parameters: 2107 ** host -- the host to shorten (stripped in place). 2108 ** [EAI: matched against $m: must be same format; 2109 ** conversion needed?] 2110 ** 2111 ** Returns: 2112 ** place where string was truncated, NULL if not truncated. 2113 */ 2114 2115 char * 2116 shorten_hostname(host) 2117 char host[]; 2118 { 2119 register char *p; 2120 char *mydom; 2121 int i; 2122 bool canon = false; 2123 2124 /* strip off final dot */ 2125 i = strlen(host); 2126 p = &host[(i == 0) ? 0 : i - 1]; 2127 if (*p == '.') 2128 { 2129 *p = '\0'; 2130 canon = true; 2131 } 2132 2133 /* see if there is any domain at all -- if not, we are done */ 2134 p = strchr(host, '.'); 2135 if (p == NULL) 2136 return NULL; 2137 2138 /* yes, we have a domain -- see if it looks like us */ 2139 mydom = macvalue('m', CurEnv); 2140 if (mydom == NULL) 2141 mydom = ""; 2142 i = strlen(++p); 2143 if ((canon ? sm_strcasecmp(p, mydom) 2144 : sm_strncasecmp(p, mydom, i)) == 0 && 2145 (mydom[i] == '.' || mydom[i] == '\0')) 2146 { 2147 *--p = '\0'; 2148 return p; 2149 } 2150 return NULL; 2151 } 2152 2153 /* 2154 ** PROG_OPEN -- open a program for reading 2155 ** 2156 ** Parameters: 2157 ** argv -- the argument list. 2158 ** pfd -- pointer to a place to store the file descriptor. 2159 ** e -- the current envelope. 2160 ** 2161 ** Returns: 2162 ** pid of the process -- -1 if it failed. 2163 */ 2164 2165 pid_t 2166 prog_open(argv, pfd, e) 2167 char **argv; 2168 int *pfd; 2169 ENVELOPE *e; 2170 { 2171 pid_t pid; 2172 int save_errno; 2173 int sff; 2174 int ret; 2175 int fdv[2]; 2176 char *p, *q; 2177 char buf[MAXPATHLEN]; 2178 extern int DtableSize; 2179 2180 if (pipe(fdv) < 0) 2181 { 2182 syserr("%s: cannot create pipe for stdout", argv[0]); 2183 return -1; 2184 } 2185 pid = fork(); 2186 if (pid < 0) 2187 { 2188 syserr("%s: cannot fork", argv[0]); 2189 (void) close(fdv[0]); 2190 (void) close(fdv[1]); 2191 return -1; 2192 } 2193 if (pid > 0) 2194 { 2195 /* parent */ 2196 (void) close(fdv[1]); 2197 *pfd = fdv[0]; 2198 return pid; 2199 } 2200 2201 /* Reset global flags */ 2202 RestartRequest = NULL; 2203 RestartWorkGroup = false; 2204 ShutdownRequest = NULL; 2205 PendingSignal = 0; 2206 CurrentPid = getpid(); 2207 2208 /* 2209 ** Initialize exception stack and default exception 2210 ** handler for child process. 2211 */ 2212 2213 sm_exc_newthread(fatal_error); 2214 2215 /* child -- close stdin */ 2216 (void) close(0); 2217 2218 /* stdout goes back to parent */ 2219 (void) close(fdv[0]); 2220 if (dup2(fdv[1], 1) < 0) 2221 { 2222 syserr("%s: cannot dup2 for stdout", argv[0]); 2223 _exit(EX_OSERR); 2224 } 2225 (void) close(fdv[1]); 2226 2227 /* stderr goes to transcript if available */ 2228 if (e->e_xfp != NULL) 2229 { 2230 int xfd; 2231 2232 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL); 2233 if (xfd >= 0 && dup2(xfd, 2) < 0) 2234 { 2235 syserr("%s: cannot dup2 for stderr", argv[0]); 2236 _exit(EX_OSERR); 2237 } 2238 } 2239 2240 /* this process has no right to the queue file */ 2241 if (e->e_lockfp != NULL) 2242 { 2243 int fd; 2244 2245 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 2246 if (fd >= 0) 2247 (void) close(fd); 2248 else 2249 syserr("%s: lockfp does not have a fd", argv[0]); 2250 } 2251 2252 /* chroot to the program mailer directory, if defined */ 2253 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL) 2254 { 2255 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e); 2256 if (chroot(buf) < 0) 2257 { 2258 syserr("prog_open: cannot chroot(%s)", buf); 2259 exit(EX_TEMPFAIL); 2260 } 2261 if (chdir("/") < 0) 2262 { 2263 syserr("prog_open: cannot chdir(/)"); 2264 exit(EX_TEMPFAIL); 2265 } 2266 } 2267 2268 /* run as default user */ 2269 endpwent(); 2270 sm_mbdb_terminate(); 2271 #if _FFR_MEMSTAT 2272 (void) sm_memstat_close(); 2273 #endif 2274 if (setgid(DefGid) < 0 && geteuid() == 0) 2275 { 2276 syserr("prog_open: setgid(%ld) failed", (long) DefGid); 2277 exit(EX_TEMPFAIL); 2278 } 2279 if (setuid(DefUid) < 0 && geteuid() == 0) 2280 { 2281 syserr("prog_open: setuid(%ld) failed", (long) DefUid); 2282 exit(EX_TEMPFAIL); 2283 } 2284 2285 /* run in some directory */ 2286 if (ProgMailer != NULL) 2287 p = ProgMailer->m_execdir; 2288 else 2289 p = NULL; 2290 for (; p != NULL; p = q) 2291 { 2292 q = strchr(p, ':'); 2293 if (q != NULL) 2294 *q = '\0'; 2295 expand(p, buf, sizeof(buf), e); 2296 if (q != NULL) 2297 *q++ = ':'; 2298 if (buf[0] != '\0' && chdir(buf) >= 0) 2299 break; 2300 } 2301 if (p == NULL) 2302 { 2303 /* backup directories */ 2304 if (chdir("/tmp") < 0) 2305 (void) chdir("/"); 2306 } 2307 2308 /* Check safety of program to be run */ 2309 sff = SFF_ROOTOK|SFF_EXECOK; 2310 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail)) 2311 sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2312 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail)) 2313 sff |= SFF_NOPATHCHECK; 2314 else 2315 sff |= SFF_SAFEDIRPATH; 2316 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL); 2317 if (ret != 0) 2318 sm_syslog(LOG_INFO, e->e_id, 2319 "Warning: prog_open: program %s unsafe: %s", 2320 argv[0], sm_errstring(ret)); 2321 2322 /* arrange for all the files to be closed */ 2323 sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2324 2325 /* now exec the process */ 2326 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 2327 2328 /* woops! failed */ 2329 save_errno = errno; 2330 syserr("%s: cannot exec", argv[0]); 2331 if (transienterror(save_errno)) 2332 _exit(EX_OSERR); 2333 _exit(EX_CONFIG); 2334 return -1; /* avoid compiler warning on IRIX */ 2335 } 2336 2337 /* 2338 ** GET_COLUMN -- look up a Column in a line buffer 2339 ** 2340 ** Parameters: 2341 ** line -- the raw text line to search. [A] 2342 ** col -- the column number to fetch. 2343 ** delim -- the delimiter between columns. If null, 2344 ** use white space. 2345 ** buf -- the output buffer. 2346 ** buflen -- the length of buf. 2347 ** 2348 ** Returns: 2349 ** buf if successful. 2350 ** NULL otherwise. 2351 */ 2352 2353 char * 2354 get_column(line, col, delim, buf, buflen) 2355 char line[]; 2356 int col; 2357 int delim; 2358 char buf[]; 2359 int buflen; 2360 { 2361 char *p; 2362 char *begin, *end; 2363 int i; 2364 char delimbuf[4]; 2365 2366 if ((char) delim == '\0') 2367 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf)); 2368 else 2369 { 2370 delimbuf[0] = (char) delim; 2371 delimbuf[1] = '\0'; 2372 } 2373 2374 p = line; 2375 if (*p == '\0') 2376 return NULL; /* line empty */ 2377 if (*p == (char) delim && col == 0) 2378 return NULL; /* first column empty */ 2379 2380 begin = line; 2381 2382 if (col == 0 && (char) delim == '\0') 2383 { 2384 while (*begin != '\0' && SM_ISSPACE(*begin)) 2385 begin++; 2386 } 2387 2388 for (i = 0; i < col; i++) 2389 { 2390 if ((begin = strpbrk(begin, delimbuf)) == NULL) 2391 return NULL; /* no such column */ 2392 begin++; 2393 if ((char) delim == '\0') 2394 { 2395 while (*begin != '\0' && SM_ISSPACE(*begin)) 2396 begin++; 2397 } 2398 } 2399 2400 end = strpbrk(begin, delimbuf); 2401 if (end == NULL) 2402 i = strlen(begin); 2403 else 2404 i = end - begin; 2405 if (i >= buflen) 2406 i = buflen - 1; 2407 (void) sm_strlcpy(buf, begin, i + 1); 2408 return buf; 2409 } 2410 2411 /* 2412 ** CLEANSTRCPY -- copy string keeping out bogus characters 2413 ** XXX: This may be a problem for EAI? 2414 ** 2415 ** Parameters: 2416 ** t -- "to" string. 2417 ** f -- "from" string. [A] 2418 ** l -- length of space available in "to" string. 2419 ** 2420 ** Returns: 2421 ** none. 2422 */ 2423 2424 void 2425 cleanstrcpy(t, f, l) 2426 register char *t; 2427 register char *f; 2428 int l; 2429 { 2430 /* check for newlines and log if necessary */ 2431 (void) denlstring(f, true, true); 2432 2433 if (l <= 0) 2434 syserr("!cleanstrcpy: length == 0"); 2435 2436 l--; 2437 while (l > 0 && *f != '\0') 2438 { 2439 if (isascii(*f) && 2440 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 2441 { 2442 l--; 2443 *t++ = *f; 2444 } 2445 f++; 2446 } 2447 *t = '\0'; 2448 } 2449 2450 /* 2451 ** DENLSTRING -- convert newlines in a string to spaces 2452 ** 2453 ** Parameters: 2454 ** s -- the input string [A] 2455 ** strict -- if set, don't permit continuation lines. 2456 ** logattacks -- if set, log attempted attacks. 2457 ** 2458 ** Returns: 2459 ** A pointer to a version of the string with newlines 2460 ** mapped to spaces. This should be copied. 2461 */ 2462 2463 char * 2464 denlstring(s, strict, logattacks) 2465 char *s; 2466 bool strict; 2467 bool logattacks; 2468 { 2469 register char *p; 2470 int l; 2471 static char *bp = NULL; 2472 static int bl = 0; 2473 2474 p = s; 2475 while ((p = strchr(p, '\n')) != NULL) 2476 if (strict || (*++p != ' ' && *p != '\t')) 2477 break; 2478 if (p == NULL) 2479 return s; 2480 2481 l = strlen(s) + 1; 2482 if (bl < l) 2483 { 2484 /* allocate more space */ 2485 char *nbp = sm_pmalloc_x(l); 2486 2487 if (bp != NULL) 2488 sm_free(bp); 2489 bp = nbp; 2490 bl = l; 2491 } 2492 (void) sm_strlcpy(bp, s, l); 2493 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 2494 *p++ = ' '; 2495 2496 if (logattacks) 2497 { 2498 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL, 2499 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", 2500 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 2501 shortenstring(bp, MAXSHORTSTR)); 2502 } 2503 2504 return bp; 2505 } 2506 2507 /* 2508 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst 2509 ** 2510 ** Parameters: 2511 ** s -- string to manipulate (in place) [A] 2512 ** c -- character to use as replacement 2513 ** 2514 ** Returns: 2515 ** true iff string did not contain "unprintable" characters 2516 */ 2517 2518 bool 2519 strreplnonprt(s, c) 2520 char *s; 2521 int c; 2522 { 2523 bool ok; 2524 2525 ok = true; 2526 if (s == NULL) 2527 return ok; 2528 while (*s != '\0') 2529 { 2530 if (!(isascii(*s) && isprint(*s))) 2531 { 2532 *s = c; 2533 ok = false; 2534 } 2535 ++s; 2536 } 2537 return ok; 2538 } 2539 2540 /* 2541 ** PATH_IS_DIR -- check to see if file exists and is a directory. 2542 ** 2543 ** There are some additional checks for security violations in 2544 ** here. This routine is intended to be used for the host status 2545 ** support. 2546 ** 2547 ** Parameters: 2548 ** pathname -- pathname to check for directory-ness. [x] 2549 ** createflag -- if set, create directory if needed. 2550 ** 2551 ** Returns: 2552 ** true -- if the indicated pathname is a directory 2553 ** false -- otherwise 2554 */ 2555 2556 bool 2557 path_is_dir(pathname, createflag) 2558 char *pathname; 2559 bool createflag; 2560 { 2561 struct stat statbuf; 2562 2563 #if HASLSTAT 2564 if (lstat(pathname, &statbuf) < 0) 2565 #else 2566 if (stat(pathname, &statbuf) < 0) 2567 #endif 2568 { 2569 if (errno != ENOENT || !createflag) 2570 return false; 2571 if (mkdir(pathname, 0755) < 0) 2572 return false; 2573 return true; 2574 } 2575 if (!S_ISDIR(statbuf.st_mode)) 2576 { 2577 errno = ENOTDIR; 2578 return false; 2579 } 2580 2581 /* security: don't allow writable directories */ 2582 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) 2583 { 2584 errno = EACCES; 2585 return false; 2586 } 2587 return true; 2588 } 2589 2590 /* 2591 ** PROC_LIST_ADD -- add process id to list of our children 2592 ** 2593 ** Parameters: 2594 ** pid -- pid to add to list. 2595 ** task -- task of pid. 2596 ** type -- type of process. 2597 ** count -- number of processes. 2598 ** other -- other information for this type. 2599 ** 2600 ** Returns: 2601 ** none 2602 ** 2603 ** Side Effects: 2604 ** May increase CurChildren. May grow ProcList. 2605 */ 2606 2607 typedef struct procs PROCS_T; 2608 2609 struct procs 2610 { 2611 pid_t proc_pid; 2612 char *proc_task; 2613 int proc_type; 2614 int proc_count; 2615 int proc_other; 2616 SOCKADDR proc_hostaddr; 2617 }; 2618 2619 static PROCS_T *volatile ProcListVec = NULL; 2620 static int ProcListSize = 0; 2621 2622 void 2623 proc_list_add(pid, task, type, count, other, hostaddr) 2624 pid_t pid; 2625 char *task; 2626 int type; 2627 int count; 2628 int other; 2629 SOCKADDR *hostaddr; 2630 { 2631 int i; 2632 2633 for (i = 0; i < ProcListSize; i++) 2634 { 2635 if (ProcListVec[i].proc_pid == NO_PID) 2636 break; 2637 } 2638 if (i >= ProcListSize) 2639 { 2640 /* probe the existing vector to avoid growing infinitely */ 2641 proc_list_probe(); 2642 2643 /* now scan again */ 2644 for (i = 0; i < ProcListSize; i++) 2645 { 2646 if (ProcListVec[i].proc_pid == NO_PID) 2647 break; 2648 } 2649 } 2650 if (i >= ProcListSize) 2651 { 2652 /* grow process list */ 2653 int chldwasblocked; 2654 PROCS_T *npv; 2655 2656 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG); 2657 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) * 2658 (ProcListSize + PROC_LIST_SEG)); 2659 2660 /* Block SIGCHLD so reapchild() doesn't mess with us */ 2661 chldwasblocked = sm_blocksignal(SIGCHLD); 2662 if (ProcListSize > 0) 2663 { 2664 memmove(npv, ProcListVec, 2665 ProcListSize * sizeof(PROCS_T)); 2666 sm_free(ProcListVec); 2667 } 2668 2669 /* XXX just use memset() to initialize this part? */ 2670 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) 2671 { 2672 npv[i].proc_pid = NO_PID; 2673 npv[i].proc_task = NULL; 2674 npv[i].proc_type = PROC_NONE; 2675 } 2676 i = ProcListSize; 2677 ProcListSize += PROC_LIST_SEG; 2678 ProcListVec = npv; 2679 if (chldwasblocked == 0) 2680 (void) sm_releasesignal(SIGCHLD); 2681 } 2682 ProcListVec[i].proc_pid = pid; 2683 PSTRSET(ProcListVec[i].proc_task, task); 2684 ProcListVec[i].proc_type = type; 2685 ProcListVec[i].proc_count = count; 2686 ProcListVec[i].proc_other = other; 2687 if (hostaddr != NULL) 2688 ProcListVec[i].proc_hostaddr = *hostaddr; 2689 else 2690 memset(&ProcListVec[i].proc_hostaddr, 0, 2691 sizeof(ProcListVec[i].proc_hostaddr)); 2692 2693 /* if process adding itself, it's not a child */ 2694 if (pid != CurrentPid) 2695 { 2696 SM_ASSERT(CurChildren < INT_MAX); 2697 CurChildren++; 2698 } 2699 } 2700 2701 /* 2702 ** PROC_LIST_SET -- set pid task in process list 2703 ** 2704 ** Parameters: 2705 ** pid -- pid to set 2706 ** task -- task of pid 2707 ** 2708 ** Returns: 2709 ** none. 2710 */ 2711 2712 void 2713 proc_list_set(pid, task) 2714 pid_t pid; 2715 char *task; 2716 { 2717 int i; 2718 2719 for (i = 0; i < ProcListSize; i++) 2720 { 2721 if (ProcListVec[i].proc_pid == pid) 2722 { 2723 PSTRSET(ProcListVec[i].proc_task, task); 2724 break; 2725 } 2726 } 2727 } 2728 2729 /* 2730 ** PROC_LIST_DROP -- drop pid from process list 2731 ** 2732 ** Parameters: 2733 ** pid -- pid to drop 2734 ** st -- process status 2735 ** other -- storage for proc_other (return). 2736 ** 2737 ** Returns: 2738 ** none. 2739 ** 2740 ** Side Effects: 2741 ** May decrease CurChildren, CurRunners, or 2742 ** set RestartRequest or ShutdownRequest. 2743 ** 2744 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2745 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2746 ** DOING. 2747 */ 2748 2749 void 2750 proc_list_drop(pid, st, other) 2751 pid_t pid; 2752 int st; 2753 int *other; 2754 { 2755 int i; 2756 int type = PROC_NONE; 2757 2758 for (i = 0; i < ProcListSize; i++) 2759 { 2760 if (ProcListVec[i].proc_pid == pid) 2761 { 2762 ProcListVec[i].proc_pid = NO_PID; 2763 type = ProcListVec[i].proc_type; 2764 if (other != NULL) 2765 *other = ProcListVec[i].proc_other; 2766 if (CurChildren > 0) 2767 CurChildren--; 2768 break; 2769 } 2770 } 2771 2772 if (type == PROC_CONTROL && WIFEXITED(st)) 2773 { 2774 /* if so, see if we need to restart or shutdown */ 2775 if (WEXITSTATUS(st) == EX_RESTART) 2776 RestartRequest = "control socket"; 2777 else if (WEXITSTATUS(st) == EX_SHUTDOWN) 2778 ShutdownRequest = "control socket"; 2779 } 2780 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) && 2781 ProcListVec[i].proc_other > -1) 2782 { 2783 /* restart this persistent runner */ 2784 mark_work_group_restart(ProcListVec[i].proc_other, st); 2785 } 2786 else if (type == PROC_QUEUE) 2787 { 2788 CurRunners -= ProcListVec[i].proc_count; 2789 2790 /* CHK_CUR_RUNNERS() can't be used here: uses syslog() */ 2791 if (CurRunners < 0) 2792 CurRunners = 0; 2793 } 2794 } 2795 2796 /* 2797 ** PROC_LIST_CLEAR -- clear the process list 2798 ** 2799 ** Parameters: 2800 ** none. 2801 ** 2802 ** Returns: 2803 ** none. 2804 ** 2805 ** Side Effects: 2806 ** Sets CurChildren to zero. 2807 */ 2808 2809 void 2810 proc_list_clear() 2811 { 2812 int i; 2813 2814 /* start from 1 since 0 is the daemon itself */ 2815 for (i = 1; i < ProcListSize; i++) 2816 ProcListVec[i].proc_pid = NO_PID; 2817 CurChildren = 0; 2818 } 2819 2820 /* 2821 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist 2822 ** 2823 ** Parameters: 2824 ** none 2825 ** 2826 ** Returns: 2827 ** none 2828 ** 2829 ** Side Effects: 2830 ** May decrease CurChildren. 2831 */ 2832 2833 void 2834 proc_list_probe() 2835 { 2836 int i, children; 2837 int chldwasblocked; 2838 pid_t pid; 2839 2840 children = 0; 2841 chldwasblocked = sm_blocksignal(SIGCHLD); 2842 2843 /* start from 1 since 0 is the daemon itself */ 2844 for (i = 1; i < ProcListSize; i++) 2845 { 2846 pid = ProcListVec[i].proc_pid; 2847 if (pid == NO_PID || pid == CurrentPid) 2848 continue; 2849 if (kill(pid, 0) < 0) 2850 { 2851 if (LogLevel > 3) 2852 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2853 "proc_list_probe: lost pid %d", 2854 (int) ProcListVec[i].proc_pid); 2855 ProcListVec[i].proc_pid = NO_PID; 2856 SM_FREE(ProcListVec[i].proc_task); 2857 2858 if (ProcListVec[i].proc_type == PROC_QUEUE) 2859 { 2860 CurRunners -= ProcListVec[i].proc_count; 2861 CHK_CUR_RUNNERS("proc_list_probe", i, 2862 ProcListVec[i].proc_count); 2863 } 2864 2865 CurChildren--; 2866 } 2867 else 2868 { 2869 ++children; 2870 } 2871 } 2872 if (CurChildren < 0) 2873 CurChildren = 0; 2874 if (chldwasblocked == 0) 2875 (void) sm_releasesignal(SIGCHLD); 2876 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid) 2877 { 2878 sm_syslog(LOG_ERR, NOQID, 2879 "proc_list_probe: found %d children, expected %d", 2880 children, CurChildren); 2881 } 2882 } 2883 2884 /* 2885 ** PROC_LIST_DISPLAY -- display the process list 2886 ** 2887 ** Parameters: 2888 ** out -- output file pointer 2889 ** prefix -- string to output in front of each line. 2890 ** 2891 ** Returns: 2892 ** none. 2893 */ 2894 2895 void 2896 proc_list_display(out, prefix) 2897 SM_FILE_T *out; 2898 char *prefix; 2899 { 2900 int i; 2901 2902 for (i = 0; i < ProcListSize; i++) 2903 { 2904 if (ProcListVec[i].proc_pid == NO_PID) 2905 continue; 2906 2907 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n", 2908 prefix, 2909 (int) ProcListVec[i].proc_pid, 2910 ProcListVec[i].proc_task != NULL ? 2911 ProcListVec[i].proc_task : "(unknown)", 2912 (OpMode == MD_SMTP || 2913 OpMode == MD_DAEMON || 2914 OpMode == MD_ARPAFTP) ? "\r" : ""); 2915 } 2916 } 2917 2918 /* 2919 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list 2920 ** 2921 ** Parameters: 2922 ** type -- type of process to signal 2923 ** signal -- the type of signal to send 2924 ** 2925 ** Returns: 2926 ** none. 2927 ** 2928 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 2929 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 2930 ** DOING. 2931 */ 2932 2933 void 2934 proc_list_signal(type, signal) 2935 int type; 2936 int signal; 2937 { 2938 int chldwasblocked; 2939 int alrmwasblocked; 2940 int i; 2941 pid_t mypid = getpid(); 2942 2943 /* block these signals so that we may signal cleanly */ 2944 chldwasblocked = sm_blocksignal(SIGCHLD); 2945 alrmwasblocked = sm_blocksignal(SIGALRM); 2946 2947 /* Find all processes of type and send signal */ 2948 for (i = 0; i < ProcListSize; i++) 2949 { 2950 if (ProcListVec[i].proc_pid == NO_PID || 2951 ProcListVec[i].proc_pid == mypid) 2952 continue; 2953 if (ProcListVec[i].proc_type != type) 2954 continue; 2955 (void) kill(ProcListVec[i].proc_pid, signal); 2956 } 2957 2958 /* restore the signals */ 2959 if (alrmwasblocked == 0) 2960 (void) sm_releasesignal(SIGALRM); 2961 if (chldwasblocked == 0) 2962 (void) sm_releasesignal(SIGCHLD); 2963 } 2964 2965 /* 2966 ** COUNT_OPEN_CONNECTIONS 2967 ** 2968 ** Parameters: 2969 ** hostaddr - ClientAddress 2970 ** 2971 ** Returns: 2972 ** the number of open connections for this client 2973 ** 2974 */ 2975 2976 int 2977 count_open_connections(hostaddr) 2978 SOCKADDR *hostaddr; 2979 { 2980 int i, n; 2981 2982 if (hostaddr == NULL) 2983 return 0; 2984 2985 /* 2986 ** This code gets called before proc_list_add() gets called, 2987 ** so we (the daemon child for this connection) have not yet 2988 ** counted ourselves. Hence initialize the counter to 1 2989 ** instead of 0 to compensate. 2990 */ 2991 2992 n = 1; 2993 for (i = 0; i < ProcListSize; i++) 2994 { 2995 if (ProcListVec[i].proc_pid == NO_PID) 2996 continue; 2997 if (hostaddr->sa.sa_family != 2998 ProcListVec[i].proc_hostaddr.sa.sa_family) 2999 continue; 3000 #if NETINET 3001 if (hostaddr->sa.sa_family == AF_INET && 3002 (hostaddr->sin.sin_addr.s_addr == 3003 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr)) 3004 n++; 3005 #endif 3006 #if NETINET6 3007 if (hostaddr->sa.sa_family == AF_INET6 && 3008 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr), 3009 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr))) 3010 n++; 3011 #endif 3012 } 3013 return n; 3014 } 3015 3016 #if _FFR_XCNCT 3017 /* 3018 ** XCONNECT -- get X-CONNECT info 3019 ** 3020 ** Parameters: 3021 ** inchannel -- FILE to check 3022 ** 3023 ** Returns: 3024 ** >0 if X-CONNECT/PROXY was used successfully (D_XCNCT*) 3025 ** 0 if X-CONNECT/PROXY was not given 3026 ** -1 on error 3027 ** -2 PROXY UNKNOWN 3028 */ 3029 3030 /* 3031 ** HA proxy version 1: 3032 ** 3033 ** PROXY TCP[4|6] IPv[4|6]-src-addr IPv[4|6]-dst-addr src-port dst-port\r\n 3034 ** PROXY UNKNOWN ... 3035 ** examples: 3036 ** "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n" 3037 ** "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" 3038 ** "PROXY UNKNOWN\r\n" 3039 */ 3040 3041 int 3042 xconnect(inchannel) 3043 SM_FILE_T *inchannel; 3044 { 3045 int r, i; 3046 char *p, *b, delim, inp[MAXINPLINE]; 3047 SOCKADDR addr; 3048 char **pvp; 3049 char pvpbuf[PSBUFSIZE]; 3050 char *peerhostname; /* name of SMTP peer or "localhost" */ 3051 extern ENVELOPE BlankEnvelope; 3052 #if _FFR_HAPROXY 3053 int haproxy = AF_UNSPEC; 3054 # define HAPROXY "PROXY " 3055 # define HAPROXYLEN (sizeof(HAPROXY) - 1) 3056 #endif 3057 3058 #define XCONNECT "X-CONNECT " 3059 #define XCNNCTLEN (sizeof(XCONNECT) - 1) 3060 3061 /* Ask the ruleset whether to use x-connect */ 3062 pvp = NULL; 3063 peerhostname = RealHostName; 3064 if (peerhostname == NULL) 3065 peerhostname = "localhost"; 3066 r = rscap("x_connect", peerhostname, 3067 anynet_ntoa(&RealHostAddr), &BlankEnvelope, 3068 &pvp, pvpbuf, sizeof(pvpbuf)); 3069 if (tTd(75, 8)) 3070 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r); 3071 if (r == EX_UNAVAILABLE) 3072 return 0; 3073 if (r != EX_OK) 3074 { 3075 /* ruleset error */ 3076 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r); 3077 return 0; 3078 } 3079 if (pvp != NULL && pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) 3080 { 3081 /* $#: no x-connect */ 3082 if (tTd(75, 7)) 3083 sm_syslog(LOG_INFO, NOQID, "x-connect: nope"); 3084 return 0; 3085 } 3086 3087 #if _FFR_HAPROXY 3088 if (pvp != NULL && pvp[0] != NULL && strcasecmp(pvp[0], "haproxy1") == 0) 3089 { 3090 haproxy = AF_LOCAL; 3091 } 3092 #endif 3093 3094 # if _FFR_XCNCT > 1 3095 if (pvp != NULL && pvp[0] != NULL && 3096 pvp[0][0] == '2' && pvp[0][1] == '2' && pvp[0][2] == '0') 3097 { 3098 char *hostname; /* my hostname ($j) */ 3099 3100 hostname = macvalue('j', &BlankEnvelope); 3101 if (tTd(75, 7)) 3102 sm_syslog(LOG_INFO, NOQID, "x-connect=%s", pvp[0]); 3103 message("220-%s %s", hostname != NULL ? hostname : "xconnect", 3104 pvp[1] != NULL ? pvp[1] : "waiting for xconnect"); 3105 sm_io_flush(OutChannel, SM_TIME_DEFAULT); 3106 } 3107 # endif 3108 3109 p = sfgets(inp, sizeof(inp), InChannel, TimeOuts.to_nextcommand, "pre"); 3110 if (tTd(75, 6)) 3111 sm_syslog(LOG_INFO, NOQID, "x-connect: input=%s", p); 3112 #if _FFR_HAPROXY 3113 if (AF_UNSPEC != haproxy) 3114 { 3115 if (p == NULL || strncasecmp(p, HAPROXY, HAPROXYLEN) != 0) 3116 return -1; 3117 p += HAPROXYLEN; 3118 # define HAPUNKNOWN "UNKNOWN" 3119 # define HAPUNKNOWNLEN (sizeof(HAPUNKNOWN) - 1) 3120 if (strncasecmp(p, HAPUNKNOWN, HAPUNKNOWNLEN) == 0) 3121 { 3122 /* how to handle this? */ 3123 sm_syslog(LOG_INFO, NOQID, "haproxy: input=%s, status=ignored", p); 3124 return -2; 3125 } 3126 # define HAPTCP4 "TCP4 " 3127 # define HAPTCP6 "TCP6 " 3128 # define HAPTCPNLEN (sizeof(HAPTCP4) - 1) 3129 if (strncasecmp(p, HAPTCP4, HAPTCPNLEN) == 0) 3130 haproxy = AF_INET; 3131 # if NETINET6 3132 if (strncasecmp(p, HAPTCP6, HAPTCPNLEN) == 0) 3133 haproxy = AF_INET6; 3134 # endif 3135 if (AF_LOCAL != haproxy) 3136 { 3137 p += HAPTCPNLEN; 3138 goto getip; 3139 } 3140 return -1; 3141 } 3142 #endif 3143 if (p == NULL || strncasecmp(p, XCONNECT, XCNNCTLEN) != 0) 3144 return -1; 3145 p += XCNNCTLEN; 3146 while (SM_ISSPACE(*p)) 3147 p++; 3148 3149 #if _FFR_HAPROXY 3150 getip: 3151 #endif 3152 /* parameters: IPAddress [Hostname[ M]] */ 3153 b = p; 3154 while (*p != '\0' && isascii(*p) && 3155 (isalnum(*p) || *p == '.' || *p== ':')) 3156 p++; 3157 delim = *p; 3158 *p = '\0'; 3159 3160 memset(&addr, '\0', sizeof(addr)); 3161 addr.sin.sin_addr.s_addr = inet_addr(b); 3162 if (addr.sin.sin_addr.s_addr != INADDR_NONE) 3163 { 3164 addr.sa.sa_family = AF_INET; 3165 memcpy(&RealHostAddr, &addr, sizeof(addr)); 3166 if (tTd(75, 2)) 3167 sm_syslog(LOG_INFO, NOQID, "x-connect: addr=%s", 3168 anynet_ntoa(&RealHostAddr)); 3169 } 3170 # if NETINET6 3171 else if ((r = inet_pton(AF_INET6, b, &addr.sin6.sin6_addr)) == 1) 3172 { 3173 addr.sa.sa_family = AF_INET6; 3174 memcpy(&RealHostAddr, &addr, sizeof(addr)); 3175 } 3176 # endif 3177 else 3178 return -1; 3179 #if _FFR_HAPROXY 3180 if (AF_UNSPEC != haproxy) 3181 { 3182 /* 3183 ** dst-addr and dst-port are ignored because 3184 ** they are not really worth to check: 3185 ** IPv[4|6]-dst-addr: must be one of "our" addresses, 3186 ** dst-port: must be the DaemonPort. 3187 ** We also do not check whether 3188 ** haproxy == addr.sa.sa_family 3189 */ 3190 3191 if (' ' == delim) 3192 { 3193 b = ++p; 3194 while (*p != '\0' && !SM_ISSPACE(*p)) 3195 ++p; 3196 if (*p != '\0' && SM_ISSPACE(*p)) 3197 ++p; 3198 if (*p != '\0') 3199 { 3200 unsigned short port; 3201 3202 port = htons(atoi(p)); 3203 if (AF_INET == haproxy) 3204 RealHostAddr.sin.sin_port = port; 3205 # if NETINET6 3206 if (AF_INET6 == haproxy) 3207 RealHostAddr.sin6.sin6_port = port; 3208 # endif 3209 } 3210 } 3211 SM_FREE(RealHostName); 3212 return D_XCNCT; 3213 } 3214 #endif 3215 3216 /* more parameters? */ 3217 if (delim != ' ') 3218 return D_XCNCT; 3219 3220 for (b = ++p, i = 0; 3221 *p != '\0' && isascii(*p) && (isalnum(*p) || *p == '.' || *p == '-'); 3222 p++, i++) 3223 ; 3224 if (i == 0) 3225 return D_XCNCT; 3226 delim = *p; 3227 if (i > MAXNAME) /* EAI:ok */ 3228 b[MAXNAME] = '\0'; /* EAI:ok */ 3229 else 3230 b[i] = '\0'; 3231 SM_FREE(RealHostName); 3232 RealHostName = newstr(b); 3233 if (tTd(75, 2)) 3234 sm_syslog(LOG_INFO, NOQID, "x-connect: host=%s", b); 3235 *p = delim; 3236 3237 b = p; 3238 if (*p != ' ') 3239 return D_XCNCT; 3240 3241 while (*p != '\0' && SM_ISSPACE(*p)) 3242 p++; 3243 3244 if (tTd(75, 4)) 3245 { 3246 char *e; 3247 3248 e = strpbrk(p, "\r\n"); 3249 if (e != NULL) 3250 *e = '\0'; 3251 sm_syslog(LOG_INFO, NOQID, "x-connect: rest=%s", p); 3252 } 3253 if (*p == 'M') 3254 return D_XCNCT_M; 3255 3256 return D_XCNCT; 3257 } 3258 #endif /* _FFR_XCNCT */ 3259