xref: /freebsd/contrib/sendmail/src/readcf.c (revision b4e38a41f584ad4391c04b8cfec81f46176b18b0)
1 /*
2  * Copyright (c) 1998-2006, 2008-2010, 2013 Proofpoint, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #include <sendmail.h>
15 #include <sm/sendmail.h>
16 #if STARTTLS
17 # include <tls.h>
18 #endif
19 #if DNSSEC_TEST
20 # include <sm_resolve.h>
21 #endif
22 
23 SM_RCSID("@(#)$Id: readcf.c,v 8.692 2013-11-22 20:51:56 ca Exp $")
24 
25 #if NETINET || NETINET6
26 # include <arpa/inet.h>
27 #endif
28 
29 
30 #define SECONDS
31 #define MINUTES	* 60
32 #define HOUR	* 3600
33 #define HOURS	HOUR
34 
35 static void	fileclass __P((int, char *, char *, bool, bool, bool));
36 static char	**makeargv __P((char *));
37 static void	settimeout __P((char *, char *, bool));
38 static void	toomany __P((int, int));
39 static char	*extrquotstr __P((char *, char **, char *, bool *));
40 static void	parse_class_words __P((int, char *));
41 
42 
43 #if _FFR_BOUNCE_QUEUE
44 static char *bouncequeue = NULL;
45 static void initbouncequeue __P((void));
46 
47 /*
48 **  INITBOUNCEQUEUE -- determine BounceQueue if option is set.
49 **
50 **	Parameters:
51 **		none.
52 **
53 **	Returns:
54 **		none.
55 **
56 **	Side Effects:
57 **		sets BounceQueue
58 */
59 
60 static void
61 initbouncequeue()
62 {
63 	STAB *s;
64 
65 	BounceQueue = NOQGRP;
66 	if (bouncequeue == NULL || bouncequeue[0] == '\0')
67 		return;
68 
69 	s = stab(bouncequeue, ST_QUEUE, ST_FIND);
70 	if (s == NULL)
71 	{
72 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73 			"Warning: option BounceQueue: unknown queue group %s\n",
74 			bouncequeue);
75 	}
76 	else
77 		BounceQueue = s->s_quegrp->qg_index;
78 }
79 #endif /* _FFR_BOUNCE_QUEUE */
80 
81 #if _FFR_RCPTFLAGS
82 void setupdynmailers __P((void));
83 #else
84 #define setupdynmailers()
85 #endif
86 
87 /*
88 **  READCF -- read configuration file.
89 **
90 **	This routine reads the configuration file and builds the internal
91 **	form.
92 **
93 **	The file is formatted as a sequence of lines, each taken
94 **	atomically.  The first character of each line describes how
95 **	the line is to be interpreted.  The lines are:
96 **		Dxval		Define macro x to have value val.
97 **		Cxword		Put word into class x.
98 **		Fxfile [fmt]	Read file for lines to put into
99 **				class x.  Use scanf string 'fmt'
100 **				or "%s" if not present.  Fmt should
101 **				only produce one string-valued result.
102 **		Hname: value	Define header with field-name 'name'
103 **				and value as specified; this will be
104 **				macro expanded immediately before
105 **				use.
106 **		Sn		Use rewriting set n.
107 **		Rlhs rhs	Rewrite addresses that match lhs to
108 **				be rhs.
109 **		Mn arg=val...	Define mailer.  n is the internal name.
110 **				Args specify mailer parameters.
111 **		Oxvalue		Set option x to value.
112 **		O option value	Set option (long name) to value.
113 **		Pname=value	Set precedence name to value.
114 **		Qn arg=val...	Define queue groups.  n is the internal name.
115 **				Args specify queue parameters.
116 **		Vversioncode[/vendorcode]
117 **				Version level/vendor name of
118 **				configuration syntax.
119 **		Kmapname mapclass arguments....
120 **				Define keyed lookup of a given class.
121 **				Arguments are class dependent.
122 **		Eenvar=value	Set the environment value to the given value.
123 **
124 **	Parameters:
125 **		cfname -- configuration file name.
126 **		safe -- true if this is the system config file;
127 **			false otherwise.
128 **		e -- the main envelope.
129 **
130 **	Returns:
131 **		none.
132 **
133 **	Side Effects:
134 **		Builds several internal tables.
135 */
136 
137 void
138 readcf(cfname, safe, e)
139 	char *cfname;
140 	bool safe;
141 	register ENVELOPE *e;
142 {
143 	SM_FILE_T *cf;
144 	int ruleset = -1;
145 	char *q;
146 	struct rewrite *rwp = NULL;
147 	char *bp;
148 	auto char *ep;
149 	int nfuzzy;
150 	char *file;
151 	bool optional;
152 	bool ok;
153 	bool ismap;
154 	int mid;
155 	register char *p;
156 	long sff = SFF_OPENASROOT;
157 	struct stat statb;
158 	char buf[MAXLINE];
159 	int bufsize;
160 	char exbuf[MAXLINE];
161 	char pvpbuf[MAXLINE + MAXATOM];
162 	static char *null_list[1] = { NULL };
163 	extern unsigned char TokTypeNoC[];
164 
165 	FileName = cfname;
166 	LineNumber = 0;
167 
168 	if (DontLockReadFiles)
169 		sff |= SFF_NOLOCK;
170 	cf = safefopen(cfname, O_RDONLY, 0444, sff);
171 	if (cf == NULL)
172 	{
173 		syserr("cannot open");
174 		finis(false, true, EX_OSFILE);
175 	}
176 
177 	if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
178 	{
179 		syserr("cannot fstat");
180 		finis(false, true, EX_OSFILE);
181 	}
182 
183 	if (!S_ISREG(statb.st_mode))
184 	{
185 		syserr("not a plain file");
186 		finis(false, true, EX_OSFILE);
187 	}
188 
189 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
190 	{
191 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS || OpMode == MD_CHECKCONFIG)
192 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
193 					     "%s: WARNING: dangerous write permissions\n",
194 					     FileName);
195 		if (LogLevel > 0)
196 			sm_syslog(LOG_CRIT, NOQID,
197 				  "%s: WARNING: dangerous write permissions",
198 				  FileName);
199 	}
200 
201 #if XLA
202 	xla_zero();
203 #endif
204 
205 	while (bufsize = sizeof(buf),
206 	       (bp = fgetfolded(buf, &bufsize, cf)) != NULL)
207 	{
208 		char *nbp;
209 
210 		if (bp[0] == '#')
211 		{
212 			if (bp != buf)
213 				sm_free(bp); /* XXX */
214 			continue;
215 		}
216 
217 		/* do macro expansion mappings */
218 		nbp = translate_dollars(bp, bp, &bufsize);
219 		if (nbp != bp && bp != buf)
220 			sm_free(bp);
221 		bp = nbp;
222 
223 		/* interpret this line */
224 		errno = 0;
225 		switch (bp[0])
226 		{
227 		  case '\0':
228 		  case '#':		/* comment */
229 			break;
230 
231 		  case 'R':		/* rewriting rule */
232 			if (ruleset < 0)
233 			{
234 				syserr("missing valid ruleset for \"%s\"", bp);
235 				break;
236 			}
237 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
238 				continue;
239 
240 			if (*p == '\0')
241 			{
242 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
243 				break;
244 			}
245 
246 			/* allocate space for the rule header */
247 			if (rwp == NULL)
248 			{
249 				RewriteRules[ruleset] = rwp =
250 					(struct rewrite *) xalloc(sizeof(*rwp));
251 			}
252 			else
253 			{
254 				rwp->r_next = (struct rewrite *) xalloc(sizeof(*rwp));
255 				rwp = rwp->r_next;
256 			}
257 			rwp->r_next = NULL;
258 
259 			/* expand and save the LHS */
260 			*p = '\0';
261 			expand(&bp[1], exbuf, sizeof(exbuf), e);
262 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
263 					     sizeof(pvpbuf), NULL,
264 					     ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
265 					     true);
266 			nfuzzy = 0;
267 			if (rwp->r_lhs != NULL)
268 			{
269 				register char **ap;
270 
271 				rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
272 
273 				/* count the number of fuzzy matches in LHS */
274 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
275 				{
276 					char *botch;
277 
278 					botch = NULL;
279 					switch (ap[0][0] & 0377)
280 					{
281 					  case MATCHZANY:
282 					  case MATCHANY:
283 					  case MATCHONE:
284 					  case MATCHCLASS:
285 					  case MATCHNCLASS:
286 						nfuzzy++;
287 						break;
288 
289 					  case MATCHREPL:
290 						botch = "$1-$9";
291 						break;
292 
293 					  case CANONUSER:
294 						botch = "$:";
295 						break;
296 
297 					  case CALLSUBR:
298 						botch = "$>";
299 						break;
300 
301 					  case CONDIF:
302 						botch = "$?";
303 						break;
304 
305 					  case CONDFI:
306 						botch = "$.";
307 						break;
308 
309 					  case HOSTBEGIN:
310 						botch = "$[";
311 						break;
312 
313 					  case HOSTEND:
314 						botch = "$]";
315 						break;
316 
317 					  case LOOKUPBEGIN:
318 						botch = "$(";
319 						break;
320 
321 					  case LOOKUPEND:
322 						botch = "$)";
323 						break;
324 					}
325 					if (botch != NULL)
326 						syserr("Inappropriate use of %s on LHS",
327 							botch);
328 				}
329 				rwp->r_line = LineNumber;
330 			}
331 			else
332 			{
333 				syserr("R line: null LHS");
334 				rwp->r_lhs = null_list;
335 			}
336 			if (nfuzzy > MAXMATCH)
337 			{
338 				syserr("R line: too many wildcards");
339 				rwp->r_lhs = null_list;
340 			}
341 
342 			/* expand and save the RHS */
343 			while (*++p == '\t')
344 				continue;
345 			q = p;
346 			while (*p != '\0' && *p != '\t')
347 				p++;
348 			*p = '\0';
349 			expand(q, exbuf, sizeof(exbuf), e);
350 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
351 					     sizeof(pvpbuf), NULL,
352 					     ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
353 					     true);
354 			if (rwp->r_rhs != NULL)
355 			{
356 				register char **ap;
357 				int args, endtoken;
358 #if _FFR_EXTRA_MAP_CHECK
359 				int nexttoken;
360 #endif
361 				bool inmap;
362 
363 				rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
364 
365 				/* check no out-of-bounds replacements */
366 				nfuzzy += '0';
367 				inmap = false;
368 				args = 0;
369 				endtoken = 0;
370 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
371 				{
372 					char *botch;
373 
374 					botch = NULL;
375 					switch (ap[0][0] & 0377)
376 					{
377 					  case MATCHREPL:
378 						if (ap[0][1] <= '0' ||
379 						    ap[0][1] > nfuzzy)
380 						{
381 							syserr("replacement $%c out of bounds",
382 								ap[0][1]);
383 						}
384 						break;
385 
386 					  case MATCHZANY:
387 						botch = "$*";
388 						break;
389 
390 					  case MATCHANY:
391 						botch = "$+";
392 						break;
393 
394 					  case MATCHONE:
395 						botch = "$-";
396 						break;
397 
398 					  case MATCHCLASS:
399 						botch = "$=";
400 						break;
401 
402 					  case MATCHNCLASS:
403 						botch = "$~";
404 						break;
405 
406 					  case CANONHOST:
407 						if (!inmap)
408 							break;
409 						if (++args >= MAX_MAP_ARGS)
410 							syserr("too many arguments for map lookup");
411 						break;
412 
413 					  case HOSTBEGIN:
414 						endtoken = HOSTEND;
415 						/* FALLTHROUGH */
416 					  case LOOKUPBEGIN:
417 						/* see above... */
418 						if ((ap[0][0] & 0377) == LOOKUPBEGIN)
419 							endtoken = LOOKUPEND;
420 						if (inmap)
421 							syserr("cannot nest map lookups");
422 						inmap = true;
423 						args = 0;
424 #if _FFR_EXTRA_MAP_CHECK
425 						if (ap[1] == NULL)
426 						{
427 							syserr("syntax error in map lookup");
428 							break;
429 						}
430 						nexttoken = ap[1][0] & 0377;
431 						if (nexttoken == CANONHOST ||
432 						    nexttoken == CANONUSER ||
433 						    nexttoken == endtoken)
434 						{
435 							syserr("missing map name for lookup");
436 							break;
437 						}
438 						if (ap[2] == NULL)
439 						{
440 							syserr("syntax error in map lookup");
441 							break;
442 						}
443 						if ((unsigned char) ap[0][0] == HOSTBEGIN)
444 							break;
445 						nexttoken = ap[2][0] & 0377;
446 						if (nexttoken == CANONHOST ||
447 						    nexttoken == CANONUSER ||
448 						    nexttoken == endtoken)
449 						{
450 							syserr("missing key name for lookup");
451 							break;
452 						}
453 #endif /* _FFR_EXTRA_MAP_CHECK */
454 						break;
455 
456 					  case HOSTEND:
457 					  case LOOKUPEND:
458 						if ((ap[0][0] & 0377) != endtoken)
459 							break;
460 						inmap = false;
461 						endtoken = 0;
462 						break;
463 
464 
465 #if 0
466 /*
467 **  This doesn't work yet as there are maps defined *after* the cf
468 **  is read such as host, user, and alias.  So for now, it's removed.
469 **  When it comes back, the RELEASE_NOTES entry will be:
470 **	Emit warnings for unknown maps when reading the .cf file.  Based on
471 **		patch from Robert Harker of Harker Systems.
472 */
473 
474 					  case LOOKUPBEGIN:
475 						/*
476 						**  Got a database lookup,
477 						**  check if map is defined.
478 						*/
479 
480 						ep = ap[1];
481 						if ((ep[0] & 0377) != MACRODEXPAND &&
482 						    stab(ep, ST_MAP, ST_FIND) == NULL)
483 						{
484 							(void) sm_io_fprintf(smioout,
485 									     SM_TIME_DEFAULT,
486 									     "Warning: %s: line %d: map %s not found\n",
487 									     FileName,
488 									     LineNumber,
489 									     ep);
490 						}
491 						break;
492 #endif /* 0 */
493 					}
494 					if (botch != NULL)
495 						syserr("Inappropriate use of %s on RHS",
496 							botch);
497 				}
498 				if (inmap)
499 					syserr("missing map closing token");
500 			}
501 			else
502 			{
503 				syserr("R line: null RHS");
504 				rwp->r_rhs = null_list;
505 			}
506 			break;
507 
508 		  case 'S':		/* select rewriting set */
509 			expand(&bp[1], exbuf, sizeof(exbuf), e);
510 			ruleset = strtorwset(exbuf, NULL, ST_ENTER);
511 			if (ruleset < 0)
512 				break;
513 
514 			rwp = RewriteRules[ruleset];
515 			if (rwp != NULL)
516 			{
517 				if (OpMode == MD_TEST || OpMode == MD_CHECKCONFIG)
518 					(void) sm_io_fprintf(smioout,
519 							     SM_TIME_DEFAULT,
520 							     "WARNING: Ruleset %s has multiple definitions\n",
521 							    &bp[1]);
522 				if (tTd(37, 1))
523 					sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
524 						   &bp[1]);
525 				while (rwp->r_next != NULL)
526 					rwp = rwp->r_next;
527 			}
528 			break;
529 
530 		  case 'D':		/* macro definition */
531 			mid = macid_parse(&bp[1], &ep);
532 			if (mid == 0)
533 				break;
534 			p = munchstring(ep, NULL, '\0');
535 			macdefine(&e->e_macro, A_TEMP, mid, p);
536 			break;
537 
538 		  case 'H':		/* required header line */
539 			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
540 			break;
541 
542 		  case 'C':		/* word class */
543 		  case 'T':		/* trusted user (set class `t') */
544 			if (bp[0] == 'C')
545 			{
546 				mid = macid_parse(&bp[1], &ep);
547 				if (mid == 0)
548 					break;
549 				expand(ep, exbuf, sizeof(exbuf), e);
550 				p = exbuf;
551 			}
552 			else
553 			{
554 				mid = 't';
555 				p = &bp[1];
556 			}
557 			while (*p != '\0')
558 			{
559 				register char *wd;
560 				char delim;
561 
562 				while (*p != '\0' && SM_ISSPACE(*p))
563 					p++;
564 				wd = p;
565 				while (*p != '\0' && !(SM_ISSPACE(*p)))
566 					p++;
567 				delim = *p;
568 				*p = '\0';
569 				if (wd[0] != '\0')
570 					setclass(mid, wd);
571 				*p = delim;
572 			}
573 			break;
574 
575 		  case 'F':		/* word class from file */
576 			mid = macid_parse(&bp[1], &ep);
577 			if (mid == 0)
578 				break;
579 			for (p = ep; SM_ISSPACE(*p); )
580 				p++;
581 			if (p[0] == '-' && p[1] == 'o')
582 			{
583 				optional = true;
584 				while (*p != '\0' &&
585 				       !(SM_ISSPACE(*p)))
586 					p++;
587 				while (SM_ISSPACE(*p))
588 					p++;
589 			}
590 			else
591 				optional = false;
592 
593 			/* check if [key]@map:spec */
594 			ismap = false;
595 			if (!SM_IS_DIR_DELIM(*p) &&
596 			    *p != '|' &&
597 			    (q = strchr(p, '@')) != NULL)
598 			{
599 				q++;
600 
601 				/* look for @LDAP or @map: in string */
602 				if (strcmp(q, "LDAP") == 0 ||
603 				    (*q != ':' &&
604 				     strchr(q, ':') != NULL))
605 					ismap = true;
606 			}
607 
608 			if (ismap)
609 			{
610 				/* use entire spec */
611 				file = p;
612 			}
613 			else
614 			{
615 				file = extrquotstr(p, &q, " ", &ok);
616 				if (!ok)
617 				{
618 					syserr("illegal filename '%s'", p);
619 					break;
620 				}
621 			}
622 
623 			if (*file == '|' || ismap)
624 				p = "%s";
625 			else
626 			{
627 				p = q;
628 				if (*p == '\0')
629 					p = "%s";
630 				else
631 				{
632 					*p = '\0';
633 					while (isascii(*++p) && isspace(*p))
634 						continue;
635 				}
636 			}
637 			fileclass(mid, file, p, ismap, safe, optional);
638 			break;
639 
640 #if XLA
641 		  case 'L':		/* extended load average description */
642 			xla_init(&bp[1]);
643 			break;
644 #endif
645 
646 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
647 		  case 'L':		/* lookup macro */
648 		  case 'G':		/* lookup class */
649 			/* reserved for Sun -- NIS+ database lookup */
650 			if (VendorCode != VENDOR_SUN)
651 				goto badline;
652 			sun_lg_config_line(bp, e);
653 			break;
654 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
655 
656 		  case 'M':		/* define mailer */
657 			makemailer(&bp[1]);
658 			break;
659 
660 		  case 'O':		/* set option */
661 			setoption(bp[1], &bp[2], safe, false, e);
662 			break;
663 
664 		  case 'P':		/* set precedence */
665 			if (NumPriorities >= MAXPRIORITIES)
666 			{
667 				toomany('P', MAXPRIORITIES);
668 				break;
669 			}
670 			for (p = &bp[1]; *p != '\0' && *p != '='; p++)
671 				continue;
672 			if (*p == '\0')
673 				goto badline;
674 			*p = '\0';
675 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
676 			Priorities[NumPriorities].pri_val = atoi(++p);
677 			NumPriorities++;
678 			break;
679 
680 		  case 'Q':		/* define queue */
681 			makequeue(&bp[1], true);
682 			break;
683 
684 		  case 'V':		/* configuration syntax version */
685 			for (p = &bp[1]; SM_ISSPACE(*p); p++)
686 				continue;
687 			if (!isascii(*p) || !isdigit(*p))
688 			{
689 				syserr("invalid argument to V line: \"%.20s\"",
690 					&bp[1]);
691 				break;
692 			}
693 			ConfigLevel = strtol(p, &ep, 10);
694 
695 			/*
696 			**  Do heuristic tweaking for back compatibility.
697 			*/
698 
699 			if (ConfigLevel >= 5)
700 			{
701 				/* level 5 configs have short name in $w */
702 				p = macvalue('w', e);
703 				if (p != NULL && (p = strchr(p, '.')) != NULL)
704 				{
705 					*p = '\0';
706 					macdefine(&e->e_macro, A_TEMP, 'w',
707 						  macvalue('w', e));
708 				}
709 			}
710 			if (ConfigLevel >= 6)
711 			{
712 				ColonOkInAddr = false;
713 			}
714 
715 			/*
716 			**  Look for vendor code.
717 			*/
718 
719 			if (*ep++ == '/')
720 			{
721 				/* extract vendor code */
722 				for (p = ep; isascii(*p) && isalpha(*p); )
723 					p++;
724 				*p = '\0';
725 
726 				if (!setvendor(ep))
727 					syserr("invalid V line vendor code: \"%s\"",
728 						ep);
729 			}
730 			break;
731 
732 		  case 'K':
733 			expand(&bp[1], exbuf, sizeof(exbuf), e);
734 			(void) makemapentry(exbuf);
735 			break;
736 
737 		  case 'E':
738 			p = strchr(bp, '=');
739 			if (p != NULL)
740 				*p++ = '\0';
741 			sm_setuserenv(&bp[1], p);
742 			break;
743 
744 		  case 'X':		/* mail filter */
745 #if MILTER
746 			milter_setup(&bp[1]);
747 #else /* MILTER */
748 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
749 					     "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
750 #endif /* MILTER */
751 			break;
752 
753 		  default:
754 		  badline:
755 			syserr("unknown configuration line \"%s\"", bp);
756 		}
757 		if (bp != buf)
758 			sm_free(bp); /* XXX */
759 	}
760 	if (sm_io_error(cf))
761 	{
762 		syserr("I/O read error");
763 		finis(false, true, EX_OSFILE);
764 	}
765 	(void) sm_io_close(cf, SM_TIME_DEFAULT);
766 	FileName = NULL;
767 
768 #if _FFR_BOUNCE_QUEUE
769 	initbouncequeue();
770 #endif
771 
772 	/* initialize host maps from local service tables */
773 	inithostmaps();
774 
775 	/* initialize daemon (if not defined yet) */
776 	initdaemon();
777 
778 	/* determine if we need to do special name-server frotz */
779 	{
780 		int nmaps;
781 		char *maptype[MAXMAPSTACK];
782 		short mapreturn[MAXMAPACTIONS];
783 
784 		nmaps = switch_map_find("hosts", maptype, mapreturn);
785 		UseNameServer = false;
786 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
787 		{
788 			register int mapno;
789 
790 			for (mapno = 0; mapno < nmaps && !UseNameServer;
791 			     mapno++)
792 			{
793 				if (strcmp(maptype[mapno], "dns") == 0)
794 					UseNameServer = true;
795 			}
796 		}
797 	}
798 	setupdynmailers();
799 }
800 
801 /*
802 **  TRANSLATE_DOLLARS -- convert $x into internal form
803 **
804 **	Actually does all appropriate pre-processing of a config line
805 **	to turn it into internal form.
806 **
807 **	Parameters:
808 **		ibp -- the buffer to translate.
809 **		obp -- where to put the translation; may be the same as obp
810 **		bsp -- a pointer to the size of obp; will be updated if
811 **			the buffer needs to be replaced.
812 **
813 **	Returns:
814 **		The buffer pointer; may differ from obp if the expansion
815 **		is larger then *bsp, in which case this will point to
816 **		malloc()ed memory which must be free()d by the caller.
817 */
818 
819 char *
820 translate_dollars(ibp, obp, bsp)
821 	char *ibp;
822 	char *obp;
823 	int *bsp;
824 {
825 	register char *p;
826 	auto char *ep;
827 	char *bp;
828 
829 	if (tTd(37, 53))
830 	{
831 		sm_dprintf("translate_dollars(");
832 		xputs(sm_debug_file(), ibp);
833 		sm_dprintf(")\n");
834 	}
835 
836 	bp = quote_internal_chars(ibp, obp, bsp);
837 
838 	for (p = bp; *p != '\0'; p++)
839 	{
840 		if (*p == '#' && p > bp && ConfigLevel >= 3)
841 		{
842 			register char *e;
843 
844 			switch (*--p & 0377)
845 			{
846 			  case MACROEXPAND:
847 				/* it's from $# -- let it go through */
848 				p++;
849 				break;
850 
851 			  case '\\':
852 				/* it's backslash escaped */
853 				(void) sm_strlcpy(p, p + 1, strlen(p));
854 				break;
855 
856 			  default:
857 				/* delete leading white space */
858 				while (SM_ISSPACE(*p) &&
859 				       *p != '\n' && p > bp)
860 				{
861 					p--;
862 				}
863 				if ((e = strchr(++p, '\n')) != NULL)
864 					(void) sm_strlcpy(p, e, strlen(p));
865 				else
866 					*p-- = '\0';
867 				break;
868 			}
869 			continue;
870 		}
871 
872 		if (*p != '$' || p[1] == '\0')
873 			continue;
874 
875 		if (p[1] == '$')
876 		{
877 			/* actual dollar sign.... */
878 			(void) sm_strlcpy(p, p + 1, strlen(p));
879 			continue;
880 		}
881 
882 		/* convert to macro expansion character */
883 		*p++ = MACROEXPAND;
884 
885 		/* special handling for $=, $~, $&, and $? */
886 		if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
887 			p++;
888 
889 		/* convert macro name to code */
890 		*p = macid_parse(p, &ep);
891 		if (ep != p + 1)
892 			(void) sm_strlcpy(p + 1, ep, strlen(p + 1));
893 	}
894 
895 	/* strip trailing white space from the line */
896 	while (--p > bp && SM_ISSPACE(*p))
897 		*p = '\0';
898 
899 	if (tTd(37, 53))
900 	{
901 		sm_dprintf("  translate_dollars => ");
902 		xputs(sm_debug_file(), bp);
903 		sm_dprintf("\n");
904 	}
905 
906 	return bp;
907 }
908 /*
909 **  TOOMANY -- signal too many of some option
910 **
911 **	Parameters:
912 **		id -- the id of the error line
913 **		maxcnt -- the maximum possible values
914 **
915 **	Returns:
916 **		none.
917 **
918 **	Side Effects:
919 **		gives a syserr.
920 */
921 
922 static void
923 toomany(id, maxcnt)
924 	int id;
925 	int maxcnt;
926 {
927 	syserr("too many %c lines, %d max", id, maxcnt);
928 }
929 /*
930 **  FILECLASS -- read members of a class from a file
931 **
932 **	Parameters:
933 **		class -- class to define.
934 **		filename -- name of file to read.
935 **		fmt -- scanf string to use for match.
936 **		ismap -- if set, this is a map lookup.
937 **		safe -- if set, this is a safe read.
938 **		optional -- if set, it is not an error for the file to
939 **			not exist.
940 **
941 **	Returns:
942 **		none
943 **
944 **	Side Effects:
945 **		puts all lines in filename that match a scanf into
946 **			the named class.
947 */
948 
949 /*
950 **  Break up the match into words and add to class.
951 */
952 
953 static void
954 parse_class_words(class, line)
955 	int class;
956 	char *line;
957 {
958 	while (line != NULL && *line != '\0')
959 	{
960 		register char *q;
961 
962 		/* strip leading spaces */
963 		while (SM_ISSPACE(*line))
964 			line++;
965 		if (*line == '\0')
966 			break;
967 
968 		/* find the end of the word */
969 		q = line;
970 		while (*line != '\0' && !(SM_ISSPACE(*line)))
971 			line++;
972 		if (*line != '\0')
973 			*line++ = '\0';
974 
975 		/* enter the word in the symbol table */
976 		setclass(class, q);
977 	}
978 }
979 
980 static void
981 fileclass(class, filename, fmt, ismap, safe, optional)
982 	int class;
983 	char *filename;
984 	char *fmt;
985 	bool ismap;
986 	bool safe;
987 	bool optional;
988 {
989 	SM_FILE_T *f;
990 	long sff;
991 	pid_t pid;
992 	register char *p;
993 	char buf[MAXLINE];
994 
995 	if (tTd(37, 2))
996 		sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
997 
998 	if (*filename == '\0')
999 	{
1000 		syserr("fileclass: missing file name");
1001 		return;
1002 	}
1003 	else if (ismap)
1004 	{
1005 		int status = 0;
1006 		char *key;
1007 		char *mn;
1008 		char *cl, *spec;
1009 		STAB *mapclass;
1010 		MAP map;
1011 
1012 		mn = newstr(macname(class));
1013 
1014 		key = filename;
1015 
1016 		/* skip past key */
1017 		if ((p = strchr(filename, '@')) == NULL)
1018 		{
1019 			/* should not happen */
1020 			syserr("fileclass: bogus map specification");
1021 			sm_free(mn);
1022 			return;
1023 		}
1024 
1025 		/* skip past '@' */
1026 		*p++ = '\0';
1027 		cl = p;
1028 
1029 #if LDAPMAP
1030 		if (strcmp(cl, "LDAP") == 0)
1031 		{
1032 			int n;
1033 			char *lc;
1034 			char jbuf[MAXHOSTNAMELEN];
1035 			char lcbuf[MAXLINE];
1036 
1037 			/* Get $j */
1038 			expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
1039 			if (jbuf[0] == '\0')
1040 			{
1041 				(void) sm_strlcpy(jbuf, "localhost",
1042 						  sizeof(jbuf));
1043 			}
1044 
1045 			/* impose the default schema */
1046 			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
1047 			if (lc == NULL)
1048 				lc = "";
1049 			else
1050 			{
1051 				expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
1052 				lc = lcbuf;
1053 			}
1054 
1055 			cl = "ldap";
1056 			n = sm_snprintf(buf, sizeof(buf),
1057 					"-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue,sendmailMTAClassSearch:FILTER:sendmailMTAClass,sendmailMTAClassURL:URL:sendmailMTAClass",
1058 					mn, lc, jbuf);
1059 			if (n >= sizeof(buf))
1060 			{
1061 				syserr("fileclass: F{%s}: Default LDAP string too long",
1062 				       mn);
1063 				sm_free(mn);
1064 				return;
1065 			}
1066 			spec = buf;
1067 		}
1068 		else
1069 #endif /* LDAPMAP */
1070 		{
1071 			if ((spec = strchr(cl, ':')) == NULL)
1072 			{
1073 				syserr("fileclass: F{%s}: missing map class",
1074 				       mn);
1075 				sm_free(mn);
1076 				return;
1077 			}
1078 			*spec++ ='\0';
1079 		}
1080 
1081 		/* set up map structure */
1082 		mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
1083 		if (mapclass == NULL)
1084 		{
1085 			syserr("fileclass: F{%s}: class %s not available",
1086 			       mn, cl);
1087 			sm_free(mn);
1088 			return;
1089 		}
1090 		memset(&map, '\0', sizeof(map));
1091 		map.map_class = &mapclass->s_mapclass;
1092 		map.map_mname = mn;
1093 		map.map_mflags |= MF_FILECLASS;
1094 
1095 		if (tTd(37, 5))
1096 			sm_dprintf("fileclass: F{%s}: map class %s, key %s, spec %s\n",
1097 				   mn, cl, key, spec);
1098 
1099 
1100 		/* parse map spec */
1101 		if (!map.map_class->map_parse(&map, spec))
1102 		{
1103 			/* map_parse() showed the error already */
1104 			sm_free(mn);
1105 			return;
1106 		}
1107 		map.map_mflags |= MF_VALID;
1108 
1109 		/* open map */
1110 		if (map.map_class->map_open(&map, O_RDONLY))
1111 		{
1112 			map.map_mflags |= MF_OPEN;
1113 			map.map_pid = getpid();
1114 		}
1115 		else
1116 		{
1117 			if (!optional &&
1118 			    !bitset(MF_OPTIONAL, map.map_mflags))
1119 				syserr("fileclass: F{%s}: map open failed",
1120 				       mn);
1121 			sm_free(mn);
1122 			return;
1123 		}
1124 
1125 		/* lookup */
1126 		p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
1127 		if (status != EX_OK && status != EX_NOTFOUND)
1128 		{
1129 			if (!optional)
1130 				syserr("fileclass: F{%s}: map lookup failed",
1131 				       mn);
1132 			p = NULL;
1133 		}
1134 
1135 		/* use the results */
1136 		if (p != NULL)
1137 			parse_class_words(class, p);
1138 
1139 		/* close map */
1140 		map.map_mflags |= MF_CLOSING;
1141 		map.map_class->map_close(&map);
1142 		map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1143 		sm_free(mn);
1144 		return;
1145 	}
1146 	else if (filename[0] == '|')
1147 	{
1148 		auto int fd;
1149 		int i;
1150 		char *argv[MAXPV + 1];
1151 
1152 		i = 0;
1153 		for (p = strtok(&filename[1], " \t");
1154 		     p != NULL && i < MAXPV;
1155 		     p = strtok(NULL, " \t"))
1156 			argv[i++] = p;
1157 		argv[i] = NULL;
1158 		pid = prog_open(argv, &fd, CurEnv);
1159 		if (pid < 0)
1160 			f = NULL;
1161 		else
1162 			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
1163 				       (void *) &fd, SM_IO_RDONLY, NULL);
1164 	}
1165 	else
1166 	{
1167 		pid = -1;
1168 		sff = SFF_REGONLY;
1169 		if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
1170 			sff |= SFF_SAFEDIRPATH;
1171 		if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
1172 			     DontBlameSendmail))
1173 			sff |= SFF_NOWLINK;
1174 		if (safe)
1175 			sff |= SFF_OPENASROOT;
1176 		else if (RealUid == 0)
1177 			sff |= SFF_ROOTOK;
1178 		if (DontLockReadFiles)
1179 			sff |= SFF_NOLOCK;
1180 		f = safefopen(filename, O_RDONLY, 0, sff);
1181 	}
1182 	if (f == NULL)
1183 	{
1184 		if (!optional)
1185 			syserr("fileclass: cannot open '%s'", filename);
1186 		return;
1187 	}
1188 
1189 	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
1190 	{
1191 #if SCANF
1192 		char wordbuf[MAXLINE + 1];
1193 #endif
1194 
1195 		if (buf[0] == '#')
1196 			continue;
1197 #if SCANF
1198 		if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
1199 			continue;
1200 		p = wordbuf;
1201 #else /* SCANF */
1202 		p = buf;
1203 #endif /* SCANF */
1204 
1205 		parse_class_words(class, p);
1206 
1207 		/*
1208 		**  If anything else is added here,
1209 		**  check if the '@' map case above
1210 		**  needs the code as well.
1211 		*/
1212 	}
1213 
1214 	(void) sm_io_close(f, SM_TIME_DEFAULT);
1215 	if (pid > 0)
1216 		(void) waitfor(pid);
1217 }
1218 
1219 #if _FFR_RCPTFLAGS
1220 /* first character for dynamically created mailers */
1221 static char dynmailerp = ' ';
1222 
1223 /* list of first characters for cf defined mailers */
1224 static char frst[MAXMAILERS + 1];
1225 
1226 /*
1227 **  SETUPDYNMAILERS -- find a char that isn't used as first element of any
1228 **		mailer name.
1229 **
1230 **	Parameters:
1231 **		none
1232 **
1233 **	Returns:
1234 **		none
1235 **
1236 **	Note: space is not valid in cf defined mailers hence the function
1237 **		will always find a char. It's not nice, but this is for
1238 **		internal names only.
1239 */
1240 
1241 void
1242 setupdynmailers()
1243 {
1244 	int i;
1245 	char pp[] = "YXZ0123456789ABCDEFGHIJKLMNOPQRSTUVWyxzabcfghijkmnoqtuvw ";
1246 
1247 	frst[MAXMAILERS] = '\0';
1248 	for (i = 0; i < strlen(pp); i++)
1249 	{
1250 		if (strchr(frst, pp[i]) == NULL)
1251 		{
1252 			dynmailerp = pp[i];
1253 			if (tTd(25, 8))
1254 				sm_dprintf("dynmailerp=%c\n", dynmailerp);
1255 			return;
1256 		}
1257 	}
1258 
1259 	/* NOTREACHED */
1260 	SM_ASSERT(0);
1261 }
1262 
1263 /*
1264 **  NEWMODMAILER -- Create a new mailer with modifications
1265 **
1266 **	Parameters:
1267 **		rcpt -- current RCPT
1268 **		fl -- flag to set
1269 **
1270 **	Returns:
1271 **		true iff successful.
1272 **
1273 **	Note: this creates a copy of the mailer for the rcpt and
1274 **		modifies exactly one flag.  It does not work
1275 **		for multiple flags!
1276 */
1277 
1278 bool
1279 newmodmailer(rcpt, fl)
1280 	ADDRESS *rcpt;
1281 	int fl;
1282 {
1283 	int idx;
1284 	struct mailer *m;
1285 	STAB *s;
1286 	char mname[256];
1287 
1288 	SM_REQUIRE(rcpt != NULL);
1289 	if (rcpt->q_mailer == NULL)
1290 		return false;
1291 	if (tTd(25, 8))
1292 		sm_dprintf("newmodmailer: rcpt=%s\n", rcpt->q_paddr);
1293 	SM_REQUIRE(rcpt->q_mailer->m_name != NULL);
1294 	SM_REQUIRE(rcpt->q_mailer->m_name[0] != '\0');
1295 	sm_strlcpy(mname, rcpt->q_mailer->m_name, sizeof(mname));
1296 	mname[0] = dynmailerp;
1297 	if (tTd(25, 8))
1298 		sm_dprintf("newmodmailer: name=%s\n", mname);
1299 	s = stab(mname, ST_MAILER, ST_ENTER);
1300 	if (s->s_mailer != NULL)
1301 	{
1302 		idx = s->s_mailer->m_mno;
1303 		if (tTd(25, 6))
1304 			sm_dprintf("newmodmailer: found idx=%d\n", idx);
1305 	}
1306 	else
1307 	{
1308 		idx = rcpt->q_mailer->m_mno;
1309 		idx += MAXMAILERS;
1310 		if (tTd(25, 6))
1311 			sm_dprintf("newmodmailer: idx=%d\n", idx);
1312 		if (idx > SM_ARRAY_SIZE(Mailer))
1313 			return false;
1314 	}
1315 
1316 	m = Mailer[idx];
1317 	if (m == NULL)
1318 		m = (struct mailer *) xalloc(sizeof(*m));
1319 	memset((char *) m, '\0', sizeof(*m));
1320 	STRUCTCOPY(*rcpt->q_mailer, *m);
1321 	Mailer[idx] = m;
1322 
1323 	/* "modify" the mailer */
1324 	setbitn(bitidx(fl), m->m_flags);
1325 	rcpt->q_mailer = m;
1326 	m->m_mno = idx;
1327 	m->m_name = newstr(mname);
1328 	if (tTd(25, 1))
1329 		sm_dprintf("newmodmailer: mailer[%d]=%s %p\n",
1330 			idx, Mailer[idx]->m_name, Mailer[idx]);
1331 
1332 	return true;
1333 }
1334 
1335 #endif /* _FFR_RCPTFLAGS */
1336 
1337 /*
1338 **  MAKEMAILER -- define a new mailer.
1339 **
1340 **	Parameters:
1341 **		line -- description of mailer.  This is in labeled
1342 **			fields.  The fields are:
1343 **			   A -- the argv for this mailer
1344 **			   C -- the character set for MIME conversions
1345 **			   D -- the directory to run in
1346 **			   E -- the eol string
1347 **			   F -- the flags associated with the mailer
1348 **			   L -- the maximum line length
1349 **			   M -- the maximum message size
1350 **			   N -- the niceness at which to run
1351 **			   P -- the path to the mailer
1352 **			   Q -- the queue group for the mailer
1353 **			   R -- the recipient rewriting set
1354 **			   S -- the sender rewriting set
1355 **			   T -- the mailer type (for DSNs)
1356 **			   U -- the uid to run as
1357 **			   W -- the time to wait at the end
1358 **			   m -- maximum messages per connection
1359 **			   r -- maximum number of recipients per message
1360 **			   / -- new root directory
1361 **			The first word is the canonical name of the mailer.
1362 **
1363 **	Returns:
1364 **		none.
1365 **
1366 **	Side Effects:
1367 **		enters the mailer into the mailer table.
1368 */
1369 
1370 
1371 void
1372 makemailer(line)
1373 	char *line;
1374 {
1375 	register char *p;
1376 	register struct mailer *m;
1377 	register STAB *s;
1378 	int i;
1379 	char fcode;
1380 	auto char *endp;
1381 	static int nextmailer = 0;	/* "free" index into Mailer struct */
1382 
1383 	/* allocate a mailer and set up defaults */
1384 	m = (struct mailer *) xalloc(sizeof(*m));
1385 	memset((char *) m, '\0', sizeof(*m));
1386 	errno = 0; /* avoid bogus error text */
1387 
1388 	/* collect the mailer name */
1389 	for (p = line;
1390 	     *p != '\0' && *p != ',' && !(SM_ISSPACE(*p));
1391 	     p++)
1392 		continue;
1393 	if (*p != '\0')
1394 		*p++ = '\0';
1395 	if (line[0] == '\0')
1396 	{
1397 		syserr("name required for mailer");
1398 		return;
1399 	}
1400 	m->m_name = newstr(line);
1401 #if _FFR_RCPTFLAGS
1402 	frst[nextmailer] = line[0];
1403 #endif
1404 	m->m_qgrp = NOQGRP;
1405 	m->m_uid = NO_UID;
1406 	m->m_gid = NO_GID;
1407 
1408 	/* now scan through and assign info from the fields */
1409 	while (*p != '\0')
1410 	{
1411 		auto char *delimptr;
1412 
1413 		while (*p != '\0' &&
1414 		       (*p == ',' || (SM_ISSPACE(*p))))
1415 			p++;
1416 
1417 		/* p now points to field code */
1418 		fcode = *p;
1419 		while (*p != '\0' && *p != '=' && *p != ',')
1420 			p++;
1421 		if (*p++ != '=')
1422 		{
1423 			syserr("mailer %s: `=' expected", m->m_name);
1424 			return;
1425 		}
1426 		while (SM_ISSPACE(*p))
1427 			p++;
1428 
1429 		/* p now points to the field body */
1430 		p = munchstring(p, &delimptr, ',');
1431 
1432 		/* install the field into the mailer struct */
1433 		switch (fcode)
1434 		{
1435 		  case 'P':		/* pathname */
1436 			if (*p != '\0')	/* error is issued below */
1437 				m->m_mailer = newstr(p);
1438 			break;
1439 
1440 		  case 'F':		/* flags */
1441 			for (; *p != '\0'; p++)
1442 			{
1443 				if (!(SM_ISSPACE(*p)))
1444 				{
1445 					if (*p == M_INTERNAL)
1446 						sm_syslog(LOG_WARNING, NOQID,
1447 							  "WARNING: mailer=%s, flag=%c deprecated",
1448 							  m->m_name, *p);
1449 					setbitn(bitidx(*p), m->m_flags);
1450 				}
1451 			}
1452 			break;
1453 
1454 		  case 'S':		/* sender rewriting ruleset */
1455 		  case 'R':		/* recipient rewriting ruleset */
1456 			i = strtorwset(p, &endp, ST_ENTER);
1457 			if (i < 0)
1458 				return;
1459 			if (fcode == 'S')
1460 				m->m_sh_rwset = m->m_se_rwset = i;
1461 			else
1462 				m->m_rh_rwset = m->m_re_rwset = i;
1463 
1464 			p = endp;
1465 			if (*p++ == '/')
1466 			{
1467 				i = strtorwset(p, NULL, ST_ENTER);
1468 				if (i < 0)
1469 					return;
1470 				if (fcode == 'S')
1471 					m->m_sh_rwset = i;
1472 				else
1473 					m->m_rh_rwset = i;
1474 			}
1475 			break;
1476 
1477 		  case 'E':		/* end of line string */
1478 			if (*p == '\0')
1479 				syserr("mailer %s: null end-of-line string",
1480 					m->m_name);
1481 			else
1482 				m->m_eol = newstr(p);
1483 			break;
1484 
1485 		  case 'A':		/* argument vector */
1486 			if (*p != '\0')	/* error is issued below */
1487 				m->m_argv = makeargv(p);
1488 			break;
1489 
1490 		  case 'M':		/* maximum message size */
1491 			m->m_maxsize = atol(p);
1492 			break;
1493 
1494 		  case 'm':		/* maximum messages per connection */
1495 			m->m_maxdeliveries = atoi(p);
1496 			break;
1497 
1498 		  case 'r':		/* max recipient per envelope */
1499 			m->m_maxrcpt = atoi(p);
1500 			break;
1501 
1502 		  case 'L':		/* maximum line length */
1503 			m->m_linelimit = atoi(p);
1504 			if (m->m_linelimit < 0)
1505 				m->m_linelimit = 0;
1506 			break;
1507 
1508 		  case 'N':		/* run niceness */
1509 			m->m_nice = atoi(p);
1510 			break;
1511 
1512 		  case 'D':		/* working directory */
1513 			if (*p == '\0')
1514 				syserr("mailer %s: null working directory",
1515 					m->m_name);
1516 			else
1517 				m->m_execdir = newstr(p);
1518 			break;
1519 
1520 		  case 'C':		/* default charset */
1521 			if (*p == '\0')
1522 				syserr("mailer %s: null charset", m->m_name);
1523 			else
1524 				m->m_defcharset = newstr(p);
1525 			break;
1526 
1527 		  case 'Q':		/* queue for this mailer */
1528 			if (*p == '\0')
1529 			{
1530 				syserr("mailer %s: null queue", m->m_name);
1531 				break;
1532 			}
1533 			s = stab(p, ST_QUEUE, ST_FIND);
1534 			if (s == NULL)
1535 				syserr("mailer %s: unknown queue %s",
1536 					m->m_name, p);
1537 			else
1538 				m->m_qgrp = s->s_quegrp->qg_index;
1539 			break;
1540 
1541 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
1542 			/* extract MTA name type; default to "dns" */
1543 			m->m_mtatype = newstr(p);
1544 			p = strchr(m->m_mtatype, '/');
1545 			if (p != NULL)
1546 			{
1547 				*p++ = '\0';
1548 				if (*p == '\0')
1549 					p = NULL;
1550 			}
1551 			if (*m->m_mtatype == '\0')
1552 				m->m_mtatype = "dns";
1553 
1554 			/* extract address type; default to "rfc822" */
1555 			m->m_addrtype = p;
1556 			if (p != NULL)
1557 				p = strchr(p, '/');
1558 			if (p != NULL)
1559 			{
1560 				*p++ = '\0';
1561 				if (*p == '\0')
1562 					p = NULL;
1563 			}
1564 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1565 				m->m_addrtype = "rfc822";
1566 
1567 			/* extract diagnostic type; default to "smtp" */
1568 			m->m_diagtype = p;
1569 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1570 				m->m_diagtype = "smtp";
1571 			break;
1572 
1573 		  case 'U':		/* user id */
1574 			if (isascii(*p) && !isdigit(*p))
1575 			{
1576 				char *q = p;
1577 				struct passwd *pw;
1578 
1579 				while (*p != '\0' && isascii(*p) &&
1580 # if _FFR_DOTTED_USERNAMES
1581 				       (isalnum(*p) || strchr(SM_PWN_CHARS, *p) != NULL))
1582 # else
1583 				       (isalnum(*p) || strchr("-_", *p) != NULL))
1584 # endif
1585 					p++;
1586 				while (SM_ISSPACE(*p))
1587 					*p++ = '\0';
1588 				if (*p != '\0')
1589 					*p++ = '\0';
1590 				if (*q == '\0')
1591 				{
1592 					syserr("mailer %s: null user name",
1593 						m->m_name);
1594 					break;
1595 				}
1596 				pw = sm_getpwnam(q);
1597 				if (pw == NULL)
1598 				{
1599 					syserr("readcf: mailer U= flag: unknown user %s", q);
1600 					break;
1601 				}
1602 				else
1603 				{
1604 					m->m_uid = pw->pw_uid;
1605 					m->m_gid = pw->pw_gid;
1606 				}
1607 			}
1608 			else
1609 			{
1610 				auto char *q;
1611 
1612 				m->m_uid = strtol(p, &q, 0);
1613 				p = q;
1614 				while (SM_ISSPACE(*p))
1615 					p++;
1616 				if (*p != '\0')
1617 					p++;
1618 			}
1619 			while (SM_ISSPACE(*p))
1620 				p++;
1621 			if (*p == '\0')
1622 				break;
1623 			if (isascii(*p) && !isdigit(*p))
1624 			{
1625 				char *q = p;
1626 				struct group *gr;
1627 
1628 				while (isascii(*p) &&
1629 				       (isalnum(*p) || strchr(SM_PWN_CHARS, *p) != NULL))
1630 					p++;
1631 				*p++ = '\0';
1632 				if (*q == '\0')
1633 				{
1634 					syserr("mailer %s: null group name",
1635 						m->m_name);
1636 					break;
1637 				}
1638 				gr = getgrnam(q);
1639 				if (gr == NULL)
1640 				{
1641 					syserr("readcf: mailer U= flag: unknown group %s", q);
1642 					break;
1643 				}
1644 				else
1645 					m->m_gid = gr->gr_gid;
1646 			}
1647 			else
1648 			{
1649 				m->m_gid = strtol(p, NULL, 0);
1650 			}
1651 			break;
1652 
1653 		  case 'W':		/* wait timeout */
1654 			m->m_wait = convtime(p, 's');
1655 			break;
1656 
1657 		  case '/':		/* new root directory */
1658 			if (*p == '\0')
1659 				syserr("mailer %s: null root directory",
1660 					m->m_name);
1661 			else
1662 				m->m_rootdir = newstr(p);
1663 			break;
1664 
1665 		  default:
1666 			syserr("M%s: unknown mailer equate %c=",
1667 			       m->m_name, fcode);
1668 			break;
1669 		}
1670 
1671 		p = delimptr;
1672 	}
1673 
1674 #if !HASRRESVPORT
1675 	if (bitnset(M_SECURE_PORT, m->m_flags))
1676 	{
1677 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1678 				     "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
1679 				     m->m_name, M_SECURE_PORT);
1680 	}
1681 #endif /* !HASRRESVPORT */
1682 
1683 #if !HASNICE
1684 	if (m->m_nice != 0)
1685 	{
1686 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1687 				     "M%s: Warning: N= set on system that doesn't support nice()\n",
1688 				     m->m_name);
1689 	}
1690 #endif /* !HASNICE */
1691 
1692 	/* do some rationality checking */
1693 	if (m->m_argv == NULL)
1694 	{
1695 		syserr("M%s: A= argument required", m->m_name);
1696 		return;
1697 	}
1698 	if (m->m_mailer == NULL)
1699 	{
1700 		syserr("M%s: P= argument required", m->m_name);
1701 		return;
1702 	}
1703 
1704 	if (nextmailer >= MAXMAILERS)
1705 	{
1706 		syserr("too many mailers defined (%d max)", MAXMAILERS);
1707 		return;
1708 	}
1709 
1710 	if (m->m_maxrcpt <= 0)
1711 		m->m_maxrcpt = DEFAULT_MAX_RCPT;
1712 
1713 	/* do some heuristic cleanup for back compatibility */
1714 	if (bitnset(M_LIMITS, m->m_flags))
1715 	{
1716 		if (m->m_linelimit == 0)
1717 			m->m_linelimit = SMTPLINELIM;
1718 		if (ConfigLevel < 2)
1719 			setbitn(M_7BITS, m->m_flags);
1720 	}
1721 
1722 	if (strcmp(m->m_mailer, "[TCP]") == 0)
1723 	{
1724 		syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
1725 		return;
1726 	}
1727 
1728 	if (strcmp(m->m_mailer, "[IPC]") == 0)
1729 	{
1730 		/* Use the second argument for host or path to socket */
1731 		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1732 		    m->m_argv[1][0] == '\0')
1733 		{
1734 			syserr("M%s: too few parameters for %s mailer",
1735 			       m->m_name, m->m_mailer);
1736 			return;
1737 		}
1738 		if (strcmp(m->m_argv[0], "TCP") != 0
1739 #if NETUNIX
1740 		    && strcmp(m->m_argv[0], "FILE") != 0
1741 #endif
1742 		    )
1743 		{
1744 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1745 					     "M%s: Warning: first argument in %s mailer must be %s\n",
1746 					     m->m_name, m->m_mailer,
1747 #if NETUNIX
1748 					     "TCP or FILE"
1749 #else
1750 					     "TCP"
1751 #endif
1752 				     );
1753 		}
1754 		if (m->m_mtatype == NULL)
1755 			m->m_mtatype = "dns";
1756 		if (m->m_addrtype == NULL)
1757 			m->m_addrtype = "rfc822";
1758 		if (m->m_diagtype == NULL)
1759 		{
1760 			if (m->m_argv[0] != NULL &&
1761 			    strcmp(m->m_argv[0], "FILE") == 0)
1762 				m->m_diagtype = "x-unix";
1763 			else
1764 				m->m_diagtype = "smtp";
1765 		}
1766 	}
1767 	else if (strcmp(m->m_mailer, "[FILE]") == 0)
1768 	{
1769 		/* Use the second argument for filename */
1770 		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1771 		    m->m_argv[2] != NULL)
1772 		{
1773 			syserr("M%s: too %s parameters for [FILE] mailer",
1774 			       m->m_name,
1775 			       (m->m_argv[0] == NULL ||
1776 				m->m_argv[1] == NULL) ? "few" : "many");
1777 			return;
1778 		}
1779 		else if (strcmp(m->m_argv[0], "FILE") != 0)
1780 		{
1781 			syserr("M%s: first argument in [FILE] mailer must be FILE",
1782 			       m->m_name);
1783 			return;
1784 		}
1785 	}
1786 
1787 	if (m->m_eol == NULL)
1788 	{
1789 		char **pp;
1790 
1791 		/* default for SMTP is \r\n; use \n for local delivery */
1792 		for (pp = m->m_argv; *pp != NULL; pp++)
1793 		{
1794 			for (p = *pp; *p != '\0'; )
1795 			{
1796 				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1797 					break;
1798 			}
1799 			if (*p != '\0')
1800 				break;
1801 		}
1802 		if (*pp == NULL)
1803 			m->m_eol = "\r\n";
1804 		else
1805 			m->m_eol = "\n";
1806 	}
1807 
1808 	/* enter the mailer into the symbol table */
1809 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1810 	if (s->s_mailer != NULL)
1811 	{
1812 		i = s->s_mailer->m_mno;
1813 		sm_free(s->s_mailer); /* XXX */
1814 	}
1815 	else
1816 	{
1817 		i = nextmailer++;
1818 	}
1819 	Mailer[i] = s->s_mailer = m;
1820 	m->m_mno = i;
1821 }
1822 /*
1823 **  MUNCHSTRING -- translate a string into internal form.
1824 **
1825 **	Parameters:
1826 **		p -- the string to munch.
1827 **		delimptr -- if non-NULL, set to the pointer of the
1828 **			field delimiter character.
1829 **		delim -- the delimiter for the field.
1830 **
1831 **	Returns:
1832 **		the munched string.
1833 **
1834 **	Side Effects:
1835 **		the munched string is a local static buffer.
1836 **		it must be copied before the function is called again.
1837 */
1838 
1839 char *
1840 munchstring(p, delimptr, delim)
1841 	register char *p;
1842 	char **delimptr;
1843 	int delim;
1844 {
1845 	register char *q;
1846 	bool backslash = false;
1847 	bool quotemode = false;
1848 	static char buf[MAXLINE];
1849 
1850 	for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1851 	{
1852 		if (backslash)
1853 		{
1854 			/* everything is roughly literal */
1855 			backslash = false;
1856 			switch (*p)
1857 			{
1858 			  case 'r':		/* carriage return */
1859 				*q++ = '\r';
1860 				continue;
1861 
1862 			  case 'n':		/* newline */
1863 				*q++ = '\n';
1864 				continue;
1865 
1866 			  case 'f':		/* form feed */
1867 				*q++ = '\f';
1868 				continue;
1869 
1870 			  case 'b':		/* backspace */
1871 				*q++ = '\b';
1872 				continue;
1873 			}
1874 			*q++ = *p;
1875 		}
1876 		else
1877 		{
1878 			if (*p == '\\')
1879 				backslash = true;
1880 			else if (*p == '"')
1881 				quotemode = !quotemode;
1882 			else if (quotemode || *p != delim)
1883 				*q++ = *p;
1884 			else
1885 				break;
1886 		}
1887 	}
1888 
1889 	if (delimptr != NULL)
1890 		*delimptr = p;
1891 	*q++ = '\0';
1892 	return buf;
1893 }
1894 /*
1895 **  EXTRQUOTSTR -- extract a (quoted) string.
1896 **
1897 **	This routine deals with quoted (") strings and escaped
1898 **	spaces (\\ ).
1899 **
1900 **	Parameters:
1901 **		p -- source string.
1902 **		delimptr -- if non-NULL, set to the pointer of the
1903 **			field delimiter character.
1904 **		delimbuf -- delimiters for the field.
1905 **		st -- if non-NULL, store the return value (whether the
1906 **			string was correctly quoted) here.
1907 **
1908 **	Returns:
1909 **		the extracted string.
1910 **
1911 **	Side Effects:
1912 **		the returned string is a local static buffer.
1913 **		it must be copied before the function is called again.
1914 */
1915 
1916 static char *
1917 extrquotstr(p, delimptr, delimbuf, st)
1918 	register char *p;
1919 	char **delimptr;
1920 	char *delimbuf;
1921 	bool *st;
1922 {
1923 	register char *q;
1924 	bool backslash = false;
1925 	bool quotemode = false;
1926 	static char buf[MAXLINE];
1927 
1928 	for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1929 	{
1930 		if (backslash)
1931 		{
1932 			backslash = false;
1933 			if (*p != ' ')
1934 				*q++ = '\\';
1935 		}
1936 		if (*p == '\\')
1937 			backslash = true;
1938 		else if (*p == '"')
1939 			quotemode = !quotemode;
1940 		else if (quotemode ||
1941 			 strchr(delimbuf, (int) *p) == NULL)
1942 			*q++ = *p;
1943 		else
1944 			break;
1945 	}
1946 
1947 	if (delimptr != NULL)
1948 		*delimptr = p;
1949 	*q++ = '\0';
1950 	if (st != NULL)
1951 		*st = !(quotemode || backslash);
1952 	return buf;
1953 }
1954 /*
1955 **  MAKEARGV -- break up a string into words
1956 **
1957 **	Parameters:
1958 **		p -- the string to break up.
1959 **
1960 **	Returns:
1961 **		a char **argv (dynamically allocated)
1962 **
1963 **	Side Effects:
1964 **		munges p.
1965 */
1966 
1967 static char **
1968 makeargv(p)
1969 	register char *p;
1970 {
1971 	char *q;
1972 	int i;
1973 	char **avp;
1974 	char *argv[MAXPV + 1];
1975 
1976 	/* take apart the words */
1977 	i = 0;
1978 	while (*p != '\0' && i < MAXPV)
1979 	{
1980 		q = p;
1981 		while (*p != '\0' && !(SM_ISSPACE(*p)))
1982 			p++;
1983 		while (SM_ISSPACE(*p))
1984 			*p++ = '\0';
1985 		argv[i++] = newstr(q);
1986 	}
1987 	argv[i++] = NULL;
1988 
1989 	/* now make a copy of the argv */
1990 	avp = (char **) xalloc(sizeof(*avp) * i);
1991 	memmove((char *) avp, (char *) argv, sizeof(*avp) * i);
1992 
1993 	return avp;
1994 }
1995 /*
1996 **  PRINTRULES -- print rewrite rules (for debugging)
1997 **
1998 **	Parameters:
1999 **		none.
2000 **
2001 **	Returns:
2002 **		none.
2003 **
2004 **	Side Effects:
2005 **		prints rewrite rules.
2006 */
2007 
2008 void
2009 printrules()
2010 {
2011 	register struct rewrite *rwp;
2012 	register int ruleset;
2013 
2014 	for (ruleset = 0; ruleset < 10; ruleset++)
2015 	{
2016 		if (RewriteRules[ruleset] == NULL)
2017 			continue;
2018 		sm_dprintf("\n----Rule Set %d:", ruleset);
2019 
2020 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
2021 		{
2022 			sm_dprintf("\nLHS:");
2023 			printav(sm_debug_file(), rwp->r_lhs);
2024 			sm_dprintf("RHS:");
2025 			printav(sm_debug_file(), rwp->r_rhs);
2026 		}
2027 	}
2028 }
2029 /*
2030 **  PRINTMAILER -- print mailer structure (for debugging)
2031 **
2032 **	Parameters:
2033 **		fp -- output file
2034 **		m -- the mailer to print
2035 **
2036 **	Returns:
2037 **		none.
2038 */
2039 
2040 void
2041 printmailer(fp, m)
2042 	SM_FILE_T *fp;
2043 	register MAILER *m;
2044 {
2045 	int j;
2046 
2047 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2048 			     "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
2049 			     m->m_mailer);
2050 	if (RuleSetNames[m->m_se_rwset] == NULL)
2051 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
2052 				     m->m_se_rwset);
2053 	else
2054 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
2055 				     RuleSetNames[m->m_se_rwset]);
2056 	if (RuleSetNames[m->m_sh_rwset] == NULL)
2057 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d R=",
2058 				     m->m_sh_rwset);
2059 	else
2060 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s R=",
2061 				     RuleSetNames[m->m_sh_rwset]);
2062 	if (RuleSetNames[m->m_re_rwset] == NULL)
2063 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
2064 				     m->m_re_rwset);
2065 	else
2066 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
2067 				     RuleSetNames[m->m_re_rwset]);
2068 	if (RuleSetNames[m->m_rh_rwset] == NULL)
2069 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d ",
2070 				     m->m_rh_rwset);
2071 	else
2072 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s ",
2073 				     RuleSetNames[m->m_rh_rwset]);
2074 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
2075 			     m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
2076 	for (j = '\0'; j <= '\177'; j++)
2077 		if (bitnset(j, m->m_flags))
2078 			(void) sm_io_putc(fp, SM_TIME_DEFAULT, j);
2079 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " L=%d E=",
2080 			     m->m_linelimit);
2081 	xputs(fp, m->m_eol);
2082 	if (m->m_defcharset != NULL)
2083 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " C=%s",
2084 				     m->m_defcharset);
2085 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " T=%s/%s/%s",
2086 			     m->m_mtatype == NULL
2087 				? "<undefined>" : m->m_mtatype,
2088 			     m->m_addrtype == NULL
2089 				? "<undefined>" : m->m_addrtype,
2090 			     m->m_diagtype == NULL
2091 				? "<undefined>" : m->m_diagtype);
2092 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
2093 	if (m->m_argv != NULL)
2094 	{
2095 		char **a = m->m_argv;
2096 
2097 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " A=");
2098 		while (*a != NULL)
2099 		{
2100 			if (a != m->m_argv)
2101 				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2102 						     " ");
2103 			xputs(fp, *a++);
2104 		}
2105 	}
2106 	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
2107 }
2108 
2109 #if STARTTLS
2110 static struct ssl_options
2111 {
2112 	const char	*sslopt_name;	/* name of the flag */
2113 	long		sslopt_bits;	/* bits to set/clear */
2114 } SSL_Option[] =
2115 {
2116 /* Workaround for bugs are turned on by default (as well as some others) */
2117 #ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
2118 	{ "SSL_OP_MICROSOFT_SESS_ID_BUG",	SSL_OP_MICROSOFT_SESS_ID_BUG	},
2119 #endif
2120 #ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
2121 	{ "SSL_OP_NETSCAPE_CHALLENGE_BUG",	SSL_OP_NETSCAPE_CHALLENGE_BUG	},
2122 #endif
2123 #ifdef SSL_OP_LEGACY_SERVER_CONNECT
2124 	{ "SSL_OP_LEGACY_SERVER_CONNECT",	SSL_OP_LEGACY_SERVER_CONNECT	},
2125 #endif
2126 #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
2127 	{ "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG	},
2128 #endif
2129 #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
2130 	{ "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG",	SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG	},
2131 #endif
2132 #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
2133 	{ "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER",	SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER	},
2134 #endif
2135 #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
2136 	{ "SSL_OP_MSIE_SSLV2_RSA_PADDING",	SSL_OP_MSIE_SSLV2_RSA_PADDING	},
2137 #endif
2138 #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
2139 	{ "SSL_OP_SSLEAY_080_CLIENT_DH_BUG",	SSL_OP_SSLEAY_080_CLIENT_DH_BUG	},
2140 #endif
2141 #ifdef SSL_OP_TLS_D5_BUG
2142 	{ "SSL_OP_TLS_D5_BUG",	SSL_OP_TLS_D5_BUG	},
2143 #endif
2144 #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
2145 	{ "SSL_OP_TLS_BLOCK_PADDING_BUG",	SSL_OP_TLS_BLOCK_PADDING_BUG	},
2146 #endif
2147 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
2148 	{ "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS",	SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS	},
2149 #endif
2150 #ifdef SSL_OP_ALL
2151 	{ "SSL_OP_ALL",	SSL_OP_ALL	},
2152 #endif
2153 #ifdef SSL_OP_NO_QUERY_MTU
2154 	{ "SSL_OP_NO_QUERY_MTU",	SSL_OP_NO_QUERY_MTU	},
2155 #endif
2156 #ifdef SSL_OP_COOKIE_EXCHANGE
2157 	{ "SSL_OP_COOKIE_EXCHANGE",	SSL_OP_COOKIE_EXCHANGE	},
2158 #endif
2159 #ifdef SSL_OP_NO_TICKET
2160 	{ "SSL_OP_NO_TICKET",	SSL_OP_NO_TICKET	},
2161 #endif
2162 #ifdef SSL_OP_CISCO_ANYCONNECT
2163 	{ "SSL_OP_CISCO_ANYCONNECT",	SSL_OP_CISCO_ANYCONNECT	},
2164 #endif
2165 #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
2166 	{ "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION",	SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION	},
2167 #endif
2168 #ifdef SSL_OP_NO_COMPRESSION
2169 	{ "SSL_OP_NO_COMPRESSION",	SSL_OP_NO_COMPRESSION	},
2170 #endif
2171 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
2172 	{ "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",	SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION	},
2173 #endif
2174 #ifdef SSL_OP_SINGLE_ECDH_USE
2175 	{ "SSL_OP_SINGLE_ECDH_USE",	SSL_OP_SINGLE_ECDH_USE	},
2176 #endif
2177 #ifdef SSL_OP_SINGLE_DH_USE
2178 	{ "SSL_OP_SINGLE_DH_USE",	SSL_OP_SINGLE_DH_USE	},
2179 #endif
2180 #ifdef SSL_OP_EPHEMERAL_RSA
2181 	{ "SSL_OP_EPHEMERAL_RSA",	SSL_OP_EPHEMERAL_RSA	},
2182 #endif
2183 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
2184 	{ "SSL_OP_CIPHER_SERVER_PREFERENCE",	SSL_OP_CIPHER_SERVER_PREFERENCE	},
2185 #endif
2186 #ifdef SSL_OP_TLS_ROLLBACK_BUG
2187 	{ "SSL_OP_TLS_ROLLBACK_BUG",	SSL_OP_TLS_ROLLBACK_BUG	},
2188 #endif
2189 #ifdef SSL_OP_NO_SSLv2
2190 	{ "SSL_OP_NO_SSLv2",	SSL_OP_NO_SSLv2	},
2191 #endif
2192 #ifdef SSL_OP_NO_SSLv3
2193 	{ "SSL_OP_NO_SSLv3",	SSL_OP_NO_SSLv3	},
2194 #endif
2195 #ifdef SSL_OP_NO_TLSv1
2196 	{ "SSL_OP_NO_TLSv1",	SSL_OP_NO_TLSv1	},
2197 #endif
2198 #ifdef SSL_OP_NO_TLSv1_3
2199 	{ "SSL_OP_NO_TLSv1_3",	SSL_OP_NO_TLSv1_3	},
2200 #endif
2201 #ifdef SSL_OP_NO_TLSv1_2
2202 	{ "SSL_OP_NO_TLSv1_2",	SSL_OP_NO_TLSv1_2	},
2203 #endif
2204 #ifdef SSL_OP_NO_TLSv1_1
2205 	{ "SSL_OP_NO_TLSv1_1",	SSL_OP_NO_TLSv1_1	},
2206 #endif
2207 #ifdef SSL_OP_PKCS1_CHECK_1
2208 	{ "SSL_OP_PKCS1_CHECK_1",	SSL_OP_PKCS1_CHECK_1	},
2209 #endif
2210 #ifdef SSL_OP_PKCS1_CHECK_2
2211 	{ "SSL_OP_PKCS1_CHECK_2",	SSL_OP_PKCS1_CHECK_2	},
2212 #endif
2213 #ifdef SSL_OP_NETSCAPE_CA_DN_BUG
2214 	{ "SSL_OP_NETSCAPE_CA_DN_BUG",	SSL_OP_NETSCAPE_CA_DN_BUG	},
2215 #endif
2216 #ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
2217 	{ "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG	},
2218 #endif
2219 #ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
2220 	{ "SSL_OP_CRYPTOPRO_TLSEXT_BUG",	SSL_OP_CRYPTOPRO_TLSEXT_BUG	},
2221 #endif
2222 #ifdef SSL_OP_TLSEXT_PADDING
2223 	{ "SSL_OP_TLSEXT_PADDING",	SSL_OP_TLSEXT_PADDING	},
2224 #endif
2225 #ifdef SSL_OP_NO_RENEGOTIATION
2226 	{ "SSL_OP_NO_RENEGOTIATION",    SSL_OP_NO_RENEGOTIATION },
2227 #endif
2228 #ifdef SSL_OP_NO_ANTI_REPLAY
2229 	{ "SSL_OP_NO_ANTI_REPLAY",	SSL_OP_NO_ANTI_REPLAY },
2230 #endif
2231 #ifdef SSL_OP_ALLOW_NO_DHE_KEX
2232 	{ "SSL_OP_ALLOW_NO_DHE_KEX",	SSL_OP_ALLOW_NO_DHE_KEX },
2233 #endif
2234 #ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
2235 	{ "SSL_OP_NO_ENCRYPT_THEN_MAC",	SSL_OP_NO_ENCRYPT_THEN_MAC },
2236 #endif
2237 #ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
2238 	{ "SSL_OP_ENABLE_MIDDLEBOX_COMPAT",	SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
2239 #endif
2240 #ifdef SSL_OP_PRIORITIZE_CHACHA
2241 	{ "SSL_OP_PRIORITIZE_CHACHA",	SSL_OP_PRIORITIZE_CHACHA },
2242 #endif
2243 	{ NULL,		0		}
2244 };
2245 
2246 /*
2247 ** READSSLOPTIONS  -- read SSL_OP_* values
2248 **
2249 **	Parameters:
2250 **		opt -- name of option (can be NULL)
2251 **		val -- string with SSL_OP_* values or hex value
2252 **		delim -- end of string (e.g., '\0' or ';')
2253 **		pssloptions -- return value (output)
2254 **
2255 **	Returns:
2256 **		0 on success.
2257 */
2258 
2259 #define SSLOPERR_NAN	1
2260 #define SSLOPERR_NOTFOUND	2
2261 
2262 static int readssloptions __P((char *, char *, unsigned long *, int ));
2263 
2264 static int
2265 readssloptions(opt, val, pssloptions, delim)
2266 	char *opt;
2267 	char *val;
2268 	unsigned long *pssloptions;
2269 	int delim;
2270 {
2271 	char *p;
2272 	int ret;
2273 
2274 	ret = 0;
2275 	for (p = val; *p != '\0' && *p != delim; )
2276 	{
2277 		bool clearmode;
2278 		char *q;
2279 		unsigned long sslopt_val;
2280 		struct ssl_options *sslopts;
2281 
2282 		while (*p == ' ')
2283 			p++;
2284 		if (*p == '\0')
2285 			break;
2286 		clearmode = false;
2287 		if (*p == '-' || *p == '+')
2288 			clearmode = *p++ == '-';
2289 		q = p;
2290 		while (*p != '\0' && !(SM_ISSPACE(*p)) && *p != ',')
2291 			p++;
2292 		if (*p != '\0')
2293 			*p++ = '\0';
2294 		sslopt_val = 0;
2295 		if (isdigit(*q))
2296 		{
2297 			char *end;
2298 
2299 			sslopt_val = strtoul(q, &end, 0);
2300 
2301 			/* not a complete "syntax" check but good enough */
2302 			if (end == q)
2303 			{
2304 				errno = 0;
2305 				ret = SSLOPERR_NAN;
2306 				if (opt != NULL)
2307 					syserr("readcf: %s option value %s not a number",
2308 						opt, q);
2309 				sslopt_val = 0;
2310 			}
2311 		}
2312 		else
2313 		{
2314 			for (sslopts = SSL_Option;
2315 			     sslopts->sslopt_name != NULL; sslopts++)
2316 			{
2317 				if (sm_strcasecmp(q, sslopts->sslopt_name) == 0)
2318 				{
2319 					sslopt_val = sslopts->sslopt_bits;
2320 					break;
2321 				}
2322 			}
2323 			if (sslopts->sslopt_name == NULL)
2324 			{
2325 				errno = 0;
2326 				ret = SSLOPERR_NOTFOUND;
2327 				if (opt != NULL)
2328 					syserr("readcf: %s option value %s unrecognized",
2329 						opt, q);
2330 			}
2331 		}
2332 		if (sslopt_val != 0)
2333 		{
2334 			if (clearmode)
2335 				*pssloptions &= ~sslopt_val;
2336 			else
2337 				*pssloptions |= sslopt_val;
2338 		}
2339 	}
2340 	return ret;
2341 }
2342 
2343 /*
2344 ** GET_TLS_SE_OPTIONS -- get TLS session options (from ruleset)
2345 **
2346 **	Parameters:
2347 **		e -- envelope
2348 **		ssl -- TLS session context
2349 **		tlsi_ctx -- TLS info context
2350 **		srv -- server?
2351 **
2352 **	Returns:
2353 **		0 on success.
2354 */
2355 
2356 int
2357 get_tls_se_options(e, ssl, tlsi_ctx, srv)
2358 	ENVELOPE *e;
2359 	SSL *ssl;
2360 	tlsi_ctx_T *tlsi_ctx;
2361 	bool srv;
2362 {
2363 	bool saveQuickAbort, saveSuprErrs, ok;
2364 	char *optionlist, *opt, *val;
2365 	char *keyfile, *certfile;
2366 	size_t len, i;
2367 	int ret;
2368 
2369 #  define who (srv ? "server" : "client")
2370 #  define NAME_C_S macvalue(macid(srv ? "{client_name}" : "{server_name}"), e)
2371 #  define ADDR_C_S macvalue(macid(srv ? "{client_addr}" : "{server_addr}"), e)
2372 #  define WHICH srv ? "srv" : "clt"
2373 
2374 	ret = 0;
2375 	keyfile = certfile = opt = val = NULL;
2376 	saveQuickAbort = QuickAbort;
2377 	saveSuprErrs = SuprErrs;
2378 	SuprErrs = true;
2379 	QuickAbort = false;
2380 
2381 	optionlist = NULL;
2382 	ok = rscheck(srv ? "tls_srv_features" : "tls_clt_features",
2383 		     NAME_C_S, ADDR_C_S, e,
2384 		     RSF_RMCOMM|RSF_ADDR|RSF_STRING,
2385 		     5, NULL, NOQID, NULL, &optionlist) == EX_OK;
2386 	if (!ok && LogLevel > 8)
2387 	{
2388 		sm_syslog(LOG_NOTICE, NOQID,
2389 			  "rscheck(tls_%s_features)=failed, relay=%s [%s], errors=%d",
2390 			  WHICH, NAME_C_S, ADDR_C_S,
2391 			  Errors);
2392 	}
2393 	QuickAbort = saveQuickAbort;
2394 	SuprErrs = saveSuprErrs;
2395 	if (ok && LogLevel > 9)
2396 	{
2397 		sm_syslog(LOG_INFO, NOQID,
2398 			  "tls_%s_features=%s, relay=%s [%s]",
2399 			  WHICH, optionlist, NAME_C_S, ADDR_C_S);
2400 	}
2401 	if (!ok || optionlist == NULL || (len = strlen(optionlist)) < 2)
2402 	{
2403 		if (LogLevel > 9)
2404 			sm_syslog(LOG_INFO, NOQID,
2405 				  "tls_%s_features=empty, relay=%s [%s]",
2406 				  WHICH, NAME_C_S, ADDR_C_S);
2407 
2408 		return ok ? 0 : 1;
2409 	}
2410 
2411 	i = 0;
2412 	if (optionlist[0] == '"' && optionlist[len - 1] == '"')
2413 	{
2414 		optionlist[0] = ' ';
2415 		optionlist[--len] = '\0';
2416 		if (len <= 2)
2417 		{
2418 			if (LogLevel > 9 && len > 1)
2419 				sm_syslog(LOG_INFO, NOQID,
2420 				  "tls_%s_features=too_short, relay=%s [%s]",
2421 				  WHICH, NAME_C_S, ADDR_C_S);
2422 
2423 			/* this is not treated as error! */
2424 			return 0;
2425 		}
2426 		i = 1;
2427 	}
2428 
2429 #  define INVALIDSYNTAX	\
2430 	do {	\
2431 		if (LogLevel > 7)	\
2432 			sm_syslog(LOG_INFO, NOQID,	\
2433 				  "tls_%s_features=invalid_syntax, opt=%s, relay=%s [%s]",	\
2434 				  WHICH, opt, NAME_C_S, ADDR_C_S);	\
2435 		return -1;	\
2436 	} while (0)
2437 
2438 #  define CHECKLEN	\
2439 	do {	\
2440 		if (i >= len)	\
2441 			INVALIDSYNTAX;	\
2442 	} while (0)
2443 
2444 #  define SKIPWS	\
2445 	do {	\
2446 		while (i < len && SM_ISSPACE(optionlist[i]))	\
2447 			++i;	\
2448 		CHECKLEN;	\
2449 	} while (0)
2450 
2451 	/* parse and handle opt=val; */
2452 	do {
2453 		char sep;
2454 
2455 		SKIPWS;
2456 		opt = optionlist + i;
2457 		sep = '=';
2458 		while (i < len && optionlist[i] != sep
2459 			&& optionlist[i] != '\0' && !SM_ISSPACE(optionlist[i]))
2460 			++i;
2461 		CHECKLEN;
2462 		while (i < len && SM_ISSPACE(optionlist[i]))
2463 			optionlist[i++] = '\0';
2464 		CHECKLEN;
2465 		if (optionlist[i] != sep)
2466 			INVALIDSYNTAX;
2467 		optionlist[i++] = '\0';
2468 
2469 		SKIPWS;
2470 		val = optionlist + i;
2471 		sep = ';';
2472 		while (i < len && optionlist[i] != sep && optionlist[i] != '\0')
2473 			++i;
2474 		if (optionlist[i] != '\0')
2475 		{
2476 			CHECKLEN;
2477 			optionlist[i++] = '\0';
2478 		}
2479 
2480 		if (LogLevel > 13)
2481 			sm_syslog(LOG_DEBUG, NOQID,
2482 				  "tls_%s_features=parsed, %s=%s, relay=%s [%s]",
2483 				  WHICH, opt, val, NAME_C_S, ADDR_C_S);
2484 
2485 		if (sm_strcasecmp(opt, "options") == 0)
2486 		{
2487 			unsigned long ssloptions;
2488 
2489 			ssloptions = 0;
2490 			ret = readssloptions(NULL, val, &ssloptions, ';');
2491 			if (ret == 0)
2492 				(void) SSL_set_options(ssl, (long) ssloptions);
2493 			else if (LogLevel > 8)
2494 			{
2495 				sm_syslog(LOG_WARNING, NOQID,
2496 					  "tls_%s_features=%s, error=%s, relay=%s [%s]",
2497 					  WHICH, val,
2498 					  (ret == SSLOPERR_NAN) ? "not a number" :
2499 					  ((ret == SSLOPERR_NOTFOUND) ? "SSL_OP not found" :
2500 					  "unknown"),
2501 					  NAME_C_S, ADDR_C_S);
2502 			}
2503 		}
2504 		else if (sm_strcasecmp(opt, "cipherlist") == 0)
2505 		{
2506 			if (SSL_set_cipher_list(ssl, val) <= 0)
2507 			{
2508 				ret = 1;
2509 				if (LogLevel > 7)
2510 				{
2511 					sm_syslog(LOG_WARNING, NOQID,
2512 						  "STARTTLS=%s, error: SSL_set_cipher_list(%s) failed",
2513 						  who, val);
2514 
2515 					tlslogerr(LOG_WARNING, 9, who);
2516 				}
2517 			}
2518 		}
2519 		else if (sm_strcasecmp(opt, "flags") == 0)
2520 		{
2521 			char *p;
2522 
2523 			for (p = val; *p != '\0'; p++)
2524 			{
2525 				if (isascii(*p) && isalnum(*p))
2526 					setbitn(bitidx(*p), tlsi_ctx->tlsi_flags);
2527 			}
2528 		}
2529 		else if (sm_strcasecmp(opt, "keyfile") == 0)
2530 			keyfile = val;
2531 		else if (sm_strcasecmp(opt, "certfile") == 0)
2532 			certfile = val;
2533 		else
2534 		{
2535 			ret = 1;
2536 			if (LogLevel > 7)
2537 			{
2538 				sm_syslog(LOG_INFO, NOQID,
2539 					  "tls_%s_features=unknown_option, opt=%s, relay=%s [%s]",
2540 					  WHICH, opt, NAME_C_S, ADDR_C_S);
2541 			}
2542 		}
2543 
2544 	} while (optionlist[i] != '\0' && i < len);
2545 
2546 	/* need cert and key before we can use the options */
2547 	/* does not implement the "," hack for 2nd cert/key pair */
2548 	if (keyfile != NULL && certfile != NULL)
2549 	{
2550 		load_certkey(ssl, srv, certfile, keyfile);
2551 		keyfile = certfile = NULL;
2552 	}
2553 	else if (keyfile != NULL || certfile != NULL)
2554 	{
2555 		ret = 1;
2556 		if (LogLevel > 7)
2557 		{
2558 			sm_syslog(LOG_INFO, NOQID,
2559 				  "tls_%s_features=only_one_of_CertFile/KeyFile_specified, relay=%s [%s]",
2560 				  WHICH, NAME_C_S, ADDR_C_S);
2561 		}
2562 	}
2563 
2564 	return ret;
2565 #  undef who
2566 #  undef NAME_C_S
2567 #  undef ADDR_C_S
2568 #  undef WHICH
2569 }
2570 #endif /* STARTTLS */
2571 
2572 /*
2573 **  SETOPTION -- set global processing option
2574 **
2575 **	Parameters:
2576 **		opt -- option name.
2577 **		val -- option value (as a text string).
2578 **		safe -- set if this came from a configuration file.
2579 **			Some options (if set from the command line) will
2580 **			reset the user id to avoid security problems.
2581 **		sticky -- if set, don't let other setoptions override
2582 **			this value.
2583 **		e -- the main envelope.
2584 **
2585 **	Returns:
2586 **		none.
2587 **
2588 **	Side Effects:
2589 **		Sets options as implied by the arguments.
2590 */
2591 
2592 static BITMAP256	StickyOpt;		/* set if option is stuck */
2593 
2594 #if NAMED_BIND
2595 
2596 static struct resolverflags
2597 {
2598 	char	*rf_name;	/* name of the flag */
2599 	long	rf_bits;	/* bits to set/clear */
2600 } ResolverFlags[] =
2601 {
2602 	{ "debug",	RES_DEBUG	},
2603 	{ "aaonly",	RES_AAONLY	},
2604 	{ "usevc",	RES_USEVC	},
2605 	{ "primary",	RES_PRIMARY	},
2606 	{ "igntc",	RES_IGNTC	},
2607 	{ "recurse",	RES_RECURSE	},
2608 	{ "defnames",	RES_DEFNAMES	},
2609 	{ "stayopen",	RES_STAYOPEN	},
2610 	{ "dnsrch",	RES_DNSRCH	},
2611 # ifdef RES_USE_INET6
2612 	{ "use_inet6",	RES_USE_INET6	},
2613 # endif
2614 # ifdef RES_USE_EDNS0
2615 	{ "use_edns0",	RES_USE_EDNS0	},
2616 # endif
2617 # ifdef RES_USE_DNSSEC
2618 	{ "use_dnssec",	RES_USE_DNSSEC	},
2619 # endif
2620 # if RES_TRUSTAD
2621 	{ "trustad",	RES_TRUSTAD	},
2622 # endif
2623 	{ "true",	0		},	/* avoid error on old syntax */
2624 	{ "true",	0		},	/* avoid error on old syntax */
2625 	{ NULL,		0		}
2626 };
2627 
2628 #endif /* NAMED_BIND */
2629 
2630 #define OI_NONE		0	/* no special treatment */
2631 #define OI_SAFE		0x0001	/* safe for random people to use */
2632 #define OI_SUBOPT	0x0002	/* option has suboptions */
2633 
2634 static struct optioninfo
2635 {
2636 	char		*o_name;	/* long name of option */
2637 	unsigned char	o_code;		/* short name of option */
2638 	unsigned short	o_flags;	/* option flags */
2639 } OptionTab[] =
2640 {
2641 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
2642 	{ "RemoteMode",			'>',		OI_NONE	},
2643 #endif
2644 	{ "SevenBitInput",		'7',		OI_SAFE	},
2645 	{ "EightBitMode",		'8',		OI_SAFE	},
2646 	{ "AliasFile",			'A',		OI_NONE	},
2647 	{ "AliasWait",			'a',		OI_NONE	},
2648 	{ "BlankSub",			'B',		OI_NONE	},
2649 	{ "MinFreeBlocks",		'b',		OI_SAFE	},
2650 	{ "CheckpointInterval",		'C',		OI_SAFE	},
2651 	{ "HoldExpensive",		'c',		OI_NONE	},
2652 	{ "DeliveryMode",		'd',		OI_SAFE	},
2653 	{ "ErrorHeader",		'E',		OI_NONE	},
2654 	{ "ErrorMode",			'e',		OI_SAFE	},
2655 	{ "TempFileMode",		'F',		OI_NONE	},
2656 	{ "SaveFromLine",		'f',		OI_NONE	},
2657 	{ "MatchGECOS",			'G',		OI_NONE	},
2658 
2659 	/* no long name, just here to avoid problems in setoption */
2660 	{ "",				'g',		OI_NONE	},
2661 	{ "HelpFile",			'H',		OI_NONE	},
2662 	{ "MaxHopCount",		'h',		OI_NONE	},
2663 	{ "ResolverOptions",		'I',		OI_NONE	},
2664 	{ "IgnoreDots",			'i',		OI_SAFE	},
2665 	{ "ForwardPath",		'J',		OI_NONE	},
2666 	{ "SendMimeErrors",		'j',		OI_SAFE	},
2667 	{ "ConnectionCacheSize",	'k',		OI_NONE	},
2668 	{ "ConnectionCacheTimeout",	'K',		OI_NONE	},
2669 	{ "UseErrorsTo",		'l',		OI_NONE	},
2670 	{ "LogLevel",			'L',		OI_SAFE	},
2671 	{ "MeToo",			'm',		OI_SAFE	},
2672 
2673 	/* no long name, just here to avoid problems in setoption */
2674 	{ "",				'M',		OI_NONE	},
2675 	{ "CheckAliases",		'n',		OI_NONE	},
2676 	{ "OldStyleHeaders",		'o',		OI_SAFE	},
2677 	{ "DaemonPortOptions",		'O',		OI_NONE	},
2678 	{ "PrivacyOptions",		'p',		OI_SAFE	},
2679 	{ "PostmasterCopy",		'P',		OI_NONE	},
2680 	{ "QueueFactor",		'q',		OI_NONE	},
2681 	{ "QueueDirectory",		'Q',		OI_NONE	},
2682 	{ "DontPruneRoutes",		'R',		OI_NONE	},
2683 	{ "Timeout",			'r',		OI_SUBOPT },
2684 	{ "StatusFile",			'S',		OI_NONE	},
2685 	{ "SuperSafe",			's',		OI_SAFE	},
2686 	{ "QueueTimeout",		'T',		OI_NONE	},
2687 	{ "TimeZoneSpec",		't',		OI_NONE	},
2688 	{ "UserDatabaseSpec",		'U',		OI_NONE	},
2689 	{ "DefaultUser",		'u',		OI_NONE	},
2690 	{ "FallbackMXhost",		'V',		OI_NONE	},
2691 	{ "Verbose",			'v',		OI_SAFE	},
2692 	{ "TryNullMXList",		'w',		OI_NONE	},
2693 	{ "QueueLA",			'x',		OI_NONE	},
2694 	{ "RefuseLA",			'X',		OI_NONE	},
2695 	{ "RecipientFactor",		'y',		OI_NONE	},
2696 	{ "ForkEachJob",		'Y',		OI_NONE	},
2697 	{ "ClassFactor",		'z',		OI_NONE	},
2698 	{ "RetryFactor",		'Z',		OI_NONE	},
2699 #define O_QUEUESORTORD	0x81
2700 	{ "QueueSortOrder",		O_QUEUESORTORD,	OI_SAFE	},
2701 #define O_HOSTSFILE	0x82
2702 	{ "HostsFile",			O_HOSTSFILE,	OI_NONE	},
2703 #define O_MQA		0x83
2704 	{ "MinQueueAge",		O_MQA,		OI_SAFE	},
2705 #define O_DEFCHARSET	0x85
2706 	{ "DefaultCharSet",		O_DEFCHARSET,	OI_SAFE	},
2707 #define O_SSFILE	0x86
2708 	{ "ServiceSwitchFile",		O_SSFILE,	OI_NONE	},
2709 #define O_DIALDELAY	0x87
2710 	{ "DialDelay",			O_DIALDELAY,	OI_SAFE	},
2711 #define O_NORCPTACTION	0x88
2712 	{ "NoRecipientAction",		O_NORCPTACTION,	OI_SAFE	},
2713 #define O_SAFEFILEENV	0x89
2714 	{ "SafeFileEnvironment",	O_SAFEFILEENV,	OI_NONE	},
2715 #define O_MAXMSGSIZE	0x8a
2716 	{ "MaxMessageSize",		O_MAXMSGSIZE,	OI_NONE	},
2717 #define O_COLONOKINADDR	0x8b
2718 	{ "ColonOkInAddr",		O_COLONOKINADDR, OI_SAFE },
2719 #define O_MAXQUEUERUN	0x8c
2720 	{ "MaxQueueRunSize",		O_MAXQUEUERUN,	OI_SAFE	},
2721 #define O_MAXCHILDREN	0x8d
2722 	{ "MaxDaemonChildren",		O_MAXCHILDREN,	OI_NONE	},
2723 #define O_KEEPCNAMES	0x8e
2724 	{ "DontExpandCnames",		O_KEEPCNAMES,	OI_NONE	},
2725 #define O_MUSTQUOTE	0x8f
2726 	{ "MustQuoteChars",		O_MUSTQUOTE,	OI_NONE	},
2727 #define O_SMTPGREETING	0x90
2728 	{ "SmtpGreetingMessage",	O_SMTPGREETING,	OI_NONE	},
2729 #define O_UNIXFROM	0x91
2730 	{ "UnixFromLine",		O_UNIXFROM,	OI_NONE	},
2731 #define O_OPCHARS	0x92
2732 	{ "OperatorChars",		O_OPCHARS,	OI_NONE	},
2733 #define O_DONTINITGRPS	0x93
2734 	{ "DontInitGroups",		O_DONTINITGRPS,	OI_NONE	},
2735 #define O_SLFH		0x94
2736 	{ "SingleLineFromHeader",	O_SLFH,		OI_SAFE	},
2737 #define O_ABH		0x95
2738 	{ "AllowBogusHELO",		O_ABH,		OI_SAFE	},
2739 #define O_CONNTHROT	0x97
2740 	{ "ConnectionRateThrottle",	O_CONNTHROT,	OI_NONE	},
2741 #define O_UGW		0x99
2742 	{ "UnsafeGroupWrites",		O_UGW,		OI_NONE	},
2743 #define O_DBLBOUNCE	0x9a
2744 	{ "DoubleBounceAddress",	O_DBLBOUNCE,	OI_NONE	},
2745 #define O_HSDIR		0x9b
2746 	{ "HostStatusDirectory",	O_HSDIR,	OI_NONE	},
2747 #define O_SINGTHREAD	0x9c
2748 	{ "SingleThreadDelivery",	O_SINGTHREAD,	OI_NONE	},
2749 #define O_RUNASUSER	0x9d
2750 	{ "RunAsUser",			O_RUNASUSER,	OI_NONE	},
2751 #define O_DSN_RRT	0x9e
2752 	{ "RrtImpliesDsn",		O_DSN_RRT,	OI_NONE	},
2753 #define O_PIDFILE	0x9f
2754 	{ "PidFile",			O_PIDFILE,	OI_NONE	},
2755 #define O_DONTBLAMESENDMAIL	0xa0
2756 	{ "DontBlameSendmail",		O_DONTBLAMESENDMAIL,	OI_NONE	},
2757 #define O_DPI		0xa1
2758 	{ "DontProbeInterfaces",	O_DPI,		OI_NONE	},
2759 #define O_MAXRCPT	0xa2
2760 	{ "MaxRecipientsPerMessage",	O_MAXRCPT,	OI_SAFE	},
2761 #define O_DEADLETTER	0xa3
2762 	{ "DeadLetterDrop",		O_DEADLETTER,	OI_NONE	},
2763 #if _FFR_DONTLOCKFILESFORREAD_OPTION
2764 # define O_DONTLOCK	0xa4
2765 	{ "DontLockFilesForRead",	O_DONTLOCK,	OI_NONE	},
2766 #endif
2767 #define O_MAXALIASRCSN	0xa5
2768 	{ "MaxAliasRecursion",		O_MAXALIASRCSN,	OI_NONE	},
2769 #define O_CNCTONLYTO	0xa6
2770 	{ "ConnectOnlyTo",		O_CNCTONLYTO,	OI_NONE	},
2771 #define O_TRUSTUSER	0xa7
2772 	{ "TrustedUser",		O_TRUSTUSER,	OI_NONE	},
2773 #define O_MAXMIMEHDRLEN	0xa8
2774 	{ "MaxMimeHeaderLength",	O_MAXMIMEHDRLEN,	OI_NONE	},
2775 #define O_CONTROLSOCKET	0xa9
2776 	{ "ControlSocketName",		O_CONTROLSOCKET,	OI_NONE	},
2777 #define O_MAXHDRSLEN	0xaa
2778 	{ "MaxHeadersLength",		O_MAXHDRSLEN,	OI_NONE	},
2779 #if _FFR_MAX_FORWARD_ENTRIES
2780 # define O_MAXFORWARD	0xab
2781 	{ "MaxForwardEntries",		O_MAXFORWARD,	OI_NONE	},
2782 #endif
2783 #define O_PROCTITLEPREFIX	0xac
2784 	{ "ProcessTitlePrefix",		O_PROCTITLEPREFIX,	OI_NONE	},
2785 #define O_SASLINFO	0xad
2786 #if _FFR_ALLOW_SASLINFO
2787 	{ "DefaultAuthInfo",		O_SASLINFO,	OI_SAFE	},
2788 #else
2789 	{ "DefaultAuthInfo",		O_SASLINFO,	OI_NONE	},
2790 #endif
2791 #define O_SASLMECH	0xae
2792 	{ "AuthMechanisms",		O_SASLMECH,	OI_NONE	},
2793 #define O_CLIENTPORT	0xaf
2794 	{ "ClientPortOptions",		O_CLIENTPORT,	OI_NONE	},
2795 #define O_DF_BUFSIZE	0xb0
2796 	{ "DataFileBufferSize",		O_DF_BUFSIZE,	OI_NONE	},
2797 #define O_XF_BUFSIZE	0xb1
2798 	{ "XscriptFileBufferSize",	O_XF_BUFSIZE,	OI_NONE	},
2799 #define O_LDAPDEFAULTSPEC	0xb2
2800 	{ "LDAPDefaultSpec",		O_LDAPDEFAULTSPEC,	OI_NONE	},
2801 #define O_SRVCERTFILE	0xb4
2802 	{ "ServerCertFile",		O_SRVCERTFILE,	OI_NONE	},
2803 #define O_SRVKEYFILE	0xb5
2804 	{ "ServerKeyFile",		O_SRVKEYFILE,	OI_NONE	},
2805 #define O_CLTCERTFILE	0xb6
2806 	{ "ClientCertFile",		O_CLTCERTFILE,	OI_NONE	},
2807 #define O_CLTKEYFILE	0xb7
2808 	{ "ClientKeyFile",		O_CLTKEYFILE,	OI_NONE	},
2809 #define O_CACERTFILE	0xb8
2810 	{ "CACertFile",			O_CACERTFILE,	OI_NONE	},
2811 #define O_CACERTPATH	0xb9
2812 	{ "CACertPath",			O_CACERTPATH,	OI_NONE	},
2813 #define O_DHPARAMS	0xba
2814 	{ "DHParameters",		O_DHPARAMS,	OI_NONE	},
2815 #define O_INPUTMILTER	0xbb
2816 	{ "InputMailFilters",		O_INPUTMILTER,	OI_NONE	},
2817 #define O_MILTER	0xbc
2818 	{ "Milter",			O_MILTER,	OI_SUBOPT	},
2819 #define O_SASLOPTS	0xbd
2820 	{ "AuthOptions",		O_SASLOPTS,	OI_NONE	},
2821 #define O_QUEUE_FILE_MODE	0xbe
2822 	{ "QueueFileMode",		O_QUEUE_FILE_MODE, OI_NONE	},
2823 #define O_DIG_ALG	0xbf
2824 	{ "CertFingerprintAlgorithm",		O_DIG_ALG,	OI_NONE	},
2825 #define O_CIPHERLIST	0xc0
2826 	{ "CipherList",			O_CIPHERLIST,	OI_NONE	},
2827 #define O_RANDFILE	0xc1
2828 	{ "RandFile",			O_RANDFILE,	OI_NONE	},
2829 #define O_TLS_SRV_OPTS	0xc2
2830 	{ "TLSSrvOptions",		O_TLS_SRV_OPTS,	OI_NONE	},
2831 #define O_RCPTTHROT	0xc3
2832 	{ "BadRcptThrottle",		O_RCPTTHROT,	OI_SAFE	},
2833 #define O_DLVR_MIN	0xc4
2834 	{ "DeliverByMin",		O_DLVR_MIN,	OI_NONE	},
2835 #define O_MAXQUEUECHILDREN	0xc5
2836 	{ "MaxQueueChildren",		O_MAXQUEUECHILDREN,	OI_NONE	},
2837 #define O_MAXRUNNERSPERQUEUE	0xc6
2838 	{ "MaxRunnersPerQueue",		O_MAXRUNNERSPERQUEUE,	OI_NONE },
2839 #define O_DIRECTSUBMODIFIERS	0xc7
2840 	{ "DirectSubmissionModifiers",	O_DIRECTSUBMODIFIERS,	OI_NONE },
2841 #define O_NICEQUEUERUN	0xc8
2842 	{ "NiceQueueRun",		O_NICEQUEUERUN,	OI_NONE	},
2843 #define O_SHMKEY	0xc9
2844 	{ "SharedMemoryKey",		O_SHMKEY,	OI_NONE	},
2845 #define O_SASLBITS	0xca
2846 	{ "AuthMaxBits",		O_SASLBITS,	OI_NONE	},
2847 #define O_MBDB		0xcb
2848 	{ "MailboxDatabase",		O_MBDB,		OI_NONE	},
2849 #define O_MSQ		0xcc
2850 	{ "UseMSP",	O_MSQ,		OI_NONE	},
2851 #define O_DELAY_LA	0xcd
2852 	{ "DelayLA",	O_DELAY_LA,	OI_NONE	},
2853 #define O_FASTSPLIT	0xce
2854 	{ "FastSplit",	O_FASTSPLIT,	OI_NONE	},
2855 #define O_SOFTBOUNCE	0xcf
2856 	{ "SoftBounce",	O_SOFTBOUNCE,	OI_NONE	},
2857 #define O_SHMKEYFILE	0xd0
2858 	{ "SharedMemoryKeyFile",	O_SHMKEYFILE,	OI_NONE	},
2859 #define O_REJECTLOGINTERVAL	0xd1
2860 	{ "RejectLogInterval",	O_REJECTLOGINTERVAL,	OI_NONE	},
2861 #define O_REQUIRES_DIR_FSYNC	0xd2
2862 	{ "RequiresDirfsync",	O_REQUIRES_DIR_FSYNC,	OI_NONE	},
2863 #define O_CONNECTION_RATE_WINDOW_SIZE	0xd3
2864 	{ "ConnectionRateWindowSize", O_CONNECTION_RATE_WINDOW_SIZE, OI_NONE },
2865 #define O_CRLFILE	0xd4
2866 	{ "CRLFile",		O_CRLFILE,	OI_NONE	},
2867 #define O_FALLBACKSMARTHOST	0xd5
2868 	{ "FallbackSmartHost",		O_FALLBACKSMARTHOST,	OI_NONE	},
2869 #define O_SASLREALM	0xd6
2870 	{ "AuthRealm",		O_SASLREALM,	OI_NONE	},
2871 #define O_CRLPATH	0xd7
2872 	{ "CRLPath",		O_CRLPATH,	OI_NONE	},
2873 #define O_HELONAME 0xd8
2874 	{ "HeloName",   O_HELONAME,     OI_NONE },
2875 #if _FFR_MEMSTAT
2876 # define O_REFUSELOWMEM	0xd9
2877 	{ "RefuseLowMem",	O_REFUSELOWMEM,	OI_NONE },
2878 # define O_QUEUELOWMEM	0xda
2879 	{ "QueueLowMem",	O_QUEUELOWMEM,	OI_NONE },
2880 # define O_MEMRESOURCE	0xdb
2881 	{ "MemoryResource",	O_MEMRESOURCE,	OI_NONE },
2882 #endif /* _FFR_MEMSTAT */
2883 #define O_MAXNOOPCOMMANDS 0xdc
2884 	{ "MaxNOOPCommands",	O_MAXNOOPCOMMANDS,	OI_NONE },
2885 #if _FFR_MSG_ACCEPT
2886 # define O_MSG_ACCEPT 0xdd
2887 	{ "MessageAccept",	O_MSG_ACCEPT,	OI_NONE },
2888 #endif
2889 #if _FFR_QUEUE_RUN_PARANOIA
2890 # define O_CHK_Q_RUNNERS 0xde
2891 	{ "CheckQueueRunners",	O_CHK_Q_RUNNERS,	OI_NONE },
2892 #endif
2893 #if _FFR_EIGHT_BIT_ADDR_OK
2894 # if !ALLOW_255
2895 #  ERROR FFR_EIGHT_BIT_ADDR_OK requires _ALLOW_255
2896 # endif
2897 # define O_EIGHT_BIT_ADDR_OK	0xdf
2898 	{ "EightBitAddrOK",	O_EIGHT_BIT_ADDR_OK,	OI_NONE },
2899 #endif /* _FFR_EIGHT_BIT_ADDR_OK */
2900 #if _FFR_ADDR_TYPE_MODES
2901 # define O_ADDR_TYPE_MODES	0xe0
2902 	{ "AddrTypeModes",	O_ADDR_TYPE_MODES,	OI_NONE },
2903 #endif
2904 #if _FFR_BADRCPT_SHUTDOWN
2905 # define O_RCPTSHUTD	0xe1
2906 	{ "BadRcptShutdown",		O_RCPTSHUTD,	OI_SAFE },
2907 # define O_RCPTSHUTDG	0xe2
2908 	{ "BadRcptShutdownGood",	O_RCPTSHUTDG,	OI_SAFE	},
2909 #endif /* _FFR_BADRCPT_SHUTDOWN */
2910 #define O_SRV_SSL_OPTIONS	0xe3
2911 	{ "ServerSSLOptions",		O_SRV_SSL_OPTIONS,	OI_NONE	},
2912 #define O_CLT_SSL_OPTIONS	0xe4
2913 	{ "ClientSSLOptions",		O_CLT_SSL_OPTIONS,	OI_NONE	},
2914 #define O_MAX_QUEUE_AGE	0xe5
2915 	{ "MaxQueueAge",	O_MAX_QUEUE_AGE,	OI_NONE },
2916 #if _FFR_RCPTTHROTDELAY
2917 # define O_RCPTTHROTDELAY	0xe6
2918 	{ "BadRcptThrottleDelay",	O_RCPTTHROTDELAY,	OI_SAFE	},
2919 #endif
2920 #if 0 && _FFR_QOS && defined(SOL_IP) && defined(IP_TOS)
2921 # define O_INETQOS	0xe7	/* reserved for FFR_QOS */
2922 	{ "InetQoS",			O_INETQOS,	OI_NONE },
2923 #endif
2924 #if STARTTLS && _FFR_FIPSMODE
2925 # define O_FIPSMODE	0xe8
2926 	{ "FIPSMode",		O_FIPSMODE,	OI_NONE	},
2927 #endif
2928 #if _FFR_REJECT_NUL_BYTE
2929 # define O_REJECTNUL	0xe9
2930 	{ "RejectNUL",	O_REJECTNUL,	OI_SAFE	},
2931 #endif
2932 #if _FFR_BOUNCE_QUEUE
2933 # define O_BOUNCEQUEUE 0xea
2934 	{ "BounceQueue",		O_BOUNCEQUEUE,	OI_NONE },
2935 #endif
2936 #if _FFR_ADD_BCC
2937 # define O_ADDBCC 0xeb
2938 	{ "AddBcc",			O_ADDBCC,	OI_NONE },
2939 #endif
2940 #define O_USECOMPRESSEDIPV6ADDRESSES 0xec
2941 	{ "UseCompressedIPv6Addresses",	O_USECOMPRESSEDIPV6ADDRESSES, OI_NONE },
2942 #if STARTTLS
2943 # define O_SSLENGINE	0xed
2944 	{ "SSLEngine",		O_SSLENGINE,	OI_NONE	},
2945 # define O_SSLENGINEPATH	0xee
2946 	{ "SSLEnginePath",	O_SSLENGINEPATH,	OI_NONE	},
2947 # define O_TLSFB2CLEAR		0xef
2948 	{ "TLSFallbacktoClear",	O_TLSFB2CLEAR,	OI_NONE	},
2949 #endif
2950 #if DNSSEC_TEST
2951 # define O_NSPORTIP		0xf0
2952 	{ "NameServer",	O_NSPORTIP,	OI_NONE	},
2953 #endif
2954 #if DANE
2955 # define O_DANE		0xf1
2956 	{ "DANE",	O_DANE,	OI_NONE	},
2957 #endif
2958 #if DNSSEC_TEST
2959 # define O_NSSRCHLIST		0xf2
2960 	{ "NameSearchList",	O_NSSRCHLIST,	OI_NONE	},
2961 #endif
2962 #if _FFR_BLANKENV_MACV
2963 # define O_HACKS	0xf4
2964 	{ "Hacks",		O_HACKS,	OI_NONE	},
2965 #endif
2966 #if _FFR_KEEPBCC
2967 # define O_KEEPBCC	0xf3
2968 	{ "KeepBcc",		O_KEEPBCC,	OI_NONE	},
2969 #endif
2970 
2971 #if _FFR_CLIENTCA
2972 #define O_CLTCACERTFILE	0xf5
2973 	{ "ClientCACertFile",			O_CLTCACERTFILE, OI_NONE },
2974 #define O_CLTCACERTPATH	0xf6
2975 	{ "ClientCACertPath",			O_CLTCACERTPATH, OI_NONE },
2976 #endif
2977 #if _FFR_TLS_ALTNAMES
2978 # define O_CHECKALTNAMES 0xf7
2979 	{ "SetCertAltnames",			O_CHECKALTNAMES, OI_NONE },
2980 #endif
2981 
2982 	{ NULL,				'\0',		OI_NONE	}
2983 };
2984 
2985 # define CANONIFY(val)
2986 
2987 # define SET_OPT_DEFAULT(opt, val)	opt = val
2988 
2989 /* set a string option by expanding the value and assigning it */
2990 /* WARNING this belongs ONLY into a case statement! */
2991 #define SET_STRING_EXP(str)	\
2992 		expand(val, exbuf, sizeof(exbuf), e);	\
2993 		newval = sm_pstrdup_x(exbuf);		\
2994 		if (str != NULL)	\
2995 			sm_free(str);	\
2996 		CANONIFY(newval);	\
2997 		str = newval;		\
2998 		break
2999 
3000 #define OPTNAME	o->o_name == NULL ? "<unknown>" : o->o_name
3001 
3002 void
3003 setoption(opt, val, safe, sticky, e)
3004 	int opt;
3005 	char *val;
3006 	bool safe;
3007 	bool sticky;
3008 	register ENVELOPE *e;
3009 {
3010 	register char *p;
3011 	register struct optioninfo *o;
3012 	char *subopt;
3013 	int i;
3014 	bool can_setuid = RunAsUid == 0;
3015 	auto char *ep;
3016 	char buf[50];
3017 	extern bool Warn_Q_option;
3018 #if _FFR_ALLOW_SASLINFO
3019 	extern unsigned int SubmitMode;
3020 #endif
3021 #if STARTTLS || SM_CONF_SHM
3022 	char *newval;
3023 	char exbuf[MAXLINE];
3024 #endif
3025 #if STARTTLS
3026 	unsigned long *pssloptions = NULL;
3027 #endif
3028 
3029 	errno = 0;
3030 	if (opt == ' ')
3031 	{
3032 		/* full word options */
3033 		struct optioninfo *sel;
3034 
3035 		p = strchr(val, '=');
3036 		if (p == NULL)
3037 			p = &val[strlen(val)];
3038 		while (*--p == ' ')
3039 			continue;
3040 		while (*++p == ' ')
3041 			*p = '\0';
3042 		if (p == val)
3043 		{
3044 			syserr("readcf: null option name");
3045 			return;
3046 		}
3047 		if (*p == '=')
3048 			*p++ = '\0';
3049 		while (*p == ' ')
3050 			p++;
3051 		subopt = strchr(val, '.');
3052 		if (subopt != NULL)
3053 			*subopt++ = '\0';
3054 		sel = NULL;
3055 		for (o = OptionTab; o->o_name != NULL; o++)
3056 		{
3057 			if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
3058 				continue;
3059 			if (strlen(o->o_name) == strlen(val))
3060 			{
3061 				/* completely specified -- this must be it */
3062 				sel = NULL;
3063 				break;
3064 			}
3065 			if (sel != NULL)
3066 				break;
3067 			sel = o;
3068 		}
3069 		if (sel != NULL && o->o_name == NULL)
3070 			o = sel;
3071 		else if (o->o_name == NULL)
3072 		{
3073 			syserr("readcf: unknown option name %s", val);
3074 			return;
3075 		}
3076 		else if (sel != NULL)
3077 		{
3078 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
3079 				val, sel->o_name, o->o_name);
3080 			return;
3081 		}
3082 		if (strlen(val) != strlen(o->o_name))
3083 		{
3084 			int oldVerbose = Verbose;
3085 
3086 			Verbose = 1;
3087 			message("Option %s used as abbreviation for %s",
3088 				val, o->o_name);
3089 			Verbose = oldVerbose;
3090 		}
3091 		opt = o->o_code;
3092 		val = p;
3093 	}
3094 	else
3095 	{
3096 		for (o = OptionTab; o->o_name != NULL; o++)
3097 		{
3098 			if (o->o_code == opt)
3099 				break;
3100 		}
3101 		if (o->o_name == NULL)
3102 		{
3103 			syserr("readcf: unknown option name 0x%x", opt & 0xff);
3104 			return;
3105 		}
3106 		subopt = NULL;
3107 	}
3108 
3109 	if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
3110 	{
3111 		if (tTd(37, 1))
3112 			sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
3113 				   OPTNAME, subopt);
3114 		subopt = NULL;
3115 	}
3116 
3117 	if (tTd(37, 1))
3118 	{
3119 		sm_dprintf(isascii(opt) && isprint(opt) ?
3120 			   "setoption %s (%c)%s%s=" :
3121 			   "setoption %s (0x%x)%s%s=",
3122 			   OPTNAME, opt, subopt == NULL ? "" : ".",
3123 			   subopt == NULL ? "" : subopt);
3124 		xputs(sm_debug_file(), val);
3125 	}
3126 
3127 	/*
3128 	**  See if this option is preset for us.
3129 	*/
3130 
3131 	if (!sticky && bitnset(opt, StickyOpt))
3132 	{
3133 		if (tTd(37, 1))
3134 			sm_dprintf(" (ignored)\n");
3135 		return;
3136 	}
3137 
3138 	/*
3139 	**  Check to see if this option can be specified by this user.
3140 	*/
3141 
3142 	if (!safe && RealUid == 0)
3143 		safe = true;
3144 	if (!safe && !bitset(OI_SAFE, o->o_flags))
3145 	{
3146 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
3147 		{
3148 			int dp;
3149 
3150 			if (tTd(37, 1))
3151 				sm_dprintf(" (unsafe)");
3152 			dp = drop_privileges(true);
3153 			setstat(dp);
3154 		}
3155 	}
3156 	if (tTd(37, 1))
3157 		sm_dprintf("\n");
3158 
3159 	switch (opt & 0xff)
3160 	{
3161 	  case '7':		/* force seven-bit input */
3162 		SevenBitInput = atobool(val);
3163 		break;
3164 
3165 	  case '8':		/* handling of 8-bit input */
3166 #if MIME8TO7
3167 		switch (*val)
3168 		{
3169 		  case 'p':		/* pass 8 bit, convert MIME */
3170 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
3171 			break;
3172 
3173 		  case 'm':		/* convert 8-bit, convert MIME */
3174 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
3175 			break;
3176 
3177 		  case 's':		/* strict adherence */
3178 			MimeMode = MM_CVTMIME;
3179 			break;
3180 
3181 # if 0
3182 		  case 'r':		/* reject 8-bit, don't convert MIME */
3183 			MimeMode = 0;
3184 			break;
3185 
3186 		  case 'j':		/* "just send 8" */
3187 			MimeMode = MM_PASS8BIT;
3188 			break;
3189 
3190 		  case 'a':		/* encode 8 bit if available */
3191 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
3192 			break;
3193 
3194 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
3195 			MimeMode = MM_MIME8BIT;
3196 			break;
3197 # endif /* 0 */
3198 
3199 		  default:
3200 			syserr("Unknown 8-bit mode %c", *val);
3201 			finis(false, true, EX_USAGE);
3202 		}
3203 #else /* MIME8TO7 */
3204 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3205 				     "Warning: Option: %s requires MIME8TO7 support\n",
3206 				     OPTNAME);
3207 #endif /* MIME8TO7 */
3208 		break;
3209 
3210 	  case 'A':		/* set default alias file */
3211 		if (val[0] == '\0')
3212 		{
3213 			char *al;
3214 
3215 			SET_OPT_DEFAULT(al, "aliases");
3216 			setalias(al);
3217 		}
3218 		else
3219 			setalias(val);
3220 		break;
3221 
3222 	  case 'a':		/* look N minutes for "@:@" in alias file */
3223 		if (val[0] == '\0')
3224 			SafeAlias = 5 MINUTES;
3225 		else
3226 			SafeAlias = convtime(val, 'm');
3227 		break;
3228 
3229 	  case 'B':		/* substitution for blank character */
3230 		SpaceSub = val[0];
3231 		if (SpaceSub == '\0')
3232 			SpaceSub = ' ';
3233 		break;
3234 
3235 	  case 'b':		/* min blocks free on queue fs/max msg size */
3236 		p = strchr(val, '/');
3237 		if (p != NULL)
3238 		{
3239 			*p++ = '\0';
3240 			MaxMessageSize = atol(p);
3241 		}
3242 		MinBlocksFree = atol(val);
3243 		break;
3244 
3245 	  case 'c':		/* don't connect to "expensive" mailers */
3246 		NoConnect = atobool(val);
3247 		break;
3248 
3249 	  case 'C':		/* checkpoint every N addresses */
3250 		if (safe || CheckpointInterval > atoi(val))
3251 			CheckpointInterval = atoi(val);
3252 		break;
3253 
3254 	  case 'd':		/* delivery mode */
3255 		switch (*val)
3256 		{
3257 		  case '\0':
3258 			set_delivery_mode(SM_DELIVER, e);
3259 			break;
3260 
3261 		  case SM_QUEUE:	/* queue only */
3262 		  case SM_DEFER:	/* queue only and defer map lookups */
3263 		  case SM_DELIVER:	/* do everything */
3264 		  case SM_FORK:		/* fork after verification */
3265 #if _FFR_DM_ONE
3266 		/* deliver first TA in background, then queue */
3267 		  case SM_DM_ONE:
3268 #endif
3269 			set_delivery_mode(*val, e);
3270 			break;
3271 
3272 #if _FFR_PROXY
3273 		  case SM_PROXY_REQ:
3274 			set_delivery_mode(*val, e);
3275 			break;
3276 #endif /* _FFR_PROXY */
3277 
3278 		  default:
3279 			syserr("Unknown delivery mode %c", *val);
3280 			finis(false, true, EX_USAGE);
3281 		}
3282 		break;
3283 
3284 	  case 'E':		/* error message header/header file */
3285 		if (*val != '\0')
3286 			ErrMsgFile = newstr(val);
3287 		break;
3288 
3289 	  case 'e':		/* set error processing mode */
3290 		switch (*val)
3291 		{
3292 		  case EM_QUIET:	/* be silent about it */
3293 		  case EM_MAIL:		/* mail back */
3294 		  case EM_BERKNET:	/* do berknet error processing */
3295 		  case EM_WRITE:	/* write back (or mail) */
3296 		  case EM_PRINT:	/* print errors normally (default) */
3297 			e->e_errormode = *val;
3298 			break;
3299 		}
3300 		break;
3301 
3302 	  case 'F':		/* file mode */
3303 		FileMode = atooct(val) & 0777;
3304 		break;
3305 
3306 	  case 'f':		/* save Unix-style From lines on front */
3307 		SaveFrom = atobool(val);
3308 		break;
3309 
3310 	  case 'G':		/* match recipients against GECOS field */
3311 		MatchGecos = atobool(val);
3312 		break;
3313 
3314 	  case 'g':		/* default gid */
3315   g_opt:
3316 		if (isascii(*val) && isdigit(*val))
3317 			DefGid = atoi(val);
3318 		else
3319 		{
3320 			register struct group *gr;
3321 
3322 			DefGid = -1;
3323 			gr = getgrnam(val);
3324 			if (gr == NULL)
3325 				syserr("readcf: option %c: unknown group %s",
3326 					opt, val);
3327 			else
3328 				DefGid = gr->gr_gid;
3329 		}
3330 		break;
3331 
3332 	  case 'H':		/* help file */
3333 		if (val[0] == '\0')
3334 		{
3335 			SET_OPT_DEFAULT(HelpFile, "helpfile");
3336 		}
3337 		else
3338 		{
3339 			CANONIFY(val);
3340 			HelpFile = newstr(val);
3341 		}
3342 		break;
3343 
3344 	  case 'h':		/* maximum hop count */
3345 		MaxHopCount = atoi(val);
3346 		break;
3347 
3348 	  case 'I':		/* use internet domain name server */
3349 #if NAMED_BIND
3350 		for (p = val; *p != 0; )
3351 		{
3352 			bool clearmode;
3353 			char *q;
3354 			struct resolverflags *rfp;
3355 
3356 			while (*p == ' ')
3357 				p++;
3358 			if (*p == '\0')
3359 				break;
3360 			clearmode = false;
3361 			if (*p == '-')
3362 				clearmode = true;
3363 			else if (*p != '+')
3364 				p--;
3365 			p++;
3366 			q = p;
3367 			while (*p != '\0' && !(SM_ISSPACE(*p)))
3368 				p++;
3369 			if (*p != '\0')
3370 				*p++ = '\0';
3371 			if (sm_strcasecmp(q, "HasWildcardMX") == 0)
3372 			{
3373 				HasWildcardMX = !clearmode;
3374 				continue;
3375 			}
3376 			if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
3377 			{
3378 				WorkAroundBrokenAAAA = !clearmode;
3379 				continue;
3380 			}
3381 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
3382 			{
3383 				if (sm_strcasecmp(q, rfp->rf_name) == 0)
3384 					break;
3385 			}
3386 			if (rfp->rf_name == NULL)
3387 				syserr("readcf: I option value %s unrecognized", q);
3388 			else if (clearmode)
3389 				_res.options &= ~rfp->rf_bits;
3390 			else
3391 				_res.options |= rfp->rf_bits;
3392 		}
3393 		if (tTd(8, 2))
3394 			sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
3395 				   (unsigned int) _res.options, HasWildcardMX);
3396 #else /* NAMED_BIND */
3397 		usrerr("name server (I option) specified but BIND not compiled in");
3398 #endif /* NAMED_BIND */
3399 		break;
3400 
3401 	  case 'i':		/* ignore dot lines in message */
3402 		IgnrDot = atobool(val);
3403 		break;
3404 
3405 	  case 'j':		/* send errors in MIME (RFC 1341) format */
3406 		SendMIMEErrors = atobool(val);
3407 		break;
3408 
3409 	  case 'J':		/* .forward search path */
3410 		CANONIFY(val);
3411 		ForwardPath = newstr(val);
3412 		break;
3413 
3414 	  case 'k':		/* connection cache size */
3415 		MaxMciCache = atoi(val);
3416 		if (MaxMciCache < 0)
3417 			MaxMciCache = 0;
3418 		break;
3419 
3420 	  case 'K':		/* connection cache timeout */
3421 		MciCacheTimeout = convtime(val, 'm');
3422 		break;
3423 
3424 	  case 'l':		/* use Errors-To: header */
3425 		UseErrorsTo = atobool(val);
3426 		break;
3427 
3428 	  case 'L':		/* log level */
3429 		if (safe || LogLevel < atoi(val))
3430 			LogLevel = atoi(val);
3431 		break;
3432 
3433 	  case 'M':		/* define macro */
3434 		sticky = false;
3435 		i = macid_parse(val, &ep);
3436 		if (i == 0)
3437 			break;
3438 		p = newstr(ep);
3439 		if (!safe)
3440 			cleanstrcpy(p, p, strlen(p) + 1);
3441 		macdefine(&CurEnv->e_macro, A_TEMP, i, p);
3442 		break;
3443 
3444 	  case 'm':		/* send to me too */
3445 		MeToo = atobool(val);
3446 		break;
3447 
3448 	  case 'n':		/* validate RHS in newaliases */
3449 		CheckAliases = atobool(val);
3450 		break;
3451 
3452 	    /* 'N' available -- was "net name" */
3453 
3454 	  case 'O':		/* daemon options */
3455 		if (!setdaemonoptions(val))
3456 			syserr("too many daemons defined (%d max)", MAXDAEMONS);
3457 		break;
3458 
3459 	  case 'o':		/* assume old style headers */
3460 		if (atobool(val))
3461 			CurEnv->e_flags |= EF_OLDSTYLE;
3462 		else
3463 			CurEnv->e_flags &= ~EF_OLDSTYLE;
3464 		break;
3465 
3466 	  case 'p':		/* select privacy level */
3467 		p = val;
3468 		for (;;)
3469 		{
3470 			register struct prival *pv;
3471 			extern struct prival PrivacyValues[];
3472 
3473 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3474 				p++;
3475 			if (*p == '\0')
3476 				break;
3477 			val = p;
3478 			while (isascii(*p) && isalnum(*p))
3479 				p++;
3480 			if (*p != '\0')
3481 				*p++ = '\0';
3482 
3483 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
3484 			{
3485 				if (sm_strcasecmp(val, pv->pv_name) == 0)
3486 					break;
3487 			}
3488 			if (pv->pv_name == NULL)
3489 				syserr("readcf: Op line: %s unrecognized", val);
3490 			else
3491 				PrivacyFlags |= pv->pv_flag;
3492 		}
3493 		sticky = false;
3494 		break;
3495 
3496 	  case 'P':		/* postmaster copy address for returned mail */
3497 		PostMasterCopy = newstr(val);
3498 		break;
3499 
3500 	  case 'q':		/* slope of queue only function */
3501 		QueueFactor = atoi(val);
3502 		break;
3503 
3504 	  case 'Q':		/* queue directory */
3505 		if (val[0] == '\0')
3506 		{
3507 			QueueDir = "mqueue";
3508 		}
3509 		else
3510 		{
3511 			QueueDir = newstr(val);
3512 		}
3513 		if (RealUid != 0 && !safe)
3514 			Warn_Q_option = true;
3515 		break;
3516 
3517 	  case 'R':		/* don't prune routes */
3518 		DontPruneRoutes = atobool(val);
3519 		break;
3520 
3521 	  case 'r':		/* read timeout */
3522 		if (subopt == NULL)
3523 			inittimeouts(val, sticky);
3524 		else
3525 			settimeout(subopt, val, sticky);
3526 		break;
3527 
3528 	  case 'S':		/* status file */
3529 		if (val[0] == '\0')
3530 		{
3531 			SET_OPT_DEFAULT(StatFile, "statistics");
3532 		}
3533 		else
3534 		{
3535 			CANONIFY(val);
3536 			StatFile = newstr(val);
3537 		}
3538 		break;
3539 
3540 	  case 's':		/* be super safe, even if expensive */
3541 		if (tolower(*val) == 'i')
3542 			SuperSafe = SAFE_INTERACTIVE;
3543 		else if (tolower(*val) == 'p')
3544 #if MILTER
3545 			SuperSafe = SAFE_REALLY_POSTMILTER;
3546 #else /* MILTER */
3547 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3548 				"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
3549 #endif /* MILTER */
3550 		else
3551 			SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
3552 		break;
3553 
3554 	  case 'T':		/* queue timeout */
3555 		p = strchr(val, '/');
3556 		if (p != NULL)
3557 		{
3558 			*p++ = '\0';
3559 			settimeout("queuewarn", p, sticky);
3560 		}
3561 		settimeout("queuereturn", val, sticky);
3562 		break;
3563 
3564 	  case 't':		/* time zone name */
3565 		TimeZoneSpec = newstr(val);
3566 		break;
3567 
3568 	  case 'U':		/* location of user database */
3569 		UdbSpec = newstr(val);
3570 		break;
3571 
3572 	  case 'u':		/* set default uid */
3573 		for (p = val; *p != '\0'; p++)
3574 		{
3575 # if _FFR_DOTTED_USERNAMES
3576 			if (*p == '/' || *p == ':')
3577 # else
3578 			if (*p == '.' || *p == '/' || *p == ':')
3579 # endif
3580 			{
3581 				*p++ = '\0';
3582 				break;
3583 			}
3584 		}
3585 		if (isascii(*val) && isdigit(*val))
3586 		{
3587 			DefUid = atoi(val);
3588 			setdefuser();
3589 		}
3590 		else
3591 		{
3592 			register struct passwd *pw;
3593 
3594 			DefUid = -1;
3595 			pw = sm_getpwnam(val);
3596 			if (pw == NULL)
3597 			{
3598 				syserr("readcf: option u: unknown user %s", val);
3599 				break;
3600 			}
3601 			else
3602 			{
3603 				DefUid = pw->pw_uid;
3604 				DefGid = pw->pw_gid;
3605 				DefUser = newstr(pw->pw_name);
3606 			}
3607 		}
3608 
3609 # ifdef UID_MAX
3610 		if (DefUid > UID_MAX)
3611 		{
3612 			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
3613 				(long)DefUid, (long)UID_MAX);
3614 			break;
3615 		}
3616 # endif /* UID_MAX */
3617 
3618 		/* handle the group if it is there */
3619 		if (*p == '\0')
3620 			break;
3621 		val = p;
3622 		goto g_opt;
3623 
3624 	  case 'V':		/* fallback MX host */
3625 		if (val[0] != '\0')
3626 			FallbackMX = newstr(val);
3627 		break;
3628 
3629 	  case 'v':		/* run in verbose mode */
3630 		Verbose = atobool(val) ? 1 : 0;
3631 		break;
3632 
3633 	  case 'w':		/* if we are best MX, try host directly */
3634 		TryNullMXList = atobool(val);
3635 		break;
3636 
3637 	    /* 'W' available -- was wizard password */
3638 
3639 	  case 'x':		/* load avg at which to auto-queue msgs */
3640 		QueueLA = atoi(val);
3641 		break;
3642 
3643 	  case 'X':	/* load avg at which to auto-reject connections */
3644 		RefuseLA = atoi(val);
3645 		break;
3646 
3647 	  case O_DELAY_LA:	/* load avg at which to delay connections */
3648 		DelayLA = atoi(val);
3649 		break;
3650 
3651 	  case 'y':		/* work recipient factor */
3652 		WkRecipFact = atoi(val);
3653 		break;
3654 
3655 	  case 'Y':		/* fork jobs during queue runs */
3656 		ForkQueueRuns = atobool(val);
3657 		break;
3658 
3659 	  case 'z':		/* work message class factor */
3660 		WkClassFact = atoi(val);
3661 		break;
3662 
3663 	  case 'Z':		/* work time factor */
3664 		WkTimeFact = atoi(val);
3665 		break;
3666 
3667 
3668 #if _FFR_QUEUE_GROUP_SORTORDER
3669 	/* coordinate this with makequeue() */
3670 #endif
3671 	  case O_QUEUESORTORD:	/* queue sorting order */
3672 		switch (*val)
3673 		{
3674 		  case 'f':	/* File Name */
3675 		  case 'F':
3676 			QueueSortOrder = QSO_BYFILENAME;
3677 			break;
3678 
3679 		  case 'h':	/* Host first */
3680 		  case 'H':
3681 			QueueSortOrder = QSO_BYHOST;
3682 			break;
3683 
3684 		  case 'm':	/* Modification time */
3685 		  case 'M':
3686 			QueueSortOrder = QSO_BYMODTIME;
3687 			break;
3688 
3689 		  case 'p':	/* Priority order */
3690 		  case 'P':
3691 			QueueSortOrder = QSO_BYPRIORITY;
3692 			break;
3693 
3694 		  case 't':	/* Submission time */
3695 		  case 'T':
3696 			QueueSortOrder = QSO_BYTIME;
3697 			break;
3698 
3699 		  case 'r':	/* Random */
3700 		  case 'R':
3701 			QueueSortOrder = QSO_RANDOM;
3702 			break;
3703 
3704 #if _FFR_RHS
3705 		  case 's':	/* Shuffled host name */
3706 		  case 'S':
3707 			QueueSortOrder = QSO_BYSHUFFLE;
3708 			break;
3709 #endif /* _FFR_RHS */
3710 
3711 		  case 'n':	/* none */
3712 		  case 'N':
3713 			QueueSortOrder = QSO_NONE;
3714 			break;
3715 
3716 		  default:
3717 			syserr("Invalid queue sort order \"%s\"", val);
3718 		}
3719 		break;
3720 
3721 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
3722 		CANONIFY(val);
3723 		HostsFile = newstr(val);
3724 		break;
3725 
3726 	  case O_MQA:		/* minimum queue age between deliveries */
3727 		MinQueueAge = convtime(val, 'm');
3728 		break;
3729 
3730 	  case O_MAX_QUEUE_AGE:
3731 		MaxQueueAge = convtime(val, 'm');
3732 		break;
3733 
3734 	  case O_DEFCHARSET:	/* default character set for mimefying */
3735 		DefaultCharSet = newstr(denlstring(val, true, true));
3736 		break;
3737 
3738 	  case O_SSFILE:	/* service switch file */
3739 		CANONIFY(val);
3740 		ServiceSwitchFile = newstr(val);
3741 		break;
3742 
3743 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
3744 		DialDelay = convtime(val, 's');
3745 		break;
3746 
3747 	  case O_NORCPTACTION:	/* what to do if no recipient */
3748 		if (sm_strcasecmp(val, "none") == 0)
3749 			NoRecipientAction = NRA_NO_ACTION;
3750 		else if (sm_strcasecmp(val, "add-to") == 0)
3751 			NoRecipientAction = NRA_ADD_TO;
3752 		else if (sm_strcasecmp(val, "add-apparently-to") == 0)
3753 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
3754 		else if (sm_strcasecmp(val, "add-bcc") == 0)
3755 			NoRecipientAction = NRA_ADD_BCC;
3756 		else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
3757 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
3758 		else
3759 			syserr("Invalid NoRecipientAction: %s", val);
3760 		break;
3761 
3762 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
3763 		if (*val == '\0')
3764 			break;
3765 
3766 		/* strip trailing slashes */
3767 		p = val + strlen(val) - 1;
3768 		while (p >= val && *p == '/')
3769 			*p-- = '\0';
3770 
3771 		if (*val == '\0')
3772 			break;
3773 
3774 		SafeFileEnv = newstr(val);
3775 		break;
3776 
3777 	  case O_MAXMSGSIZE:	/* maximum message size */
3778 		MaxMessageSize = atol(val);
3779 		break;
3780 
3781 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
3782 		ColonOkInAddr = atobool(val);
3783 		break;
3784 
3785 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
3786 		MaxQueueRun = atoi(val);
3787 		break;
3788 
3789 	  case O_MAXCHILDREN:	/* max # of children of daemon */
3790 		MaxChildren = atoi(val);
3791 		break;
3792 
3793 	  case O_MAXQUEUECHILDREN: /* max # of children of daemon */
3794 		MaxQueueChildren = atoi(val);
3795 		break;
3796 
3797 	  case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
3798 		MaxRunnersPerQueue = atoi(val);
3799 		break;
3800 
3801 	  case O_NICEQUEUERUN:		/* nice queue runs */
3802 #if !HASNICE
3803 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3804 				     "Warning: NiceQueueRun set on system that doesn't support nice()\n");
3805 #endif
3806 
3807 		/* XXX do we want to check the range? > 0 ? */
3808 		NiceQueueRun = atoi(val);
3809 		break;
3810 
3811 	  case O_SHMKEY:		/* shared memory key */
3812 #if SM_CONF_SHM
3813 		ShmKey = atol(val);
3814 #else /* SM_CONF_SHM */
3815 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3816 				     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3817 				     OPTNAME);
3818 #endif /* SM_CONF_SHM */
3819 		break;
3820 
3821 	  case O_SHMKEYFILE:		/* shared memory key file */
3822 #if SM_CONF_SHM
3823 		SET_STRING_EXP(ShmKeyFile);
3824 #else /* SM_CONF_SHM */
3825 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3826 				     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3827 				     OPTNAME);
3828 		break;
3829 #endif /* SM_CONF_SHM */
3830 
3831 #if _FFR_MAX_FORWARD_ENTRIES
3832 	  case O_MAXFORWARD:	/* max # of forward entries */
3833 		MaxForwardEntries = atoi(val);
3834 		break;
3835 #endif
3836 
3837 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
3838 		DontExpandCnames = atobool(val);
3839 		break;
3840 
3841 	  case O_MUSTQUOTE:	/* must quote these characters in phrases */
3842 		(void) sm_strlcpy(buf, "@,;:\\()[]", sizeof(buf));
3843 		if (strlen(val) < sizeof(buf) - 10)
3844 			(void) sm_strlcat(buf, val, sizeof(buf));
3845 		else
3846 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3847 					     "Warning: MustQuoteChars too long, ignored.\n");
3848 		MustQuoteChars = newstr(buf);
3849 		break;
3850 
3851 	  case O_SMTPGREETING:	/* SMTP greeting message (old $e macro) */
3852 		SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
3853 		break;
3854 
3855 	  case O_UNIXFROM:	/* UNIX From_ line (old $l macro) */
3856 		UnixFromLine = newstr(munchstring(val, NULL, '\0'));
3857 		break;
3858 
3859 	  case O_OPCHARS:	/* operator characters (old $o macro) */
3860 		if (OperatorChars != NULL)
3861 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3862 					     "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
3863 		OperatorChars = newstr(munchstring(val, NULL, '\0'));
3864 		break;
3865 
3866 	  case O_DONTINITGRPS:	/* don't call initgroups(3) */
3867 		DontInitGroups = atobool(val);
3868 		break;
3869 
3870 	  case O_SLFH:		/* make sure from fits on one line */
3871 		SingleLineFromHeader = atobool(val);
3872 		break;
3873 
3874 	  case O_ABH:		/* allow HELO commands with syntax errors */
3875 		AllowBogusHELO = atobool(val);
3876 		break;
3877 
3878 	  case O_CONNTHROT:	/* connection rate throttle */
3879 		ConnRateThrottle = atoi(val);
3880 		break;
3881 
3882 	  case O_UGW:		/* group writable files are unsafe */
3883 		if (!atobool(val))
3884 		{
3885 			setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
3886 				DontBlameSendmail);
3887 			setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
3888 				DontBlameSendmail);
3889 		}
3890 		break;
3891 
3892 	  case O_DBLBOUNCE:	/* address to which to send double bounces */
3893 		DoubleBounceAddr = newstr(val);
3894 		break;
3895 
3896 	  case O_HSDIR:		/* persistent host status directory */
3897 		if (val[0] != '\0')
3898 		{
3899 			CANONIFY(val);
3900 			HostStatDir = newstr(val);
3901 		}
3902 		break;
3903 
3904 	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
3905 		SingleThreadDelivery = atobool(val);
3906 		break;
3907 
3908 	  case O_RUNASUSER:	/* run bulk of code as this user */
3909 		for (p = val; *p != '\0'; p++)
3910 		{
3911 # if _FFR_DOTTED_USERNAMES
3912 			if (*p == '/' || *p == ':')
3913 # else
3914 			if (*p == '.' || *p == '/' || *p == ':')
3915 # endif
3916 			{
3917 				*p++ = '\0';
3918 				break;
3919 			}
3920 		}
3921 		if (isascii(*val) && isdigit(*val))
3922 		{
3923 			if (can_setuid)
3924 				RunAsUid = atoi(val);
3925 		}
3926 		else
3927 		{
3928 			register struct passwd *pw;
3929 
3930 			pw = sm_getpwnam(val);
3931 			if (pw == NULL)
3932 			{
3933 				syserr("readcf: option RunAsUser: unknown user %s", val);
3934 				break;
3935 			}
3936 			else if (can_setuid)
3937 			{
3938 				if (*p == '\0')
3939 					RunAsUserName = newstr(val);
3940 				RunAsUid = pw->pw_uid;
3941 				RunAsGid = pw->pw_gid;
3942 			}
3943 			else if (EffGid == pw->pw_gid)
3944 				RunAsGid = pw->pw_gid;
3945 			else if (UseMSP && *p == '\0')
3946 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3947 						     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3948 						     (long) EffGid,
3949 						     (long) pw->pw_gid);
3950 		}
3951 # ifdef UID_MAX
3952 		if (RunAsUid > UID_MAX)
3953 		{
3954 			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3955 				(long) RunAsUid, (long) UID_MAX);
3956 			break;
3957 		}
3958 # endif /* UID_MAX */
3959 		if (*p != '\0')
3960 		{
3961 			if (isascii(*p) && isdigit(*p))
3962 			{
3963 				gid_t runasgid;
3964 
3965 				runasgid = (gid_t) atoi(p);
3966 				if (can_setuid || EffGid == runasgid)
3967 					RunAsGid = runasgid;
3968 				else if (UseMSP)
3969 					(void) sm_io_fprintf(smioout,
3970 							     SM_TIME_DEFAULT,
3971 							     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3972 							     (long) EffGid,
3973 							     (long) runasgid);
3974 			}
3975 			else
3976 			{
3977 				register struct group *gr;
3978 
3979 				gr = getgrnam(p);
3980 				if (gr == NULL)
3981 					syserr("readcf: option RunAsUser: unknown group %s",
3982 						p);
3983 				else if (can_setuid || EffGid == gr->gr_gid)
3984 					RunAsGid = gr->gr_gid;
3985 				else if (UseMSP)
3986 					(void) sm_io_fprintf(smioout,
3987 							     SM_TIME_DEFAULT,
3988 							     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3989 							     (long) EffGid,
3990 							     (long) gr->gr_gid);
3991 			}
3992 		}
3993 		if (tTd(47, 5))
3994 			sm_dprintf("readcf: RunAsUser = %d:%d\n",
3995 				   (int) RunAsUid, (int) RunAsGid);
3996 		break;
3997 
3998 	  case O_DSN_RRT:
3999 		RrtImpliesDsn = atobool(val);
4000 		break;
4001 
4002 	  case O_PIDFILE:
4003 		PSTRSET(PidFile, val);
4004 		break;
4005 
4006 	  case O_DONTBLAMESENDMAIL:
4007 		p = val;
4008 		for (;;)
4009 		{
4010 			register struct dbsval *dbs;
4011 			extern struct dbsval DontBlameSendmailValues[];
4012 
4013 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
4014 				p++;
4015 			if (*p == '\0')
4016 				break;
4017 			val = p;
4018 			while (isascii(*p) && isalnum(*p))
4019 				p++;
4020 			if (*p != '\0')
4021 				*p++ = '\0';
4022 
4023 			for (dbs = DontBlameSendmailValues;
4024 			     dbs->dbs_name != NULL; dbs++)
4025 			{
4026 				if (sm_strcasecmp(val, dbs->dbs_name) == 0)
4027 					break;
4028 			}
4029 			if (dbs->dbs_name == NULL)
4030 				syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
4031 			else if (dbs->dbs_flag == DBS_SAFE)
4032 				clrbitmap(DontBlameSendmail);
4033 			else
4034 				setbitn(dbs->dbs_flag, DontBlameSendmail);
4035 		}
4036 		sticky = false;
4037 		break;
4038 
4039 	  case O_DPI:
4040 		if (sm_strcasecmp(val, "loopback") == 0)
4041 			DontProbeInterfaces = DPI_SKIPLOOPBACK;
4042 		else if (atobool(val))
4043 			DontProbeInterfaces = DPI_PROBENONE;
4044 		else
4045 			DontProbeInterfaces = DPI_PROBEALL;
4046 		break;
4047 
4048 	  case O_MAXRCPT:
4049 		MaxRcptPerMsg = atoi(val);
4050 		break;
4051 
4052 	  case O_RCPTTHROT:
4053 		BadRcptThrottle = atoi(val);
4054 		break;
4055 
4056 #if _FFR_RCPTTHROTDELAY
4057 	  case O_RCPTTHROTDELAY:
4058 		BadRcptThrottleDelay = atoi(val);
4059 		break;
4060 #endif
4061 
4062 	  case O_DEADLETTER:
4063 		CANONIFY(val);
4064 		PSTRSET(DeadLetterDrop, val);
4065 		break;
4066 
4067 #if _FFR_DONTLOCKFILESFORREAD_OPTION
4068 	  case O_DONTLOCK:
4069 		DontLockReadFiles = atobool(val);
4070 		break;
4071 #endif
4072 
4073 	  case O_MAXALIASRCSN:
4074 		MaxAliasRecursion = atoi(val);
4075 		break;
4076 
4077 	  case O_CNCTONLYTO:
4078 		/* XXX should probably use gethostbyname */
4079 #if NETINET || NETINET6
4080 		i = 0;
4081 		if ((subopt = strchr(val, '@')) != NULL)
4082 		{
4083 			*subopt = '\0';
4084 			i = (int) strtoul(val, NULL, 0);
4085 
4086 			/* stricter checks? probably not useful. */
4087 			if (i > USHRT_MAX)
4088 			{
4089 				syserr("readcf: option ConnectOnlyTo: invalid port %s",
4090 					val);
4091 				break;
4092 			}
4093 			val = subopt + 1;
4094 		}
4095 		ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
4096 # if NETINET6
4097 		if (anynet_pton(AF_INET6, val,
4098 				&ConnectOnlyTo.sin6.sin6_addr) == 1)
4099 		{
4100 			ConnectOnlyTo.sa.sa_family = AF_INET6;
4101 			if (i != 0)
4102 				ConnectOnlyTo.sin6.sin6_port = htons(i);
4103 		}
4104 		else
4105 # endif /* NETINET6 */
4106 # if NETINET
4107 		{
4108 			ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
4109 			if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
4110 				ConnectOnlyTo.sa.sa_family = AF_INET;
4111 			if (i != 0)
4112 				ConnectOnlyTo.sin.sin_port = htons(i);
4113 		}
4114 
4115 # endif /* NETINET */
4116 		if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
4117 		{
4118 			syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
4119 			       val);
4120 			break;
4121 		}
4122 #endif /* NETINET || NETINET6 */
4123 		break;
4124 
4125 	  case O_TRUSTUSER:
4126 # if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
4127 		if (!UseMSP)
4128 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4129 					     "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
4130 # endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
4131 		if (isascii(*val) && isdigit(*val))
4132 			TrustedUid = atoi(val);
4133 		else
4134 		{
4135 			register struct passwd *pw;
4136 
4137 			TrustedUid = 0;
4138 			pw = sm_getpwnam(val);
4139 			if (pw == NULL)
4140 			{
4141 				syserr("readcf: option TrustedUser: unknown user %s", val);
4142 				break;
4143 			}
4144 			else
4145 				TrustedUid = pw->pw_uid;
4146 		}
4147 
4148 # ifdef UID_MAX
4149 		if (TrustedUid > UID_MAX)
4150 		{
4151 			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
4152 				(long) TrustedUid, (long) UID_MAX);
4153 			TrustedUid = 0;
4154 		}
4155 # endif /* UID_MAX */
4156 		break;
4157 
4158 	  case O_MAXMIMEHDRLEN:
4159 		p = strchr(val, '/');
4160 		if (p != NULL)
4161 			*p++ = '\0';
4162 		MaxMimeHeaderLength = atoi(val);
4163 		if (p != NULL && *p != '\0')
4164 			MaxMimeFieldLength = atoi(p);
4165 		else
4166 			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
4167 
4168 		if (MaxMimeHeaderLength <= 0)
4169 			MaxMimeHeaderLength = 0;
4170 		else if (MaxMimeHeaderLength < 128)
4171 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4172 					     "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
4173 
4174 		if (MaxMimeFieldLength <= 0)
4175 			MaxMimeFieldLength = 0;
4176 		else if (MaxMimeFieldLength < 40)
4177 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4178 					     "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
4179 
4180 		/*
4181 		**  Headers field values now include leading space, so let's
4182 		**  adjust the values to be "backward compatible".
4183 		*/
4184 
4185 		if (MaxMimeHeaderLength > 0)
4186 			MaxMimeHeaderLength++;
4187 		if (MaxMimeFieldLength > 0)
4188 			MaxMimeFieldLength++;
4189 		break;
4190 
4191 	  case O_CONTROLSOCKET:
4192 		PSTRSET(ControlSocketName, val);
4193 		break;
4194 
4195 	  case O_MAXHDRSLEN:
4196 		MaxHeadersLength = atoi(val);
4197 
4198 		if (MaxHeadersLength > 0 &&
4199 		    MaxHeadersLength < (MAXHDRSLEN / 2))
4200 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4201 					     "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
4202 					     (MAXHDRSLEN / 2));
4203 		break;
4204 
4205 	  case O_PROCTITLEPREFIX:
4206 		PSTRSET(ProcTitlePrefix, val);
4207 		break;
4208 
4209 #if SASL
4210 	  case O_SASLINFO:
4211 # if _FFR_ALLOW_SASLINFO
4212 		/*
4213 		**  Allow users to select their own authinfo file
4214 		**  under certain circumstances, otherwise just ignore
4215 		**  the option.  If the option isn't ignored, several
4216 		**  commands don't work very well, e.g., mailq.
4217 		**  However, this is not a "perfect" solution.
4218 		**  If mail is queued, the authentication info
4219 		**  will not be used in subsequent delivery attempts.
4220 		**  If we really want to support this, then it has
4221 		**  to be stored in the queue file.
4222 		*/
4223 		if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
4224 		    RunAsUid != RealUid)
4225 			break;
4226 # endif /* _FFR_ALLOW_SASLINFO */
4227 		PSTRSET(SASLInfo, val);
4228 		break;
4229 
4230 	  case O_SASLMECH:
4231 		if (AuthMechanisms != NULL)
4232 			sm_free(AuthMechanisms); /* XXX */
4233 		if (*val != '\0')
4234 			AuthMechanisms = newstr(val);
4235 		else
4236 			AuthMechanisms = NULL;
4237 		break;
4238 
4239 	  case O_SASLREALM:
4240 		if (AuthRealm != NULL)
4241 			sm_free(AuthRealm);
4242 		if (*val != '\0')
4243 			AuthRealm = newstr(val);
4244 		else
4245 			AuthRealm = NULL;
4246 		break;
4247 
4248 	  case O_SASLOPTS:
4249 		while (val != NULL && *val != '\0')
4250 		{
4251 			switch (*val)
4252 			{
4253 			  case 'A':
4254 				SASLOpts |= SASL_AUTH_AUTH;
4255 				break;
4256 
4257 			  case 'a':
4258 				SASLOpts |= SASL_SEC_NOACTIVE;
4259 				break;
4260 
4261 			  case 'c':
4262 				SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
4263 				break;
4264 
4265 			  case 'd':
4266 				SASLOpts |= SASL_SEC_NODICTIONARY;
4267 				break;
4268 
4269 			  case 'f':
4270 				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
4271 				break;
4272 
4273 #  if SASL >= 20101
4274 			  case 'm':
4275 				SASLOpts |= SASL_SEC_MUTUAL_AUTH;
4276 				break;
4277 #  endif /* SASL >= 20101 */
4278 
4279 			  case 'p':
4280 				SASLOpts |= SASL_SEC_NOPLAINTEXT;
4281 				break;
4282 
4283 			  case 'y':
4284 				SASLOpts |= SASL_SEC_NOANONYMOUS;
4285 				break;
4286 
4287 			  case ' ':	/* ignore */
4288 			  case '\t':	/* ignore */
4289 			  case ',':	/* ignore */
4290 				break;
4291 
4292 			  default:
4293 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4294 						     "Warning: Option: %s unknown parameter '%c'\n",
4295 						     OPTNAME,
4296 						     (isascii(*val) &&
4297 							isprint(*val))
4298 							? *val : '?');
4299 				break;
4300 			}
4301 			++val;
4302 			val = strpbrk(val, ", \t");
4303 			if (val != NULL)
4304 				++val;
4305 		}
4306 		break;
4307 
4308 	  case O_SASLBITS:
4309 		MaxSLBits = atoi(val);
4310 		break;
4311 
4312 #else /* SASL */
4313 	  case O_SASLINFO:
4314 	  case O_SASLMECH:
4315 	  case O_SASLREALM:
4316 	  case O_SASLOPTS:
4317 	  case O_SASLBITS:
4318 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4319 				     "Warning: Option: %s requires SASL support (-DSASL)\n",
4320 				     OPTNAME);
4321 		break;
4322 #endif /* SASL */
4323 
4324 #if STARTTLS
4325 	  case O_TLSFB2CLEAR:
4326 		TLSFallbacktoClear = atobool(val);
4327 		break;
4328 	  case O_SRVCERTFILE:
4329 		SET_STRING_EXP(SrvCertFile);
4330 	  case O_SRVKEYFILE:
4331 		SET_STRING_EXP(SrvKeyFile);
4332 	  case O_CLTCERTFILE:
4333 		SET_STRING_EXP(CltCertFile);
4334 	  case O_CLTKEYFILE:
4335 		SET_STRING_EXP(CltKeyFile);
4336 	  case O_CACERTFILE:
4337 		SET_STRING_EXP(CACertFile);
4338 	  case O_CACERTPATH:
4339 		SET_STRING_EXP(CACertPath);
4340 #if _FFR_CLIENTCA
4341 	  case O_CLTCACERTFILE:
4342 		SET_STRING_EXP(CltCACertFile);
4343 	  case O_CLTCACERTPATH:
4344 		SET_STRING_EXP(CltCACertPath);
4345 #endif
4346 	  case O_DHPARAMS:
4347 		SET_STRING_EXP(DHParams);
4348 	  case O_CIPHERLIST:
4349 		SET_STRING_EXP(CipherList);
4350 	  case O_DIG_ALG:
4351 		SET_STRING_EXP(CertFingerprintAlgorithm);
4352 	  case O_SSLENGINEPATH:
4353 		SET_STRING_EXP(SSLEnginePath);
4354 	  case O_SSLENGINE:
4355 		newval = sm_pstrdup_x(val);
4356 		if (SSLEngine != NULL)
4357 			sm_free(SSLEngine);
4358 		SSLEngine = newval;
4359 
4360 		/*
4361 		**  Which engines need to be initialized before fork()?
4362 		**  XXX hack, should be an option?
4363 		*/
4364 
4365 		if (strcmp(SSLEngine, "chil") == 0)
4366 			SSLEngineprefork = true;
4367 		break;
4368 	  case O_SRV_SSL_OPTIONS:
4369 		pssloptions = &Srv_SSL_Options;
4370 	  case O_CLT_SSL_OPTIONS:
4371 		if (pssloptions == NULL)
4372 			pssloptions = &Clt_SSL_Options;
4373 		(void) readssloptions(o->o_name, val, pssloptions, '\0');
4374 		if (tTd(37, 8))
4375 			sm_dprintf("ssloptions=%#lx\n", *pssloptions);
4376 
4377 		pssloptions = NULL;
4378 		break;
4379 
4380 	  case O_CRLFILE:
4381 		SET_STRING_EXP(CRLFile);
4382 		break;
4383 
4384 	  case O_CRLPATH:
4385 		SET_STRING_EXP(CRLPath);
4386 		break;
4387 
4388 	/*
4389 	**  XXX How about options per daemon/client instead of globally?
4390 	**  This doesn't work well for some options, e.g., no server cert,
4391 	**  but fine for others.
4392 	**
4393 	**  XXX Some people may want different certs per server.
4394 	**
4395 	**  See also srvfeatures()
4396 	*/
4397 
4398 	  case O_TLS_SRV_OPTS:
4399 		while (val != NULL && *val != '\0')
4400 		{
4401 			switch (*val)
4402 			{
4403 			  case 'V':
4404 				TLS_Srv_Opts |= TLS_I_NO_VRFY;
4405 				break;
4406 			/*
4407 			**  Server without a cert? That works only if
4408 			**  AnonDH is enabled as cipher, which is not in the
4409 			**  default list. Hence the CipherList option must
4410 			**  be available. Moreover: which clients support this
4411 			**  besides sendmail with this setting?
4412 			*/
4413 
4414 			  case 'C':
4415 				TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
4416 				break;
4417 			  case ' ':	/* ignore */
4418 			  case '\t':	/* ignore */
4419 			  case ',':	/* ignore */
4420 				break;
4421 			  default:
4422 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4423 						     "Warning: Option: %s unknown parameter '%c'\n",
4424 						     OPTNAME,
4425 						     (isascii(*val) &&
4426 							isprint(*val))
4427 							? *val : '?');
4428 				break;
4429 			}
4430 			++val;
4431 			val = strpbrk(val, ", \t");
4432 			if (val != NULL)
4433 				++val;
4434 		}
4435 		break;
4436 
4437 	  case O_RANDFILE:
4438 		PSTRSET(RandFile, val);
4439 		break;
4440 
4441 #else /* STARTTLS */
4442 	  case O_SRVCERTFILE:
4443 	  case O_SRVKEYFILE:
4444 	  case O_CLTCERTFILE:
4445 	  case O_CLTKEYFILE:
4446 	  case O_CACERTFILE:
4447 	  case O_CACERTPATH:
4448 #if _FFR_CLIENTCA
4449 	  case O_CLTCACERTFILE:
4450 	  case O_CLTCACERTPATH:
4451 #endif
4452 	  case O_DHPARAMS:
4453 	  case O_SRV_SSL_OPTIONS:
4454 	  case O_CLT_SSL_OPTIONS:
4455 	  case O_CIPHERLIST:
4456 	  case O_DIG_ALG:
4457 	  case O_CRLFILE:
4458 	  case O_CRLPATH:
4459 	  case O_RANDFILE:
4460 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4461 				     "Warning: Option: %s requires TLS support\n",
4462 				     OPTNAME);
4463 		break;
4464 
4465 #endif /* STARTTLS */
4466 #if STARTTLS && _FFR_FIPSMODE
4467 	  case O_FIPSMODE:
4468 		FipsMode = atobool(val);
4469 		break;
4470 #endif
4471 
4472 	  case O_CLIENTPORT:
4473 		setclientoptions(val);
4474 		break;
4475 
4476 	  case O_DF_BUFSIZE:
4477 		DataFileBufferSize = atoi(val);
4478 		break;
4479 
4480 	  case O_XF_BUFSIZE:
4481 		XscriptFileBufferSize = atoi(val);
4482 		break;
4483 
4484 	  case O_LDAPDEFAULTSPEC:
4485 #if LDAPMAP
4486 		ldapmap_set_defaults(val);
4487 #else /* LDAPMAP */
4488 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4489 				     "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
4490 				     OPTNAME);
4491 #endif /* LDAPMAP */
4492 		break;
4493 
4494 	  case O_INPUTMILTER:
4495 #if MILTER
4496 		InputFilterList = newstr(val);
4497 #else /* MILTER */
4498 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4499 				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
4500 				     OPTNAME);
4501 #endif /* MILTER */
4502 		break;
4503 
4504 	  case O_MILTER:
4505 #if MILTER
4506 		milter_set_option(subopt, val, sticky);
4507 #else /* MILTER */
4508 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4509 				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
4510 				     OPTNAME);
4511 #endif /* MILTER */
4512 		break;
4513 
4514 	  case O_QUEUE_FILE_MODE:	/* queue file mode */
4515 		QueueFileMode = atooct(val) & 0777;
4516 		break;
4517 
4518 	  case O_DLVR_MIN:	/* deliver by minimum time */
4519 		DeliverByMin = convtime(val, 's');
4520 		break;
4521 
4522 	  /* modifiers {daemon_flags} for direct submissions */
4523 	  case O_DIRECTSUBMODIFIERS:
4524 		{
4525 			BITMAP256 m;	/* ignored */
4526 			extern ENVELOPE BlankEnvelope;
4527 
4528 			macdefine(&BlankEnvelope.e_macro, A_PERM,
4529 				  macid("{daemon_flags}"),
4530 				  getmodifiers(val, m));
4531 		}
4532 		break;
4533 
4534 	  case O_FASTSPLIT:
4535 		FastSplit = atoi(val);
4536 		break;
4537 
4538 	  case O_MBDB:
4539 		Mbdb = newstr(val);
4540 		break;
4541 
4542 	  case O_MSQ:
4543 		UseMSP = atobool(val);
4544 		break;
4545 
4546 	  case O_SOFTBOUNCE:
4547 		SoftBounce = atobool(val);
4548 		break;
4549 
4550 	  case O_REJECTLOGINTERVAL:	/* time btwn log msgs while refusing */
4551 		RejectLogInterval = convtime(val, 'h');
4552 		break;
4553 
4554 	  case O_REQUIRES_DIR_FSYNC:
4555 #if REQUIRES_DIR_FSYNC
4556 		RequiresDirfsync = atobool(val);
4557 #else
4558 		/* silently ignored... required for cf file option */
4559 #endif
4560 		break;
4561 
4562 	  case O_CONNECTION_RATE_WINDOW_SIZE:
4563 		ConnectionRateWindowSize = convtime(val, 's');
4564 		break;
4565 
4566 	  case O_FALLBACKSMARTHOST:	/* fallback smart host */
4567 		if (val[0] != '\0')
4568 			FallbackSmartHost = newstr(val);
4569 		break;
4570 
4571 	  case O_HELONAME:
4572 		HeloName = newstr(val);
4573 		break;
4574 
4575 #if _FFR_MEMSTAT
4576 	  case O_REFUSELOWMEM:
4577 		RefuseLowMem = atoi(val);
4578 		break;
4579 	  case O_QUEUELOWMEM:
4580 		QueueLowMem = atoi(val);
4581 		break;
4582 	  case O_MEMRESOURCE:
4583 		MemoryResource = newstr(val);
4584 		break;
4585 #endif /* _FFR_MEMSTAT */
4586 
4587 	  case O_MAXNOOPCOMMANDS:
4588 		MaxNOOPCommands = atoi(val);
4589 		break;
4590 
4591 #if _FFR_MSG_ACCEPT
4592 	  case O_MSG_ACCEPT:
4593 		MessageAccept = newstr(val);
4594 		break;
4595 #endif
4596 
4597 #if _FFR_QUEUE_RUN_PARANOIA
4598 	  case O_CHK_Q_RUNNERS:
4599 		CheckQueueRunners = atoi(val);
4600 		break;
4601 #endif
4602 
4603 #if _FFR_EIGHT_BIT_ADDR_OK
4604 	  case O_EIGHT_BIT_ADDR_OK:
4605 		EightBitAddrOK = atobool(val);
4606 		break;
4607 #endif
4608 
4609 #if _FFR_ADDR_TYPE_MODES
4610 	  case O_ADDR_TYPE_MODES:
4611 		AddrTypeModes = atobool(val);
4612 		break;
4613 #endif
4614 
4615 #if _FFR_BADRCPT_SHUTDOWN
4616 	  case O_RCPTSHUTD:
4617 		BadRcptShutdown = atoi(val);
4618 		break;
4619 
4620 	  case O_RCPTSHUTDG:
4621 		BadRcptShutdownGood = atoi(val);
4622 		break;
4623 #endif /* _FFR_BADRCPT_SHUTDOWN */
4624 
4625 #if _FFR_REJECT_NUL_BYTE
4626 	  case O_REJECTNUL:
4627 		RejectNUL = atobool(val);
4628 		break;
4629 #endif
4630 
4631 #if _FFR_BOUNCE_QUEUE
4632 	  case O_BOUNCEQUEUE:
4633 		bouncequeue = newstr(val);
4634 		break;
4635 #endif
4636 
4637 #if _FFR_ADD_BCC
4638 	  case O_ADDBCC:
4639 		AddBcc = atobool(val);
4640 		break;
4641 #endif
4642 	  case O_USECOMPRESSEDIPV6ADDRESSES:
4643 		UseCompressedIPv6Addresses = atobool(val);
4644 		break;
4645 
4646 #if DNSSEC_TEST
4647 	  case O_NSPORTIP:
4648 		nsportip(val);
4649 		break;
4650 	  case O_NSSRCHLIST:
4651 		NameSearchList = sm_strdup(val);
4652 		break;
4653 #endif
4654 
4655 #if DANE
4656 	  case O_DANE:
4657 		if (sm_strcasecmp(val, "always") == 0)
4658 			Dane = DANE_ALWAYS;
4659 		else
4660 			Dane = atobool(val) ? DANE_SECURE : DANE_NEVER;
4661 		break;
4662 #endif
4663 
4664 #if _FFR_BLANKENV_MACV
4665 	  case O_HACKS:
4666 		Hacks = (int) strtol(val, NULL, 0);
4667 		break;
4668 #endif
4669 
4670 #if _FFR_KEEPBCC
4671 	  case O_KEEPBCC:
4672 		KeepBcc = atobool(val);
4673 		break;
4674 #endif
4675 
4676 # if _FFR_TLS_ALTNAMES
4677 	  case O_CHECKALTNAMES:
4678 		SetCertAltnames = atobool(val);
4679 		break;
4680 # endif
4681 
4682 	  default:
4683 		if (tTd(37, 1))
4684 		{
4685 			if (isascii(opt) && isprint(opt))
4686 				sm_dprintf("Warning: option %c unknown\n", opt);
4687 			else
4688 				sm_dprintf("Warning: option 0x%x unknown\n", opt);
4689 		}
4690 		break;
4691 	}
4692 
4693 	/*
4694 	**  Options with suboptions are responsible for taking care
4695 	**  of sticky-ness (e.g., that a command line setting is kept
4696 	**  when reading in the sendmail.cf file).  This has to be done
4697 	**  when the suboptions are parsed since each suboption must be
4698 	**  sticky, not the root option.
4699 	*/
4700 
4701 	if (sticky && !bitset(OI_SUBOPT, o->o_flags))
4702 		setbitn(opt, StickyOpt);
4703 }
4704 /*
4705 **  SETCLASS -- set a string into a class
4706 **
4707 **	Parameters:
4708 **		class -- the class to put the string in.
4709 **		str -- the string to enter
4710 **
4711 **	Returns:
4712 **		none.
4713 **
4714 **	Side Effects:
4715 **		puts the word into the symbol table.
4716 */
4717 
4718 void
4719 setclass(class, str)
4720 	int class;
4721 	char *str;
4722 {
4723 	register STAB *s;
4724 
4725 	if ((str[0] & 0377) == MATCHCLASS)
4726 	{
4727 		int mid;
4728 
4729 		str++;
4730 		mid = macid(str);
4731 		if (mid == 0)
4732 			return;
4733 
4734 		if (tTd(37, 8))
4735 			sm_dprintf("setclass(%s, $=%s)\n",
4736 				   macname(class), macname(mid));
4737 		copy_class(mid, class);
4738 	}
4739 	else
4740 	{
4741 		if (tTd(37, 8))
4742 			sm_dprintf("setclass(%s, %s)\n", macname(class), str);
4743 
4744 		s = stab(str, ST_CLASS, ST_ENTER);
4745 		setbitn(bitidx(class), s->s_class);
4746 	}
4747 }
4748 /*
4749 **  MAKEMAPENTRY -- create a map entry
4750 **
4751 **	Parameters:
4752 **		line -- the config file line
4753 **
4754 **	Returns:
4755 **		A pointer to the map that has been created.
4756 **		NULL if there was a syntax error.
4757 **
4758 **	Side Effects:
4759 **		Enters the map into the dictionary.
4760 */
4761 
4762 MAP *
4763 makemapentry(line)
4764 	char *line;
4765 {
4766 	register char *p;
4767 	char *mapname;
4768 	char *classname;
4769 	register STAB *s;
4770 	STAB *class;
4771 
4772 	for (p = line; SM_ISSPACE(*p); p++)
4773 		continue;
4774 	if (!(isascii(*p) && isalnum(*p)))
4775 	{
4776 		syserr("readcf: config K line: no map name");
4777 		return NULL;
4778 	}
4779 
4780 	mapname = p;
4781 	while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
4782 		continue;
4783 	if (*p != '\0')
4784 		*p++ = '\0';
4785 	while (SM_ISSPACE(*p))
4786 		p++;
4787 	if (!(isascii(*p) && isalnum(*p)))
4788 	{
4789 		syserr("readcf: config K line, map %s: no map class", mapname);
4790 		return NULL;
4791 	}
4792 	classname = p;
4793 	while (isascii(*++p) && isalnum(*p))
4794 		continue;
4795 	if (*p != '\0')
4796 		*p++ = '\0';
4797 	while (SM_ISSPACE(*p))
4798 		p++;
4799 
4800 	/* look up the class */
4801 	class = stab(classname, ST_MAPCLASS, ST_FIND);
4802 	if (class == NULL)
4803 	{
4804 		syserr("readcf: map %s: class %s not available", mapname,
4805 			classname);
4806 		return NULL;
4807 	}
4808 
4809 	/* enter the map */
4810 	s = stab(mapname, ST_MAP, ST_ENTER);
4811 	s->s_map.map_class = &class->s_mapclass;
4812 	s->s_map.map_mname = newstr(mapname);
4813 
4814 	if (class->s_mapclass.map_parse(&s->s_map, p))
4815 		s->s_map.map_mflags |= MF_VALID;
4816 
4817 	if (tTd(37, 5))
4818 	{
4819 		sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
4820 			   s->s_map.map_mname, s->s_map.map_class->map_cname,
4821 			   s->s_map.map_mflags, s->s_map.map_file);
4822 		sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
4823 			   s->s_map.map_app, s->s_map.map_domain,
4824 			   s->s_map.map_rebuild);
4825 	}
4826 	return &s->s_map;
4827 }
4828 /*
4829 **  STRTORWSET -- convert string to rewriting set number
4830 **
4831 **	Parameters:
4832 **		p -- the pointer to the string to decode.
4833 **		endp -- if set, store the trailing delimiter here.
4834 **		stabmode -- ST_ENTER to create this entry, ST_FIND if
4835 **			it must already exist.
4836 **
4837 **	Returns:
4838 **		The appropriate ruleset number.
4839 **		-1 if it is not valid (error already printed)
4840 */
4841 
4842 int
4843 strtorwset(p, endp, stabmode)
4844 	char *p;
4845 	char **endp;
4846 	int stabmode;
4847 {
4848 	int ruleset;
4849 	static int nextruleset = MAXRWSETS;
4850 
4851 	while (SM_ISSPACE(*p))
4852 		p++;
4853 	if (!isascii(*p))
4854 	{
4855 		syserr("invalid ruleset name: \"%.20s\"", p);
4856 		return -1;
4857 	}
4858 	if (isdigit(*p))
4859 	{
4860 		ruleset = strtol(p, endp, 10);
4861 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4862 		{
4863 			syserr("bad ruleset %d (%d max)",
4864 				ruleset, MAXRWSETS / 2);
4865 			ruleset = -1;
4866 		}
4867 	}
4868 	else
4869 	{
4870 		STAB *s;
4871 		char delim;
4872 		char *q = NULL;
4873 
4874 		q = p;
4875 		while (*p != '\0' && isascii(*p) && (isalnum(*p) || *p == '_'))
4876 			p++;
4877 		if (q == p || !(isascii(*q) && isalpha(*q)))
4878 		{
4879 			/* no valid characters */
4880 			syserr("invalid ruleset name: \"%.20s\"", q);
4881 			return -1;
4882 		}
4883 		while (SM_ISSPACE(*p))
4884 			*p++ = '\0';
4885 		delim = *p;
4886 		if (delim != '\0')
4887 			*p = '\0';
4888 		s = stab(q, ST_RULESET, stabmode);
4889 		if (delim != '\0')
4890 			*p = delim;
4891 
4892 		if (s == NULL)
4893 			return -1;
4894 
4895 		if (stabmode == ST_ENTER && delim == '=')
4896 		{
4897 			while (isascii(*++p) && isspace(*p))
4898 				continue;
4899 			if (!(isascii(*p) && isdigit(*p)))
4900 			{
4901 				syserr("bad ruleset definition \"%s\" (number required after `=')", q);
4902 				ruleset = -1;
4903 			}
4904 			else
4905 			{
4906 				ruleset = strtol(p, endp, 10);
4907 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4908 				{
4909 					syserr("bad ruleset number %d in \"%s\" (%d max)",
4910 						ruleset, q, MAXRWSETS / 2);
4911 					ruleset = -1;
4912 				}
4913 			}
4914 		}
4915 		else
4916 		{
4917 			if (endp != NULL)
4918 				*endp = p;
4919 			if (s->s_ruleset >= 0)
4920 				ruleset = s->s_ruleset;
4921 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
4922 			{
4923 				syserr("%s: too many named rulesets (%d max)",
4924 					q, MAXRWSETS / 2);
4925 				ruleset = -1;
4926 			}
4927 		}
4928 		if (s->s_ruleset >= 0 &&
4929 		    ruleset >= 0 &&
4930 		    ruleset != s->s_ruleset)
4931 		{
4932 			syserr("%s: ruleset changed value (old %d, new %d)",
4933 				q, s->s_ruleset, ruleset);
4934 			ruleset = s->s_ruleset;
4935 		}
4936 		else if (ruleset >= 0)
4937 		{
4938 			s->s_ruleset = ruleset;
4939 		}
4940 		if (stabmode == ST_ENTER && ruleset >= 0)
4941 		{
4942 			char *h = NULL;
4943 
4944 			if (RuleSetNames[ruleset] != NULL)
4945 				sm_free(RuleSetNames[ruleset]); /* XXX */
4946 			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
4947 				*h = '\0';
4948 			RuleSetNames[ruleset] = newstr(q);
4949 			if (delim == '/' && h != NULL)
4950 				*h = delim;	/* put back delim */
4951 		}
4952 	}
4953 	return ruleset;
4954 }
4955 /*
4956 **  SETTIMEOUT -- set an individual timeout
4957 **
4958 **	Parameters:
4959 **		name -- the name of the timeout.
4960 **		val -- the value of the timeout.
4961 **		sticky -- if set, don't let other setoptions override
4962 **			this value.
4963 **
4964 **	Returns:
4965 **		none.
4966 */
4967 
4968 /* set if Timeout sub-option is stuck */
4969 static BITMAP256	StickyTimeoutOpt;
4970 
4971 static struct timeoutinfo
4972 {
4973 	char		*to_name;	/* long name of timeout */
4974 	unsigned char	to_code;	/* code for option */
4975 } TimeOutTab[] =
4976 {
4977 #define TO_INITIAL			0x01
4978 	{ "initial",			TO_INITIAL			},
4979 #define TO_MAIL				0x02
4980 	{ "mail",			TO_MAIL				},
4981 #define TO_RCPT				0x03
4982 	{ "rcpt",			TO_RCPT				},
4983 #define TO_DATAINIT			0x04
4984 	{ "datainit",			TO_DATAINIT			},
4985 #define TO_DATABLOCK			0x05
4986 	{ "datablock",			TO_DATABLOCK			},
4987 #define TO_DATAFINAL			0x06
4988 	{ "datafinal",			TO_DATAFINAL			},
4989 #define TO_COMMAND			0x07
4990 	{ "command",			TO_COMMAND			},
4991 #define TO_RSET				0x08
4992 	{ "rset",			TO_RSET				},
4993 #define TO_HELO				0x09
4994 	{ "helo",			TO_HELO				},
4995 #define TO_QUIT				0x0A
4996 	{ "quit",			TO_QUIT				},
4997 #define TO_MISC				0x0B
4998 	{ "misc",			TO_MISC				},
4999 #define TO_IDENT			0x0C
5000 	{ "ident",			TO_IDENT			},
5001 #define TO_FILEOPEN			0x0D
5002 	{ "fileopen",			TO_FILEOPEN			},
5003 #define TO_CONNECT			0x0E
5004 	{ "connect",			TO_CONNECT			},
5005 #define TO_ICONNECT			0x0F
5006 	{ "iconnect",			TO_ICONNECT			},
5007 #define TO_QUEUEWARN			0x10
5008 	{ "queuewarn",			TO_QUEUEWARN			},
5009 	{ "queuewarn.*",		TO_QUEUEWARN			},
5010 #define TO_QUEUEWARN_NORMAL		0x11
5011 	{ "queuewarn.normal",		TO_QUEUEWARN_NORMAL		},
5012 #define TO_QUEUEWARN_URGENT		0x12
5013 	{ "queuewarn.urgent",		TO_QUEUEWARN_URGENT		},
5014 #define TO_QUEUEWARN_NON_URGENT		0x13
5015 	{ "queuewarn.non-urgent",	TO_QUEUEWARN_NON_URGENT		},
5016 #define TO_QUEUERETURN			0x14
5017 	{ "queuereturn",		TO_QUEUERETURN			},
5018 	{ "queuereturn.*",		TO_QUEUERETURN			},
5019 #define TO_QUEUERETURN_NORMAL		0x15
5020 	{ "queuereturn.normal",		TO_QUEUERETURN_NORMAL		},
5021 #define TO_QUEUERETURN_URGENT		0x16
5022 	{ "queuereturn.urgent",		TO_QUEUERETURN_URGENT		},
5023 #define TO_QUEUERETURN_NON_URGENT	0x17
5024 	{ "queuereturn.non-urgent",	TO_QUEUERETURN_NON_URGENT	},
5025 #define TO_HOSTSTATUS			0x18
5026 	{ "hoststatus",			TO_HOSTSTATUS			},
5027 #define TO_RESOLVER_RETRANS		0x19
5028 	{ "resolver.retrans",		TO_RESOLVER_RETRANS		},
5029 #define TO_RESOLVER_RETRANS_NORMAL	0x1A
5030 	{ "resolver.retrans.normal",	TO_RESOLVER_RETRANS_NORMAL	},
5031 #define TO_RESOLVER_RETRANS_FIRST	0x1B
5032 	{ "resolver.retrans.first",	TO_RESOLVER_RETRANS_FIRST	},
5033 #define TO_RESOLVER_RETRY		0x1C
5034 	{ "resolver.retry",		TO_RESOLVER_RETRY		},
5035 #define TO_RESOLVER_RETRY_NORMAL	0x1D
5036 	{ "resolver.retry.normal",	TO_RESOLVER_RETRY_NORMAL	},
5037 #define TO_RESOLVER_RETRY_FIRST		0x1E
5038 	{ "resolver.retry.first",	TO_RESOLVER_RETRY_FIRST		},
5039 #define TO_CONTROL			0x1F
5040 	{ "control",			TO_CONTROL			},
5041 #define TO_LHLO				0x20
5042 	{ "lhlo",			TO_LHLO				},
5043 #define TO_AUTH				0x21
5044 	{ "auth",			TO_AUTH				},
5045 #define TO_STARTTLS			0x22
5046 	{ "starttls",			TO_STARTTLS			},
5047 #define TO_ACONNECT			0x23
5048 	{ "aconnect",			TO_ACONNECT			},
5049 #define TO_QUEUEWARN_DSN		0x24
5050 	{ "queuewarn.dsn",		TO_QUEUEWARN_DSN		},
5051 #define TO_QUEUERETURN_DSN		0x25
5052 	{ "queuereturn.dsn",		TO_QUEUERETURN_DSN		},
5053 	{ NULL,				0				},
5054 };
5055 
5056 
5057 static void
5058 settimeout(name, val, sticky)
5059 	char *name;
5060 	char *val;
5061 	bool sticky;
5062 {
5063 	register struct timeoutinfo *to;
5064 	int i, addopts;
5065 	time_t toval;
5066 
5067 	if (tTd(37, 2))
5068 		sm_dprintf("settimeout(%s = %s)", name, val);
5069 
5070 	for (to = TimeOutTab; to->to_name != NULL; to++)
5071 	{
5072 		if (sm_strcasecmp(to->to_name, name) == 0)
5073 			break;
5074 	}
5075 
5076 	if (to->to_name == NULL)
5077 	{
5078 		errno = 0; /* avoid bogus error text */
5079 		syserr("settimeout: invalid timeout %s", name);
5080 		return;
5081 	}
5082 
5083 	/*
5084 	**  See if this option is preset for us.
5085 	*/
5086 
5087 	if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
5088 	{
5089 		if (tTd(37, 2))
5090 			sm_dprintf(" (ignored)\n");
5091 		return;
5092 	}
5093 
5094 	if (tTd(37, 2))
5095 		sm_dprintf("\n");
5096 
5097 	toval = convtime(val, 'm');
5098 	addopts = 0;
5099 
5100 	switch (to->to_code)
5101 	{
5102 	  case TO_INITIAL:
5103 		TimeOuts.to_initial = toval;
5104 		break;
5105 
5106 	  case TO_MAIL:
5107 		TimeOuts.to_mail = toval;
5108 		break;
5109 
5110 	  case TO_RCPT:
5111 		TimeOuts.to_rcpt = toval;
5112 		break;
5113 
5114 	  case TO_DATAINIT:
5115 		TimeOuts.to_datainit = toval;
5116 		break;
5117 
5118 	  case TO_DATABLOCK:
5119 		TimeOuts.to_datablock = toval;
5120 		break;
5121 
5122 	  case TO_DATAFINAL:
5123 		TimeOuts.to_datafinal = toval;
5124 		break;
5125 
5126 	  case TO_COMMAND:
5127 		TimeOuts.to_nextcommand = toval;
5128 		break;
5129 
5130 	  case TO_RSET:
5131 		TimeOuts.to_rset = toval;
5132 		break;
5133 
5134 	  case TO_HELO:
5135 		TimeOuts.to_helo = toval;
5136 		break;
5137 
5138 	  case TO_QUIT:
5139 		TimeOuts.to_quit = toval;
5140 		break;
5141 
5142 	  case TO_MISC:
5143 		TimeOuts.to_miscshort = toval;
5144 		break;
5145 
5146 	  case TO_IDENT:
5147 		TimeOuts.to_ident = toval;
5148 		break;
5149 
5150 	  case TO_FILEOPEN:
5151 		TimeOuts.to_fileopen = toval;
5152 		break;
5153 
5154 	  case TO_CONNECT:
5155 		TimeOuts.to_connect = toval;
5156 		break;
5157 
5158 	  case TO_ICONNECT:
5159 		TimeOuts.to_iconnect = toval;
5160 		break;
5161 
5162 	  case TO_ACONNECT:
5163 		TimeOuts.to_aconnect = toval;
5164 		break;
5165 
5166 	  case TO_QUEUEWARN:
5167 		toval = convtime(val, 'h');
5168 		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
5169 		TimeOuts.to_q_warning[TOC_URGENT] = toval;
5170 		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
5171 		TimeOuts.to_q_warning[TOC_DSN] = toval;
5172 		addopts = 2;
5173 		break;
5174 
5175 	  case TO_QUEUEWARN_NORMAL:
5176 		toval = convtime(val, 'h');
5177 		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
5178 		break;
5179 
5180 	  case TO_QUEUEWARN_URGENT:
5181 		toval = convtime(val, 'h');
5182 		TimeOuts.to_q_warning[TOC_URGENT] = toval;
5183 		break;
5184 
5185 	  case TO_QUEUEWARN_NON_URGENT:
5186 		toval = convtime(val, 'h');
5187 		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
5188 		break;
5189 
5190 	  case TO_QUEUEWARN_DSN:
5191 		toval = convtime(val, 'h');
5192 		TimeOuts.to_q_warning[TOC_DSN] = toval;
5193 		break;
5194 
5195 	  case TO_QUEUERETURN:
5196 		toval = convtime(val, 'd');
5197 		TimeOuts.to_q_return[TOC_NORMAL] = toval;
5198 		TimeOuts.to_q_return[TOC_URGENT] = toval;
5199 		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
5200 		TimeOuts.to_q_return[TOC_DSN] = toval;
5201 		addopts = 2;
5202 		break;
5203 
5204 	  case TO_QUEUERETURN_NORMAL:
5205 		toval = convtime(val, 'd');
5206 		TimeOuts.to_q_return[TOC_NORMAL] = toval;
5207 		break;
5208 
5209 	  case TO_QUEUERETURN_URGENT:
5210 		toval = convtime(val, 'd');
5211 		TimeOuts.to_q_return[TOC_URGENT] = toval;
5212 		break;
5213 
5214 	  case TO_QUEUERETURN_NON_URGENT:
5215 		toval = convtime(val, 'd');
5216 		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
5217 		break;
5218 
5219 	  case TO_QUEUERETURN_DSN:
5220 		toval = convtime(val, 'd');
5221 		TimeOuts.to_q_return[TOC_DSN] = toval;
5222 		break;
5223 
5224 	  case TO_HOSTSTATUS:
5225 		MciInfoTimeout = toval;
5226 		break;
5227 
5228 	  case TO_RESOLVER_RETRANS:
5229 		toval = convtime(val, 's');
5230 		TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
5231 		TimeOuts.res_retrans[RES_TO_FIRST] = toval;
5232 		TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
5233 		addopts = 2;
5234 		break;
5235 
5236 	  case TO_RESOLVER_RETRY:
5237 		i = atoi(val);
5238 		TimeOuts.res_retry[RES_TO_DEFAULT] = i;
5239 		TimeOuts.res_retry[RES_TO_FIRST] = i;
5240 		TimeOuts.res_retry[RES_TO_NORMAL] = i;
5241 		addopts = 2;
5242 		break;
5243 
5244 	  case TO_RESOLVER_RETRANS_NORMAL:
5245 		TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
5246 		break;
5247 
5248 	  case TO_RESOLVER_RETRY_NORMAL:
5249 		TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
5250 		break;
5251 
5252 	  case TO_RESOLVER_RETRANS_FIRST:
5253 		TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
5254 		break;
5255 
5256 	  case TO_RESOLVER_RETRY_FIRST:
5257 		TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
5258 		break;
5259 
5260 	  case TO_CONTROL:
5261 		TimeOuts.to_control = toval;
5262 		break;
5263 
5264 	  case TO_LHLO:
5265 		TimeOuts.to_lhlo = toval;
5266 		break;
5267 
5268 #if SASL
5269 	  case TO_AUTH:
5270 		TimeOuts.to_auth = toval;
5271 		break;
5272 #endif
5273 
5274 #if STARTTLS
5275 	  case TO_STARTTLS:
5276 		TimeOuts.to_starttls = toval;
5277 		break;
5278 #endif
5279 
5280 	  default:
5281 		syserr("settimeout: invalid timeout %s", name);
5282 		break;
5283 	}
5284 
5285 	if (sticky)
5286 	{
5287 		for (i = 0; i <= addopts; i++)
5288 			setbitn(to->to_code + i, StickyTimeoutOpt);
5289 	}
5290 }
5291 /*
5292 **  INITTIMEOUTS -- parse and set timeout values
5293 **
5294 **	Parameters:
5295 **		val -- a pointer to the values.  If NULL, do initial
5296 **			settings.
5297 **		sticky -- if set, don't let other setoptions override
5298 **			this suboption value.
5299 **
5300 **	Returns:
5301 **		none.
5302 **
5303 **	Side Effects:
5304 **		Initializes the TimeOuts structure
5305 */
5306 
5307 void
5308 inittimeouts(val, sticky)
5309 	register char *val;
5310 	bool sticky;
5311 {
5312 	register char *p;
5313 
5314 	if (tTd(37, 2))
5315 		sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
5316 	if (val == NULL)
5317 	{
5318 		TimeOuts.to_connect = (time_t) 0 SECONDS;
5319 		TimeOuts.to_aconnect = (time_t) 0 SECONDS;
5320 		TimeOuts.to_iconnect = (time_t) 0 SECONDS;
5321 		TimeOuts.to_initial = (time_t) 5 MINUTES;
5322 		TimeOuts.to_helo = (time_t) 5 MINUTES;
5323 		TimeOuts.to_mail = (time_t) 10 MINUTES;
5324 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
5325 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
5326 		TimeOuts.to_datablock = (time_t) 1 HOUR;
5327 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
5328 		TimeOuts.to_rset = (time_t) 5 MINUTES;
5329 		TimeOuts.to_quit = (time_t) 2 MINUTES;
5330 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
5331 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
5332 #if IDENTPROTO
5333 		TimeOuts.to_ident = (time_t) 5 SECONDS;
5334 #else
5335 		TimeOuts.to_ident = (time_t) 0 SECONDS;
5336 #endif
5337 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
5338 		TimeOuts.to_control = (time_t) 2 MINUTES;
5339 		TimeOuts.to_lhlo = (time_t) 2 MINUTES;
5340 #if SASL
5341 		TimeOuts.to_auth = (time_t) 10 MINUTES;
5342 #endif
5343 #if STARTTLS
5344 		TimeOuts.to_starttls = (time_t) 1 HOUR;
5345 #endif
5346 		if (tTd(37, 5))
5347 		{
5348 			sm_dprintf("Timeouts:\n");
5349 			sm_dprintf("  connect = %ld\n",
5350 				   (long) TimeOuts.to_connect);
5351 			sm_dprintf("  aconnect = %ld\n",
5352 				   (long) TimeOuts.to_aconnect);
5353 			sm_dprintf("  initial = %ld\n",
5354 				   (long) TimeOuts.to_initial);
5355 			sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
5356 			sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
5357 			sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
5358 			sm_dprintf("  datainit = %ld\n",
5359 				   (long) TimeOuts.to_datainit);
5360 			sm_dprintf("  datablock = %ld\n",
5361 				   (long) TimeOuts.to_datablock);
5362 			sm_dprintf("  datafinal = %ld\n",
5363 				   (long) TimeOuts.to_datafinal);
5364 			sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
5365 			sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
5366 			sm_dprintf("  nextcommand = %ld\n",
5367 				   (long) TimeOuts.to_nextcommand);
5368 			sm_dprintf("  miscshort = %ld\n",
5369 				   (long) TimeOuts.to_miscshort);
5370 			sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
5371 			sm_dprintf("  fileopen = %ld\n",
5372 				   (long) TimeOuts.to_fileopen);
5373 			sm_dprintf("  lhlo = %ld\n",
5374 				   (long) TimeOuts.to_lhlo);
5375 			sm_dprintf("  control = %ld\n",
5376 				   (long) TimeOuts.to_control);
5377 		}
5378 		return;
5379 	}
5380 
5381 	for (;; val = p)
5382 	{
5383 		while (SM_ISSPACE(*val))
5384 			val++;
5385 		if (*val == '\0')
5386 			break;
5387 		for (p = val; *p != '\0' && *p != ','; p++)
5388 			continue;
5389 		if (*p != '\0')
5390 			*p++ = '\0';
5391 
5392 		if (isascii(*val) && isdigit(*val))
5393 		{
5394 			/* old syntax -- set everything */
5395 			TimeOuts.to_mail = convtime(val, 'm');
5396 			TimeOuts.to_rcpt = TimeOuts.to_mail;
5397 			TimeOuts.to_datainit = TimeOuts.to_mail;
5398 			TimeOuts.to_datablock = TimeOuts.to_mail;
5399 			TimeOuts.to_datafinal = TimeOuts.to_mail;
5400 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
5401 			if (sticky)
5402 			{
5403 				setbitn(TO_MAIL, StickyTimeoutOpt);
5404 				setbitn(TO_RCPT, StickyTimeoutOpt);
5405 				setbitn(TO_DATAINIT, StickyTimeoutOpt);
5406 				setbitn(TO_DATABLOCK, StickyTimeoutOpt);
5407 				setbitn(TO_DATAFINAL, StickyTimeoutOpt);
5408 				setbitn(TO_COMMAND, StickyTimeoutOpt);
5409 			}
5410 			continue;
5411 		}
5412 		else
5413 		{
5414 			register char *q = strchr(val, ':');
5415 
5416 			if (q == NULL && (q = strchr(val, '=')) == NULL)
5417 			{
5418 				/* syntax error */
5419 				continue;
5420 			}
5421 			*q++ = '\0';
5422 			settimeout(val, q, sticky);
5423 		}
5424 	}
5425 }
5426