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