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