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