1 /* 2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #ifndef lint 15 static char id[] = "@(#)$Id: readcf.c,v 8.382.4.31 2000/12/18 18:00:43 ca Exp $"; 16 #endif /* ! lint */ 17 18 #include <sendmail.h> 19 20 21 #if NETINET || NETINET6 22 # include <arpa/inet.h> 23 #endif /* NETINET || NETINET6 */ 24 25 #define SECONDS 26 #define MINUTES * 60 27 #define HOUR * 3600 28 #define HOURS HOUR 29 30 static void fileclass __P((int, char *, char *, bool, bool)); 31 static char **makeargv __P((char *)); 32 static void settimeout __P((char *, char *, bool)); 33 static void toomany __P((int, int)); 34 35 /* 36 ** READCF -- read configuration file. 37 ** 38 ** This routine reads the configuration file and builds the internal 39 ** form. 40 ** 41 ** The file is formatted as a sequence of lines, each taken 42 ** atomically. The first character of each line describes how 43 ** the line is to be interpreted. The lines are: 44 ** Dxval Define macro x to have value val. 45 ** Cxword Put word into class x. 46 ** Fxfile [fmt] Read file for lines to put into 47 ** class x. Use scanf string 'fmt' 48 ** or "%s" if not present. Fmt should 49 ** only produce one string-valued result. 50 ** Hname: value Define header with field-name 'name' 51 ** and value as specified; this will be 52 ** macro expanded immediately before 53 ** use. 54 ** Sn Use rewriting set n. 55 ** Rlhs rhs Rewrite addresses that match lhs to 56 ** be rhs. 57 ** Mn arg=val... Define mailer. n is the internal name. 58 ** Args specify mailer parameters. 59 ** Oxvalue Set option x to value. 60 ** Pname=value Set precedence name to value. 61 ** Vversioncode[/vendorcode] 62 ** Version level/vendor name of 63 ** configuration syntax. 64 ** Kmapname mapclass arguments.... 65 ** Define keyed lookup of a given class. 66 ** Arguments are class dependent. 67 ** Eenvar=value Set the environment value to the given value. 68 ** 69 ** Parameters: 70 ** cfname -- configuration file name. 71 ** safe -- TRUE if this is the system config file; 72 ** FALSE otherwise. 73 ** e -- the main envelope. 74 ** 75 ** Returns: 76 ** none. 77 ** 78 ** Side Effects: 79 ** Builds several internal tables. 80 */ 81 82 void 83 readcf(cfname, safe, e) 84 char *cfname; 85 bool safe; 86 register ENVELOPE *e; 87 { 88 FILE *cf; 89 int ruleset = -1; 90 char *q; 91 struct rewrite *rwp = NULL; 92 char *bp; 93 auto char *ep; 94 int nfuzzy; 95 char *file; 96 bool optional; 97 int mid; 98 register char *p; 99 long sff = SFF_OPENASROOT; 100 struct stat statb; 101 char buf[MAXLINE]; 102 char exbuf[MAXLINE]; 103 char pvpbuf[MAXLINE + MAXATOM]; 104 static char *null_list[1] = { NULL }; 105 extern u_char TokTypeNoC[]; 106 107 FileName = cfname; 108 LineNumber = 0; 109 110 if (DontLockReadFiles) 111 sff |= SFF_NOLOCK; 112 cf = safefopen(cfname, O_RDONLY, 0444, sff); 113 if (cf == NULL) 114 { 115 syserr("cannot open"); 116 finis(FALSE, EX_OSFILE); 117 } 118 119 if (fstat(fileno(cf), &statb) < 0) 120 { 121 syserr("cannot fstat"); 122 finis(FALSE, EX_OSFILE); 123 } 124 125 if (!S_ISREG(statb.st_mode)) 126 { 127 syserr("not a plain file"); 128 finis(FALSE, EX_OSFILE); 129 } 130 131 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 132 { 133 if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) 134 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 135 FileName); 136 if (LogLevel > 0) 137 sm_syslog(LOG_CRIT, NOQID, 138 "%s: WARNING: dangerous write permissions", 139 FileName); 140 } 141 142 #ifdef XLA 143 xla_zero(); 144 #endif /* XLA */ 145 146 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 147 { 148 if (bp[0] == '#') 149 { 150 if (bp != buf) 151 free(bp); 152 continue; 153 } 154 155 /* do macro expansion mappings */ 156 translate_dollars(bp); 157 158 /* interpret this line */ 159 errno = 0; 160 switch (bp[0]) 161 { 162 case '\0': 163 case '#': /* comment */ 164 break; 165 166 case 'R': /* rewriting rule */ 167 if (ruleset < 0) 168 { 169 syserr("missing valid ruleset for \"%s\"", bp); 170 break; 171 } 172 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 173 continue; 174 175 if (*p == '\0') 176 { 177 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 178 break; 179 } 180 181 /* allocate space for the rule header */ 182 if (rwp == NULL) 183 { 184 RewriteRules[ruleset] = rwp = 185 (struct rewrite *) xalloc(sizeof *rwp); 186 } 187 else 188 { 189 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 190 rwp = rwp->r_next; 191 } 192 rwp->r_next = NULL; 193 194 /* expand and save the LHS */ 195 *p = '\0'; 196 expand(&bp[1], exbuf, sizeof exbuf, e); 197 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 198 sizeof pvpbuf, NULL, 199 ConfigLevel >= 9 ? TokTypeNoC : NULL); 200 nfuzzy = 0; 201 if (rwp->r_lhs != NULL) 202 { 203 register char **ap; 204 205 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 206 207 /* count the number of fuzzy matches in LHS */ 208 for (ap = rwp->r_lhs; *ap != NULL; ap++) 209 { 210 char *botch; 211 212 botch = NULL; 213 switch (**ap & 0377) 214 { 215 case MATCHZANY: 216 case MATCHANY: 217 case MATCHONE: 218 case MATCHCLASS: 219 case MATCHNCLASS: 220 nfuzzy++; 221 break; 222 223 case MATCHREPL: 224 botch = "$0-$9"; 225 break; 226 227 case CANONUSER: 228 botch = "$:"; 229 break; 230 231 case CALLSUBR: 232 botch = "$>"; 233 break; 234 235 case CONDIF: 236 botch = "$?"; 237 break; 238 239 case CONDFI: 240 botch = "$."; 241 break; 242 243 case HOSTBEGIN: 244 botch = "$["; 245 break; 246 247 case HOSTEND: 248 botch = "$]"; 249 break; 250 251 case LOOKUPBEGIN: 252 botch = "$("; 253 break; 254 255 case LOOKUPEND: 256 botch = "$)"; 257 break; 258 } 259 if (botch != NULL) 260 syserr("Inappropriate use of %s on LHS", 261 botch); 262 } 263 rwp->r_line = LineNumber; 264 } 265 else 266 { 267 syserr("R line: null LHS"); 268 rwp->r_lhs = null_list; 269 } 270 271 /* expand and save the RHS */ 272 while (*++p == '\t') 273 continue; 274 q = p; 275 while (*p != '\0' && *p != '\t') 276 p++; 277 *p = '\0'; 278 expand(q, exbuf, sizeof exbuf, e); 279 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 280 sizeof pvpbuf, NULL, 281 ConfigLevel >= 9 ? TokTypeNoC : NULL); 282 if (rwp->r_rhs != NULL) 283 { 284 register char **ap; 285 286 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 287 288 /* check no out-of-bounds replacements */ 289 nfuzzy += '0'; 290 for (ap = rwp->r_rhs; *ap != NULL; ap++) 291 { 292 char *botch; 293 294 botch = NULL; 295 switch (**ap & 0377) 296 { 297 case MATCHREPL: 298 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 299 { 300 syserr("replacement $%c out of bounds", 301 (*ap)[1]); 302 } 303 break; 304 305 case MATCHZANY: 306 botch = "$*"; 307 break; 308 309 case MATCHANY: 310 botch = "$+"; 311 break; 312 313 case MATCHONE: 314 botch = "$-"; 315 break; 316 317 case MATCHCLASS: 318 botch = "$="; 319 break; 320 321 case MATCHNCLASS: 322 botch = "$~"; 323 break; 324 } 325 if (botch != NULL) 326 syserr("Inappropriate use of %s on RHS", 327 botch); 328 } 329 } 330 else 331 { 332 syserr("R line: null RHS"); 333 rwp->r_rhs = null_list; 334 } 335 break; 336 337 case 'S': /* select rewriting set */ 338 expand(&bp[1], exbuf, sizeof exbuf, e); 339 ruleset = strtorwset(exbuf, NULL, ST_ENTER); 340 if (ruleset < 0) 341 break; 342 343 rwp = RewriteRules[ruleset]; 344 if (rwp != NULL) 345 { 346 if (OpMode == MD_TEST) 347 printf("WARNING: Ruleset %s has multiple definitions\n", 348 &bp[1]); 349 if (tTd(37, 1)) 350 dprintf("WARNING: Ruleset %s has multiple definitions\n", 351 &bp[1]); 352 while (rwp->r_next != NULL) 353 rwp = rwp->r_next; 354 } 355 break; 356 357 case 'D': /* macro definition */ 358 mid = macid(&bp[1], &ep); 359 if (mid == 0) 360 break; 361 p = munchstring(ep, NULL, '\0'); 362 define(mid, newstr(p), e); 363 break; 364 365 case 'H': /* required header line */ 366 (void) chompheader(&bp[1], CHHDR_DEF, NULL, e); 367 break; 368 369 case 'C': /* word class */ 370 case 'T': /* trusted user (set class `t') */ 371 if (bp[0] == 'C') 372 { 373 mid = macid(&bp[1], &ep); 374 if (mid == 0) 375 break; 376 expand(ep, exbuf, sizeof exbuf, e); 377 p = exbuf; 378 } 379 else 380 { 381 mid = 't'; 382 p = &bp[1]; 383 } 384 while (*p != '\0') 385 { 386 register char *wd; 387 char delim; 388 389 while (*p != '\0' && isascii(*p) && isspace(*p)) 390 p++; 391 wd = p; 392 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 393 p++; 394 delim = *p; 395 *p = '\0'; 396 if (wd[0] != '\0') 397 setclass(mid, wd); 398 *p = delim; 399 } 400 break; 401 402 case 'F': /* word class from file */ 403 mid = macid(&bp[1], &ep); 404 if (mid == 0) 405 break; 406 for (p = ep; isascii(*p) && isspace(*p); ) 407 p++; 408 if (p[0] == '-' && p[1] == 'o') 409 { 410 optional = TRUE; 411 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 412 p++; 413 while (isascii(*p) && isspace(*p)) 414 p++; 415 } 416 else 417 optional = FALSE; 418 419 file = p; 420 q = p; 421 while (*q != '\0' && !(isascii(*q) && isspace(*q))) 422 q++; 423 if (*file == '|') 424 p = "%s"; 425 else 426 { 427 p = q; 428 if (*p == '\0') 429 p = "%s"; 430 else 431 { 432 *p = '\0'; 433 while (isascii(*++p) && isspace(*p)) 434 continue; 435 } 436 } 437 fileclass(mid, file, p, safe, optional); 438 break; 439 440 #ifdef XLA 441 case 'L': /* extended load average description */ 442 xla_init(&bp[1]); 443 break; 444 #endif /* XLA */ 445 446 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) 447 case 'L': /* lookup macro */ 448 case 'G': /* lookup class */ 449 /* reserved for Sun -- NIS+ database lookup */ 450 if (VendorCode != VENDOR_SUN) 451 goto badline; 452 sun_lg_config_line(bp, e); 453 break; 454 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */ 455 456 case 'M': /* define mailer */ 457 makemailer(&bp[1]); 458 break; 459 460 case 'O': /* set option */ 461 setoption(bp[1], &bp[2], safe, FALSE, e); 462 break; 463 464 case 'P': /* set precedence */ 465 if (NumPriorities >= MAXPRIORITIES) 466 { 467 toomany('P', MAXPRIORITIES); 468 break; 469 } 470 for (p = &bp[1]; *p != '\0' && *p != '='; p++) 471 continue; 472 if (*p == '\0') 473 goto badline; 474 *p = '\0'; 475 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 476 Priorities[NumPriorities].pri_val = atoi(++p); 477 NumPriorities++; 478 break; 479 480 case 'V': /* configuration syntax version */ 481 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 482 continue; 483 if (!isascii(*p) || !isdigit(*p)) 484 { 485 syserr("invalid argument to V line: \"%.20s\"", 486 &bp[1]); 487 break; 488 } 489 ConfigLevel = strtol(p, &ep, 10); 490 491 /* 492 ** Do heuristic tweaking for back compatibility. 493 */ 494 495 if (ConfigLevel >= 5) 496 { 497 /* level 5 configs have short name in $w */ 498 p = macvalue('w', e); 499 if (p != NULL && (p = strchr(p, '.')) != NULL) 500 *p = '\0'; 501 define('w', macvalue('w', e), e); 502 } 503 if (ConfigLevel >= 6) 504 { 505 ColonOkInAddr = FALSE; 506 } 507 508 /* 509 ** Look for vendor code. 510 */ 511 512 if (*ep++ == '/') 513 { 514 /* extract vendor code */ 515 for (p = ep; isascii(*p) && isalpha(*p); ) 516 p++; 517 *p = '\0'; 518 519 if (!setvendor(ep)) 520 syserr("invalid V line vendor code: \"%s\"", 521 ep); 522 } 523 break; 524 525 case 'K': 526 expand(&bp[1], exbuf, sizeof exbuf, e); 527 (void) makemapentry(exbuf); 528 break; 529 530 case 'E': 531 p = strchr(bp, '='); 532 if (p != NULL) 533 *p++ = '\0'; 534 setuserenv(&bp[1], p); 535 break; 536 537 #if _FFR_MILTER 538 case 'X': /* mail filter */ 539 milter_setup(&bp[1]); 540 break; 541 #endif /* _FFR_MILTER */ 542 543 default: 544 badline: 545 syserr("unknown configuration line \"%s\"", bp); 546 } 547 if (bp != buf) 548 free(bp); 549 } 550 if (ferror(cf)) 551 { 552 syserr("I/O read error"); 553 finis(FALSE, EX_OSFILE); 554 } 555 (void) fclose(cf); 556 FileName = NULL; 557 558 /* initialize host maps from local service tables */ 559 inithostmaps(); 560 561 /* initialize daemon (if not defined yet) */ 562 initdaemon(); 563 564 /* determine if we need to do special name-server frotz */ 565 { 566 int nmaps; 567 char *maptype[MAXMAPSTACK]; 568 short mapreturn[MAXMAPACTIONS]; 569 570 nmaps = switch_map_find("hosts", maptype, mapreturn); 571 UseNameServer = FALSE; 572 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 573 { 574 register int mapno; 575 576 for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 577 { 578 if (strcmp(maptype[mapno], "dns") == 0) 579 UseNameServer = TRUE; 580 } 581 } 582 583 #ifdef HESIOD 584 nmaps = switch_map_find("passwd", maptype, mapreturn); 585 UseHesiod = FALSE; 586 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 587 { 588 register int mapno; 589 590 for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) 591 { 592 if (strcmp(maptype[mapno], "hesiod") == 0) 593 UseHesiod = TRUE; 594 } 595 } 596 #endif /* HESIOD */ 597 } 598 } 599 /* 600 ** TRANSLATE_DOLLARS -- convert $x into internal form 601 ** 602 ** Actually does all appropriate pre-processing of a config line 603 ** to turn it into internal form. 604 ** 605 ** Parameters: 606 ** bp -- the buffer to translate. 607 ** 608 ** Returns: 609 ** None. The buffer is translated in place. Since the 610 ** translations always make the buffer shorter, this is 611 ** safe without a size parameter. 612 */ 613 614 void 615 translate_dollars(bp) 616 char *bp; 617 { 618 register char *p; 619 auto char *ep; 620 621 for (p = bp; *p != '\0'; p++) 622 { 623 if (*p == '#' && p > bp && ConfigLevel >= 3) 624 { 625 /* this is an on-line comment */ 626 register char *e; 627 628 switch (*--p & 0377) 629 { 630 case MACROEXPAND: 631 /* it's from $# -- let it go through */ 632 p++; 633 break; 634 635 case '\\': 636 /* it's backslash escaped */ 637 (void) strlcpy(p, p + 1, strlen(p)); 638 break; 639 640 default: 641 /* delete leading white space */ 642 while (isascii(*p) && isspace(*p) && 643 *p != '\n' && p > bp) 644 p--; 645 if ((e = strchr(++p, '\n')) != NULL) 646 (void) strlcpy(p, e, strlen(p)); 647 else 648 *p-- = '\0'; 649 break; 650 } 651 continue; 652 } 653 654 if (*p != '$' || p[1] == '\0') 655 continue; 656 657 if (p[1] == '$') 658 { 659 /* actual dollar sign.... */ 660 (void) strlcpy(p, p + 1, strlen(p)); 661 continue; 662 } 663 664 /* convert to macro expansion character */ 665 *p++ = MACROEXPAND; 666 667 /* special handling for $=, $~, $&, and $? */ 668 if (*p == '=' || *p == '~' || *p == '&' || *p == '?') 669 p++; 670 671 /* convert macro name to code */ 672 *p = macid(p, &ep); 673 if (ep != p + 1) 674 (void) strlcpy(p + 1, ep, strlen(p + 1)); 675 } 676 677 /* strip trailing white space from the line */ 678 while (--p > bp && isascii(*p) && isspace(*p)) 679 *p = '\0'; 680 } 681 /* 682 ** TOOMANY -- signal too many of some option 683 ** 684 ** Parameters: 685 ** id -- the id of the error line 686 ** maxcnt -- the maximum possible values 687 ** 688 ** Returns: 689 ** none. 690 ** 691 ** Side Effects: 692 ** gives a syserr. 693 */ 694 695 static void 696 toomany(id, maxcnt) 697 int id; 698 int maxcnt; 699 { 700 syserr("too many %c lines, %d max", id, maxcnt); 701 } 702 /* 703 ** FILECLASS -- read members of a class from a file 704 ** 705 ** Parameters: 706 ** class -- class to define. 707 ** filename -- name of file to read. 708 ** fmt -- scanf string to use for match. 709 ** safe -- if set, this is a safe read. 710 ** optional -- if set, it is not an error for the file to 711 ** not exist. 712 ** 713 ** Returns: 714 ** none 715 ** 716 ** Side Effects: 717 ** 718 ** puts all lines in filename that match a scanf into 719 ** the named class. 720 */ 721 722 static void 723 fileclass(class, filename, fmt, safe, optional) 724 int class; 725 char *filename; 726 char *fmt; 727 bool safe; 728 bool optional; 729 { 730 FILE *f; 731 long sff; 732 pid_t pid; 733 register char *p; 734 char buf[MAXLINE]; 735 736 if (tTd(37, 2)) 737 dprintf("fileclass(%s, fmt=%s)\n", filename, fmt); 738 739 if (filename[0] == '|') 740 { 741 auto int fd; 742 int i; 743 char *argv[MAXPV + 1]; 744 745 i = 0; 746 for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) 747 { 748 if (i >= MAXPV) 749 break; 750 argv[i++] = p; 751 } 752 argv[i] = NULL; 753 pid = prog_open(argv, &fd, CurEnv); 754 if (pid < 0) 755 f = NULL; 756 else 757 f = fdopen(fd, "r"); 758 } 759 else 760 { 761 pid = -1; 762 sff = SFF_REGONLY; 763 if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail)) 764 sff |= SFF_SAFEDIRPATH; 765 if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR, 766 DontBlameSendmail)) 767 sff |= SFF_NOWLINK; 768 if (safe) 769 sff |= SFF_OPENASROOT; 770 if (DontLockReadFiles) 771 sff |= SFF_NOLOCK; 772 f = safefopen(filename, O_RDONLY, 0, sff); 773 } 774 if (f == NULL) 775 { 776 if (!optional) 777 syserr("fileclass: cannot open '%s'", filename); 778 return; 779 } 780 781 while (fgets(buf, sizeof buf, f) != NULL) 782 { 783 #if SCANF 784 char wordbuf[MAXLINE + 1]; 785 #endif /* SCANF */ 786 787 if (buf[0] == '#') 788 continue; 789 #if SCANF 790 if (sscanf(buf, fmt, wordbuf) != 1) 791 continue; 792 p = wordbuf; 793 #else /* SCANF */ 794 p = buf; 795 #endif /* SCANF */ 796 797 /* 798 ** Break up the match into words. 799 */ 800 801 while (*p != '\0') 802 { 803 register char *q; 804 805 /* strip leading spaces */ 806 while (isascii(*p) && isspace(*p)) 807 p++; 808 if (*p == '\0') 809 break; 810 811 /* find the end of the word */ 812 q = p; 813 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 814 p++; 815 if (*p != '\0') 816 *p++ = '\0'; 817 818 /* enter the word in the symbol table */ 819 setclass(class, q); 820 } 821 } 822 823 (void) fclose(f); 824 if (pid > 0) 825 (void) waitfor(pid); 826 } 827 /* 828 ** MAKEMAILER -- define a new mailer. 829 ** 830 ** Parameters: 831 ** line -- description of mailer. This is in labeled 832 ** fields. The fields are: 833 ** A -- the argv for this mailer 834 ** C -- the character set for MIME conversions 835 ** D -- the directory to run in 836 ** E -- the eol string 837 ** F -- the flags associated with the mailer 838 ** L -- the maximum line length 839 ** M -- the maximum message size 840 ** N -- the niceness at which to run 841 ** P -- the path to the mailer 842 ** R -- the recipient rewriting set 843 ** S -- the sender rewriting set 844 ** T -- the mailer type (for DSNs) 845 ** U -- the uid to run as 846 ** W -- the time to wait at the end 847 ** The first word is the canonical name of the mailer. 848 ** 849 ** Returns: 850 ** none. 851 ** 852 ** Side Effects: 853 ** enters the mailer into the mailer table. 854 */ 855 856 void 857 makemailer(line) 858 char *line; 859 { 860 register char *p; 861 register struct mailer *m; 862 register STAB *s; 863 int i; 864 char fcode; 865 auto char *endp; 866 extern int NextMailer; 867 868 /* allocate a mailer and set up defaults */ 869 m = (struct mailer *) xalloc(sizeof *m); 870 memset((char *) m, '\0', sizeof *m); 871 872 /* collect the mailer name */ 873 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 874 continue; 875 if (*p != '\0') 876 *p++ = '\0'; 877 if (line[0] == '\0') 878 { 879 syserr("name required for mailer"); 880 return; 881 } 882 m->m_name = newstr(line); 883 884 /* now scan through and assign info from the fields */ 885 while (*p != '\0') 886 { 887 auto char *delimptr; 888 889 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 890 p++; 891 892 /* p now points to field code */ 893 fcode = *p; 894 while (*p != '\0' && *p != '=' && *p != ',') 895 p++; 896 if (*p++ != '=') 897 { 898 syserr("mailer %s: `=' expected", m->m_name); 899 return; 900 } 901 while (isascii(*p) && isspace(*p)) 902 p++; 903 904 /* p now points to the field body */ 905 p = munchstring(p, &delimptr, ','); 906 907 /* install the field into the mailer struct */ 908 switch (fcode) 909 { 910 case 'P': /* pathname */ 911 if (*p == '\0') 912 syserr("mailer %s: empty path name", m->m_name); 913 else 914 m->m_mailer = newstr(p); 915 break; 916 917 case 'F': /* flags */ 918 for (; *p != '\0'; p++) 919 if (!(isascii(*p) && isspace(*p))) 920 setbitn(bitidx(*p), m->m_flags); 921 break; 922 923 case 'S': /* sender rewriting ruleset */ 924 case 'R': /* recipient rewriting ruleset */ 925 i = strtorwset(p, &endp, ST_ENTER); 926 if (i < 0) 927 return; 928 if (fcode == 'S') 929 m->m_sh_rwset = m->m_se_rwset = i; 930 else 931 m->m_rh_rwset = m->m_re_rwset = i; 932 933 p = endp; 934 if (*p++ == '/') 935 { 936 i = strtorwset(p, NULL, ST_ENTER); 937 if (i < 0) 938 return; 939 if (fcode == 'S') 940 m->m_sh_rwset = i; 941 else 942 m->m_rh_rwset = i; 943 } 944 break; 945 946 case 'E': /* end of line string */ 947 if (*p == '\0') 948 syserr("mailer %s: null end-of-line string", 949 m->m_name); 950 else 951 m->m_eol = newstr(p); 952 break; 953 954 case 'A': /* argument vector */ 955 if (*p == '\0') 956 syserr("mailer %s: null argument vector", 957 m->m_name); 958 else 959 m->m_argv = makeargv(p); 960 break; 961 962 case 'M': /* maximum message size */ 963 m->m_maxsize = atol(p); 964 break; 965 966 case 'm': /* maximum messages per connection */ 967 m->m_maxdeliveries = atoi(p); 968 break; 969 970 #if _FFR_DYNAMIC_TOBUF 971 case 'r': /* max recipient per envelope */ 972 m->m_maxrcpt = atoi(p); 973 break; 974 #endif /* _FFR_DYNAMIC_TOBUF */ 975 976 case 'L': /* maximum line length */ 977 m->m_linelimit = atoi(p); 978 if (m->m_linelimit < 0) 979 m->m_linelimit = 0; 980 break; 981 982 case 'N': /* run niceness */ 983 m->m_nice = atoi(p); 984 break; 985 986 case 'D': /* working directory */ 987 if (*p == '\0') 988 syserr("mailer %s: null working directory", 989 m->m_name); 990 else 991 m->m_execdir = newstr(p); 992 break; 993 994 case 'C': /* default charset */ 995 if (*p == '\0') 996 syserr("mailer %s: null charset", m->m_name); 997 else 998 m->m_defcharset = newstr(p); 999 break; 1000 1001 case 'T': /* MTA-Name/Address/Diagnostic types */ 1002 /* extract MTA name type; default to "dns" */ 1003 m->m_mtatype = newstr(p); 1004 p = strchr(m->m_mtatype, '/'); 1005 if (p != NULL) 1006 { 1007 *p++ = '\0'; 1008 if (*p == '\0') 1009 p = NULL; 1010 } 1011 if (*m->m_mtatype == '\0') 1012 m->m_mtatype = "dns"; 1013 1014 /* extract address type; default to "rfc822" */ 1015 m->m_addrtype = p; 1016 if (p != NULL) 1017 p = strchr(p, '/'); 1018 if (p != NULL) 1019 { 1020 *p++ = '\0'; 1021 if (*p == '\0') 1022 p = NULL; 1023 } 1024 if (m->m_addrtype == NULL || *m->m_addrtype == '\0') 1025 m->m_addrtype = "rfc822"; 1026 1027 /* extract diagnostic type; default to "smtp" */ 1028 m->m_diagtype = p; 1029 if (m->m_diagtype == NULL || *m->m_diagtype == '\0') 1030 m->m_diagtype = "smtp"; 1031 break; 1032 1033 case 'U': /* user id */ 1034 if (isascii(*p) && !isdigit(*p)) 1035 { 1036 char *q = p; 1037 struct passwd *pw; 1038 1039 while (*p != '\0' && isascii(*p) && 1040 (isalnum(*p) || strchr("-_", *p) != NULL)) 1041 p++; 1042 while (isascii(*p) && isspace(*p)) 1043 *p++ = '\0'; 1044 if (*p != '\0') 1045 *p++ = '\0'; 1046 if (*q == '\0') 1047 { 1048 syserr("mailer %s: null user name", 1049 m->m_name); 1050 break; 1051 } 1052 pw = sm_getpwnam(q); 1053 if (pw == NULL) 1054 { 1055 syserr("readcf: mailer U= flag: unknown user %s", q); 1056 break; 1057 } 1058 else 1059 { 1060 m->m_uid = pw->pw_uid; 1061 m->m_gid = pw->pw_gid; 1062 } 1063 } 1064 else 1065 { 1066 auto char *q; 1067 1068 m->m_uid = strtol(p, &q, 0); 1069 p = q; 1070 while (isascii(*p) && isspace(*p)) 1071 p++; 1072 if (*p != '\0') 1073 p++; 1074 } 1075 while (isascii(*p) && isspace(*p)) 1076 p++; 1077 if (*p == '\0') 1078 break; 1079 if (isascii(*p) && !isdigit(*p)) 1080 { 1081 char *q = p; 1082 struct group *gr; 1083 1084 while (isascii(*p) && isalnum(*p)) 1085 p++; 1086 *p++ = '\0'; 1087 if (*q == '\0') 1088 { 1089 syserr("mailer %s: null group name", 1090 m->m_name); 1091 break; 1092 } 1093 gr = getgrnam(q); 1094 if (gr == NULL) 1095 { 1096 syserr("readcf: mailer U= flag: unknown group %s", q); 1097 break; 1098 } 1099 else 1100 m->m_gid = gr->gr_gid; 1101 } 1102 else 1103 { 1104 m->m_gid = strtol(p, NULL, 0); 1105 } 1106 break; 1107 1108 case 'W': /* wait timeout */ 1109 m->m_wait = convtime(p, 's'); 1110 break; 1111 1112 case '/': /* new root directory */ 1113 if (*p == '\0') 1114 syserr("mailer %s: null root directory", 1115 m->m_name); 1116 else 1117 m->m_rootdir = newstr(p); 1118 break; 1119 1120 default: 1121 syserr("M%s: unknown mailer equate %c=", 1122 m->m_name, fcode); 1123 break; 1124 } 1125 1126 p = delimptr; 1127 } 1128 1129 /* do some rationality checking */ 1130 if (m->m_argv == NULL) 1131 { 1132 syserr("M%s: A= argument required", m->m_name); 1133 return; 1134 } 1135 if (m->m_mailer == NULL) 1136 { 1137 syserr("M%s: P= argument required", m->m_name); 1138 return; 1139 } 1140 1141 if (NextMailer >= MAXMAILERS) 1142 { 1143 syserr("too many mailers defined (%d max)", MAXMAILERS); 1144 return; 1145 } 1146 1147 #if _FFR_DYNAMIC_TOBUF 1148 if (m->m_maxrcpt <= 0) 1149 m->m_maxrcpt = DEFAULT_MAX_RCPT; 1150 #endif /* _FFR_DYNAMIC_TOBUF */ 1151 1152 /* do some heuristic cleanup for back compatibility */ 1153 if (bitnset(M_LIMITS, m->m_flags)) 1154 { 1155 if (m->m_linelimit == 0) 1156 m->m_linelimit = SMTPLINELIM; 1157 if (ConfigLevel < 2) 1158 setbitn(M_7BITS, m->m_flags); 1159 } 1160 1161 if (strcmp(m->m_mailer, "[TCP]") == 0) 1162 { 1163 #if _FFR_REMOVE_TCP_MAILER_PATH 1164 syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n", 1165 m->m_name); 1166 return; 1167 #else /* _FFR_REMOVE_TCP_MAILER_PATH */ 1168 printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n", 1169 m->m_name); 1170 #endif /* _FFR_REMOVE_TCP_MAILER_PATH */ 1171 } 1172 1173 if (strcmp(m->m_mailer, "[IPC]") == 0 1174 #if !_FFR_REMOVE_TCP_MAILER_PATH 1175 || strcmp(m->m_mailer, "[TCP]") == 0 1176 #endif /* !_FFR_REMOVE_TCP_MAILER_PATH */ 1177 ) 1178 { 1179 /* Use the second argument for host or path to socket */ 1180 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1181 m->m_argv[1][0] == '\0') 1182 { 1183 syserr("M%s: too few parameters for %s mailer", 1184 m->m_name, m->m_mailer); 1185 return; 1186 } 1187 if (strcmp(m->m_argv[0], "TCP") != 0 1188 #if NETUNIX 1189 && strcmp(m->m_argv[0], "FILE") != 0 1190 #endif /* NETUNIX */ 1191 #if !_FFR_DEPRECATE_IPC_MAILER_ARG 1192 && strcmp(m->m_argv[0], "IPC") != 0 1193 #endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */ 1194 ) 1195 { 1196 printf("M%s: Warning: first argument in %s mailer must be %s\n", 1197 m->m_name, m->m_mailer, 1198 #if NETUNIX 1199 "TCP or FILE" 1200 #else /* NETUNIX */ 1201 "TCP" 1202 #endif /* NETUNIX */ 1203 ); 1204 } 1205 1206 } 1207 else if (strcmp(m->m_mailer, "[FILE]") == 0) 1208 { 1209 /* Use the second argument for filename */ 1210 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1211 m->m_argv[2] != NULL) 1212 { 1213 syserr("M%s: too %s parameters for [FILE] mailer", 1214 m->m_name, 1215 (m->m_argv[0] == NULL || 1216 m->m_argv[1] == NULL) ? "few" : "many"); 1217 return; 1218 } 1219 else if (strcmp(m->m_argv[0], "FILE") != 0) 1220 { 1221 syserr("M%s: first argument in [FILE] mailer must be FILE", 1222 m->m_name); 1223 return; 1224 } 1225 } 1226 1227 if (strcmp(m->m_mailer, "[IPC]") == 0 || 1228 strcmp(m->m_mailer, "[TCP]") == 0) 1229 { 1230 if (m->m_mtatype == NULL) 1231 m->m_mtatype = "dns"; 1232 if (m->m_addrtype == NULL) 1233 m->m_addrtype = "rfc822"; 1234 if (m->m_diagtype == NULL) 1235 { 1236 if (m->m_argv[0] != NULL && 1237 strcmp(m->m_argv[0], "FILE") == 0) 1238 m->m_diagtype = "x-unix"; 1239 else 1240 m->m_diagtype = "smtp"; 1241 } 1242 } 1243 1244 if (m->m_eol == NULL) 1245 { 1246 char **pp; 1247 1248 /* default for SMTP is \r\n; use \n for local delivery */ 1249 for (pp = m->m_argv; *pp != NULL; pp++) 1250 { 1251 for (p = *pp; *p != '\0'; ) 1252 { 1253 if ((*p++ & 0377) == MACROEXPAND && *p == 'u') 1254 break; 1255 } 1256 if (*p != '\0') 1257 break; 1258 } 1259 if (*pp == NULL) 1260 m->m_eol = "\r\n"; 1261 else 1262 m->m_eol = "\n"; 1263 } 1264 1265 /* enter the mailer into the symbol table */ 1266 s = stab(m->m_name, ST_MAILER, ST_ENTER); 1267 if (s->s_mailer != NULL) 1268 { 1269 i = s->s_mailer->m_mno; 1270 free(s->s_mailer); 1271 } 1272 else 1273 { 1274 i = NextMailer++; 1275 } 1276 Mailer[i] = s->s_mailer = m; 1277 m->m_mno = i; 1278 } 1279 /* 1280 ** MUNCHSTRING -- translate a string into internal form. 1281 ** 1282 ** Parameters: 1283 ** p -- the string to munch. 1284 ** delimptr -- if non-NULL, set to the pointer of the 1285 ** field delimiter character. 1286 ** delim -- the delimiter for the field. 1287 ** 1288 ** Returns: 1289 ** the munched string. 1290 ** 1291 ** Side Effects: 1292 ** the munched string is a local static buffer. 1293 ** it must be copied before the function is called again. 1294 */ 1295 1296 char * 1297 munchstring(p, delimptr, delim) 1298 register char *p; 1299 char **delimptr; 1300 int delim; 1301 { 1302 register char *q; 1303 bool backslash = FALSE; 1304 bool quotemode = FALSE; 1305 static char buf[MAXLINE]; 1306 1307 for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) 1308 { 1309 if (backslash) 1310 { 1311 /* everything is roughly literal */ 1312 backslash = FALSE; 1313 switch (*p) 1314 { 1315 case 'r': /* carriage return */ 1316 *q++ = '\r'; 1317 continue; 1318 1319 case 'n': /* newline */ 1320 *q++ = '\n'; 1321 continue; 1322 1323 case 'f': /* form feed */ 1324 *q++ = '\f'; 1325 continue; 1326 1327 case 'b': /* backspace */ 1328 *q++ = '\b'; 1329 continue; 1330 } 1331 *q++ = *p; 1332 } 1333 else 1334 { 1335 if (*p == '\\') 1336 backslash = TRUE; 1337 else if (*p == '"') 1338 quotemode = !quotemode; 1339 else if (quotemode || *p != delim) 1340 *q++ = *p; 1341 else 1342 break; 1343 } 1344 } 1345 1346 if (delimptr != NULL) 1347 *delimptr = p; 1348 *q++ = '\0'; 1349 return buf; 1350 } 1351 /* 1352 ** MAKEARGV -- break up a string into words 1353 ** 1354 ** Parameters: 1355 ** p -- the string to break up. 1356 ** 1357 ** Returns: 1358 ** a char **argv (dynamically allocated) 1359 ** 1360 ** Side Effects: 1361 ** munges p. 1362 */ 1363 1364 static char ** 1365 makeargv(p) 1366 register char *p; 1367 { 1368 char *q; 1369 int i; 1370 char **avp; 1371 char *argv[MAXPV + 1]; 1372 1373 /* take apart the words */ 1374 i = 0; 1375 while (*p != '\0' && i < MAXPV) 1376 { 1377 q = p; 1378 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1379 p++; 1380 while (isascii(*p) && isspace(*p)) 1381 *p++ = '\0'; 1382 argv[i++] = newstr(q); 1383 } 1384 argv[i++] = NULL; 1385 1386 /* now make a copy of the argv */ 1387 avp = (char **) xalloc(sizeof *avp * i); 1388 memmove((char *) avp, (char *) argv, sizeof *avp * i); 1389 1390 return avp; 1391 } 1392 /* 1393 ** PRINTRULES -- print rewrite rules (for debugging) 1394 ** 1395 ** Parameters: 1396 ** none. 1397 ** 1398 ** Returns: 1399 ** none. 1400 ** 1401 ** Side Effects: 1402 ** prints rewrite rules. 1403 */ 1404 1405 void 1406 printrules() 1407 { 1408 register struct rewrite *rwp; 1409 register int ruleset; 1410 1411 for (ruleset = 0; ruleset < 10; ruleset++) 1412 { 1413 if (RewriteRules[ruleset] == NULL) 1414 continue; 1415 printf("\n----Rule Set %d:", ruleset); 1416 1417 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1418 { 1419 printf("\nLHS:"); 1420 printav(rwp->r_lhs); 1421 printf("RHS:"); 1422 printav(rwp->r_rhs); 1423 } 1424 } 1425 } 1426 /* 1427 ** PRINTMAILER -- print mailer structure (for debugging) 1428 ** 1429 ** Parameters: 1430 ** m -- the mailer to print 1431 ** 1432 ** Returns: 1433 ** none. 1434 */ 1435 1436 void 1437 printmailer(m) 1438 register MAILER *m; 1439 { 1440 int j; 1441 1442 printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer); 1443 if (RuleSetNames[m->m_se_rwset] == NULL) 1444 printf("%d/", m->m_se_rwset); 1445 else 1446 printf("%s/", RuleSetNames[m->m_se_rwset]); 1447 if (RuleSetNames[m->m_sh_rwset] == NULL) 1448 printf("%d R=", m->m_sh_rwset); 1449 else 1450 printf("%s R=", RuleSetNames[m->m_sh_rwset]); 1451 if (RuleSetNames[m->m_re_rwset] == NULL) 1452 printf("%d/", m->m_re_rwset); 1453 else 1454 printf("%s/", RuleSetNames[m->m_re_rwset]); 1455 if (RuleSetNames[m->m_rh_rwset] == NULL) 1456 printf("%d ", m->m_rh_rwset); 1457 else 1458 printf("%s ", RuleSetNames[m->m_rh_rwset]); 1459 printf("M=%ld U=%d:%d F=", m->m_maxsize, 1460 (int) m->m_uid, (int) m->m_gid); 1461 for (j = '\0'; j <= '\177'; j++) 1462 if (bitnset(j, m->m_flags)) 1463 (void) putchar(j); 1464 printf(" L=%d E=", m->m_linelimit); 1465 xputs(m->m_eol); 1466 if (m->m_defcharset != NULL) 1467 printf(" C=%s", m->m_defcharset); 1468 printf(" T=%s/%s/%s", 1469 m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, 1470 m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, 1471 m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); 1472 #if _FFR_DYNAMIC_TOBUF 1473 printf(" r=%d", m->m_maxrcpt); 1474 #endif /* _FFR_DYNAMIC_TOBUF */ 1475 if (m->m_argv != NULL) 1476 { 1477 char **a = m->m_argv; 1478 1479 printf(" A="); 1480 while (*a != NULL) 1481 { 1482 if (a != m->m_argv) 1483 printf(" "); 1484 xputs(*a++); 1485 } 1486 } 1487 printf("\n"); 1488 } 1489 /* 1490 ** SETOPTION -- set global processing option 1491 ** 1492 ** Parameters: 1493 ** opt -- option name. 1494 ** val -- option value (as a text string). 1495 ** safe -- set if this came from a configuration file. 1496 ** Some options (if set from the command line) will 1497 ** reset the user id to avoid security problems. 1498 ** sticky -- if set, don't let other setoptions override 1499 ** this value. 1500 ** e -- the main envelope. 1501 ** 1502 ** Returns: 1503 ** none. 1504 ** 1505 ** Side Effects: 1506 ** Sets options as implied by the arguments. 1507 */ 1508 1509 static BITMAP256 StickyOpt; /* set if option is stuck */ 1510 1511 #if NAMED_BIND 1512 1513 static struct resolverflags 1514 { 1515 char *rf_name; /* name of the flag */ 1516 long rf_bits; /* bits to set/clear */ 1517 } ResolverFlags[] = 1518 { 1519 { "debug", RES_DEBUG }, 1520 { "aaonly", RES_AAONLY }, 1521 { "usevc", RES_USEVC }, 1522 { "primary", RES_PRIMARY }, 1523 { "igntc", RES_IGNTC }, 1524 { "recurse", RES_RECURSE }, 1525 { "defnames", RES_DEFNAMES }, 1526 { "stayopen", RES_STAYOPEN }, 1527 { "dnsrch", RES_DNSRCH }, 1528 { "true", 0 }, /* avoid error on old syntax */ 1529 { NULL, 0 } 1530 }; 1531 1532 #endif /* NAMED_BIND */ 1533 1534 #define OI_NONE 0 /* no special treatment */ 1535 #define OI_SAFE 0x0001 /* safe for random people to use */ 1536 #define OI_SUBOPT 0x0002 /* option has suboptions */ 1537 1538 static struct optioninfo 1539 { 1540 char *o_name; /* long name of option */ 1541 u_char o_code; /* short name of option */ 1542 u_short o_flags; /* option flags */ 1543 } OptionTab[] = 1544 { 1545 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) 1546 { "RemoteMode", '>', OI_NONE }, 1547 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */ 1548 { "SevenBitInput", '7', OI_SAFE }, 1549 #if MIME8TO7 1550 { "EightBitMode", '8', OI_SAFE }, 1551 #endif /* MIME8TO7 */ 1552 { "AliasFile", 'A', OI_NONE }, 1553 { "AliasWait", 'a', OI_NONE }, 1554 { "BlankSub", 'B', OI_NONE }, 1555 { "MinFreeBlocks", 'b', OI_SAFE }, 1556 { "CheckpointInterval", 'C', OI_SAFE }, 1557 { "HoldExpensive", 'c', OI_NONE }, 1558 #if !_FFR_REMOVE_AUTOREBUILD 1559 { "AutoRebuildAliases", 'D', OI_NONE }, 1560 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 1561 { "DeliveryMode", 'd', OI_SAFE }, 1562 { "ErrorHeader", 'E', OI_NONE }, 1563 { "ErrorMode", 'e', OI_SAFE }, 1564 { "TempFileMode", 'F', OI_NONE }, 1565 { "SaveFromLine", 'f', OI_NONE }, 1566 { "MatchGECOS", 'G', OI_NONE }, 1567 { "HelpFile", 'H', OI_NONE }, 1568 { "MaxHopCount", 'h', OI_NONE }, 1569 { "ResolverOptions", 'I', OI_NONE }, 1570 { "IgnoreDots", 'i', OI_SAFE }, 1571 { "ForwardPath", 'J', OI_NONE }, 1572 { "SendMimeErrors", 'j', OI_SAFE }, 1573 { "ConnectionCacheSize", 'k', OI_NONE }, 1574 { "ConnectionCacheTimeout", 'K', OI_NONE }, 1575 { "UseErrorsTo", 'l', OI_NONE }, 1576 { "LogLevel", 'L', OI_SAFE }, 1577 { "MeToo", 'm', OI_SAFE }, 1578 { "CheckAliases", 'n', OI_NONE }, 1579 { "OldStyleHeaders", 'o', OI_SAFE }, 1580 { "DaemonPortOptions", 'O', OI_NONE }, 1581 { "PrivacyOptions", 'p', OI_SAFE }, 1582 { "PostmasterCopy", 'P', OI_NONE }, 1583 { "QueueFactor", 'q', OI_NONE }, 1584 { "QueueDirectory", 'Q', OI_NONE }, 1585 { "DontPruneRoutes", 'R', OI_NONE }, 1586 { "Timeout", 'r', OI_SUBOPT }, 1587 { "StatusFile", 'S', OI_NONE }, 1588 { "SuperSafe", 's', OI_SAFE }, 1589 { "QueueTimeout", 'T', OI_NONE }, 1590 { "TimeZoneSpec", 't', OI_NONE }, 1591 { "UserDatabaseSpec", 'U', OI_NONE }, 1592 { "DefaultUser", 'u', OI_NONE }, 1593 { "FallbackMXhost", 'V', OI_NONE }, 1594 { "Verbose", 'v', OI_SAFE }, 1595 { "TryNullMXList", 'w', OI_NONE }, 1596 { "QueueLA", 'x', OI_NONE }, 1597 { "RefuseLA", 'X', OI_NONE }, 1598 { "RecipientFactor", 'y', OI_NONE }, 1599 { "ForkEachJob", 'Y', OI_NONE }, 1600 { "ClassFactor", 'z', OI_NONE }, 1601 { "RetryFactor", 'Z', OI_NONE }, 1602 #define O_QUEUESORTORD 0x81 1603 { "QueueSortOrder", O_QUEUESORTORD, OI_SAFE }, 1604 #define O_HOSTSFILE 0x82 1605 { "HostsFile", O_HOSTSFILE, OI_NONE }, 1606 #define O_MQA 0x83 1607 { "MinQueueAge", O_MQA, OI_SAFE }, 1608 #define O_DEFCHARSET 0x85 1609 { "DefaultCharSet", O_DEFCHARSET, OI_SAFE }, 1610 #define O_SSFILE 0x86 1611 { "ServiceSwitchFile", O_SSFILE, OI_NONE }, 1612 #define O_DIALDELAY 0x87 1613 { "DialDelay", O_DIALDELAY, OI_SAFE }, 1614 #define O_NORCPTACTION 0x88 1615 { "NoRecipientAction", O_NORCPTACTION, OI_SAFE }, 1616 #define O_SAFEFILEENV 0x89 1617 { "SafeFileEnvironment", O_SAFEFILEENV, OI_NONE }, 1618 #define O_MAXMSGSIZE 0x8a 1619 { "MaxMessageSize", O_MAXMSGSIZE, OI_NONE }, 1620 #define O_COLONOKINADDR 0x8b 1621 { "ColonOkInAddr", O_COLONOKINADDR, OI_SAFE }, 1622 #define O_MAXQUEUERUN 0x8c 1623 { "MaxQueueRunSize", O_MAXQUEUERUN, OI_SAFE }, 1624 #define O_MAXCHILDREN 0x8d 1625 { "MaxDaemonChildren", O_MAXCHILDREN, OI_NONE }, 1626 #define O_KEEPCNAMES 0x8e 1627 { "DontExpandCnames", O_KEEPCNAMES, OI_NONE }, 1628 #define O_MUSTQUOTE 0x8f 1629 { "MustQuoteChars", O_MUSTQUOTE, OI_NONE }, 1630 #define O_SMTPGREETING 0x90 1631 { "SmtpGreetingMessage", O_SMTPGREETING, OI_NONE }, 1632 #define O_UNIXFROM 0x91 1633 { "UnixFromLine", O_UNIXFROM, OI_NONE }, 1634 #define O_OPCHARS 0x92 1635 { "OperatorChars", O_OPCHARS, OI_NONE }, 1636 #define O_DONTINITGRPS 0x93 1637 { "DontInitGroups", O_DONTINITGRPS, OI_NONE }, 1638 #define O_SLFH 0x94 1639 { "SingleLineFromHeader", O_SLFH, OI_SAFE }, 1640 #define O_ABH 0x95 1641 { "AllowBogusHELO", O_ABH, OI_SAFE }, 1642 #define O_CONNTHROT 0x97 1643 { "ConnectionRateThrottle", O_CONNTHROT, OI_NONE }, 1644 #define O_UGW 0x99 1645 { "UnsafeGroupWrites", O_UGW, OI_NONE }, 1646 #define O_DBLBOUNCE 0x9a 1647 { "DoubleBounceAddress", O_DBLBOUNCE, OI_NONE }, 1648 #define O_HSDIR 0x9b 1649 { "HostStatusDirectory", O_HSDIR, OI_NONE }, 1650 #define O_SINGTHREAD 0x9c 1651 { "SingleThreadDelivery", O_SINGTHREAD, OI_NONE }, 1652 #define O_RUNASUSER 0x9d 1653 { "RunAsUser", O_RUNASUSER, OI_NONE }, 1654 #define O_DSN_RRT 0x9e 1655 { "RrtImpliesDsn", O_DSN_RRT, OI_NONE }, 1656 #define O_PIDFILE 0x9f 1657 { "PidFile", O_PIDFILE, OI_NONE }, 1658 #define O_DONTBLAMESENDMAIL 0xa0 1659 { "DontBlameSendmail", O_DONTBLAMESENDMAIL, OI_NONE }, 1660 #define O_DPI 0xa1 1661 { "DontProbeInterfaces", O_DPI, OI_NONE }, 1662 #define O_MAXRCPT 0xa2 1663 { "MaxRecipientsPerMessage", O_MAXRCPT, OI_SAFE }, 1664 #define O_DEADLETTER 0xa3 1665 { "DeadLetterDrop", O_DEADLETTER, OI_NONE }, 1666 #if _FFR_DONTLOCKFILESFORREAD_OPTION 1667 # define O_DONTLOCK 0xa4 1668 { "DontLockFilesForRead", O_DONTLOCK, OI_NONE }, 1669 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 1670 #define O_MAXALIASRCSN 0xa5 1671 { "MaxAliasRecursion", O_MAXALIASRCSN, OI_NONE }, 1672 #define O_CNCTONLYTO 0xa6 1673 { "ConnectOnlyTo", O_CNCTONLYTO, OI_NONE }, 1674 #define O_TRUSTUSER 0xa7 1675 { "TrustedUser", O_TRUSTUSER, OI_NONE }, 1676 #define O_MAXMIMEHDRLEN 0xa8 1677 { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, OI_NONE }, 1678 #define O_CONTROLSOCKET 0xa9 1679 { "ControlSocketName", O_CONTROLSOCKET, OI_NONE }, 1680 #define O_MAXHDRSLEN 0xaa 1681 { "MaxHeadersLength", O_MAXHDRSLEN, OI_NONE }, 1682 #if _FFR_MAX_FORWARD_ENTRIES 1683 # define O_MAXFORWARD 0xab 1684 { "MaxForwardEntries", O_MAXFORWARD, OI_NONE }, 1685 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 1686 #define O_PROCTITLEPREFIX 0xac 1687 { "ProcessTitlePrefix", O_PROCTITLEPREFIX, OI_NONE }, 1688 #define O_SASLINFO 0xad 1689 #if _FFR_ALLOW_SASLINFO 1690 { "DefaultAuthInfo", O_SASLINFO, OI_SAFE }, 1691 #else /* _FFR_ALLOW_SASLINFO */ 1692 { "DefaultAuthInfo", O_SASLINFO, OI_NONE }, 1693 #endif /* _FFR_ALLOW_SASLINFO */ 1694 #define O_SASLMECH 0xae 1695 { "AuthMechanisms", O_SASLMECH, OI_NONE }, 1696 #define O_CLIENTPORT 0xaf 1697 { "ClientPortOptions", O_CLIENTPORT, OI_NONE }, 1698 #define O_DF_BUFSIZE 0xb0 1699 { "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE }, 1700 #define O_XF_BUFSIZE 0xb1 1701 { "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE }, 1702 # define O_LDAPDEFAULTSPEC 0xb2 1703 { "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE }, 1704 #if _FFR_QUEUEDELAY 1705 #define O_QUEUEDELAY 0xb3 1706 { "QueueDelay", O_QUEUEDELAY, OI_NONE }, 1707 #endif /* _FFR_QUEUEDELAY */ 1708 # define O_SRVCERTFILE 0xb4 1709 { "ServerCertFile", O_SRVCERTFILE, OI_NONE }, 1710 # define O_SRVKEYFILE 0xb5 1711 { "Serverkeyfile", O_SRVKEYFILE, OI_NONE }, 1712 # define O_CLTCERTFILE 0xb6 1713 { "ClientCertFile", O_CLTCERTFILE, OI_NONE }, 1714 # define O_CLTKEYFILE 0xb7 1715 { "Clientkeyfile", O_CLTKEYFILE, OI_NONE }, 1716 # define O_CACERTFILE 0xb8 1717 { "CACERTFile", O_CACERTFILE, OI_NONE }, 1718 # define O_CACERTPATH 0xb9 1719 { "CACERTPath", O_CACERTPATH, OI_NONE }, 1720 # define O_DHPARAMS 0xba 1721 { "DHParameters", O_DHPARAMS, OI_NONE }, 1722 #if _FFR_MILTER 1723 #define O_INPUTMILTER 0xbb 1724 { "InputMailFilters", O_INPUTMILTER, OI_NONE }, 1725 #define O_MILTER 0xbc 1726 { "Milter", O_MILTER, OI_SUBOPT }, 1727 #endif /* _FFR_MILTER */ 1728 #define O_SASLOPTS 0xbd 1729 { "AuthOptions", O_SASLOPTS, OI_NONE }, 1730 #if _FFR_QUEUE_FILE_MODE 1731 #define O_QUEUE_FILE_MODE 0xbe 1732 { "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE }, 1733 #endif /* _FFR_QUEUE_FILE_MODE */ 1734 # if _FFR_TLS_1 1735 # define O_DHPARAMS5 0xbf 1736 { "DHParameters512", O_DHPARAMS5, OI_NONE }, 1737 # define O_CIPHERLIST 0xc0 1738 { "CipherList", O_CIPHERLIST, OI_NONE }, 1739 # endif /* _FFR_TLS_1 */ 1740 # define O_RANDFILE 0xc1 1741 { "RandFile", O_RANDFILE, OI_NONE }, 1742 { NULL, '\0', OI_NONE } 1743 }; 1744 1745 void 1746 setoption(opt, val, safe, sticky, e) 1747 int opt; 1748 char *val; 1749 bool safe; 1750 bool sticky; 1751 register ENVELOPE *e; 1752 { 1753 register char *p; 1754 register struct optioninfo *o; 1755 char *subopt; 1756 int mid; 1757 bool can_setuid = RunAsUid == 0; 1758 auto char *ep; 1759 char buf[50]; 1760 extern bool Warn_Q_option; 1761 #if _FFR_ALLOW_SASLINFO 1762 extern int SubmitMode; 1763 #endif /* _FFR_ALLOW_SASLINFO */ 1764 1765 errno = 0; 1766 if (opt == ' ') 1767 { 1768 /* full word options */ 1769 struct optioninfo *sel; 1770 1771 p = strchr(val, '='); 1772 if (p == NULL) 1773 p = &val[strlen(val)]; 1774 while (*--p == ' ') 1775 continue; 1776 while (*++p == ' ') 1777 *p = '\0'; 1778 if (p == val) 1779 { 1780 syserr("readcf: null option name"); 1781 return; 1782 } 1783 if (*p == '=') 1784 *p++ = '\0'; 1785 while (*p == ' ') 1786 p++; 1787 subopt = strchr(val, '.'); 1788 if (subopt != NULL) 1789 *subopt++ = '\0'; 1790 sel = NULL; 1791 for (o = OptionTab; o->o_name != NULL; o++) 1792 { 1793 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1794 continue; 1795 if (strlen(o->o_name) == strlen(val)) 1796 { 1797 /* completely specified -- this must be it */ 1798 sel = NULL; 1799 break; 1800 } 1801 if (sel != NULL) 1802 break; 1803 sel = o; 1804 } 1805 if (sel != NULL && o->o_name == NULL) 1806 o = sel; 1807 else if (o->o_name == NULL) 1808 { 1809 syserr("readcf: unknown option name %s", val); 1810 return; 1811 } 1812 else if (sel != NULL) 1813 { 1814 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1815 val, sel->o_name, o->o_name); 1816 return; 1817 } 1818 if (strlen(val) != strlen(o->o_name)) 1819 { 1820 int oldVerbose = Verbose; 1821 1822 Verbose = 1; 1823 message("Option %s used as abbreviation for %s", 1824 val, o->o_name); 1825 Verbose = oldVerbose; 1826 } 1827 opt = o->o_code; 1828 val = p; 1829 } 1830 else 1831 { 1832 for (o = OptionTab; o->o_name != NULL; o++) 1833 { 1834 if (o->o_code == opt) 1835 break; 1836 } 1837 subopt = NULL; 1838 } 1839 1840 if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags)) 1841 { 1842 if (tTd(37, 1)) 1843 dprintf("setoption: %s does not support suboptions, ignoring .%s\n", 1844 o->o_name == NULL ? "<unknown>" : o->o_name, 1845 subopt); 1846 subopt = NULL; 1847 } 1848 1849 if (tTd(37, 1)) 1850 { 1851 dprintf(isascii(opt) && isprint(opt) ? 1852 "setoption %s (%c)%s%s=" : 1853 "setoption %s (0x%x)%s%s=", 1854 o->o_name == NULL ? "<unknown>" : o->o_name, 1855 opt, 1856 subopt == NULL ? "" : ".", 1857 subopt == NULL ? "" : subopt); 1858 xputs(val); 1859 } 1860 1861 /* 1862 ** See if this option is preset for us. 1863 */ 1864 1865 if (!sticky && bitnset(opt, StickyOpt)) 1866 { 1867 if (tTd(37, 1)) 1868 dprintf(" (ignored)\n"); 1869 return; 1870 } 1871 1872 /* 1873 ** Check to see if this option can be specified by this user. 1874 */ 1875 1876 if (!safe && RealUid == 0) 1877 safe = TRUE; 1878 if (!safe && !bitset(OI_SAFE, o->o_flags)) 1879 { 1880 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1881 { 1882 int dp; 1883 1884 if (tTd(37, 1)) 1885 dprintf(" (unsafe)"); 1886 dp = drop_privileges(TRUE); 1887 setstat(dp); 1888 } 1889 } 1890 if (tTd(37, 1)) 1891 dprintf("\n"); 1892 1893 switch (opt & 0xff) 1894 { 1895 case '7': /* force seven-bit input */ 1896 SevenBitInput = atobool(val); 1897 break; 1898 1899 #if MIME8TO7 1900 case '8': /* handling of 8-bit input */ 1901 switch (*val) 1902 { 1903 case 'm': /* convert 8-bit, convert MIME */ 1904 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1905 break; 1906 1907 case 'p': /* pass 8 bit, convert MIME */ 1908 MimeMode = MM_CVTMIME|MM_PASS8BIT; 1909 break; 1910 1911 case 's': /* strict adherence */ 1912 MimeMode = MM_CVTMIME; 1913 break; 1914 1915 # if 0 1916 case 'r': /* reject 8-bit, don't convert MIME */ 1917 MimeMode = 0; 1918 break; 1919 1920 case 'j': /* "just send 8" */ 1921 MimeMode = MM_PASS8BIT; 1922 break; 1923 1924 case 'a': /* encode 8 bit if available */ 1925 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1926 break; 1927 1928 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1929 MimeMode = MM_MIME8BIT; 1930 break; 1931 # endif /* 0 */ 1932 1933 default: 1934 syserr("Unknown 8-bit mode %c", *val); 1935 finis(FALSE, EX_USAGE); 1936 } 1937 break; 1938 #endif /* MIME8TO7 */ 1939 1940 case 'A': /* set default alias file */ 1941 if (val[0] == '\0') 1942 setalias("aliases"); 1943 else 1944 setalias(val); 1945 break; 1946 1947 case 'a': /* look N minutes for "@:@" in alias file */ 1948 if (val[0] == '\0') 1949 SafeAlias = 5 * 60; /* five minutes */ 1950 else 1951 SafeAlias = convtime(val, 'm'); 1952 break; 1953 1954 case 'B': /* substitution for blank character */ 1955 SpaceSub = val[0]; 1956 if (SpaceSub == '\0') 1957 SpaceSub = ' '; 1958 break; 1959 1960 case 'b': /* min blocks free on queue fs/max msg size */ 1961 p = strchr(val, '/'); 1962 if (p != NULL) 1963 { 1964 *p++ = '\0'; 1965 MaxMessageSize = atol(p); 1966 } 1967 MinBlocksFree = atol(val); 1968 break; 1969 1970 case 'c': /* don't connect to "expensive" mailers */ 1971 NoConnect = atobool(val); 1972 break; 1973 1974 case 'C': /* checkpoint every N addresses */ 1975 CheckpointInterval = atoi(val); 1976 break; 1977 1978 case 'd': /* delivery mode */ 1979 switch (*val) 1980 { 1981 case '\0': 1982 set_delivery_mode(SM_DELIVER, e); 1983 break; 1984 1985 case SM_QUEUE: /* queue only */ 1986 case SM_DEFER: /* queue only and defer map lookups */ 1987 #if !QUEUE 1988 syserr("need QUEUE to set -odqueue or -oddefer"); 1989 break; 1990 #endif /* !QUEUE */ 1991 /* FALLTHROUGH */ 1992 1993 case SM_DELIVER: /* do everything */ 1994 case SM_FORK: /* fork after verification */ 1995 set_delivery_mode(*val, e); 1996 break; 1997 1998 default: 1999 syserr("Unknown delivery mode %c", *val); 2000 finis(FALSE, EX_USAGE); 2001 } 2002 break; 2003 2004 #if !_FFR_REMOVE_AUTOREBUILD 2005 case 'D': /* rebuild alias database as needed */ 2006 AutoRebuild = atobool(val); 2007 break; 2008 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 2009 2010 case 'E': /* error message header/header file */ 2011 if (*val != '\0') 2012 ErrMsgFile = newstr(val); 2013 break; 2014 2015 case 'e': /* set error processing mode */ 2016 switch (*val) 2017 { 2018 case EM_QUIET: /* be silent about it */ 2019 case EM_MAIL: /* mail back */ 2020 case EM_BERKNET: /* do berknet error processing */ 2021 case EM_WRITE: /* write back (or mail) */ 2022 case EM_PRINT: /* print errors normally (default) */ 2023 e->e_errormode = *val; 2024 break; 2025 } 2026 break; 2027 2028 case 'F': /* file mode */ 2029 FileMode = atooct(val) & 0777; 2030 break; 2031 2032 case 'f': /* save Unix-style From lines on front */ 2033 SaveFrom = atobool(val); 2034 break; 2035 2036 case 'G': /* match recipients against GECOS field */ 2037 MatchGecos = atobool(val); 2038 break; 2039 2040 case 'g': /* default gid */ 2041 g_opt: 2042 if (isascii(*val) && isdigit(*val)) 2043 DefGid = atoi(val); 2044 else 2045 { 2046 register struct group *gr; 2047 2048 DefGid = -1; 2049 gr = getgrnam(val); 2050 if (gr == NULL) 2051 syserr("readcf: option %c: unknown group %s", 2052 opt, val); 2053 else 2054 DefGid = gr->gr_gid; 2055 } 2056 break; 2057 2058 case 'H': /* help file */ 2059 if (val[0] == '\0') 2060 HelpFile = "helpfile"; 2061 else 2062 HelpFile = newstr(val); 2063 break; 2064 2065 case 'h': /* maximum hop count */ 2066 MaxHopCount = atoi(val); 2067 break; 2068 2069 case 'I': /* use internet domain name server */ 2070 #if NAMED_BIND 2071 for (p = val; *p != 0; ) 2072 { 2073 bool clearmode; 2074 char *q; 2075 struct resolverflags *rfp; 2076 2077 while (*p == ' ') 2078 p++; 2079 if (*p == '\0') 2080 break; 2081 clearmode = FALSE; 2082 if (*p == '-') 2083 clearmode = TRUE; 2084 else if (*p != '+') 2085 p--; 2086 p++; 2087 q = p; 2088 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 2089 p++; 2090 if (*p != '\0') 2091 *p++ = '\0'; 2092 if (strcasecmp(q, "HasWildcardMX") == 0) 2093 { 2094 HasWildcardMX = !clearmode; 2095 continue; 2096 } 2097 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 2098 { 2099 if (strcasecmp(q, rfp->rf_name) == 0) 2100 break; 2101 } 2102 if (rfp->rf_name == NULL) 2103 syserr("readcf: I option value %s unrecognized", q); 2104 else if (clearmode) 2105 _res.options &= ~rfp->rf_bits; 2106 else 2107 _res.options |= rfp->rf_bits; 2108 } 2109 if (tTd(8, 2)) 2110 dprintf("_res.options = %x, HasWildcardMX = %d\n", 2111 (u_int) _res.options, HasWildcardMX); 2112 #else /* NAMED_BIND */ 2113 usrerr("name server (I option) specified but BIND not compiled in"); 2114 #endif /* NAMED_BIND */ 2115 break; 2116 2117 case 'i': /* ignore dot lines in message */ 2118 IgnrDot = atobool(val); 2119 break; 2120 2121 case 'j': /* send errors in MIME (RFC 1341) format */ 2122 SendMIMEErrors = atobool(val); 2123 break; 2124 2125 case 'J': /* .forward search path */ 2126 ForwardPath = newstr(val); 2127 break; 2128 2129 case 'k': /* connection cache size */ 2130 MaxMciCache = atoi(val); 2131 if (MaxMciCache < 0) 2132 MaxMciCache = 0; 2133 break; 2134 2135 case 'K': /* connection cache timeout */ 2136 MciCacheTimeout = convtime(val, 'm'); 2137 break; 2138 2139 case 'l': /* use Errors-To: header */ 2140 UseErrorsTo = atobool(val); 2141 break; 2142 2143 case 'L': /* log level */ 2144 if (safe || LogLevel < atoi(val)) 2145 LogLevel = atoi(val); 2146 break; 2147 2148 case 'M': /* define macro */ 2149 sticky = FALSE; 2150 mid = macid(val, &ep); 2151 if (mid == 0) 2152 break; 2153 p = newstr(ep); 2154 if (!safe) 2155 cleanstrcpy(p, p, MAXNAME); 2156 define(mid, p, CurEnv); 2157 break; 2158 2159 case 'm': /* send to me too */ 2160 MeToo = atobool(val); 2161 break; 2162 2163 case 'n': /* validate RHS in newaliases */ 2164 CheckAliases = atobool(val); 2165 break; 2166 2167 /* 'N' available -- was "net name" */ 2168 2169 case 'O': /* daemon options */ 2170 #if DAEMON 2171 if (!setdaemonoptions(val)) 2172 syserr("too many daemons defined (%d max)", MAXDAEMONS); 2173 #else /* DAEMON */ 2174 syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); 2175 #endif /* DAEMON */ 2176 break; 2177 2178 case 'o': /* assume old style headers */ 2179 if (atobool(val)) 2180 CurEnv->e_flags |= EF_OLDSTYLE; 2181 else 2182 CurEnv->e_flags &= ~EF_OLDSTYLE; 2183 break; 2184 2185 case 'p': /* select privacy level */ 2186 p = val; 2187 for (;;) 2188 { 2189 register struct prival *pv; 2190 extern struct prival PrivacyValues[]; 2191 2192 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2193 p++; 2194 if (*p == '\0') 2195 break; 2196 val = p; 2197 while (isascii(*p) && isalnum(*p)) 2198 p++; 2199 if (*p != '\0') 2200 *p++ = '\0'; 2201 2202 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 2203 { 2204 if (strcasecmp(val, pv->pv_name) == 0) 2205 break; 2206 } 2207 if (pv->pv_name == NULL) 2208 syserr("readcf: Op line: %s unrecognized", val); 2209 else 2210 PrivacyFlags |= pv->pv_flag; 2211 } 2212 sticky = FALSE; 2213 break; 2214 2215 case 'P': /* postmaster copy address for returned mail */ 2216 PostMasterCopy = newstr(val); 2217 break; 2218 2219 case 'q': /* slope of queue only function */ 2220 QueueFactor = atoi(val); 2221 break; 2222 2223 case 'Q': /* queue directory */ 2224 if (val[0] == '\0') 2225 { 2226 QueueDir = "mqueue"; 2227 } 2228 else 2229 { 2230 QueueDir = newstr(val); 2231 } 2232 if (RealUid != 0 && !safe) 2233 Warn_Q_option = TRUE; 2234 break; 2235 2236 case 'R': /* don't prune routes */ 2237 DontPruneRoutes = atobool(val); 2238 break; 2239 2240 case 'r': /* read timeout */ 2241 if (subopt == NULL) 2242 inittimeouts(val, sticky); 2243 else 2244 settimeout(subopt, val, sticky); 2245 break; 2246 2247 case 'S': /* status file */ 2248 if (val[0] == '\0') 2249 StatFile = "statistics"; 2250 else 2251 StatFile = newstr(val); 2252 break; 2253 2254 case 's': /* be super safe, even if expensive */ 2255 SuperSafe = atobool(val); 2256 break; 2257 2258 case 'T': /* queue timeout */ 2259 p = strchr(val, '/'); 2260 if (p != NULL) 2261 { 2262 *p++ = '\0'; 2263 settimeout("queuewarn", p, sticky); 2264 } 2265 settimeout("queuereturn", val, sticky); 2266 break; 2267 2268 case 't': /* time zone name */ 2269 TimeZoneSpec = newstr(val); 2270 break; 2271 2272 case 'U': /* location of user database */ 2273 UdbSpec = newstr(val); 2274 break; 2275 2276 case 'u': /* set default uid */ 2277 for (p = val; *p != '\0'; p++) 2278 { 2279 if (*p == '.' || *p == '/' || *p == ':') 2280 { 2281 *p++ = '\0'; 2282 break; 2283 } 2284 } 2285 if (isascii(*val) && isdigit(*val)) 2286 { 2287 DefUid = atoi(val); 2288 setdefuser(); 2289 } 2290 else 2291 { 2292 register struct passwd *pw; 2293 2294 DefUid = -1; 2295 pw = sm_getpwnam(val); 2296 if (pw == NULL) 2297 { 2298 syserr("readcf: option u: unknown user %s", val); 2299 break; 2300 } 2301 else 2302 { 2303 DefUid = pw->pw_uid; 2304 DefGid = pw->pw_gid; 2305 DefUser = newstr(pw->pw_name); 2306 } 2307 } 2308 2309 #ifdef UID_MAX 2310 if (DefUid > UID_MAX) 2311 { 2312 syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", 2313 (long) DefUid, (long) UID_MAX); 2314 break; 2315 } 2316 #endif /* UID_MAX */ 2317 2318 /* handle the group if it is there */ 2319 if (*p == '\0') 2320 break; 2321 val = p; 2322 goto g_opt; 2323 2324 case 'V': /* fallback MX host */ 2325 if (val[0] != '\0') 2326 FallBackMX = newstr(val); 2327 break; 2328 2329 case 'v': /* run in verbose mode */ 2330 Verbose = atobool(val) ? 1 : 0; 2331 break; 2332 2333 case 'w': /* if we are best MX, try host directly */ 2334 TryNullMXList = atobool(val); 2335 break; 2336 2337 /* 'W' available -- was wizard password */ 2338 2339 case 'x': /* load avg at which to auto-queue msgs */ 2340 QueueLA = atoi(val); 2341 break; 2342 2343 case 'X': /* load avg at which to auto-reject connections */ 2344 RefuseLA = atoi(val); 2345 break; 2346 2347 case 'y': /* work recipient factor */ 2348 WkRecipFact = atoi(val); 2349 break; 2350 2351 case 'Y': /* fork jobs during queue runs */ 2352 ForkQueueRuns = atobool(val); 2353 break; 2354 2355 case 'z': /* work message class factor */ 2356 WkClassFact = atoi(val); 2357 break; 2358 2359 case 'Z': /* work time factor */ 2360 WkTimeFact = atoi(val); 2361 break; 2362 2363 2364 case O_QUEUESORTORD: /* queue sorting order */ 2365 switch (*val) 2366 { 2367 case 'h': /* Host first */ 2368 case 'H': 2369 QueueSortOrder = QSO_BYHOST; 2370 break; 2371 2372 case 'p': /* Priority order */ 2373 case 'P': 2374 QueueSortOrder = QSO_BYPRIORITY; 2375 break; 2376 2377 case 't': /* Submission time */ 2378 case 'T': 2379 QueueSortOrder = QSO_BYTIME; 2380 break; 2381 2382 case 'f': /* File Name */ 2383 case 'F': 2384 QueueSortOrder = QSO_BYFILENAME; 2385 break; 2386 2387 default: 2388 syserr("Invalid queue sort order \"%s\"", val); 2389 } 2390 break; 2391 2392 #if _FFR_QUEUEDELAY 2393 case O_QUEUEDELAY: /* queue delay algorithm */ 2394 switch (*val) 2395 { 2396 case 'e': /* exponential */ 2397 case 'E': 2398 QueueAlg = QD_EXP; 2399 QueueInitDelay = 10 MINUTES; 2400 QueueMaxDelay = 2 HOURS; 2401 p = strchr(val, '/'); 2402 if (p != NULL) 2403 { 2404 char *q; 2405 2406 *p++ = '\0'; 2407 q = strchr(p, '/'); 2408 if (q != NULL) 2409 *q++ = '\0'; 2410 QueueInitDelay = convtime(p, 's'); 2411 if (q != NULL) 2412 { 2413 QueueMaxDelay = convtime(q, 's'); 2414 } 2415 } 2416 break; 2417 2418 case 'l': /* linear */ 2419 case 'L': 2420 QueueAlg = QD_LINEAR; 2421 break; 2422 2423 default: 2424 syserr("Invalid queue delay algorithm \"%s\"", val); 2425 } 2426 break; 2427 #endif /* _FFR_QUEUEDELAY */ 2428 2429 case O_HOSTSFILE: /* pathname of /etc/hosts file */ 2430 HostsFile = newstr(val); 2431 break; 2432 2433 case O_MQA: /* minimum queue age between deliveries */ 2434 MinQueueAge = convtime(val, 'm'); 2435 break; 2436 2437 case O_DEFCHARSET: /* default character set for mimefying */ 2438 DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 2439 break; 2440 2441 case O_SSFILE: /* service switch file */ 2442 ServiceSwitchFile = newstr(val); 2443 break; 2444 2445 case O_DIALDELAY: /* delay for dial-on-demand operation */ 2446 DialDelay = convtime(val, 's'); 2447 break; 2448 2449 case O_NORCPTACTION: /* what to do if no recipient */ 2450 if (strcasecmp(val, "none") == 0) 2451 NoRecipientAction = NRA_NO_ACTION; 2452 else if (strcasecmp(val, "add-to") == 0) 2453 NoRecipientAction = NRA_ADD_TO; 2454 else if (strcasecmp(val, "add-apparently-to") == 0) 2455 NoRecipientAction = NRA_ADD_APPARENTLY_TO; 2456 else if (strcasecmp(val, "add-bcc") == 0) 2457 NoRecipientAction = NRA_ADD_BCC; 2458 else if (strcasecmp(val, "add-to-undisclosed") == 0) 2459 NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 2460 else 2461 syserr("Invalid NoRecipientAction: %s", val); 2462 break; 2463 2464 case O_SAFEFILEENV: /* chroot() environ for writing to files */ 2465 SafeFileEnv = newstr(val); 2466 break; 2467 2468 case O_MAXMSGSIZE: /* maximum message size */ 2469 MaxMessageSize = atol(val); 2470 break; 2471 2472 case O_COLONOKINADDR: /* old style handling of colon addresses */ 2473 ColonOkInAddr = atobool(val); 2474 break; 2475 2476 case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 2477 MaxQueueRun = atol(val); 2478 break; 2479 2480 case O_MAXCHILDREN: /* max # of children of daemon */ 2481 MaxChildren = atoi(val); 2482 break; 2483 2484 #if _FFR_MAX_FORWARD_ENTRIES 2485 case O_MAXFORWARD: /* max # of forward entries */ 2486 MaxForwardEntries = atoi(val); 2487 break; 2488 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 2489 2490 case O_KEEPCNAMES: /* don't expand CNAME records */ 2491 DontExpandCnames = atobool(val); 2492 break; 2493 2494 case O_MUSTQUOTE: /* must quote these characters in phrases */ 2495 (void) strlcpy(buf, "@,;:\\()[]", sizeof buf); 2496 if (strlen(val) < (SIZE_T) sizeof buf - 10) 2497 (void) strlcat(buf, val, sizeof buf); 2498 else 2499 printf("Warning: MustQuoteChars too long, ignored.\n"); 2500 MustQuoteChars = newstr(buf); 2501 break; 2502 2503 case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ 2504 SmtpGreeting = newstr(munchstring(val, NULL, '\0')); 2505 break; 2506 2507 case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ 2508 UnixFromLine = newstr(munchstring(val, NULL, '\0')); 2509 break; 2510 2511 case O_OPCHARS: /* operator characters (old $o macro) */ 2512 if (OperatorChars != NULL) 2513 printf("Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n"); 2514 OperatorChars = newstr(munchstring(val, NULL, '\0')); 2515 break; 2516 2517 case O_DONTINITGRPS: /* don't call initgroups(3) */ 2518 DontInitGroups = atobool(val); 2519 break; 2520 2521 case O_SLFH: /* make sure from fits on one line */ 2522 SingleLineFromHeader = atobool(val); 2523 break; 2524 2525 case O_ABH: /* allow HELO commands with syntax errors */ 2526 AllowBogusHELO = atobool(val); 2527 break; 2528 2529 case O_CONNTHROT: /* connection rate throttle */ 2530 ConnRateThrottle = atoi(val); 2531 break; 2532 2533 case O_UGW: /* group writable files are unsafe */ 2534 if (!atobool(val)) 2535 { 2536 setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE, 2537 DontBlameSendmail); 2538 setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE, 2539 DontBlameSendmail); 2540 } 2541 break; 2542 2543 case O_DBLBOUNCE: /* address to which to send double bounces */ 2544 if (val[0] != '\0') 2545 DoubleBounceAddr = newstr(val); 2546 else 2547 syserr("readcf: option DoubleBounceAddress: value required"); 2548 break; 2549 2550 case O_HSDIR: /* persistent host status directory */ 2551 if (val[0] != '\0') 2552 HostStatDir = newstr(val); 2553 break; 2554 2555 case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ 2556 SingleThreadDelivery = atobool(val); 2557 break; 2558 2559 case O_RUNASUSER: /* run bulk of code as this user */ 2560 for (p = val; *p != '\0'; p++) 2561 { 2562 if (*p == '.' || *p == '/' || *p == ':') 2563 { 2564 *p++ = '\0'; 2565 break; 2566 } 2567 } 2568 if (isascii(*val) && isdigit(*val)) 2569 { 2570 if (can_setuid) 2571 RunAsUid = atoi(val); 2572 } 2573 else 2574 { 2575 register struct passwd *pw; 2576 2577 pw = sm_getpwnam(val); 2578 if (pw == NULL) 2579 { 2580 syserr("readcf: option RunAsUser: unknown user %s", val); 2581 break; 2582 } 2583 else if (can_setuid) 2584 { 2585 if (*p == '\0') 2586 RunAsUserName = newstr(val); 2587 RunAsUid = pw->pw_uid; 2588 RunAsGid = pw->pw_gid; 2589 } 2590 } 2591 #ifdef UID_MAX 2592 if (RunAsUid > UID_MAX) 2593 { 2594 syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", 2595 (long) RunAsUid, (long) UID_MAX); 2596 break; 2597 } 2598 #endif /* UID_MAX */ 2599 if (*p != '\0') 2600 { 2601 if (isascii(*p) && isdigit(*p)) 2602 { 2603 if (can_setuid) 2604 RunAsGid = atoi(p); 2605 } 2606 else 2607 { 2608 register struct group *gr; 2609 2610 gr = getgrnam(p); 2611 if (gr == NULL) 2612 syserr("readcf: option RunAsUser: unknown group %s", 2613 p); 2614 else if (can_setuid) 2615 RunAsGid = gr->gr_gid; 2616 } 2617 } 2618 if (tTd(47, 5)) 2619 dprintf("readcf: RunAsUser = %d:%d\n", 2620 (int)RunAsUid, (int)RunAsGid); 2621 break; 2622 2623 case O_DSN_RRT: 2624 RrtImpliesDsn = atobool(val); 2625 break; 2626 2627 case O_PIDFILE: 2628 if (PidFile != NULL) 2629 free(PidFile); 2630 PidFile = newstr(val); 2631 break; 2632 2633 case O_DONTBLAMESENDMAIL: 2634 p = val; 2635 for (;;) 2636 { 2637 register struct dbsval *dbs; 2638 extern struct dbsval DontBlameSendmailValues[]; 2639 2640 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2641 p++; 2642 if (*p == '\0') 2643 break; 2644 val = p; 2645 while (isascii(*p) && isalnum(*p)) 2646 p++; 2647 if (*p != '\0') 2648 *p++ = '\0'; 2649 2650 for (dbs = DontBlameSendmailValues; 2651 dbs->dbs_name != NULL; dbs++) 2652 { 2653 if (strcasecmp(val, dbs->dbs_name) == 0) 2654 break; 2655 } 2656 if (dbs->dbs_name == NULL) 2657 syserr("readcf: DontBlameSendmail option: %s unrecognized", val); 2658 else if (dbs->dbs_flag == DBS_SAFE) 2659 clrbitmap(DontBlameSendmail); 2660 else 2661 setbitn(dbs->dbs_flag, DontBlameSendmail); 2662 } 2663 sticky = FALSE; 2664 break; 2665 2666 case O_DPI: 2667 DontProbeInterfaces = atobool(val); 2668 break; 2669 2670 case O_MAXRCPT: 2671 MaxRcptPerMsg = atoi(val); 2672 break; 2673 2674 case O_DEADLETTER: 2675 if (DeadLetterDrop != NULL) 2676 free(DeadLetterDrop); 2677 DeadLetterDrop = newstr(val); 2678 break; 2679 2680 #if _FFR_DONTLOCKFILESFORREAD_OPTION 2681 case O_DONTLOCK: 2682 DontLockReadFiles = atobool(val); 2683 break; 2684 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 2685 2686 case O_MAXALIASRCSN: 2687 MaxAliasRecursion = atoi(val); 2688 break; 2689 2690 case O_CNCTONLYTO: 2691 /* XXX should probably use gethostbyname */ 2692 #if NETINET || NETINET6 2693 # if NETINET6 2694 if (inet_addr(val) == INADDR_NONE) 2695 { 2696 ConnectOnlyTo.sa.sa_family = AF_INET6; 2697 if (inet_pton(AF_INET6, val, 2698 &ConnectOnlyTo.sin6.sin6_addr) != 1) 2699 syserr("readcf: option ConnectOnlyTo: invalid IP address %s", 2700 val); 2701 } 2702 else 2703 # endif /* NETINET6 */ 2704 { 2705 ConnectOnlyTo.sa.sa_family = AF_INET; 2706 ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val); 2707 } 2708 #endif /* NETINET || NETINET6 */ 2709 break; 2710 2711 case O_TRUSTUSER: 2712 #if HASFCHOWN 2713 if (isascii(*val) && isdigit(*val)) 2714 TrustedUid = atoi(val); 2715 else 2716 { 2717 register struct passwd *pw; 2718 2719 TrustedUid = 0; 2720 pw = sm_getpwnam(val); 2721 if (pw == NULL) 2722 { 2723 syserr("readcf: option TrustedUser: unknown user %s", val); 2724 break; 2725 } 2726 else 2727 TrustedUid = pw->pw_uid; 2728 } 2729 2730 # ifdef UID_MAX 2731 if (TrustedUid > UID_MAX) 2732 { 2733 syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)", 2734 (long) TrustedUid, (long) UID_MAX); 2735 TrustedUid = 0; 2736 } 2737 # endif /* UID_MAX */ 2738 #else /* HASFCHOWN */ 2739 syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()"); 2740 #endif /* HASFCHOWN */ 2741 break; 2742 2743 case O_MAXMIMEHDRLEN: 2744 p = strchr(val, '/'); 2745 if (p != NULL) 2746 *p++ = '\0'; 2747 MaxMimeHeaderLength = atoi(val); 2748 if (p != NULL && *p != '\0') 2749 MaxMimeFieldLength = atoi(p); 2750 else 2751 MaxMimeFieldLength = MaxMimeHeaderLength / 2; 2752 2753 if (MaxMimeHeaderLength < 0) 2754 MaxMimeHeaderLength = 0; 2755 else if (MaxMimeHeaderLength < 128) 2756 printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); 2757 2758 if (MaxMimeFieldLength < 0) 2759 MaxMimeFieldLength = 0; 2760 else if (MaxMimeFieldLength < 40) 2761 printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); 2762 break; 2763 2764 case O_CONTROLSOCKET: 2765 if (ControlSocketName != NULL) 2766 free(ControlSocketName); 2767 ControlSocketName = newstr(val); 2768 break; 2769 2770 case O_MAXHDRSLEN: 2771 MaxHeadersLength = atoi(val); 2772 2773 if (MaxHeadersLength > 0 && 2774 MaxHeadersLength < (MAXHDRSLEN / 2)) 2775 printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2)); 2776 break; 2777 2778 case O_PROCTITLEPREFIX: 2779 if (ProcTitlePrefix != NULL) 2780 free(ProcTitlePrefix); 2781 ProcTitlePrefix = newstr(val); 2782 break; 2783 2784 #if SASL 2785 case O_SASLINFO: 2786 #if _FFR_ALLOW_SASLINFO 2787 /* 2788 ** Allow users to select their own authinfo file. 2789 ** However, this is not a "perfect" solution. 2790 ** If mail is queued, the authentication info 2791 ** will not be used in subsequent delivery attempts. 2792 ** If we really want to support this, then it has 2793 ** to be stored in the queue file. 2794 */ 2795 if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 && 2796 RunAsUid != RealUid) 2797 { 2798 errno = 0; 2799 syserr("Error: %s only allowed with -U\n", 2800 o->o_name == NULL ? "<unknown>" : o->o_name); 2801 ExitStat = EX_USAGE; 2802 break; 2803 } 2804 #endif /* _FFR_ALLOW_SASLINFO */ 2805 if (SASLInfo != NULL) 2806 free(SASLInfo); 2807 SASLInfo = newstr(val); 2808 break; 2809 2810 case O_SASLMECH: 2811 if (AuthMechanisms != NULL) 2812 free(AuthMechanisms); 2813 if (*val != '\0') 2814 AuthMechanisms = newstr(val); 2815 else 2816 AuthMechanisms = NULL; 2817 break; 2818 2819 case O_SASLOPTS: 2820 while (val != NULL && *val != '\0') 2821 { 2822 switch(*val) 2823 { 2824 case 'A': 2825 SASLOpts |= SASL_AUTH_AUTH; 2826 break; 2827 # if _FFR_SASL_OPTS 2828 case 'a': 2829 SASLOpts |= SASL_SEC_NOACTIVE; 2830 break; 2831 case 'c': 2832 SASLOpts |= SASL_SEC_PASS_CREDENTIALS; 2833 break; 2834 case 'd': 2835 SASLOpts |= SASL_SEC_NODICTIONARY; 2836 break; 2837 case 'f': 2838 SASLOpts |= SASL_SEC_FORWARD_SECRECY; 2839 break; 2840 case 'p': 2841 SASLOpts |= SASL_SEC_NOPLAINTEXT; 2842 break; 2843 case 'y': 2844 SASLOpts |= SASL_SEC_NOANONYMOUS; 2845 break; 2846 # endif /* _FFR_SASL_OPTS */ 2847 default: 2848 printf("Warning: Option: %s unknown parameter '%c'\n", 2849 o->o_name == NULL ? "<unknown>" 2850 : o->o_name, 2851 (isascii(*val) && isprint(*val)) ? *val 2852 : '?'); 2853 break; 2854 } 2855 ++val; 2856 val = strpbrk(val, ", \t"); 2857 if (val != NULL) 2858 ++val; 2859 } 2860 break; 2861 2862 #else /* SASL */ 2863 case O_SASLINFO: 2864 case O_SASLMECH: 2865 case O_SASLOPTS: 2866 printf("Warning: Option: %s requires SASL support (-DSASL)\n", 2867 o->o_name == NULL ? "<unknown>" : o->o_name); 2868 break; 2869 #endif /* SASL */ 2870 2871 #if STARTTLS 2872 case O_SRVCERTFILE: 2873 if (SrvCERTfile != NULL) 2874 free(SrvCERTfile); 2875 SrvCERTfile = newstr(val); 2876 break; 2877 2878 case O_SRVKEYFILE: 2879 if (Srvkeyfile != NULL) 2880 free(Srvkeyfile); 2881 Srvkeyfile = newstr(val); 2882 break; 2883 2884 case O_CLTCERTFILE: 2885 if (CltCERTfile != NULL) 2886 free(CltCERTfile); 2887 CltCERTfile = newstr(val); 2888 break; 2889 2890 case O_CLTKEYFILE: 2891 if (Cltkeyfile != NULL) 2892 free(Cltkeyfile); 2893 Cltkeyfile = newstr(val); 2894 break; 2895 2896 case O_CACERTFILE: 2897 if (CACERTfile != NULL) 2898 free(CACERTfile); 2899 CACERTfile = newstr(val); 2900 break; 2901 2902 case O_CACERTPATH: 2903 if (CACERTpath != NULL) 2904 free(CACERTpath); 2905 CACERTpath = newstr(val); 2906 break; 2907 2908 case O_DHPARAMS: 2909 if (DHParams != NULL) 2910 free(DHParams); 2911 DHParams = newstr(val); 2912 break; 2913 2914 # if _FFR_TLS_1 2915 case O_DHPARAMS5: 2916 if (DHParams5 != NULL) 2917 free(DHParams5); 2918 DHParams5 = newstr(val); 2919 break; 2920 2921 case O_CIPHERLIST: 2922 if (CipherList != NULL) 2923 free(CipherList); 2924 CipherList = newstr(val); 2925 break; 2926 # endif /* _FFR_TLS_1 */ 2927 2928 case O_RANDFILE: 2929 if (RandFile != NULL) 2930 free(RandFile); 2931 RandFile= newstr(val); 2932 break; 2933 2934 # else /* STARTTLS */ 2935 case O_SRVCERTFILE: 2936 case O_SRVKEYFILE: 2937 case O_CLTCERTFILE: 2938 case O_CLTKEYFILE: 2939 case O_CACERTFILE: 2940 case O_CACERTPATH: 2941 case O_DHPARAMS: 2942 # if _FFR_TLS_1 2943 case O_DHPARAMS5: 2944 case O_CIPHERLIST: 2945 # endif /* _FFR_TLS_1 */ 2946 case O_RANDFILE: 2947 printf("Warning: Option: %s requires TLS support\n", 2948 o->o_name == NULL ? "<unknown>" : o->o_name); 2949 break; 2950 2951 # endif /* STARTTLS */ 2952 2953 case O_CLIENTPORT: 2954 #if DAEMON 2955 setclientoptions(val); 2956 #else /* DAEMON */ 2957 syserr("ClientPortOptions (O option) set but DAEMON not compiled in"); 2958 #endif /* DAEMON */ 2959 break; 2960 2961 case O_DF_BUFSIZE: 2962 DataFileBufferSize = atoi(val); 2963 break; 2964 2965 case O_XF_BUFSIZE: 2966 XscriptFileBufferSize = atoi(val); 2967 break; 2968 2969 case O_LDAPDEFAULTSPEC: 2970 #ifdef LDAPMAP 2971 ldapmap_set_defaults(val); 2972 #else /* LDAPMAP */ 2973 printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n", 2974 o->o_name == NULL ? "<unknown>" : o->o_name); 2975 #endif /* LDAPMAP */ 2976 break; 2977 2978 #if _FFR_MILTER 2979 case O_INPUTMILTER: 2980 InputFilterList = newstr(val); 2981 break; 2982 2983 case O_MILTER: 2984 milter_set_option(subopt, val, sticky); 2985 break; 2986 #endif /* _FFR_MILTER */ 2987 2988 #if _FFR_QUEUE_FILE_MODE 2989 case O_QUEUE_FILE_MODE: /* queue file mode */ 2990 QueueFileMode = atooct(val) & 0777; 2991 break; 2992 #endif /* _FFR_QUEUE_FILE_MODE */ 2993 2994 default: 2995 if (tTd(37, 1)) 2996 { 2997 if (isascii(opt) && isprint(opt)) 2998 dprintf("Warning: option %c unknown\n", opt); 2999 else 3000 dprintf("Warning: option 0x%x unknown\n", opt); 3001 } 3002 break; 3003 } 3004 3005 /* 3006 ** Options with suboptions are responsible for taking care 3007 ** of sticky-ness (e.g., that a command line setting is kept 3008 ** when reading in the sendmail.cf file). This has to be done 3009 ** when the suboptions are parsed since each suboption must be 3010 ** sticky, not the root option. 3011 */ 3012 3013 if (sticky && !bitset(OI_SUBOPT, o->o_flags)) 3014 setbitn(opt, StickyOpt); 3015 } 3016 /* 3017 ** SETCLASS -- set a string into a class 3018 ** 3019 ** Parameters: 3020 ** class -- the class to put the string in. 3021 ** str -- the string to enter 3022 ** 3023 ** Returns: 3024 ** none. 3025 ** 3026 ** Side Effects: 3027 ** puts the word into the symbol table. 3028 */ 3029 3030 void 3031 setclass(class, str) 3032 int class; 3033 char *str; 3034 { 3035 register STAB *s; 3036 3037 if ((*str & 0377) == MATCHCLASS) 3038 { 3039 int mid; 3040 3041 str++; 3042 mid = macid(str, NULL); 3043 if (mid == 0) 3044 return; 3045 3046 if (tTd(37, 8)) 3047 dprintf("setclass(%s, $=%s)\n", 3048 macname(class), macname(mid)); 3049 copy_class(mid, class); 3050 } 3051 else 3052 { 3053 if (tTd(37, 8)) 3054 dprintf("setclass(%s, %s)\n", macname(class), str); 3055 3056 s = stab(str, ST_CLASS, ST_ENTER); 3057 setbitn(bitidx(class), s->s_class); 3058 } 3059 } 3060 /* 3061 ** MAKEMAPENTRY -- create a map entry 3062 ** 3063 ** Parameters: 3064 ** line -- the config file line 3065 ** 3066 ** Returns: 3067 ** A pointer to the map that has been created. 3068 ** NULL if there was a syntax error. 3069 ** 3070 ** Side Effects: 3071 ** Enters the map into the dictionary. 3072 */ 3073 3074 MAP * 3075 makemapentry(line) 3076 char *line; 3077 { 3078 register char *p; 3079 char *mapname; 3080 char *classname; 3081 register STAB *s; 3082 STAB *class; 3083 3084 for (p = line; isascii(*p) && isspace(*p); p++) 3085 continue; 3086 if (!(isascii(*p) && isalnum(*p))) 3087 { 3088 syserr("readcf: config K line: no map name"); 3089 return NULL; 3090 } 3091 3092 mapname = p; 3093 while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') 3094 continue; 3095 if (*p != '\0') 3096 *p++ = '\0'; 3097 while (isascii(*p) && isspace(*p)) 3098 p++; 3099 if (!(isascii(*p) && isalnum(*p))) 3100 { 3101 syserr("readcf: config K line, map %s: no map class", mapname); 3102 return NULL; 3103 } 3104 classname = p; 3105 while (isascii(*++p) && isalnum(*p)) 3106 continue; 3107 if (*p != '\0') 3108 *p++ = '\0'; 3109 while (isascii(*p) && isspace(*p)) 3110 p++; 3111 3112 /* look up the class */ 3113 class = stab(classname, ST_MAPCLASS, ST_FIND); 3114 if (class == NULL) 3115 { 3116 syserr("readcf: map %s: class %s not available", mapname, classname); 3117 return NULL; 3118 } 3119 3120 /* enter the map */ 3121 s = stab(mapname, ST_MAP, ST_ENTER); 3122 s->s_map.map_class = &class->s_mapclass; 3123 s->s_map.map_mname = newstr(mapname); 3124 3125 if (class->s_mapclass.map_parse(&s->s_map, p)) 3126 s->s_map.map_mflags |= MF_VALID; 3127 3128 if (tTd(37, 5)) 3129 { 3130 dprintf("map %s, class %s, flags %lx, file %s,\n", 3131 s->s_map.map_mname, s->s_map.map_class->map_cname, 3132 s->s_map.map_mflags, 3133 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 3134 dprintf("\tapp %s, domain %s, rebuild %s\n", 3135 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 3136 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 3137 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 3138 } 3139 3140 return &s->s_map; 3141 } 3142 /* 3143 ** STRTORWSET -- convert string to rewriting set number 3144 ** 3145 ** Parameters: 3146 ** p -- the pointer to the string to decode. 3147 ** endp -- if set, store the trailing delimiter here. 3148 ** stabmode -- ST_ENTER to create this entry, ST_FIND if 3149 ** it must already exist. 3150 ** 3151 ** Returns: 3152 ** The appropriate ruleset number. 3153 ** -1 if it is not valid (error already printed) 3154 */ 3155 3156 int 3157 strtorwset(p, endp, stabmode) 3158 char *p; 3159 char **endp; 3160 int stabmode; 3161 { 3162 int ruleset; 3163 static int nextruleset = MAXRWSETS; 3164 3165 while (isascii(*p) && isspace(*p)) 3166 p++; 3167 if (!isascii(*p)) 3168 { 3169 syserr("invalid ruleset name: \"%.20s\"", p); 3170 return -1; 3171 } 3172 if (isdigit(*p)) 3173 { 3174 ruleset = strtol(p, endp, 10); 3175 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3176 { 3177 syserr("bad ruleset %d (%d max)", 3178 ruleset, MAXRWSETS / 2); 3179 ruleset = -1; 3180 } 3181 } 3182 else 3183 { 3184 STAB *s; 3185 char delim; 3186 char *q = NULL; 3187 3188 q = p; 3189 while (*p != '\0' && isascii(*p) && 3190 (isalnum(*p) || *p == '_')) 3191 p++; 3192 if (q == p || !(isascii(*q) && isalpha(*q))) 3193 { 3194 /* no valid characters */ 3195 syserr("invalid ruleset name: \"%.20s\"", q); 3196 return -1; 3197 } 3198 while (isascii(*p) && isspace(*p)) 3199 *p++ = '\0'; 3200 delim = *p; 3201 if (delim != '\0') 3202 *p = '\0'; 3203 s = stab(q, ST_RULESET, stabmode); 3204 if (delim != '\0') 3205 *p = delim; 3206 3207 if (s == NULL) 3208 return -1; 3209 3210 if (stabmode == ST_ENTER && delim == '=') 3211 { 3212 while (isascii(*++p) && isspace(*p)) 3213 continue; 3214 if (!(isascii(*p) && isdigit(*p))) 3215 { 3216 syserr("bad ruleset definition \"%s\" (number required after `=')", q); 3217 ruleset = -1; 3218 } 3219 else 3220 { 3221 ruleset = strtol(p, endp, 10); 3222 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3223 { 3224 syserr("bad ruleset number %d in \"%s\" (%d max)", 3225 ruleset, q, MAXRWSETS / 2); 3226 ruleset = -1; 3227 } 3228 } 3229 } 3230 else 3231 { 3232 if (endp != NULL) 3233 *endp = p; 3234 if (s->s_ruleset >= 0) 3235 ruleset = s->s_ruleset; 3236 else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 3237 { 3238 syserr("%s: too many named rulesets (%d max)", 3239 q, MAXRWSETS / 2); 3240 ruleset = -1; 3241 } 3242 } 3243 if (s->s_ruleset >= 0 && 3244 ruleset >= 0 && 3245 ruleset != s->s_ruleset) 3246 { 3247 syserr("%s: ruleset changed value (old %d, new %d)", 3248 q, s->s_ruleset, ruleset); 3249 ruleset = s->s_ruleset; 3250 } 3251 else if (ruleset >= 0) 3252 { 3253 s->s_ruleset = ruleset; 3254 } 3255 if (stabmode == ST_ENTER && ruleset >= 0) 3256 { 3257 char *h = NULL; 3258 3259 if (RuleSetNames[ruleset] != NULL) 3260 free(RuleSetNames[ruleset]); 3261 if (delim != '\0' && (h = strchr(q, delim)) != NULL) 3262 *h = '\0'; 3263 RuleSetNames[ruleset] = newstr(q); 3264 if (delim == '/' && h != NULL) 3265 *h = delim; /* put back delim */ 3266 } 3267 } 3268 return ruleset; 3269 } 3270 /* 3271 ** SETTIMEOUT -- set an individual timeout 3272 ** 3273 ** Parameters: 3274 ** name -- the name of the timeout. 3275 ** val -- the value of the timeout. 3276 ** sticky -- if set, don't let other setoptions override 3277 ** this value. 3278 ** 3279 ** Returns: 3280 ** none. 3281 */ 3282 3283 /* set if Timeout sub-option is stuck */ 3284 static BITMAP256 StickyTimeoutOpt; 3285 3286 static struct timeoutinfo 3287 { 3288 char *to_name; /* long name of timeout */ 3289 u_char to_code; /* code for option */ 3290 } TimeOutTab[] = 3291 { 3292 #define TO_INITIAL 0x01 3293 { "initial", TO_INITIAL }, 3294 #define TO_MAIL 0x02 3295 { "mail", TO_MAIL }, 3296 #define TO_RCPT 0x03 3297 { "rcpt", TO_RCPT }, 3298 #define TO_DATAINIT 0x04 3299 { "datainit", TO_DATAINIT }, 3300 #define TO_DATABLOCK 0x05 3301 { "datablock", TO_DATABLOCK }, 3302 #define TO_DATAFINAL 0x06 3303 { "datafinal", TO_DATAFINAL }, 3304 #define TO_COMMAND 0x07 3305 { "command", TO_COMMAND }, 3306 #define TO_RSET 0x08 3307 { "rset", TO_RSET }, 3308 #define TO_HELO 0x09 3309 { "helo", TO_HELO }, 3310 #define TO_QUIT 0x0A 3311 { "quit", TO_QUIT }, 3312 #define TO_MISC 0x0B 3313 { "misc", TO_MISC }, 3314 #define TO_IDENT 0x0C 3315 { "ident", TO_IDENT }, 3316 #define TO_FILEOPEN 0x0D 3317 { "fileopen", TO_FILEOPEN }, 3318 #define TO_CONNECT 0x0E 3319 { "connect", TO_CONNECT }, 3320 #define TO_ICONNECT 0x0F 3321 { "iconnect", TO_ICONNECT }, 3322 #define TO_QUEUEWARN 0x10 3323 { "queuewarn", TO_QUEUEWARN }, 3324 { "queuewarn.*", TO_QUEUEWARN }, 3325 #define TO_QUEUEWARN_NORMAL 0x11 3326 { "queuewarn.normal", TO_QUEUEWARN_NORMAL }, 3327 #define TO_QUEUEWARN_URGENT 0x12 3328 { "queuewarn.urgent", TO_QUEUEWARN_URGENT }, 3329 #define TO_QUEUEWARN_NON_URGENT 0x13 3330 { "queuewarn.non-urgent", TO_QUEUEWARN_NON_URGENT }, 3331 #define TO_QUEUERETURN 0x14 3332 { "queuereturn", TO_QUEUERETURN }, 3333 { "queuereturn.*", TO_QUEUERETURN }, 3334 #define TO_QUEUERETURN_NORMAL 0x15 3335 { "queuereturn.normal", TO_QUEUERETURN_NORMAL }, 3336 #define TO_QUEUERETURN_URGENT 0x16 3337 { "queuereturn.urgent", TO_QUEUERETURN_URGENT }, 3338 #define TO_QUEUERETURN_NON_URGENT 0x17 3339 { "queuereturn.non-urgent", TO_QUEUERETURN_NON_URGENT }, 3340 #define TO_HOSTSTATUS 0x18 3341 { "hoststatus", TO_HOSTSTATUS }, 3342 #define TO_RESOLVER_RETRANS 0x19 3343 { "resolver.retrans", TO_RESOLVER_RETRANS }, 3344 #define TO_RESOLVER_RETRANS_NORMAL 0x1A 3345 { "resolver.retrans.normal", TO_RESOLVER_RETRANS_NORMAL }, 3346 #define TO_RESOLVER_RETRANS_FIRST 0x1B 3347 { "resolver.retrans.first", TO_RESOLVER_RETRANS_FIRST }, 3348 #define TO_RESOLVER_RETRY 0x1C 3349 { "resolver.retry", TO_RESOLVER_RETRY }, 3350 #define TO_RESOLVER_RETRY_NORMAL 0x1D 3351 { "resolver.retry.normal", TO_RESOLVER_RETRY_NORMAL }, 3352 #define TO_RESOLVER_RETRY_FIRST 0x1E 3353 { "resolver.retry.first", TO_RESOLVER_RETRY_FIRST }, 3354 #define TO_CONTROL 0x1F 3355 { "control", TO_CONTROL }, 3356 { NULL, 0 }, 3357 }; 3358 3359 3360 static void 3361 settimeout(name, val, sticky) 3362 char *name; 3363 char *val; 3364 bool sticky; 3365 { 3366 register struct timeoutinfo *to; 3367 int i; 3368 time_t toval; 3369 3370 if (tTd(37, 2)) 3371 dprintf("settimeout(%s = %s)", name, val); 3372 3373 for (to = TimeOutTab; to->to_name != NULL; to++) 3374 { 3375 if (strcasecmp(to->to_name, name) == 0) 3376 break; 3377 } 3378 3379 if (to->to_name == NULL) 3380 { 3381 errno = 0; /* avoid bogus error text */ 3382 syserr("settimeout: invalid timeout %s", name); 3383 return; 3384 } 3385 3386 /* 3387 ** See if this option is preset for us. 3388 */ 3389 3390 if (!sticky && bitnset(to->to_code, StickyTimeoutOpt)) 3391 { 3392 if (tTd(37, 2)) 3393 dprintf(" (ignored)\n"); 3394 return; 3395 } 3396 3397 if (tTd(37, 2)) 3398 dprintf("\n"); 3399 3400 toval = convtime(val, 'm'); 3401 3402 switch (to->to_code) 3403 { 3404 case TO_INITIAL: 3405 TimeOuts.to_initial = toval; 3406 break; 3407 3408 case TO_MAIL: 3409 TimeOuts.to_mail = toval; 3410 break; 3411 3412 case TO_RCPT: 3413 TimeOuts.to_rcpt = toval; 3414 break; 3415 3416 case TO_DATAINIT: 3417 TimeOuts.to_datainit = toval; 3418 break; 3419 3420 case TO_DATABLOCK: 3421 TimeOuts.to_datablock = toval; 3422 break; 3423 3424 case TO_DATAFINAL: 3425 TimeOuts.to_datafinal = toval; 3426 break; 3427 3428 case TO_COMMAND: 3429 TimeOuts.to_nextcommand = toval; 3430 break; 3431 3432 case TO_RSET: 3433 TimeOuts.to_rset = toval; 3434 break; 3435 3436 case TO_HELO: 3437 TimeOuts.to_helo = toval; 3438 break; 3439 3440 case TO_QUIT: 3441 TimeOuts.to_quit = toval; 3442 break; 3443 3444 case TO_MISC: 3445 TimeOuts.to_miscshort = toval; 3446 break; 3447 3448 case TO_IDENT: 3449 TimeOuts.to_ident = toval; 3450 break; 3451 3452 case TO_FILEOPEN: 3453 TimeOuts.to_fileopen = toval; 3454 break; 3455 3456 case TO_CONNECT: 3457 TimeOuts.to_connect = toval; 3458 break; 3459 3460 case TO_ICONNECT: 3461 TimeOuts.to_iconnect = toval; 3462 break; 3463 3464 case TO_QUEUEWARN: 3465 toval = convtime(val, 'h'); 3466 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3467 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3468 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3469 break; 3470 3471 case TO_QUEUEWARN_NORMAL: 3472 toval = convtime(val, 'h'); 3473 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3474 break; 3475 3476 case TO_QUEUEWARN_URGENT: 3477 toval = convtime(val, 'h'); 3478 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3479 break; 3480 3481 case TO_QUEUEWARN_NON_URGENT: 3482 toval = convtime(val, 'h'); 3483 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3484 break; 3485 3486 case TO_QUEUERETURN: 3487 toval = convtime(val, 'd'); 3488 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3489 TimeOuts.to_q_return[TOC_URGENT] = toval; 3490 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3491 break; 3492 3493 case TO_QUEUERETURN_NORMAL: 3494 toval = convtime(val, 'd'); 3495 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3496 break; 3497 3498 case TO_QUEUERETURN_URGENT: 3499 toval = convtime(val, 'd'); 3500 TimeOuts.to_q_return[TOC_URGENT] = toval; 3501 break; 3502 3503 case TO_QUEUERETURN_NON_URGENT: 3504 toval = convtime(val, 'd'); 3505 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3506 break; 3507 3508 3509 case TO_HOSTSTATUS: 3510 MciInfoTimeout = toval; 3511 break; 3512 3513 case TO_RESOLVER_RETRANS: 3514 toval = convtime(val, 's'); 3515 TimeOuts.res_retrans[RES_TO_DEFAULT] = toval; 3516 TimeOuts.res_retrans[RES_TO_FIRST] = toval; 3517 TimeOuts.res_retrans[RES_TO_NORMAL] = toval; 3518 break; 3519 3520 case TO_RESOLVER_RETRY: 3521 i = atoi(val); 3522 TimeOuts.res_retry[RES_TO_DEFAULT] = i; 3523 TimeOuts.res_retry[RES_TO_FIRST] = i; 3524 TimeOuts.res_retry[RES_TO_NORMAL] = i; 3525 break; 3526 3527 case TO_RESOLVER_RETRANS_NORMAL: 3528 TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's'); 3529 break; 3530 3531 case TO_RESOLVER_RETRY_NORMAL: 3532 TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val); 3533 break; 3534 3535 case TO_RESOLVER_RETRANS_FIRST: 3536 TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's'); 3537 break; 3538 3539 case TO_RESOLVER_RETRY_FIRST: 3540 TimeOuts.res_retry[RES_TO_FIRST] = atoi(val); 3541 break; 3542 3543 case TO_CONTROL: 3544 TimeOuts.to_control = toval; 3545 break; 3546 3547 default: 3548 syserr("settimeout: invalid timeout %s", name); 3549 break; 3550 } 3551 3552 if (sticky) 3553 setbitn(to->to_code, StickyTimeoutOpt); 3554 } 3555 /* 3556 ** INITTIMEOUTS -- parse and set timeout values 3557 ** 3558 ** Parameters: 3559 ** val -- a pointer to the values. If NULL, do initial 3560 ** settings. 3561 ** sticky -- if set, don't let other setoptions override 3562 ** this suboption value. 3563 ** 3564 ** Returns: 3565 ** none. 3566 ** 3567 ** Side Effects: 3568 ** Initializes the TimeOuts structure 3569 */ 3570 3571 void 3572 inittimeouts(val, sticky) 3573 register char *val; 3574 bool sticky; 3575 { 3576 register char *p; 3577 3578 if (tTd(37, 2)) 3579 dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); 3580 if (val == NULL) 3581 { 3582 TimeOuts.to_connect = (time_t) 0 SECONDS; 3583 TimeOuts.to_initial = (time_t) 5 MINUTES; 3584 TimeOuts.to_helo = (time_t) 5 MINUTES; 3585 TimeOuts.to_mail = (time_t) 10 MINUTES; 3586 TimeOuts.to_rcpt = (time_t) 1 HOUR; 3587 TimeOuts.to_datainit = (time_t) 5 MINUTES; 3588 TimeOuts.to_datablock = (time_t) 1 HOUR; 3589 TimeOuts.to_datafinal = (time_t) 1 HOUR; 3590 TimeOuts.to_rset = (time_t) 5 MINUTES; 3591 TimeOuts.to_quit = (time_t) 2 MINUTES; 3592 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 3593 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 3594 #if IDENTPROTO 3595 TimeOuts.to_ident = (time_t) 5 SECONDS; 3596 #else /* IDENTPROTO */ 3597 TimeOuts.to_ident = (time_t) 0 SECONDS; 3598 #endif /* IDENTPROTO */ 3599 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 3600 TimeOuts.to_control = (time_t) 2 MINUTES; 3601 if (tTd(37, 5)) 3602 { 3603 dprintf("Timeouts:\n"); 3604 dprintf(" connect = %ld\n", (long)TimeOuts.to_connect); 3605 dprintf(" initial = %ld\n", (long)TimeOuts.to_initial); 3606 dprintf(" helo = %ld\n", (long)TimeOuts.to_helo); 3607 dprintf(" mail = %ld\n", (long)TimeOuts.to_mail); 3608 dprintf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); 3609 dprintf(" datainit = %ld\n", (long)TimeOuts.to_datainit); 3610 dprintf(" datablock = %ld\n", (long)TimeOuts.to_datablock); 3611 dprintf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); 3612 dprintf(" rset = %ld\n", (long)TimeOuts.to_rset); 3613 dprintf(" quit = %ld\n", (long)TimeOuts.to_quit); 3614 dprintf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); 3615 dprintf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); 3616 dprintf(" ident = %ld\n", (long)TimeOuts.to_ident); 3617 dprintf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); 3618 dprintf(" control = %ld\n", (long)TimeOuts.to_control); 3619 } 3620 return; 3621 } 3622 3623 for (;; val = p) 3624 { 3625 while (isascii(*val) && isspace(*val)) 3626 val++; 3627 if (*val == '\0') 3628 break; 3629 for (p = val; *p != '\0' && *p != ','; p++) 3630 continue; 3631 if (*p != '\0') 3632 *p++ = '\0'; 3633 3634 if (isascii(*val) && isdigit(*val)) 3635 { 3636 /* old syntax -- set everything */ 3637 TimeOuts.to_mail = convtime(val, 'm'); 3638 TimeOuts.to_rcpt = TimeOuts.to_mail; 3639 TimeOuts.to_datainit = TimeOuts.to_mail; 3640 TimeOuts.to_datablock = TimeOuts.to_mail; 3641 TimeOuts.to_datafinal = TimeOuts.to_mail; 3642 TimeOuts.to_nextcommand = TimeOuts.to_mail; 3643 if (sticky) 3644 { 3645 setbitn(TO_MAIL, StickyTimeoutOpt); 3646 setbitn(TO_RCPT, StickyTimeoutOpt); 3647 setbitn(TO_DATAINIT, StickyTimeoutOpt); 3648 setbitn(TO_DATABLOCK, StickyTimeoutOpt); 3649 setbitn(TO_DATAFINAL, StickyTimeoutOpt); 3650 setbitn(TO_COMMAND, StickyTimeoutOpt); 3651 } 3652 continue; 3653 } 3654 else 3655 { 3656 register char *q = strchr(val, ':'); 3657 3658 if (q == NULL && (q = strchr(val, '=')) == NULL) 3659 { 3660 /* syntax error */ 3661 continue; 3662 } 3663 *q++ = '\0'; 3664 settimeout(val, q, sticky); 3665 } 3666 } 3667 } 3668