1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)readcf.c 6.4 (Berkeley) 01/18/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sys/stat.h> 15 # include <unistd.h> 16 #ifdef NAMED_BIND 17 # include <arpa/nameser.h> 18 # include <resolv.h> 19 #endif 20 21 /* 22 ** READCF -- read control file. 23 ** 24 ** This routine reads the control file and builds the internal 25 ** form. 26 ** 27 ** The file is formatted as a sequence of lines, each taken 28 ** atomically. The first character of each line describes how 29 ** the line is to be interpreted. The lines are: 30 ** Dxval Define macro x to have value val. 31 ** Cxword Put word into class x. 32 ** Fxfile [fmt] Read file for lines to put into 33 ** class x. Use scanf string 'fmt' 34 ** or "%s" if not present. Fmt should 35 ** only produce one string-valued result. 36 ** Hname: value Define header with field-name 'name' 37 ** and value as specified; this will be 38 ** macro expanded immediately before 39 ** use. 40 ** Sn Use rewriting set n. 41 ** Rlhs rhs Rewrite addresses that match lhs to 42 ** be rhs. 43 ** Mn arg=val... Define mailer. n is the internal name. 44 ** Args specify mailer parameters. 45 ** Oxvalue Set option x to value. 46 ** Pname=value Set precedence name to value. 47 ** Vversioncode Version level of configuration syntax. 48 ** Kmapname mapclass arguments.... 49 ** Define keyed lookup of a given class. 50 ** Arguments are class dependent. 51 ** 52 ** Parameters: 53 ** cfname -- control file name. 54 ** safe -- TRUE if this is the system config file; 55 ** FALSE otherwise. 56 ** e -- the main envelope. 57 ** 58 ** Returns: 59 ** none. 60 ** 61 ** Side Effects: 62 ** Builds several internal tables. 63 */ 64 65 readcf(cfname, safe, e) 66 char *cfname; 67 bool safe; 68 register ENVELOPE *e; 69 { 70 FILE *cf; 71 int ruleset = 0; 72 char *q; 73 char **pv; 74 struct rewrite *rwp = NULL; 75 char *bp; 76 int nfuzzy; 77 char buf[MAXLINE]; 78 register char *p; 79 extern char **prescan(); 80 extern char **copyplist(); 81 struct stat statb; 82 char exbuf[MAXLINE]; 83 char pvpbuf[PSBUFSIZE]; 84 extern char *fgetfolded(); 85 extern char *munchstring(); 86 extern void makemapentry(); 87 88 FileName = cfname; 89 LineNumber = 0; 90 91 cf = fopen(cfname, "r"); 92 if (cf == NULL) 93 { 94 syserr("cannot open"); 95 exit(EX_OSFILE); 96 } 97 98 if (fstat(fileno(cf), &statb) < 0) 99 { 100 syserr("cannot fstat"); 101 exit(EX_OSFILE); 102 } 103 104 if (!S_ISREG(statb.st_mode)) 105 { 106 syserr("not a plain file"); 107 exit(EX_OSFILE); 108 } 109 110 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 111 { 112 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 113 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 114 FileName); 115 #ifdef LOG 116 if (LogLevel > 0) 117 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 118 FileName); 119 #endif 120 } 121 122 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 123 { 124 if (bp[0] == '#') 125 { 126 if (bp != buf) 127 free(bp); 128 continue; 129 } 130 131 /* map $ into \001 (ASCII SOH) for macro expansion */ 132 for (p = bp; *p != '\0'; p++) 133 { 134 if (*p == '#' && p > bp && ConfigLevel >= 3) 135 { 136 /* this is an on-line comment */ 137 register char *e; 138 139 switch (*--p) 140 { 141 case '\001': 142 /* it's from $# -- let it go through */ 143 p++; 144 break; 145 146 case '\\': 147 /* it's backslash escaped */ 148 (void) strcpy(p, p + 1); 149 break; 150 151 default: 152 /* delete preceeding white space */ 153 while (isspace(*p) && p > bp) 154 p--; 155 if ((e = strchr(++p, '\n')) != NULL) 156 (void) strcpy(p, e); 157 else 158 p[0] = p[1] = '\0'; 159 break; 160 } 161 continue; 162 } 163 164 if (*p != '$') 165 continue; 166 167 if (p[1] == '$') 168 { 169 /* actual dollar sign.... */ 170 (void) strcpy(p, p + 1); 171 continue; 172 } 173 174 /* convert to macro expansion character */ 175 *p = '\001'; 176 } 177 178 /* interpret this line */ 179 switch (bp[0]) 180 { 181 case '\0': 182 case '#': /* comment */ 183 break; 184 185 case 'R': /* rewriting rule */ 186 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 187 continue; 188 189 if (*p == '\0') 190 { 191 syserr("invalid rewrite line \"%s\"", bp); 192 break; 193 } 194 195 /* allocate space for the rule header */ 196 if (rwp == NULL) 197 { 198 RewriteRules[ruleset] = rwp = 199 (struct rewrite *) xalloc(sizeof *rwp); 200 } 201 else 202 { 203 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 204 rwp = rwp->r_next; 205 } 206 rwp->r_next = NULL; 207 208 /* expand and save the LHS */ 209 *p = '\0'; 210 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 211 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 212 nfuzzy = 0; 213 if (rwp->r_lhs != NULL) 214 { 215 register char **ap; 216 217 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 218 219 /* count the number of fuzzy matches in LHS */ 220 for (ap = rwp->r_lhs; *ap != NULL; ap++) 221 { 222 switch (**ap) 223 { 224 case MATCHZANY: 225 case MATCHANY: 226 case MATCHONE: 227 case MATCHCLASS: 228 case MATCHNCLASS: 229 nfuzzy++; 230 } 231 } 232 } 233 else 234 syserr("R line: null LHS"); 235 236 /* expand and save the RHS */ 237 while (*++p == '\t') 238 continue; 239 q = p; 240 while (*p != '\0' && *p != '\t') 241 p++; 242 *p = '\0'; 243 expand(q, exbuf, &exbuf[sizeof exbuf], e); 244 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 245 if (rwp->r_rhs != NULL) 246 { 247 register char **ap; 248 249 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 250 251 /* check no out-of-bounds replacements */ 252 nfuzzy += '0'; 253 for (ap = rwp->r_rhs; *ap != NULL; ap++) 254 { 255 if (**ap != MATCHREPL) 256 continue; 257 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 258 { 259 syserr("replacement $%c out of bounds", 260 (*ap)[1]); 261 } 262 } 263 } 264 else 265 syserr("R line: null RHS"); 266 break; 267 268 case 'S': /* select rewriting set */ 269 ruleset = atoi(&bp[1]); 270 if (ruleset >= MAXRWSETS || ruleset < 0) 271 { 272 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 273 ruleset = 0; 274 } 275 rwp = NULL; 276 break; 277 278 case 'D': /* macro definition */ 279 define(bp[1], newstr(munchstring(&bp[2])), e); 280 break; 281 282 case 'H': /* required header line */ 283 (void) chompheader(&bp[1], TRUE, e); 284 break; 285 286 case 'C': /* word class */ 287 case 'F': /* word class from file */ 288 /* read list of words from argument or file */ 289 if (bp[0] == 'F') 290 { 291 /* read from file */ 292 for (p = &bp[2]; *p != '\0' && !isspace(*p); p++) 293 continue; 294 if (*p == '\0') 295 p = "%s"; 296 else 297 { 298 *p = '\0'; 299 while (isspace(*++p)) 300 continue; 301 } 302 fileclass(bp[1], &bp[2], p, safe); 303 break; 304 } 305 306 /* scan the list of words and set class for all */ 307 for (p = &bp[2]; *p != '\0'; ) 308 { 309 register char *wd; 310 char delim; 311 312 while (*p != '\0' && isspace(*p)) 313 p++; 314 wd = p; 315 while (*p != '\0' && !isspace(*p)) 316 p++; 317 delim = *p; 318 *p = '\0'; 319 if (wd[0] != '\0') 320 setclass(bp[1], wd); 321 *p = delim; 322 } 323 break; 324 325 case 'M': /* define mailer */ 326 makemailer(&bp[1]); 327 break; 328 329 case 'O': /* set option */ 330 setoption(bp[1], &bp[2], safe, FALSE); 331 break; 332 333 case 'P': /* set precedence */ 334 if (NumPriorities >= MAXPRIORITIES) 335 { 336 toomany('P', MAXPRIORITIES); 337 break; 338 } 339 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 340 continue; 341 if (*p == '\0') 342 goto badline; 343 *p = '\0'; 344 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 345 Priorities[NumPriorities].pri_val = atoi(++p); 346 NumPriorities++; 347 break; 348 349 case 'T': /* trusted user(s) */ 350 p = &bp[1]; 351 while (*p != '\0') 352 { 353 while (isspace(*p)) 354 p++; 355 q = p; 356 while (*p != '\0' && !isspace(*p)) 357 p++; 358 if (*p != '\0') 359 *p++ = '\0'; 360 if (*q == '\0') 361 continue; 362 for (pv = TrustedUsers; *pv != NULL; pv++) 363 continue; 364 if (pv >= &TrustedUsers[MAXTRUST]) 365 { 366 toomany('T', MAXTRUST); 367 break; 368 } 369 *pv = newstr(q); 370 } 371 break; 372 373 case 'V': /* configuration syntax version */ 374 ConfigLevel = atoi(&bp[1]); 375 break; 376 377 case 'K': 378 makemapentry(&bp[1]); 379 break; 380 381 default: 382 badline: 383 syserr("unknown control line \"%s\"", bp); 384 } 385 if (bp != buf) 386 free(bp); 387 } 388 if (ferror(cf)) 389 { 390 syserr("I/O read error", cfname); 391 exit(EX_OSFILE); 392 } 393 fclose(cf); 394 FileName = NULL; 395 396 if (stab("host", ST_MAP, ST_FIND) == NULL) 397 { 398 /* user didn't initialize: set up host map */ 399 strcpy(buf, "host host"); 400 if (ConfigLevel >= 2) 401 strcat(buf, " -a."); 402 makemapentry(buf); 403 } 404 } 405 /* 406 ** TOOMANY -- signal too many of some option 407 ** 408 ** Parameters: 409 ** id -- the id of the error line 410 ** maxcnt -- the maximum possible values 411 ** 412 ** Returns: 413 ** none. 414 ** 415 ** Side Effects: 416 ** gives a syserr. 417 */ 418 419 toomany(id, maxcnt) 420 char id; 421 int maxcnt; 422 { 423 syserr("too many %c lines, %d max", id, maxcnt); 424 } 425 /* 426 ** FILECLASS -- read members of a class from a file 427 ** 428 ** Parameters: 429 ** class -- class to define. 430 ** filename -- name of file to read. 431 ** fmt -- scanf string to use for match. 432 ** 433 ** Returns: 434 ** none 435 ** 436 ** Side Effects: 437 ** 438 ** puts all lines in filename that match a scanf into 439 ** the named class. 440 */ 441 442 fileclass(class, filename, fmt, safe) 443 int class; 444 char *filename; 445 char *fmt; 446 bool safe; 447 { 448 FILE *f; 449 struct stat stbuf; 450 char buf[MAXLINE]; 451 452 if (stat(filename, &stbuf) < 0) 453 { 454 syserr("fileclass: cannot stat %s", filename); 455 return; 456 } 457 if (!S_ISREG(stbuf.st_mode)) 458 { 459 syserr("fileclass: %s not a regular file", filename); 460 return; 461 } 462 if (!safe && access(filename, R_OK) < 0) 463 { 464 syserr("fileclass: access denied on %s", filename); 465 return; 466 } 467 f = fopen(filename, "r"); 468 if (f == NULL) 469 { 470 syserr("fileclass: cannot open %s", filename); 471 return; 472 } 473 474 while (fgets(buf, sizeof buf, f) != NULL) 475 { 476 register STAB *s; 477 register char *p; 478 # ifdef SCANF 479 char wordbuf[MAXNAME+1]; 480 481 if (sscanf(buf, fmt, wordbuf) != 1) 482 continue; 483 p = wordbuf; 484 # else /* SCANF */ 485 p = buf; 486 # endif /* SCANF */ 487 488 /* 489 ** Break up the match into words. 490 */ 491 492 while (*p != '\0') 493 { 494 register char *q; 495 496 /* strip leading spaces */ 497 while (isspace(*p)) 498 p++; 499 if (*p == '\0') 500 break; 501 502 /* find the end of the word */ 503 q = p; 504 while (*p != '\0' && !isspace(*p)) 505 p++; 506 if (*p != '\0') 507 *p++ = '\0'; 508 509 /* enter the word in the symbol table */ 510 s = stab(q, ST_CLASS, ST_ENTER); 511 setbitn(class, s->s_class); 512 } 513 } 514 515 (void) fclose(f); 516 } 517 /* 518 ** MAKEMAILER -- define a new mailer. 519 ** 520 ** Parameters: 521 ** line -- description of mailer. This is in labeled 522 ** fields. The fields are: 523 ** P -- the path to the mailer 524 ** F -- the flags associated with the mailer 525 ** A -- the argv for this mailer 526 ** S -- the sender rewriting set 527 ** R -- the recipient rewriting set 528 ** E -- the eol string 529 ** The first word is the canonical name of the mailer. 530 ** 531 ** Returns: 532 ** none. 533 ** 534 ** Side Effects: 535 ** enters the mailer into the mailer table. 536 */ 537 538 makemailer(line) 539 char *line; 540 { 541 register char *p; 542 register struct mailer *m; 543 register STAB *s; 544 int i; 545 char fcode; 546 extern int NextMailer; 547 extern char **makeargv(); 548 extern char *munchstring(); 549 extern char *DelimChar; 550 extern long atol(); 551 552 /* allocate a mailer and set up defaults */ 553 m = (struct mailer *) xalloc(sizeof *m); 554 bzero((char *) m, sizeof *m); 555 m->m_eol = "\n"; 556 557 /* collect the mailer name */ 558 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 559 continue; 560 if (*p != '\0') 561 *p++ = '\0'; 562 m->m_name = newstr(line); 563 564 /* now scan through and assign info from the fields */ 565 while (*p != '\0') 566 { 567 while (*p != '\0' && (*p == ',' || isspace(*p))) 568 p++; 569 570 /* p now points to field code */ 571 fcode = *p; 572 while (*p != '\0' && *p != '=' && *p != ',') 573 p++; 574 if (*p++ != '=') 575 { 576 syserr("mailer %s: `=' expected", m->m_name); 577 return; 578 } 579 while (isspace(*p)) 580 p++; 581 582 /* p now points to the field body */ 583 p = munchstring(p); 584 585 /* install the field into the mailer struct */ 586 switch (fcode) 587 { 588 case 'P': /* pathname */ 589 m->m_mailer = newstr(p); 590 break; 591 592 case 'F': /* flags */ 593 for (; *p != '\0'; p++) 594 if (!isspace(*p)) 595 setbitn(*p, m->m_flags); 596 break; 597 598 case 'S': /* sender rewriting ruleset */ 599 case 'R': /* recipient rewriting ruleset */ 600 i = atoi(p); 601 if (i < 0 || i >= MAXRWSETS) 602 { 603 syserr("invalid rewrite set, %d max", MAXRWSETS); 604 return; 605 } 606 if (fcode == 'S') 607 m->m_s_rwset = i; 608 else 609 m->m_r_rwset = i; 610 break; 611 612 case 'E': /* end of line string */ 613 m->m_eol = newstr(p); 614 break; 615 616 case 'A': /* argument vector */ 617 m->m_argv = makeargv(p); 618 break; 619 620 case 'M': /* maximum message size */ 621 m->m_maxsize = atol(p); 622 break; 623 624 case 'L': /* maximum line length */ 625 m->m_linelimit = atoi(p); 626 break; 627 } 628 629 p = DelimChar; 630 } 631 632 /* do some heuristic cleanup for back compatibility */ 633 if (bitnset(M_LIMITS, m->m_flags)) 634 { 635 if (m->m_linelimit == 0) 636 m->m_linelimit = SMTPLINELIM; 637 if (ConfigLevel < 2) 638 setbitn(M_7BITS, m->m_flags); 639 } 640 641 if (NextMailer >= MAXMAILERS) 642 { 643 syserr("too many mailers defined (%d max)", MAXMAILERS); 644 return; 645 } 646 647 s = stab(m->m_name, ST_MAILER, ST_ENTER); 648 if (s->s_mailer != NULL) 649 { 650 i = s->s_mailer->m_mno; 651 free(s->s_mailer); 652 } 653 else 654 { 655 i = NextMailer++; 656 } 657 Mailer[i] = s->s_mailer = m; 658 m->m_mno = i; 659 } 660 /* 661 ** MUNCHSTRING -- translate a string into internal form. 662 ** 663 ** Parameters: 664 ** p -- the string to munch. 665 ** 666 ** Returns: 667 ** the munched string. 668 ** 669 ** Side Effects: 670 ** Sets "DelimChar" to point to the string that caused us 671 ** to stop. 672 */ 673 674 char * 675 munchstring(p) 676 register char *p; 677 { 678 register char *q; 679 bool backslash = FALSE; 680 bool quotemode = FALSE; 681 static char buf[MAXLINE]; 682 extern char *DelimChar; 683 684 for (q = buf; *p != '\0'; p++) 685 { 686 if (backslash) 687 { 688 /* everything is roughly literal */ 689 backslash = FALSE; 690 switch (*p) 691 { 692 case 'r': /* carriage return */ 693 *q++ = '\r'; 694 continue; 695 696 case 'n': /* newline */ 697 *q++ = '\n'; 698 continue; 699 700 case 'f': /* form feed */ 701 *q++ = '\f'; 702 continue; 703 704 case 'b': /* backspace */ 705 *q++ = '\b'; 706 continue; 707 } 708 *q++ = *p; 709 } 710 else 711 { 712 if (*p == '\\') 713 backslash = TRUE; 714 else if (*p == '"') 715 quotemode = !quotemode; 716 else if (quotemode || *p != ',') 717 *q++ = *p; 718 else 719 break; 720 } 721 } 722 723 DelimChar = p; 724 *q++ = '\0'; 725 return (buf); 726 } 727 /* 728 ** MAKEARGV -- break up a string into words 729 ** 730 ** Parameters: 731 ** p -- the string to break up. 732 ** 733 ** Returns: 734 ** a char **argv (dynamically allocated) 735 ** 736 ** Side Effects: 737 ** munges p. 738 */ 739 740 char ** 741 makeargv(p) 742 register char *p; 743 { 744 char *q; 745 int i; 746 char **avp; 747 char *argv[MAXPV + 1]; 748 749 /* take apart the words */ 750 i = 0; 751 while (*p != '\0' && i < MAXPV) 752 { 753 q = p; 754 while (*p != '\0' && !isspace(*p)) 755 p++; 756 while (isspace(*p)) 757 *p++ = '\0'; 758 argv[i++] = newstr(q); 759 } 760 argv[i++] = NULL; 761 762 /* now make a copy of the argv */ 763 avp = (char **) xalloc(sizeof *avp * i); 764 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 765 766 return (avp); 767 } 768 /* 769 ** PRINTRULES -- print rewrite rules (for debugging) 770 ** 771 ** Parameters: 772 ** none. 773 ** 774 ** Returns: 775 ** none. 776 ** 777 ** Side Effects: 778 ** prints rewrite rules. 779 */ 780 781 printrules() 782 { 783 register struct rewrite *rwp; 784 register int ruleset; 785 786 for (ruleset = 0; ruleset < 10; ruleset++) 787 { 788 if (RewriteRules[ruleset] == NULL) 789 continue; 790 printf("\n----Rule Set %d:", ruleset); 791 792 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 793 { 794 printf("\nLHS:"); 795 printav(rwp->r_lhs); 796 printf("RHS:"); 797 printav(rwp->r_rhs); 798 } 799 } 800 } 801 802 /* 803 ** SETOPTION -- set global processing option 804 ** 805 ** Parameters: 806 ** opt -- option name. 807 ** val -- option value (as a text string). 808 ** safe -- set if this came from a configuration file. 809 ** Some options (if set from the command line) will 810 ** reset the user id to avoid security problems. 811 ** sticky -- if set, don't let other setoptions override 812 ** this value. 813 ** 814 ** Returns: 815 ** none. 816 ** 817 ** Side Effects: 818 ** Sets options as implied by the arguments. 819 */ 820 821 static BITMAP StickyOpt; /* set if option is stuck */ 822 823 824 #ifdef NAMED_BIND 825 826 struct resolverflags 827 { 828 char *rf_name; /* name of the flag */ 829 long rf_bits; /* bits to set/clear */ 830 } ResolverFlags[] = 831 { 832 "debug", RES_DEBUG, 833 "aaonly", RES_AAONLY, 834 "usevc", RES_USEVC, 835 "primary", RES_PRIMARY, 836 "igntc", RES_IGNTC, 837 "recurse", RES_RECURSE, 838 "defnames", RES_DEFNAMES, 839 "stayopen", RES_STAYOPEN, 840 "dnsrch", RES_DNSRCH, 841 NULL, 0 842 }; 843 844 #endif 845 846 setoption(opt, val, safe, sticky) 847 char opt; 848 char *val; 849 bool safe; 850 bool sticky; 851 { 852 register char *p; 853 extern bool atobool(); 854 extern time_t convtime(); 855 extern int QueueLA; 856 extern int RefuseLA; 857 extern bool trusteduser(); 858 extern char *username(); 859 860 if (tTd(37, 1)) 861 printf("setoption %c=%s", opt, val); 862 863 /* 864 ** See if this option is preset for us. 865 */ 866 867 if (bitnset(opt, StickyOpt)) 868 { 869 if (tTd(37, 1)) 870 printf(" (ignored)\n"); 871 return; 872 } 873 874 /* 875 ** Check to see if this option can be specified by this user. 876 */ 877 878 if (!safe && getuid() == 0) 879 safe = TRUE; 880 if (!safe && strchr("deEiLmorsvC8", opt) == NULL) 881 { 882 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 883 { 884 if (tTd(37, 1)) 885 printf(" (unsafe)"); 886 if (getuid() != geteuid()) 887 { 888 if (tTd(37, 1)) 889 printf("(Resetting uid)"); 890 (void) setgid(getgid()); 891 (void) setuid(getuid()); 892 } 893 } 894 } 895 if (tTd(37, 1)) 896 printf("\n"); 897 898 switch (opt) 899 { 900 case '8': /* allow eight-bit input */ 901 EightBit = atobool(val); 902 break; 903 904 case 'A': /* set default alias file */ 905 if (val[0] == '\0') 906 AliasFile = "aliases"; 907 else 908 AliasFile = newstr(val); 909 break; 910 911 case 'a': /* look N minutes for "@:@" in alias file */ 912 if (val[0] == '\0') 913 SafeAlias = 5; 914 else 915 SafeAlias = atoi(val); 916 break; 917 918 case 'B': /* substitution for blank character */ 919 SpaceSub = val[0]; 920 if (SpaceSub == '\0') 921 SpaceSub = ' '; 922 break; 923 924 case 'c': /* don't connect to "expensive" mailers */ 925 NoConnect = atobool(val); 926 break; 927 928 case 'C': /* checkpoint every N addresses */ 929 CheckpointInterval = atoi(val); 930 break; 931 932 case 'd': /* delivery mode */ 933 switch (*val) 934 { 935 case '\0': 936 SendMode = SM_DELIVER; 937 break; 938 939 case SM_QUEUE: /* queue only */ 940 #ifndef QUEUE 941 syserr("need QUEUE to set -odqueue"); 942 #endif /* QUEUE */ 943 /* fall through..... */ 944 945 case SM_DELIVER: /* do everything */ 946 case SM_FORK: /* fork after verification */ 947 SendMode = *val; 948 break; 949 950 default: 951 syserr("Unknown delivery mode %c", *val); 952 exit(EX_USAGE); 953 } 954 break; 955 956 case 'D': /* rebuild alias database as needed */ 957 AutoRebuild = atobool(val); 958 break; 959 960 case 'E': /* error message header/header file */ 961 if (*val != '\0') 962 ErrMsgFile = newstr(val); 963 break; 964 965 case 'e': /* set error processing mode */ 966 switch (*val) 967 { 968 case EM_QUIET: /* be silent about it */ 969 case EM_MAIL: /* mail back */ 970 case EM_BERKNET: /* do berknet error processing */ 971 case EM_WRITE: /* write back (or mail) */ 972 HoldErrs = TRUE; 973 /* fall through... */ 974 975 case EM_PRINT: /* print errors normally (default) */ 976 ErrorMode = *val; 977 break; 978 } 979 break; 980 981 case 'F': /* file mode */ 982 FileMode = atooct(val) & 0777; 983 break; 984 985 case 'f': /* save Unix-style From lines on front */ 986 SaveFrom = atobool(val); 987 break; 988 989 case 'G': /* match recipients against GECOS field */ 990 MatchGecos = atobool(val); 991 break; 992 993 case 'g': /* default gid */ 994 DefGid = atoi(val); 995 break; 996 997 case 'H': /* help file */ 998 if (val[0] == '\0') 999 HelpFile = "sendmail.hf"; 1000 else 1001 HelpFile = newstr(val); 1002 break; 1003 1004 case 'h': /* maximum hop count */ 1005 MaxHopCount = atoi(val); 1006 break; 1007 1008 case 'I': /* use internet domain name server */ 1009 #ifdef NAMED_BIND 1010 UseNameServer = TRUE; 1011 for (p = val; *p != 0; ) 1012 { 1013 bool clearmode; 1014 char *q; 1015 struct resolverflags *rfp; 1016 1017 while (*p == ' ') 1018 p++; 1019 if (*p == '\0') 1020 break; 1021 clearmode = FALSE; 1022 if (*p == '-') 1023 clearmode = TRUE; 1024 else if (*p != '+') 1025 p--; 1026 p++; 1027 q = p; 1028 while (*p != '\0' && !isspace(*p)) 1029 p++; 1030 if (*p != '\0') 1031 *p++ = '\0'; 1032 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1033 { 1034 if (strcasecmp(q, rfp->rf_name) == 0) 1035 break; 1036 } 1037 if (clearmode) 1038 _res.options &= ~rfp->rf_bits; 1039 else 1040 _res.options |= rfp->rf_bits; 1041 } 1042 if (tTd(8, 2)) 1043 printf("_res.options = %x\n", _res.options); 1044 #else 1045 usrerr("name server (I option) specified but BIND not compiled in"); 1046 #endif 1047 break; 1048 1049 case 'i': /* ignore dot lines in message */ 1050 IgnrDot = atobool(val); 1051 break; 1052 1053 case 'J': /* .forward search path */ 1054 ForwardPath = newstr(val); 1055 break; 1056 1057 case 'k': /* connection cache size */ 1058 MaxMciCache = atoi(val); 1059 if (MaxMciCache < 0) 1060 MaxMciCache = 0; 1061 break; 1062 1063 case 'K': /* connection cache timeout */ 1064 MciCacheTimeout = convtime(val); 1065 break; 1066 1067 case 'L': /* log level */ 1068 LogLevel = atoi(val); 1069 break; 1070 1071 case 'M': /* define macro */ 1072 define(val[0], newstr(&val[1]), CurEnv); 1073 sticky = FALSE; 1074 break; 1075 1076 case 'm': /* send to me too */ 1077 MeToo = atobool(val); 1078 break; 1079 1080 case 'n': /* validate RHS in newaliases */ 1081 CheckAliases = atobool(val); 1082 break; 1083 1084 case 'o': /* assume old style headers */ 1085 if (atobool(val)) 1086 CurEnv->e_flags |= EF_OLDSTYLE; 1087 else 1088 CurEnv->e_flags &= ~EF_OLDSTYLE; 1089 break; 1090 1091 case 'P': /* postmaster copy address for returned mail */ 1092 PostMasterCopy = newstr(val); 1093 break; 1094 1095 case 'q': /* slope of queue only function */ 1096 QueueFactor = atoi(val); 1097 break; 1098 1099 case 'Q': /* queue directory */ 1100 if (val[0] == '\0') 1101 QueueDir = "mqueue"; 1102 else 1103 QueueDir = newstr(val); 1104 break; 1105 1106 case 'r': /* read timeout */ 1107 ReadTimeout = convtime(val); 1108 break; 1109 1110 case 'S': /* status file */ 1111 if (val[0] == '\0') 1112 StatFile = "sendmail.st"; 1113 else 1114 StatFile = newstr(val); 1115 break; 1116 1117 case 's': /* be super safe, even if expensive */ 1118 SuperSafe = atobool(val); 1119 break; 1120 1121 case 'T': /* queue timeout */ 1122 TimeOut = convtime(val); 1123 break; 1124 1125 case 't': /* time zone name */ 1126 TimeZoneSpec = newstr(val); 1127 break; 1128 1129 case 'U': /* location of user database */ 1130 UdbSpec = newstr(val); 1131 break; 1132 1133 case 'u': /* set default uid */ 1134 DefUid = atoi(val); 1135 setdefuser(); 1136 break; 1137 1138 case 'v': /* run in verbose mode */ 1139 Verbose = atobool(val); 1140 break; 1141 1142 case 'w': /* we don't have wildcard MX records */ 1143 NoWildcardMX = atobool(val); 1144 break; 1145 1146 case 'x': /* load avg at which to auto-queue msgs */ 1147 QueueLA = atoi(val); 1148 break; 1149 1150 case 'X': /* load avg at which to auto-reject connections */ 1151 RefuseLA = atoi(val); 1152 break; 1153 1154 case 'y': /* work recipient factor */ 1155 WkRecipFact = atoi(val); 1156 break; 1157 1158 case 'Y': /* fork jobs during queue runs */ 1159 ForkQueueRuns = atobool(val); 1160 break; 1161 1162 case 'z': /* work message class factor */ 1163 WkClassFact = atoi(val); 1164 break; 1165 1166 case 'Z': /* work time factor */ 1167 WkTimeFact = atoi(val); 1168 break; 1169 1170 default: 1171 break; 1172 } 1173 if (sticky) 1174 setbitn(opt, StickyOpt); 1175 return; 1176 } 1177 /* 1178 ** SETCLASS -- set a word into a class 1179 ** 1180 ** Parameters: 1181 ** class -- the class to put the word in. 1182 ** word -- the word to enter 1183 ** 1184 ** Returns: 1185 ** none. 1186 ** 1187 ** Side Effects: 1188 ** puts the word into the symbol table. 1189 */ 1190 1191 setclass(class, word) 1192 int class; 1193 char *word; 1194 { 1195 register STAB *s; 1196 1197 s = stab(word, ST_CLASS, ST_ENTER); 1198 setbitn(class, s->s_class); 1199 } 1200 /* 1201 ** MAKEMAPENTRY -- create a map entry 1202 ** 1203 ** Parameters: 1204 ** line -- the config file line 1205 ** 1206 ** Returns: 1207 ** TRUE if it successfully entered the map entry. 1208 ** FALSE otherwise (usually syntax error). 1209 ** 1210 ** Side Effects: 1211 ** Enters the map into the dictionary. 1212 */ 1213 1214 void 1215 makemapentry(line) 1216 char *line; 1217 { 1218 register char *p; 1219 char *mapname; 1220 char *classname; 1221 register STAB *map; 1222 STAB *class; 1223 1224 for (p = line; isspace(*p); p++) 1225 continue; 1226 if (!isalnum(*p)) 1227 { 1228 syserr("readcf: config K line: no map name"); 1229 return; 1230 } 1231 1232 mapname = p; 1233 while (isalnum(*++p)) 1234 continue; 1235 if (*p != '\0') 1236 *p++ = '\0'; 1237 while (isspace(*p)) 1238 p++; 1239 if (!isalnum(*p)) 1240 { 1241 syserr("readcf: config K line, map %s: no map class", mapname); 1242 return; 1243 } 1244 classname = p; 1245 while (isalnum(*++p)) 1246 continue; 1247 if (*p != '\0') 1248 *p++ = '\0'; 1249 while (isspace(*p)) 1250 p++; 1251 1252 /* look up the class */ 1253 class = stab(classname, ST_MAPCLASS, ST_FIND); 1254 if (class == NULL) 1255 { 1256 syserr("readcf: map %s: class %s not available", mapname, classname); 1257 return; 1258 } 1259 1260 /* enter the map */ 1261 map = stab(mapname, ST_MAP, ST_ENTER); 1262 map->s_map.map_class = &class->s_mapclass; 1263 1264 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1265 map->s_map.map_flags |= MF_VALID; 1266 } 1267