main.c revision 77349
1/*
2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char copyright[] =
16"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
17	All rights reserved.\n\
18     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
19     Copyright (c) 1988, 1993\n\
20	The Regents of the University of California.  All rights reserved.\n";
21#endif /* ! lint */
22
23#ifndef lint
24static char id[] = "@(#)$Id: main.c,v 8.485.4.60 2001/05/27 22:00:26 gshapiro Exp $";
25#endif /* ! lint */
26
27#define	_DEFINE
28
29#include <sendmail.h>
30
31
32#if NETINET || NETINET6
33# include <arpa/inet.h>
34#endif /* NETINET || NETINET6 */
35
36static SIGFUNC_DECL	intindebug __P((int));
37static SIGFUNC_DECL	quiesce __P((int));
38static SIGFUNC_DECL	sigusr1 __P((int));
39static SIGFUNC_DECL	term_daemon __P((int));
40static void	dump_class __P((STAB *, int));
41static void	obsolete __P((char **));
42static void	testmodeline __P((char *, ENVELOPE *));
43
44/*
45**  SENDMAIL -- Post mail to a set of destinations.
46**
47**	This is the basic mail router.  All user mail programs should
48**	call this routine to actually deliver mail.  Sendmail in
49**	turn calls a bunch of mail servers that do the real work of
50**	delivering the mail.
51**
52**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
53**	(read by readcf.c).
54**
55**	Usage:
56**		/usr/lib/sendmail [flags] addr ...
57**
58**		See the associated documentation for details.
59**
60**	Author:
61**		Eric Allman, UCB/INGRES (until 10/81).
62**			     Britton-Lee, Inc., purveyors of fine
63**				database computers (11/81 - 10/88).
64**			     International Computer Science Institute
65**				(11/88 - 9/89).
66**			     UCB/Mammoth Project (10/89 - 7/95).
67**			     InReference, Inc. (8/95 - 1/97).
68**			     Sendmail, Inc. (1/98 - present).
69**		The support of the my employers is gratefully acknowledged.
70**			Few of them (Britton-Lee in particular) have had
71**			anything to gain from my involvement in this project.
72*/
73
74
75int		NextMailer;	/* "free" index into Mailer struct */
76char		*FullName;	/* sender's full name */
77ENVELOPE	BlankEnvelope;	/* a "blank" envelope */
78static ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
79ADDRESS		NullAddress =	/* a null address */
80		{ "", "", NULL, "" };
81char		*CommandLineArgs;	/* command line args for pid file */
82bool		Warn_Q_option = FALSE;	/* warn about Q option use */
83static int	MissingFds = 0;	/* bit map of fds missing on startup */
84
85#ifdef NGROUPS_MAX
86GIDSET_T	InitialGidSet[NGROUPS_MAX];
87#endif /* NGROUPS_MAX */
88
89#if DAEMON && !SMTP
90ERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
91#endif /* DAEMON && !SMTP */
92#if SMTP && !QUEUE
93ERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
94#endif /* SMTP && !QUEUE */
95
96#define MAXCONFIGLEVEL	9	/* highest config version level known */
97
98#if SASL
99static sasl_callback_t srvcallbacks[] =
100{
101	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
102	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
103	{	SASL_CB_LIST_END,	NULL,		NULL	}
104};
105
106#endif /* SASL */
107
108int SubmitMode;
109
110int
111main(argc, argv, envp)
112	int argc;
113	char **argv;
114	char **envp;
115{
116	register char *p;
117	char **av;
118	extern char Version[];
119	char *ep, *from;
120	STAB *st;
121	register int i;
122	int j;
123	int dp;
124	bool safecf = TRUE;
125	BITMAP256 *p_flags = NULL;	/* daemon flags */
126	bool warn_C_flag = FALSE;
127	bool auth = TRUE;		/* whether to set e_auth_param */
128	char warn_f_flag = '\0';
129	bool run_in_foreground = FALSE;	/* -bD mode */
130	static bool reenter = FALSE;
131	struct passwd *pw;
132	struct hostent *hp;
133	char *nullserver = NULL;
134	char *authinfo = NULL;
135	char *sysloglabel = NULL;	/* label for syslog */
136	bool forged;
137	struct stat traf_st;		/* for TrafficLog FIFO check */
138	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
139	static char rnamebuf[MAXNAME];	/* holds RealUserName */
140	char *emptyenviron[1];
141# if STARTTLS
142	bool tls_ok;
143# endif /* STARTTLS */
144	QUEUE_CHAR *new;
145	extern int DtableSize;
146	extern int optind;
147	extern int opterr;
148	extern char *optarg;
149	extern char **environ;
150
151	/*
152	**  Check to see if we reentered.
153	**	This would normally happen if e_putheader or e_putbody
154	**	were NULL when invoked.
155	*/
156
157	if (reenter)
158	{
159		syserr("main: reentered!");
160		abort();
161	}
162	reenter = TRUE;
163
164	/* avoid null pointer dereferences */
165	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
166
167	/*
168	**  Seed the random number generator.
169	**  Used for queue file names, picking a queue directory, and
170	**  MX randomization.
171	*/
172
173	seed_random();
174
175	/* do machine-dependent initializations */
176	init_md(argc, argv);
177
178
179	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
180	DtableSize = getdtsize();
181	if (DtableSize > 256)
182		DtableSize = 256;
183
184	/*
185	**  Be sure we have enough file descriptors.
186	**	But also be sure that 0, 1, & 2 are open.
187	*/
188
189	fill_fd(STDIN_FILENO, NULL);
190	fill_fd(STDOUT_FILENO, NULL);
191	fill_fd(STDERR_FILENO, NULL);
192
193	i = DtableSize;
194	while (--i > 0)
195	{
196		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
197			(void) close(i);
198	}
199	errno = 0;
200
201#if LOG
202#  ifdef LOG_MAIL
203	openlog("sendmail", LOG_PID, LOG_MAIL);
204#  else /* LOG_MAIL */
205	openlog("sendmail", LOG_PID);
206#  endif /* LOG_MAIL */
207#endif /* LOG */
208
209	if (MissingFds != 0)
210	{
211		char mbuf[MAXLINE];
212
213		mbuf[0] = '\0';
214		if (bitset(1 << STDIN_FILENO, MissingFds))
215			(void) strlcat(mbuf, ", stdin", sizeof mbuf);
216		if (bitset(1 << STDOUT_FILENO, MissingFds))
217			(void) strlcat(mbuf, ", stdout", sizeof mbuf);
218		if (bitset(1 << STDERR_FILENO, MissingFds))
219			(void) strlcat(mbuf, ", stderr", sizeof mbuf);
220		syserr("File descriptors missing on startup: %s", &mbuf[2]);
221	}
222
223	/* reset status from syserr() calls for missing file descriptors */
224	Errors = 0;
225	ExitStat = EX_OK;
226
227	SubmitMode = SUBMIT_UNKNOWN;
228#if XDEBUG
229	checkfd012("after openlog");
230#endif /* XDEBUG */
231
232	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
233
234#ifdef NGROUPS_MAX
235	/* save initial group set for future checks */
236	i = getgroups(NGROUPS_MAX, InitialGidSet);
237	if (i == 0)
238		InitialGidSet[0] = (GID_T) -1;
239	while (i < NGROUPS_MAX)
240		InitialGidSet[i++] = InitialGidSet[0];
241#endif /* NGROUPS_MAX */
242
243	/* drop group id privileges (RunAsUser not yet set) */
244	dp = drop_privileges(FALSE);
245	setstat(dp);
246
247# ifdef SIGUSR1
248	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
249	if (getuid() == 0 ||
250	    (getuid() == geteuid() && getgid() == getegid()))
251	{
252		/* arrange to dump state on user-1 signal */
253		(void) setsignal(SIGUSR1, sigusr1);
254	}
255# endif /* SIGUSR1 */
256
257	/* initialize for setproctitle */
258	initsetproctitle(argc, argv, envp);
259
260	/* Handle any non-getoptable constructions. */
261	obsolete(argv);
262
263	/*
264	**  Do a quick prescan of the argument list.
265	*/
266
267
268#if defined(__osf__) || defined(_AIX3)
269# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x"
270#endif /* defined(__osf__) || defined(_AIX3) */
271#if defined(sony_news)
272# define OPTIONS	"B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
273#endif /* defined(sony_news) */
274#ifndef OPTIONS
275# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
276#endif /* ! OPTIONS */
277	opterr = 0;
278	while ((j = getopt(argc, argv, OPTIONS)) != -1)
279	{
280		switch (j)
281		{
282		  case 'd':
283			/* hack attack -- see if should use ANSI mode */
284			if (strcmp(optarg, "ANSI") == 0)
285			{
286				TermEscape.te_rv_on = "\033[7m";
287				TermEscape.te_rv_off = "\033[0m";
288				break;
289			}
290			tTflag(optarg);
291			setbuf(stdout, (char *) NULL);
292			break;
293
294		  case 'G':	/* relay (gateway) submission */
295			SubmitMode |= SUBMIT_MTA;
296			break;
297
298		  case 'L':
299			j = min(strlen(optarg), 24) + 1;
300			sysloglabel = xalloc(j);
301			(void) strlcpy(sysloglabel, optarg, j);
302			break;
303
304		  case 'U':	/* initial (user) submission */
305			SubmitMode |= SUBMIT_MSA;
306			break;
307		}
308	}
309	opterr = 1;
310
311#if LOG
312	if (sysloglabel != NULL)
313	{
314		/* Sanitize the string */
315		for (p = sysloglabel; *p != '\0'; p++)
316		{
317			if (!isascii(*p) || !isprint(*p) || *p == '%')
318				*p = '*';
319		}
320		closelog();
321#  ifdef LOG_MAIL
322		openlog(sysloglabel, LOG_PID, LOG_MAIL);
323#  else /* LOG_MAIL */
324		openlog(sysloglabel, LOG_PID);
325#  endif /* LOG_MAIL */
326	}
327#endif /* LOG */
328
329	/* set up the blank envelope */
330	BlankEnvelope.e_puthdr = putheader;
331	BlankEnvelope.e_putbody = putbody;
332	BlankEnvelope.e_xfp = NULL;
333	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
334	CurEnv = &BlankEnvelope;
335	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
336
337	/*
338	**  Set default values for variables.
339	**	These cannot be in initialized data space.
340	*/
341
342	setdefaults(&BlankEnvelope);
343
344	RealUid = getuid();
345	RealGid = getgid();
346
347	pw = sm_getpwuid(RealUid);
348	if (pw != NULL)
349		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
350	else
351		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
352				(int) RealUid);
353
354	RealUserName = rnamebuf;
355
356	if (tTd(0, 101))
357	{
358		dprintf("Version %s\n", Version);
359		finis(FALSE, EX_OK);
360	}
361
362	/*
363	**  if running non-setuid binary as non-root, pretend
364	**  we are the RunAsUid
365	*/
366
367	if (RealUid != 0 && geteuid() == RealUid)
368	{
369		if (tTd(47, 1))
370			dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n",
371				(int)RealUid);
372		RunAsUid = RealUid;
373	}
374	else if (geteuid() != 0)
375		RunAsUid = geteuid();
376
377	if (RealUid != 0 && getegid() == RealGid)
378		RunAsGid = RealGid;
379
380	if (tTd(47, 5))
381	{
382		dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
383			(int)geteuid(), (int)getuid(),
384			(int)getegid(), (int)getgid());
385		dprintf("main: RunAsUser = %d:%d\n",
386			(int)RunAsUid, (int)RunAsGid);
387	}
388
389	/* save command line arguments */
390	j = 0;
391	for (av = argv; *av != NULL; )
392		j += strlen(*av++) + 1;
393	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
394	CommandLineArgs = xalloc(j);
395	p = CommandLineArgs;
396	for (av = argv, i = 0; *av != NULL; )
397	{
398		int h;
399
400		SaveArgv[i++] = newstr(*av);
401		if (av != argv)
402			*p++ = ' ';
403		(void) strlcpy(p, *av++, j);
404		h = strlen(p);
405		p += h;
406		j -= h + 1;
407	}
408	SaveArgv[i] = NULL;
409
410	if (tTd(0, 1))
411	{
412		int ll;
413		extern char *CompileOptions[];
414
415		dprintf("Version %s\n Compiled with:", Version);
416		av = CompileOptions;
417		ll = 7;
418		while (*av != NULL)
419		{
420			if (ll + strlen(*av) > 63)
421			{
422				dprintf("\n");
423				ll = 0;
424			}
425			if (ll == 0)
426				dprintf("\t\t");
427			else
428				dprintf(" ");
429			dprintf("%s", *av);
430			ll += strlen(*av++) + 1;
431		}
432		dprintf("\n");
433	}
434	if (tTd(0, 10))
435	{
436		int ll;
437		extern char *OsCompileOptions[];
438
439		dprintf("    OS Defines:");
440		av = OsCompileOptions;
441		ll = 7;
442		while (*av != NULL)
443		{
444			if (ll + strlen(*av) > 63)
445			{
446				dprintf("\n");
447				ll = 0;
448			}
449			if (ll == 0)
450				dprintf("\t\t");
451			else
452				dprintf(" ");
453			dprintf("%s", *av);
454			ll += strlen(*av++) + 1;
455		}
456		dprintf("\n");
457#ifdef _PATH_UNIX
458		dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
459#endif /* _PATH_UNIX */
460		dprintf(" Def Conf file:\t%s\n", getcfname());
461		dprintf("  Def Pid file:\t%s\n", PidFile);
462	}
463
464	InChannel = stdin;
465	OutChannel = stdout;
466
467	/* clear sendmail's environment */
468	ExternalEnviron = environ;
469	emptyenviron[0] = NULL;
470	environ = emptyenviron;
471
472	/*
473	**  restore any original TZ setting until TimeZoneSpec has been
474	**  determined - or early log messages may get bogus time stamps
475	*/
476	if ((p = getextenv("TZ")) != NULL)
477	{
478		char *tz;
479		int tzlen;
480
481		tzlen = strlen(p) + 4;
482		tz = xalloc(tzlen);
483		(void) snprintf(tz, tzlen, "TZ=%s", p);
484		(void) putenv(tz);
485	}
486
487	/* prime the child environment */
488	setuserenv("AGENT", "sendmail");
489	(void) setsignal(SIGPIPE, SIG_IGN);
490
491	OldUmask = umask(022);
492	OpMode = MD_DELIVER;
493	FullName = getextenv("NAME");
494
495	/*
496	**  Initialize name server if it is going to be used.
497	*/
498
499#if NAMED_BIND
500	if (!bitset(RES_INIT, _res.options))
501		(void) res_init();
502
503	/*
504	**  hack to avoid crashes when debugging for the resolver is
505	**  turned on and sfio is used
506	*/
507	if (tTd(8, 8))
508# if !SFIO || SFIO_STDIO_COMPAT
509		_res.options |= RES_DEBUG;
510# else /* !SFIO || SFIO_STDIO_COMPAT */
511		dprintf("RES_DEBUG not available due to SFIO\n");
512# endif /* !SFIO || SFIO_STDIO_COMPAT */
513	else
514		_res.options &= ~RES_DEBUG;
515# ifdef RES_NOALIASES
516	_res.options |= RES_NOALIASES;
517# endif /* RES_NOALIASES */
518	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
519	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
520	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
521	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
522	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
523	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
524#endif /* NAMED_BIND */
525
526	errno = 0;
527	from = NULL;
528
529	/* initialize some macros, etc. */
530	initmacros(CurEnv);
531	init_vendor_macros(CurEnv);
532
533	/* version */
534	define('v', Version, CurEnv);
535
536	/* hostname */
537	hp = myhostname(jbuf, sizeof jbuf);
538	if (jbuf[0] != '\0')
539	{
540		struct	utsname	utsname;
541
542		if (tTd(0, 4))
543			dprintf("canonical name: %s\n", jbuf);
544		define('w', newstr(jbuf), CurEnv);	/* must be new string */
545		define('j', newstr(jbuf), CurEnv);
546		setclass('w', jbuf);
547
548		p = strchr(jbuf, '.');
549		if (p != NULL)
550		{
551			if (p[1] != '\0')
552			{
553				define('m', newstr(&p[1]), CurEnv);
554			}
555			while (p != NULL && strchr(&p[1], '.') != NULL)
556			{
557				*p = '\0';
558				if (tTd(0, 4))
559					dprintf("\ta.k.a.: %s\n", jbuf);
560				setclass('w', jbuf);
561				*p++ = '.';
562				p = strchr(p, '.');
563			}
564		}
565
566		if (uname(&utsname) >= 0)
567			p = utsname.nodename;
568		else
569		{
570			if (tTd(0, 22))
571				dprintf("uname failed (%s)\n",
572					errstring(errno));
573			makelower(jbuf);
574			p = jbuf;
575		}
576		if (tTd(0, 4))
577			dprintf(" UUCP nodename: %s\n", p);
578		p = newstr(p);
579		define('k', p, CurEnv);
580		setclass('k', p);
581		setclass('w', p);
582	}
583	if (hp != NULL)
584	{
585		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
586		{
587			if (tTd(0, 4))
588				dprintf("\ta.k.a.: %s\n", *av);
589			setclass('w', *av);
590		}
591#if NETINET || NETINET6
592		for (i = 0; hp->h_addr_list[i] != NULL; i++)
593		{
594# if NETINET6
595			char *addr;
596			char buf6[INET6_ADDRSTRLEN];
597			struct in6_addr ia6;
598# endif /* NETINET6 */
599# if NETINET
600			struct in_addr ia;
601# endif /* NETINET */
602			char ipbuf[103];
603
604			ipbuf[0] = '\0';
605			switch (hp->h_addrtype)
606			{
607# if NETINET
608			  case AF_INET:
609				if (hp->h_length != INADDRSZ)
610					break;
611
612				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
613				(void) snprintf(ipbuf,	 sizeof ipbuf,
614						"[%.100s]", inet_ntoa(ia));
615				break;
616# endif /* NETINET */
617
618# if NETINET6
619			  case AF_INET6:
620				if (hp->h_length != IN6ADDRSZ)
621					break;
622
623				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
624				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
625				if (addr != NULL)
626					(void) snprintf(ipbuf, sizeof ipbuf,
627							"[%.100s]", addr);
628				break;
629# endif /* NETINET6 */
630			}
631			if (ipbuf[0] == '\0')
632				break;
633
634			if (tTd(0, 4))
635				dprintf("\ta.k.a.: %s\n", ipbuf);
636			setclass('w', ipbuf);
637		}
638#endif /* NETINET || NETINET6 */
639#if _FFR_FREEHOSTENT && NETINET6
640		freehostent(hp);
641		hp = NULL;
642#endif /* _FFR_FREEHOSTENT && NETINET6 */
643	}
644
645	/* current time */
646	define('b', arpadate((char *) NULL), CurEnv);
647	/* current load average */
648	CurrentLA = sm_getla(CurEnv);
649
650	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
651	QueueLimitSender = (QUEUE_CHAR *) NULL;
652	QueueLimitId = (QUEUE_CHAR *) NULL;
653
654	/*
655	**  Crack argv.
656	*/
657
658	av = argv;
659	p = strrchr(*av, '/');
660	if (p++ == NULL)
661		p = *av;
662	if (strcmp(p, "newaliases") == 0)
663		OpMode = MD_INITALIAS;
664	else if (strcmp(p, "mailq") == 0)
665		OpMode = MD_PRINT;
666	else if (strcmp(p, "smtpd") == 0)
667		OpMode = MD_DAEMON;
668	else if (strcmp(p, "hoststat") == 0)
669		OpMode = MD_HOSTSTAT;
670	else if (strcmp(p, "purgestat") == 0)
671		OpMode = MD_PURGESTAT;
672
673	optind = 1;
674	while ((j = getopt(argc, argv, OPTIONS)) != -1)
675	{
676		switch (j)
677		{
678		  case 'b':	/* operations mode */
679			switch (j = *optarg)
680			{
681			  case MD_DAEMON:
682			  case MD_FGDAEMON:
683#if !DAEMON
684				usrerr("Daemon mode not implemented");
685				ExitStat = EX_USAGE;
686				break;
687#endif /* !DAEMON */
688			  case MD_SMTP:
689#if !SMTP
690				usrerr("I don't speak SMTP");
691				ExitStat = EX_USAGE;
692				break;
693#endif /* !SMTP */
694
695			  case MD_INITALIAS:
696			  case MD_DELIVER:
697			  case MD_VERIFY:
698			  case MD_TEST:
699			  case MD_PRINT:
700			  case MD_HOSTSTAT:
701			  case MD_PURGESTAT:
702			  case MD_ARPAFTP:
703				OpMode = j;
704				break;
705
706			  case MD_FREEZE:
707				usrerr("Frozen configurations unsupported");
708				ExitStat = EX_USAGE;
709				break;
710
711			  default:
712				usrerr("Invalid operation mode %c", j);
713				ExitStat = EX_USAGE;
714				break;
715			}
716			break;
717
718		  case 'B':	/* body type */
719			CurEnv->e_bodytype = newstr(optarg);
720			break;
721
722		  case 'C':	/* select configuration file (already done) */
723			if (RealUid != 0)
724				warn_C_flag = TRUE;
725			ConfFile = newstr(optarg);
726			dp = drop_privileges(TRUE);
727			setstat(dp);
728			safecf = FALSE;
729			break;
730
731		  case 'd':	/* debugging -- already done */
732			break;
733
734		  case 'f':	/* from address */
735		  case 'r':	/* obsolete -f flag */
736			if (from != NULL)
737			{
738				usrerr("More than one \"from\" person");
739				ExitStat = EX_USAGE;
740				break;
741			}
742			from = newstr(denlstring(optarg, TRUE, TRUE));
743			if (strcmp(RealUserName, from) != 0)
744				warn_f_flag = j;
745			break;
746
747		  case 'F':	/* set full name */
748			FullName = newstr(optarg);
749			break;
750
751		  case 'G':	/* relay (gateway) submission */
752			/* already set */
753			break;
754
755		  case 'h':	/* hop count */
756			CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
757			if (*ep)
758			{
759				usrerr("Bad hop count (%s)", optarg);
760				ExitStat = EX_USAGE;
761			}
762			break;
763
764		  case 'L':	/* program label */
765			/* already set */
766			break;
767
768		  case 'n':	/* don't alias */
769			NoAlias = TRUE;
770			break;
771
772		  case 'N':	/* delivery status notifications */
773			DefaultNotify |= QHASNOTIFY;
774			define(macid("{dsn_notify}", NULL),
775			       newstr(optarg), CurEnv);
776			if (strcasecmp(optarg, "never") == 0)
777				break;
778			for (p = optarg; p != NULL; optarg = p)
779			{
780				p = strchr(p, ',');
781				if (p != NULL)
782					*p++ = '\0';
783				if (strcasecmp(optarg, "success") == 0)
784					DefaultNotify |= QPINGONSUCCESS;
785				else if (strcasecmp(optarg, "failure") == 0)
786					DefaultNotify |= QPINGONFAILURE;
787				else if (strcasecmp(optarg, "delay") == 0)
788					DefaultNotify |= QPINGONDELAY;
789				else
790				{
791					usrerr("Invalid -N argument");
792					ExitStat = EX_USAGE;
793				}
794			}
795			break;
796
797		  case 'o':	/* set option */
798			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
799			break;
800
801		  case 'O':	/* set option (long form) */
802			setoption(' ', optarg, FALSE, TRUE, CurEnv);
803			break;
804
805		  case 'p':	/* set protocol */
806			p = strchr(optarg, ':');
807			if (p != NULL)
808			{
809				*p++ = '\0';
810				if (*p != '\0')
811				{
812					ep = xalloc(strlen(p) + 1);
813					cleanstrcpy(ep, p, MAXNAME);
814					define('s', ep, CurEnv);
815				}
816			}
817			if (*optarg != '\0')
818			{
819				ep = xalloc(strlen(optarg) + 1);
820				cleanstrcpy(ep, optarg, MAXNAME);
821				define('r', ep, CurEnv);
822			}
823			break;
824
825		  case 'q':	/* run queue files at intervals */
826#if QUEUE
827			/* sanity check */
828			if (OpMode != MD_DELIVER &&
829			    OpMode != MD_DAEMON &&
830			    OpMode != MD_FGDAEMON &&
831			    OpMode != MD_PRINT &&
832			    OpMode != MD_QUEUERUN)
833			{
834				usrerr("Can not use -q with -b%c", OpMode);
835				ExitStat = EX_USAGE;
836				break;
837			}
838
839			/* don't override -bd, -bD or -bp */
840			if (OpMode == MD_DELIVER)
841				OpMode = MD_QUEUERUN;
842
843			FullName = NULL;
844
845			switch (optarg[0])
846			{
847			  case 'I':
848				new = (QUEUE_CHAR *) xalloc(sizeof *new);
849				new->queue_match = newstr(&optarg[1]);
850				new->queue_next = QueueLimitId;
851				QueueLimitId = new;
852				break;
853
854			  case 'R':
855				new = (QUEUE_CHAR *) xalloc(sizeof *new);
856				new->queue_match = newstr(&optarg[1]);
857				new->queue_next = QueueLimitRecipient;
858				QueueLimitRecipient = new;
859				break;
860
861			  case 'S':
862				new = (QUEUE_CHAR *) xalloc(sizeof *new);
863				new->queue_match = newstr(&optarg[1]);
864				new->queue_next = QueueLimitSender;
865				QueueLimitSender = new;
866				break;
867
868			  default:
869				i = Errors;
870				QueueIntvl = convtime(optarg, 'm');
871
872				/* check for bad conversion */
873				if (i < Errors)
874					ExitStat = EX_USAGE;
875				break;
876			}
877#else /* QUEUE */
878			usrerr("I don't know about queues");
879			ExitStat = EX_USAGE;
880#endif /* QUEUE */
881			break;
882
883		  case 'R':	/* DSN RET: what to return */
884			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
885			{
886				usrerr("Duplicate -R flag");
887				ExitStat = EX_USAGE;
888				break;
889			}
890			CurEnv->e_flags |= EF_RET_PARAM;
891			if (strcasecmp(optarg, "hdrs") == 0)
892				CurEnv->e_flags |= EF_NO_BODY_RETN;
893			else if (strcasecmp(optarg, "full") != 0)
894			{
895				usrerr("Invalid -R value");
896				ExitStat = EX_USAGE;
897			}
898			define(macid("{dsn_ret}", NULL),
899			       newstr(optarg), CurEnv);
900			break;
901
902		  case 't':	/* read recipients from message */
903			GrabTo = TRUE;
904			break;
905
906		  case 'U':	/* initial (user) submission */
907			/* already set */
908			break;
909
910		  case 'V':	/* DSN ENVID: set "original" envelope id */
911			if (!xtextok(optarg))
912			{
913				usrerr("Invalid syntax in -V flag");
914				ExitStat = EX_USAGE;
915			}
916			else
917			{
918				CurEnv->e_envid = newstr(optarg);
919				define(macid("{dsn_envid}", NULL),
920				       newstr(optarg), CurEnv);
921			}
922			break;
923
924		  case 'X':	/* traffic log file */
925			dp = drop_privileges(TRUE);
926			setstat(dp);
927			if (stat(optarg, &traf_st) == 0 &&
928			    S_ISFIFO(traf_st.st_mode))
929				TrafficLogFile = fopen(optarg, "w");
930			else
931				TrafficLogFile = fopen(optarg, "a");
932			if (TrafficLogFile == NULL)
933			{
934				syserr("cannot open %s", optarg);
935				ExitStat = EX_CANTCREAT;
936				break;
937			}
938#if HASSETVBUF
939			(void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
940#else /* HASSETVBUF */
941			(void) setlinebuf(TrafficLogFile);
942#endif /* HASSETVBUF */
943			break;
944
945			/* compatibility flags */
946		  case 'c':	/* connect to non-local mailers */
947		  case 'i':	/* don't let dot stop me */
948		  case 'm':	/* send to me too */
949		  case 'T':	/* set timeout interval */
950		  case 'v':	/* give blow-by-blow description */
951			setoption(j, "T", FALSE, TRUE, CurEnv);
952			break;
953
954		  case 'e':	/* error message disposition */
955		  case 'M':	/* define macro */
956			setoption(j, optarg, FALSE, TRUE, CurEnv);
957			break;
958
959		  case 's':	/* save From lines in headers */
960			setoption('f', "T", FALSE, TRUE, CurEnv);
961			break;
962
963#ifdef DBM
964		  case 'I':	/* initialize alias DBM file */
965			OpMode = MD_INITALIAS;
966			break;
967#endif /* DBM */
968
969#if defined(__osf__) || defined(_AIX3)
970		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
971			break;
972#endif /* defined(__osf__) || defined(_AIX3) */
973#if defined(sony_news)
974		  case 'E':
975		  case 'J':	/* ignore flags for Japanese code conversion
976				   implemented on Sony NEWS */
977			break;
978#endif /* defined(sony_news) */
979
980		  default:
981			finis(TRUE, EX_USAGE);
982			break;
983		}
984	}
985	av += optind;
986
987	if (bitset(SUBMIT_MTA, SubmitMode) &&
988	    bitset(SUBMIT_MSA, SubmitMode))
989	{
990		/* sanity check */
991		errno = 0;	/* reset to avoid bogus error messages */
992		syserr("Cannot use both -G and -U together");
993	}
994	else if (bitset(SUBMIT_MTA, SubmitMode))
995		define(macid("{daemon_flags}", NULL), "CC f", CurEnv);
996	else if (bitset(SUBMIT_MSA, SubmitMode))
997	{
998		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
999
1000		/* check for wrong OpMode */
1001		if (OpMode != MD_DELIVER && OpMode != MD_SMTP)
1002		{
1003			errno = 0;	/* reset to avoid bogus error msgs */
1004			syserr("Cannot use -U and -b%c", OpMode);
1005		}
1006	}
1007	else
1008	{
1009#if _FFR_DEFAULT_SUBMIT_TO_MSA
1010		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
1011#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */
1012		/* EMPTY */
1013#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */
1014	}
1015
1016	/*
1017	**  Do basic initialization.
1018	**	Read system control file.
1019	**	Extract special fields for local use.
1020	*/
1021
1022	/* set up ${opMode} for use in config file */
1023	{
1024		char mbuf[2];
1025
1026		mbuf[0] = OpMode;
1027		mbuf[1] = '\0';
1028		define(MID_OPMODE, newstr(mbuf), CurEnv);
1029	}
1030
1031#if XDEBUG
1032	checkfd012("before readcf");
1033#endif /* XDEBUG */
1034	vendor_pre_defaults(CurEnv);
1035
1036	readcf(getcfname(), safecf, CurEnv);
1037	ConfigFileRead = TRUE;
1038	vendor_post_defaults(CurEnv);
1039
1040	/* Remove the ability for a normal user to send signals */
1041	if (RealUid != 0 &&
1042	    RealUid != geteuid())
1043	{
1044		uid_t new_uid = geteuid();
1045
1046#if HASSETREUID
1047		/*
1048		**  Since we can differentiate between uid and euid,
1049		**  make the uid a different user so the real user
1050		**  can't send signals.  However, it doesn't need to be
1051		**  root (euid has root).
1052		*/
1053
1054		if (new_uid == 0)
1055			new_uid = DefUid;
1056		if (tTd(47, 5))
1057			dprintf("Changing real uid to %d\n", (int) new_uid);
1058		if (setreuid(new_uid, geteuid()) < 0)
1059		{
1060			syserr("main: setreuid(%d, %d) failed",
1061			       (int) new_uid, (int) geteuid());
1062			finis(FALSE, EX_OSERR);
1063			/* NOTREACHED */
1064		}
1065		if (tTd(47, 10))
1066			dprintf("Now running as e/ruid %d:%d\n",
1067				(int) geteuid(), (int) getuid());
1068#else /* HASSETREUID */
1069		/*
1070		**  Have to change both effective and real so need to
1071		**  change them both to effective to keep privs.
1072		*/
1073
1074		if (tTd(47, 5))
1075			dprintf("Changing uid to %d\n", (int) new_uid);
1076		if (setuid(new_uid) < 0)
1077		{
1078			syserr("main: setuid(%d) failed", (int) new_uid);
1079			finis(FALSE, EX_OSERR);
1080			/* NOTREACHED */
1081		}
1082		if (tTd(47, 10))
1083			dprintf("Now running as e/ruid %d:%d\n",
1084				(int) geteuid(), (int) getuid());
1085#endif /* HASSETREUID */
1086	}
1087
1088	/* set up the basic signal handlers */
1089	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
1090		(void) setsignal(SIGINT, intsig);
1091	(void) setsignal(SIGTERM, intsig);
1092
1093	/* Enforce use of local time (null string overrides this) */
1094	if (TimeZoneSpec == NULL)
1095		unsetenv("TZ");
1096	else if (TimeZoneSpec[0] != '\0')
1097		setuserenv("TZ", TimeZoneSpec);
1098	else
1099		setuserenv("TZ", NULL);
1100	tzset();
1101
1102	/* avoid denial-of-service attacks */
1103	resetlimits();
1104
1105	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
1106	{
1107		/* drop privileges -- daemon mode done after socket/bind */
1108		dp = drop_privileges(FALSE);
1109		setstat(dp);
1110	}
1111
1112#if NAMED_BIND
1113	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
1114	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1115#endif /* NAMED_BIND */
1116
1117	/*
1118	**  Find our real host name for future logging.
1119	*/
1120
1121	authinfo = getauthinfo(STDIN_FILENO, &forged);
1122	define('_', authinfo, CurEnv);
1123
1124	/* suppress error printing if errors mailed back or whatever */
1125	if (CurEnv->e_errormode != EM_PRINT)
1126		HoldErrs = TRUE;
1127
1128	/* set up the $=m class now, after .cf has a chance to redefine $m */
1129	expand("\201m", jbuf, sizeof jbuf, CurEnv);
1130	if (jbuf[0] != '\0')
1131		setclass('m', jbuf);
1132
1133	/* probe interfaces and locate any additional names */
1134	if (!DontProbeInterfaces)
1135		load_if_names();
1136
1137	if (tTd(0, 1))
1138	{
1139		dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
1140		dprintf("\n      (short domain name) $w = ");
1141		xputs(macvalue('w', CurEnv));
1142		dprintf("\n  (canonical domain name) $j = ");
1143		xputs(macvalue('j', CurEnv));
1144		dprintf("\n         (subdomain name) $m = ");
1145		xputs(macvalue('m', CurEnv));
1146		dprintf("\n              (node name) $k = ");
1147		xputs(macvalue('k', CurEnv));
1148		dprintf("\n========================================================\n\n");
1149	}
1150
1151	/*
1152	**  Do more command line checking -- these are things that
1153	**  have to modify the results of reading the config file.
1154	*/
1155
1156	/* process authorization warnings from command line */
1157	if (warn_C_flag)
1158		auth_warning(CurEnv, "Processed by %s with -C %s",
1159			RealUserName, ConfFile);
1160	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
1161		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
1162
1163	/* check body type for legality */
1164	if (CurEnv->e_bodytype == NULL)
1165		/* EMPTY */
1166		/* nothing */ ;
1167	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
1168		SevenBitInput = TRUE;
1169	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
1170		SevenBitInput = FALSE;
1171	else
1172	{
1173		usrerr("Illegal body type %s", CurEnv->e_bodytype);
1174		CurEnv->e_bodytype = NULL;
1175	}
1176
1177	/* tweak default DSN notifications */
1178	if (DefaultNotify == 0)
1179		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1180
1181	/* be sure we don't pick up bogus HOSTALIASES environment variable */
1182	if (OpMode == MD_QUEUERUN && RealUid != 0)
1183		(void) unsetenv("HOSTALIASES");
1184
1185	/* check for sane configuration level */
1186	if (ConfigLevel > MAXCONFIGLEVEL)
1187	{
1188		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
1189			ConfigLevel, Version, MAXCONFIGLEVEL);
1190	}
1191
1192	/* need MCI cache to have persistence */
1193	if (HostStatDir != NULL && MaxMciCache == 0)
1194	{
1195		HostStatDir = NULL;
1196		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
1197	}
1198
1199	/* need HostStatusDir in order to have SingleThreadDelivery */
1200	if (SingleThreadDelivery && HostStatDir == NULL)
1201	{
1202		SingleThreadDelivery = FALSE;
1203		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
1204	}
1205
1206	/* check for permissions */
1207	if ((OpMode == MD_DAEMON ||
1208	     OpMode == MD_FGDAEMON ||
1209	     OpMode == MD_PURGESTAT) &&
1210	    RealUid != 0 &&
1211	    RealUid != TrustedUid)
1212	{
1213		if (LogLevel > 1)
1214			sm_syslog(LOG_ALERT, NOQID,
1215				  "user %d attempted to %s",
1216				  RealUid,
1217				  OpMode != MD_PURGESTAT ? "run daemon"
1218							 : "purge host status");
1219		usrerr("Permission denied");
1220		finis(FALSE, EX_USAGE);
1221	}
1222	if (OpMode == MD_INITALIAS &&
1223	    RealUid != 0 &&
1224	    RealUid != TrustedUid &&
1225	    !wordinclass(RealUserName, 't'))
1226	{
1227		if (LogLevel > 1)
1228			sm_syslog(LOG_ALERT, NOQID,
1229				  "user %d attempted to rebuild the alias map",
1230				  RealUid);
1231		usrerr("Permission denied");
1232		finis(FALSE, EX_USAGE);
1233	}
1234
1235	if (MeToo)
1236		BlankEnvelope.e_flags |= EF_METOO;
1237
1238	switch (OpMode)
1239	{
1240	  case MD_TEST:
1241		/* don't have persistent host status in test mode */
1242		HostStatDir = NULL;
1243		if (Verbose == 0)
1244			Verbose = 2;
1245		CurEnv->e_errormode = EM_PRINT;
1246		HoldErrs = FALSE;
1247		break;
1248
1249	  case MD_VERIFY:
1250		CurEnv->e_errormode = EM_PRINT;
1251		HoldErrs = FALSE;
1252
1253		/* arrange to exit cleanly on hangup signal */
1254		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1255			(void) setsignal(SIGHUP, intsig);
1256		break;
1257
1258	  case MD_FGDAEMON:
1259		run_in_foreground = TRUE;
1260		OpMode = MD_DAEMON;
1261		/* FALLTHROUGH */
1262
1263	  case MD_DAEMON:
1264		vendor_daemon_setup(CurEnv);
1265
1266		/* remove things that don't make sense in daemon mode */
1267		FullName = NULL;
1268		GrabTo = FALSE;
1269
1270		/* arrange to restart on hangup signal */
1271		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1272			sm_syslog(LOG_WARNING, NOQID,
1273				  "daemon invoked without full pathname; kill -1 won't work");
1274		(void) setsignal(SIGTERM, term_daemon);
1275		break;
1276
1277	  case MD_INITALIAS:
1278		Verbose = 2;
1279		CurEnv->e_errormode = EM_PRINT;
1280		HoldErrs = FALSE;
1281		/* FALLTHROUGH */
1282
1283	  default:
1284		/* arrange to exit cleanly on hangup signal */
1285		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1286			(void) setsignal(SIGHUP, intsig);
1287		break;
1288	}
1289
1290	/* special considerations for FullName */
1291	if (FullName != NULL)
1292	{
1293		char *full = NULL;
1294
1295		/* full names can't have newlines */
1296		if (strchr(FullName, '\n') != NULL)
1297		{
1298			full = newstr(denlstring(FullName, TRUE, TRUE));
1299			FullName = full;
1300		}
1301
1302		/* check for characters that may have to be quoted */
1303		if (!rfc822_string(FullName))
1304		{
1305			/*
1306			**  Quote a full name with special characters
1307			**  as a comment so crackaddr() doesn't destroy
1308			**  the name portion of the address.
1309			*/
1310
1311			FullName = addquotes(FullName);
1312			if (full != NULL)
1313				sm_free(full);
1314		}
1315	}
1316
1317	/* do heuristic mode adjustment */
1318	if (Verbose)
1319	{
1320		/* turn off noconnect option */
1321		setoption('c', "F", TRUE, FALSE, CurEnv);
1322
1323		/* turn on interactive delivery */
1324		setoption('d', "", TRUE, FALSE, CurEnv);
1325	}
1326
1327#ifdef VENDOR_CODE
1328	/* check for vendor mismatch */
1329	if (VendorCode != VENDOR_CODE)
1330	{
1331		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1332			getvendor(VENDOR_CODE), getvendor(VendorCode));
1333	}
1334#endif /* VENDOR_CODE */
1335
1336	/* check for out of date configuration level */
1337	if (ConfigLevel < MAXCONFIGLEVEL)
1338	{
1339		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1340			Version, MAXCONFIGLEVEL, ConfigLevel);
1341	}
1342
1343	if (ConfigLevel < 3)
1344		UseErrorsTo = TRUE;
1345
1346	/* set options that were previous macros */
1347	if (SmtpGreeting == NULL)
1348	{
1349		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1350			SmtpGreeting = newstr(p);
1351		else
1352			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1353	}
1354	if (UnixFromLine == NULL)
1355	{
1356		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1357			UnixFromLine = newstr(p);
1358		else
1359			UnixFromLine = "From \201g  \201d";
1360	}
1361	SmtpError[0] = '\0';
1362
1363	/* our name for SMTP codes */
1364	expand("\201j", jbuf, sizeof jbuf, CurEnv);
1365	if (jbuf[0] == '\0')
1366		MyHostName = newstr("localhost");
1367	else
1368		MyHostName = jbuf;
1369	if (strchr(MyHostName, '.') == NULL)
1370		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1371			MyHostName);
1372
1373	/* make certain that this name is part of the $=w class */
1374	setclass('w', MyHostName);
1375
1376	/* the indices of built-in mailers */
1377	st = stab("local", ST_MAILER, ST_FIND);
1378	if (st != NULL)
1379		LocalMailer = st->s_mailer;
1380	else if (OpMode != MD_TEST || !warn_C_flag)
1381		syserr("No local mailer defined");
1382
1383	st = stab("prog", ST_MAILER, ST_FIND);
1384	if (st == NULL)
1385		syserr("No prog mailer defined");
1386	else
1387	{
1388		ProgMailer = st->s_mailer;
1389		clrbitn(M_MUSER, ProgMailer->m_flags);
1390	}
1391
1392	st = stab("*file*", ST_MAILER, ST_FIND);
1393	if (st == NULL)
1394		syserr("No *file* mailer defined");
1395	else
1396	{
1397		FileMailer = st->s_mailer;
1398		clrbitn(M_MUSER, FileMailer->m_flags);
1399	}
1400
1401	st = stab("*include*", ST_MAILER, ST_FIND);
1402	if (st == NULL)
1403		syserr("No *include* mailer defined");
1404	else
1405		InclMailer = st->s_mailer;
1406
1407	if (ConfigLevel < 6)
1408	{
1409		/* heuristic tweaking of local mailer for back compat */
1410		if (LocalMailer != NULL)
1411		{
1412			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1413			setbitn(M_HASPWENT, LocalMailer->m_flags);
1414			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1415			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1416			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1417			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1418			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1419		}
1420		if (ProgMailer != NULL)
1421			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1422		if (FileMailer != NULL)
1423			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1424	}
1425	if (ConfigLevel < 7)
1426	{
1427		if (LocalMailer != NULL)
1428			setbitn(M_VRFY250, LocalMailer->m_flags);
1429		if (ProgMailer != NULL)
1430			setbitn(M_VRFY250, ProgMailer->m_flags);
1431		if (FileMailer != NULL)
1432			setbitn(M_VRFY250, FileMailer->m_flags);
1433	}
1434
1435	/* MIME Content-Types that cannot be transfer encoded */
1436	setclass('n', "multipart/signed");
1437
1438	/* MIME message/xxx subtypes that can be treated as messages */
1439	setclass('s', "rfc822");
1440
1441	/* MIME Content-Transfer-Encodings that can be encoded */
1442	setclass('e', "7bit");
1443	setclass('e', "8bit");
1444	setclass('e', "binary");
1445
1446#ifdef USE_B_CLASS
1447	/* MIME Content-Types that should be treated as binary */
1448	setclass('b', "image");
1449	setclass('b', "audio");
1450	setclass('b', "video");
1451	setclass('b', "application/octet-stream");
1452#endif /* USE_B_CLASS */
1453
1454	/* MIME headers which have fields to check for overflow */
1455	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1456	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1457
1458	/* MIME headers to check for length overflow */
1459	setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1460
1461	/* MIME headers to check for overflow and rebalance */
1462	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1463	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1464	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1465	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1466	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1467
1468	/* Macros to save in the qf file -- don't remove any */
1469	setclass(macid("{persistentMacros}", NULL), "r");
1470	setclass(macid("{persistentMacros}", NULL), "s");
1471	setclass(macid("{persistentMacros}", NULL), "_");
1472	setclass(macid("{persistentMacros}", NULL), "{if_addr}");
1473	setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
1474	setclass(macid("{persistentMacros}", NULL), "{client_flags}");
1475
1476	/* operate in queue directory */
1477	if (QueueDir == NULL)
1478	{
1479		if (OpMode != MD_TEST)
1480		{
1481			syserr("QueueDirectory (Q) option must be set");
1482			ExitStat = EX_CONFIG;
1483		}
1484	}
1485	else
1486	{
1487		/*
1488		**  If multiple queues wildcarded, use one for
1489		**  the daemon's home. Note that this preconditions
1490		**  a wildcarded QueueDir to a real pathname.
1491		*/
1492
1493		if (OpMode != MD_TEST)
1494			multiqueue_cache();
1495	}
1496
1497	/* check host status directory for validity */
1498	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1499	{
1500		/* cannot use this value */
1501		if (tTd(0, 2))
1502			dprintf("Cannot use HostStatusDirectory = %s: %s\n",
1503				HostStatDir, errstring(errno));
1504		HostStatDir = NULL;
1505	}
1506
1507#if QUEUE
1508	if (OpMode == MD_QUEUERUN && RealUid != 0 &&
1509	    bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1510	{
1511		struct stat stbuf;
1512
1513		/* check to see if we own the queue directory */
1514		if (stat(".", &stbuf) < 0)
1515			syserr("main: cannot stat %s", QueueDir);
1516		if (stbuf.st_uid != RealUid)
1517		{
1518			/* nope, really a botch */
1519			usrerr("You do not have permission to process the queue");
1520			finis(FALSE, EX_NOPERM);
1521		}
1522	}
1523#endif /* QUEUE */
1524
1525#if _FFR_MILTER
1526	/* sanity checks on milter filters */
1527	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
1528		milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
1529#endif /* _FFR_MILTER */
1530
1531
1532	/* if we've had errors so far, exit now */
1533	if (ExitStat != EX_OK && OpMode != MD_TEST)
1534		finis(FALSE, ExitStat);
1535
1536#if XDEBUG
1537	checkfd012("before main() initmaps");
1538#endif /* XDEBUG */
1539
1540	/*
1541	**  Do operation-mode-dependent initialization.
1542	*/
1543
1544	switch (OpMode)
1545	{
1546	  case MD_PRINT:
1547		/* print the queue */
1548#if QUEUE
1549		dropenvelope(CurEnv, TRUE);
1550		(void) setsignal(SIGPIPE, quiesce);
1551		printqueue();
1552		finis(FALSE, EX_OK);
1553#else /* QUEUE */
1554		usrerr("No queue to print");
1555		finis(FALSE, EX_UNAVAILABLE);
1556#endif /* QUEUE */
1557		break;
1558
1559	  case MD_HOSTSTAT:
1560		(void) setsignal(SIGPIPE, quiesce);
1561		(void) mci_traverse_persistent(mci_print_persistent, NULL);
1562		finis(FALSE, EX_OK);
1563		break;
1564
1565	  case MD_PURGESTAT:
1566		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
1567		finis(FALSE, EX_OK);
1568		break;
1569
1570	  case MD_INITALIAS:
1571		/* initialize maps */
1572		initmaps();
1573		finis(FALSE, ExitStat);
1574		break;
1575
1576	  case MD_SMTP:
1577	  case MD_DAEMON:
1578		/* reset DSN parameters */
1579		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1580		define(macid("{dsn_notify}", NULL), NULL, CurEnv);
1581		CurEnv->e_envid = NULL;
1582		define(macid("{dsn_envid}", NULL), NULL, CurEnv);
1583		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1584		define(macid("{dsn_ret}", NULL), NULL, CurEnv);
1585
1586		/* don't open maps for daemon -- done below in child */
1587		break;
1588	}
1589
1590	if (tTd(0, 15))
1591	{
1592		/* print configuration table (or at least part of it) */
1593		if (tTd(0, 90))
1594			printrules();
1595		for (i = 0; i < MAXMAILERS; i++)
1596		{
1597			if (Mailer[i] != NULL)
1598				printmailer(Mailer[i]);
1599		}
1600	}
1601
1602	/*
1603	**  Switch to the main envelope.
1604	*/
1605
1606	CurEnv = newenvelope(&MainEnvelope, CurEnv);
1607	MainEnvelope.e_flags = BlankEnvelope.e_flags;
1608
1609	/*
1610	**  If test mode, read addresses from stdin and process.
1611	*/
1612
1613	if (OpMode == MD_TEST)
1614	{
1615		char buf[MAXLINE];
1616
1617#if _FFR_TESTMODE_DROP_PRIVS
1618		dp = drop_privileges(TRUE);
1619		if (dp != EX_OK)
1620		{
1621			CurEnv->e_id = NULL;
1622			finis(TRUE, dp);
1623		}
1624#endif /* _FFR_TESTMODE_DROP_PRIVS */
1625
1626		if (isatty(fileno(stdin)))
1627			Verbose = 2;
1628
1629		if (Verbose)
1630		{
1631			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1632			printf("Enter <ruleset> <address>\n");
1633		}
1634		if (setjmp(TopFrame) > 0)
1635			printf("\n");
1636		(void) setsignal(SIGINT, intindebug);
1637		for (;;)
1638		{
1639			if (Verbose == 2)
1640				printf("> ");
1641			(void) fflush(stdout);
1642			if (fgets(buf, sizeof buf, stdin) == NULL)
1643				testmodeline("/quit", CurEnv);
1644			p = strchr(buf, '\n');
1645			if (p != NULL)
1646				*p = '\0';
1647			if (Verbose < 2)
1648				printf("> %s\n", buf);
1649			testmodeline(buf, CurEnv);
1650		}
1651	}
1652
1653#if SMTP
1654# if STARTTLS
1655	tls_ok = init_tls_library();
1656# endif /* STARTTLS */
1657#endif /* SMTP */
1658
1659#if QUEUE
1660	/*
1661	**  If collecting stuff from the queue, go start doing that.
1662	*/
1663
1664	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
1665	{
1666# if SMTP
1667#  if STARTTLS
1668		if (tls_ok
1669		   )
1670		{
1671			/* init TLS for client, ignore result for now */
1672			(void) initclttls();
1673		}
1674#  endif /* STARTTLS */
1675# endif /* SMTP */
1676		(void) runqueue(FALSE, Verbose);
1677		finis(TRUE, ExitStat);
1678	}
1679#endif /* QUEUE */
1680
1681# if SASL
1682	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1683	{
1684		/* give a syserr or just disable AUTH ? */
1685		if ((i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
1686			syserr("!sasl_server_init failed! [%s]",
1687			       sasl_errstring(i, NULL, NULL));
1688	}
1689# endif /* SASL */
1690
1691	/*
1692	**  If a daemon, wait for a request.
1693	**	getrequests will always return in a child.
1694	**	If we should also be processing the queue, start
1695	**		doing it in background.
1696	**	We check for any errors that might have happened
1697	**		during startup.
1698	*/
1699
1700	if (OpMode == MD_DAEMON || QueueIntvl != 0)
1701	{
1702		char dtype[200];
1703
1704		if (!run_in_foreground && !tTd(99, 100))
1705		{
1706			/* put us in background */
1707			i = fork();
1708			if (i < 0)
1709				syserr("daemon: cannot fork");
1710			if (i != 0)
1711				finis(FALSE, EX_OK);
1712
1713			/* disconnect from our controlling tty */
1714			disconnect(2, CurEnv);
1715		}
1716
1717		dtype[0] = '\0';
1718		if (OpMode == MD_DAEMON)
1719			(void) strlcat(dtype, "+SMTP", sizeof dtype);
1720		if (QueueIntvl != 0)
1721		{
1722			(void) strlcat(dtype, "+queueing@", sizeof dtype);
1723			(void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
1724				       sizeof dtype);
1725		}
1726		if (tTd(0, 1))
1727			(void) strlcat(dtype, "+debugging", sizeof dtype);
1728
1729		sm_syslog(LOG_INFO, NOQID,
1730			  "starting daemon (%s): %s", Version, dtype + 1);
1731#ifdef XLA
1732		xla_create_file();
1733#endif /* XLA */
1734
1735		/* save daemon type in a macro for possible PidFile use */
1736		define(macid("{daemon_info}", NULL),
1737		       newstr(dtype + 1), &BlankEnvelope);
1738
1739		/* save queue interval in a macro for possible PidFile use */
1740		define(macid("{queue_interval}", NULL),
1741		       newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
1742
1743#if QUEUE
1744		if (QueueIntvl != 0)
1745		{
1746			(void) runqueue(TRUE, FALSE);
1747			if (OpMode != MD_DAEMON)
1748			{
1749				/* write the pid to file */
1750				log_sendmail_pid(CurEnv);
1751				(void) setsignal(SIGTERM, term_daemon);
1752				for (;;)
1753				{
1754					(void) pause();
1755					if (ShutdownRequest != NULL)
1756						shutdown_daemon();
1757					else if (DoQueueRun)
1758						(void) runqueue(TRUE, FALSE);
1759				}
1760			}
1761		}
1762#endif /* QUEUE */
1763		dropenvelope(CurEnv, TRUE);
1764
1765#if DAEMON
1766# if STARTTLS
1767		/* init TLS for server, ignore result for now */
1768		(void) initsrvtls();
1769# endif /* STARTTLS */
1770		p_flags = getrequests(CurEnv);
1771
1772		/* drop privileges */
1773		(void) drop_privileges(FALSE);
1774
1775		/* at this point we are in a child: reset state */
1776		(void) newenvelope(CurEnv, CurEnv);
1777
1778		/*
1779		**  Get authentication data
1780		*/
1781
1782		authinfo = getauthinfo(fileno(InChannel), &forged);
1783		define('_', authinfo, &BlankEnvelope);
1784#endif /* DAEMON */
1785	}
1786
1787	if (LogLevel > 9)
1788	{
1789		/* log connection information */
1790		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
1791	}
1792
1793#if SMTP
1794	/*
1795	**  If running SMTP protocol, start collecting and executing
1796	**  commands.  This will never return.
1797	*/
1798
1799	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1800	{
1801		char pbuf[20];
1802
1803		/*
1804		**  Save some macros for check_* rulesets.
1805		*/
1806
1807		if (forged)
1808		{
1809			char ipbuf[103];
1810
1811			(void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1812					anynet_ntoa(&RealHostAddr));
1813			define(macid("{client_name}", NULL),
1814			       newstr(ipbuf), &BlankEnvelope);
1815			define(macid("{client_resolve}", NULL),
1816			       "FORGED", &BlankEnvelope);
1817		}
1818		else
1819			define(macid("{client_name}", NULL), RealHostName,
1820			       &BlankEnvelope);
1821		define(macid("{client_addr}", NULL),
1822		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1823		(void)sm_getla(&BlankEnvelope);
1824
1825		switch(RealHostAddr.sa.sa_family)
1826		{
1827# if NETINET
1828		  case AF_INET:
1829			(void) snprintf(pbuf, sizeof pbuf, "%d",
1830					RealHostAddr.sin.sin_port);
1831			break;
1832# endif /* NETINET */
1833# if NETINET6
1834		  case AF_INET6:
1835			(void) snprintf(pbuf, sizeof pbuf, "%d",
1836					RealHostAddr.sin6.sin6_port);
1837			break;
1838# endif /* NETINET6 */
1839		  default:
1840			(void) snprintf(pbuf, sizeof pbuf, "0");
1841			break;
1842		}
1843		define(macid("{client_port}", NULL),
1844		       newstr(pbuf), &BlankEnvelope);
1845
1846		if (OpMode == MD_DAEMON)
1847		{
1848			/* validate the connection */
1849			HoldErrs = TRUE;
1850			nullserver = validate_connection(&RealHostAddr,
1851							 RealHostName, CurEnv);
1852			HoldErrs = FALSE;
1853		}
1854		else if (p_flags == NULL)
1855		{
1856			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
1857			clrbitmap(p_flags);
1858		}
1859# if STARTTLS
1860		if (OpMode == MD_SMTP)
1861			(void) initsrvtls();
1862# endif /* STARTTLS */
1863
1864
1865		smtp(nullserver, *p_flags, CurEnv);
1866	}
1867#endif /* SMTP */
1868
1869	clearenvelope(CurEnv, FALSE);
1870	if (OpMode == MD_VERIFY)
1871	{
1872		set_delivery_mode(SM_VERIFY, CurEnv);
1873		PostMasterCopy = NULL;
1874	}
1875	else
1876	{
1877		/* interactive -- all errors are global */
1878		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1879	}
1880
1881	/*
1882	**  Do basic system initialization and set the sender
1883	*/
1884
1885	initsys(CurEnv);
1886	define(macid("{ntries}", NULL), "0", CurEnv);
1887	setsender(from, CurEnv, NULL, '\0', FALSE);
1888	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
1889	    (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) ||
1890	     strcmp(CurEnv->e_from.q_user, RealUserName) != 0))
1891	{
1892		auth_warning(CurEnv, "%s set sender to %s using -%c",
1893			RealUserName, from, warn_f_flag);
1894#if SASL
1895		auth = FALSE;
1896#endif /* SASL */
1897	}
1898	if (auth)
1899	{
1900		char *fv;
1901
1902		/* set the initial sender for AUTH= to $f@$j */
1903		fv = macvalue('f', CurEnv);
1904		if (fv == NULL || *fv == '\0')
1905			CurEnv->e_auth_param = NULL;
1906		else
1907		{
1908			if (strchr(fv, '@') == NULL)
1909			{
1910				i = strlen(fv) + strlen(macvalue('j', CurEnv))
1911				    + 2;
1912				p = xalloc(i);
1913				(void) snprintf(p, i, "%s@%s", fv,
1914						macvalue('j', CurEnv));
1915			}
1916			else
1917				p = newstr(fv);
1918			CurEnv->e_auth_param = newstr(xtextify(p, "="));
1919		}
1920	}
1921	if (macvalue('s', CurEnv) == NULL)
1922		define('s', RealHostName, CurEnv);
1923
1924	if (*av == NULL && !GrabTo)
1925	{
1926		CurEnv->e_to = NULL;
1927		CurEnv->e_flags |= EF_GLOBALERRS;
1928		HoldErrs = FALSE;
1929		SuperSafe = FALSE;
1930		usrerr("Recipient names must be specified");
1931
1932		/* collect body for UUCP return */
1933		if (OpMode != MD_VERIFY)
1934			collect(InChannel, FALSE, NULL, CurEnv);
1935		finis(TRUE, EX_USAGE);
1936	}
1937
1938	/*
1939	**  Scan argv and deliver the message to everyone.
1940	*/
1941
1942	sendtoargv(av, CurEnv);
1943
1944	/* if we have had errors sofar, arrange a meaningful exit stat */
1945	if (Errors > 0 && ExitStat == EX_OK)
1946		ExitStat = EX_USAGE;
1947
1948#if _FFR_FIX_DASHT
1949	/*
1950	**  If using -t, force not sending to argv recipients, even
1951	**  if they are mentioned in the headers.
1952	*/
1953
1954	if (GrabTo)
1955	{
1956		ADDRESS *q;
1957
1958		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1959			q->q_state = QS_REMOVED;
1960	}
1961#endif /* _FFR_FIX_DASHT */
1962
1963	/*
1964	**  Read the input mail.
1965	*/
1966
1967	CurEnv->e_to = NULL;
1968	if (OpMode != MD_VERIFY || GrabTo)
1969	{
1970		int savederrors = Errors;
1971		long savedflags = CurEnv->e_flags & EF_FATALERRS;
1972
1973		CurEnv->e_flags |= EF_GLOBALERRS;
1974		CurEnv->e_flags &= ~EF_FATALERRS;
1975		Errors = 0;
1976		buffer_errors();
1977		collect(InChannel, FALSE, NULL, CurEnv);
1978
1979		/* header checks failed */
1980		if (Errors > 0)
1981		{
1982			/* Log who the mail would have gone to */
1983			if (LogLevel > 8 && CurEnv->e_message != NULL &&
1984			    !GrabTo)
1985			{
1986				ADDRESS *a;
1987
1988				for (a = CurEnv->e_sendqueue;
1989				     a != NULL;
1990				     a = a->q_next)
1991				{
1992					if (!QS_IS_UNDELIVERED(a->q_state))
1993						continue;
1994
1995					CurEnv->e_to = a->q_paddr;
1996					logdelivery(NULL, NULL, NULL,
1997						    CurEnv->e_message,
1998						    NULL, (time_t) 0, CurEnv);
1999				}
2000				CurEnv->e_to = NULL;
2001			}
2002			flush_errors(TRUE);
2003			finis(TRUE, ExitStat);
2004			/* NOTREACHED */
2005			return -1;
2006		}
2007
2008		/* bail out if message too large */
2009		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
2010		{
2011			finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
2012			/* NOTREACHED */
2013			return -1;
2014		}
2015		Errors = savederrors;
2016		CurEnv->e_flags |= savedflags;
2017	}
2018	errno = 0;
2019
2020	if (tTd(1, 1))
2021		dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
2022
2023	/*
2024	**  Actually send everything.
2025	**	If verifying, just ack.
2026	*/
2027
2028	CurEnv->e_from.q_state = QS_SENDER;
2029	if (tTd(1, 5))
2030	{
2031		dprintf("main: QS_SENDER ");
2032		printaddr(&CurEnv->e_from, FALSE);
2033	}
2034	CurEnv->e_to = NULL;
2035	CurrentLA = sm_getla(CurEnv);
2036	GrabTo = FALSE;
2037#if NAMED_BIND
2038	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
2039	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2040#endif /* NAMED_BIND */
2041	sendall(CurEnv, SM_DEFAULT);
2042
2043	/*
2044	**  All done.
2045	**	Don't send return error message if in VERIFY mode.
2046	*/
2047
2048	finis(TRUE, ExitStat);
2049	/* NOTREACHED */
2050	return ExitStat;
2051}
2052/*
2053**  QUIESCE -- signal handler for SIGPIPE
2054**
2055**	Parameters:
2056**		sig -- incoming signal.
2057**
2058**	Returns:
2059**		none.
2060**
2061**	Side Effects:
2062**		Sets StopRequest which should cause the mailq/hoststatus
2063**		display to stop.
2064**
2065**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2066**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2067**		DOING.
2068*/
2069
2070/* ARGSUSED */
2071static SIGFUNC_DECL
2072quiesce(sig)
2073	int sig;
2074{
2075	int save_errno = errno;
2076
2077	FIX_SYSV_SIGNAL(sig, quiesce);
2078	StopRequest = TRUE;
2079	errno = save_errno;
2080	return SIGFUNC_RETURN;
2081}
2082/*
2083**  STOP_SENDMAIL -- Stop the running program
2084**
2085**	Parameters:
2086**		none.
2087**
2088**	Returns:
2089**		none.
2090**
2091**	Side Effects:
2092**		exits.
2093*/
2094
2095void
2096stop_sendmail()
2097{
2098	/* reset uid for process accounting */
2099	endpwent();
2100	(void) setuid(RealUid);
2101	exit(EX_OK);
2102}
2103
2104/*
2105**  INTINDEBUG -- signal handler for SIGINT in -bt mode
2106**
2107**	Parameters:
2108**		sig -- incoming signal.
2109**
2110**	Returns:
2111**		none.
2112**
2113**	Side Effects:
2114**		longjmps back to test mode loop.
2115**
2116**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2117**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2118**		DOING.
2119**
2120**	XXX: More work is needed for this signal handler.
2121*/
2122
2123/* ARGSUSED */
2124static SIGFUNC_DECL
2125intindebug(sig)
2126	int sig;
2127{
2128	int save_errno = errno;
2129
2130	FIX_SYSV_SIGNAL(sig, intindebug);
2131	errno = save_errno;
2132	CHECK_CRITICAL(sig);
2133
2134	errno = save_errno;
2135	longjmp(TopFrame, 1);
2136	return SIGFUNC_RETURN;
2137}
2138/*
2139**  FINIS -- Clean up and exit.
2140**
2141**	Parameters:
2142**		drop -- whether or not to drop CurEnv envelope
2143**		exitstat -- exit status to use for exit() call
2144**
2145**	Returns:
2146**		never
2147**
2148**	Side Effects:
2149**		exits sendmail
2150*/
2151
2152void
2153finis(drop, exitstat)
2154	bool drop;
2155	volatile int exitstat;
2156{
2157	/* Still want to process new timeouts added below */
2158	clear_events();
2159	releasesignal(SIGALRM);
2160
2161	if (tTd(2, 1))
2162	{
2163		dprintf("\n====finis: stat %d e_id=%s e_flags=",
2164			exitstat,
2165			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
2166		printenvflags(CurEnv);
2167	}
2168	if (tTd(2, 9))
2169		printopenfds(FALSE);
2170
2171	/* if we fail in finis(), just exit */
2172	if (setjmp(TopFrame) != 0)
2173	{
2174		/* failed -- just give it up */
2175		goto forceexit;
2176	}
2177
2178	/* clean up temp files */
2179	CurEnv->e_to = NULL;
2180	if (drop)
2181	{
2182		if (CurEnv->e_id != NULL)
2183			dropenvelope(CurEnv, TRUE);
2184		else
2185			poststats(StatFile);
2186	}
2187
2188	/* flush any cached connections */
2189	mci_flush(TRUE, NULL);
2190
2191	/* close maps belonging to this pid */
2192	closemaps();
2193
2194#if USERDB
2195	/* close UserDatabase */
2196	_udbx_close();
2197#endif /* USERDB */
2198
2199#ifdef XLA
2200	/* clean up extended load average stuff */
2201	xla_all_end();
2202#endif /* XLA */
2203
2204	/* and exit */
2205  forceexit:
2206	if (LogLevel > 78)
2207		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2208			  "finis, pid=%d",
2209			  (int) getpid());
2210	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
2211		exitstat = EX_OK;
2212
2213	sync_queue_time();
2214
2215	/* reset uid for process accounting */
2216	endpwent();
2217	(void) setuid(RealUid);
2218	exit(exitstat);
2219}
2220/*
2221**  TERM_DEAMON -- SIGTERM handler for the daemon
2222**
2223**	Parameters:
2224**		sig -- signal number.
2225**
2226**	Returns:
2227**		none.
2228**
2229**	Side Effects:
2230**		Sets ShutdownRequest which will hopefully trigger
2231**		the daemon to exit.
2232**
2233**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2234**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2235**		DOING.
2236*/
2237
2238/* ARGSUSED */
2239static SIGFUNC_DECL
2240term_daemon(sig)
2241	int sig;
2242{
2243	int save_errno = errno;
2244
2245	FIX_SYSV_SIGNAL(sig, term_daemon);
2246	ShutdownRequest = "signal";
2247	errno = save_errno;
2248	return SIGFUNC_RETURN;
2249}
2250/*
2251**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
2252**
2253**	Parameters:
2254**		none.
2255**
2256**	Returns:
2257**		none.
2258**
2259**	Side Effects:
2260**		closes control socket, exits.
2261*/
2262
2263void
2264shutdown_daemon()
2265{
2266	char *reason;
2267
2268	allsignals(TRUE);
2269
2270	reason = ShutdownRequest;
2271	ShutdownRequest = NULL;
2272	PendingSignal = 0;
2273
2274	if (LogLevel > 79)
2275		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
2276
2277	FileName = NULL;
2278	closecontrolsocket(TRUE);
2279#ifdef XLA
2280	xla_all_end();
2281#endif /* XLA */
2282
2283	finis(FALSE, EX_OK);
2284}
2285/*
2286**  INTSIG -- clean up on interrupt
2287**
2288**	This just arranges to exit.  It pessimizes in that it
2289**	may resend a message.
2290**
2291**	Parameters:
2292**		none.
2293**
2294**	Returns:
2295**		none.
2296**
2297**	Side Effects:
2298**		Unlocks the current job.
2299**
2300**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2301**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2302**		DOING.
2303**
2304**		XXX: More work is needed for this signal handler.
2305*/
2306
2307/* ARGSUSED */
2308SIGFUNC_DECL
2309intsig(sig)
2310	int sig;
2311{
2312	bool drop = FALSE;
2313	int save_errno = errno;
2314
2315	FIX_SYSV_SIGNAL(sig, intsig);
2316	errno = save_errno;
2317	CHECK_CRITICAL(sig);
2318	allsignals(TRUE);
2319	if (sig != 0 && LogLevel > 79)
2320		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
2321	FileName = NULL;
2322
2323	/* Clean-up on aborted stdin message submission */
2324	if (CurEnv->e_id != NULL &&
2325	    (OpMode == MD_SMTP ||
2326	     OpMode == MD_DELIVER ||
2327	     OpMode == MD_ARPAFTP))
2328	{
2329		register ADDRESS *q;
2330
2331		/* don't return an error indication */
2332		CurEnv->e_to = NULL;
2333		CurEnv->e_flags &= ~EF_FATALERRS;
2334		CurEnv->e_flags |= EF_CLRQUEUE;
2335
2336		/*
2337		**  Spin through the addresses and
2338		**  mark them dead to prevent bounces
2339		*/
2340
2341		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
2342			q->q_state = QS_DONTSEND;
2343
2344		/* and don't try to deliver the partial message either */
2345		if (InChild)
2346			ExitStat = EX_QUIT;
2347
2348		drop = TRUE;
2349	}
2350	else
2351		unlockqueue(CurEnv);
2352
2353	finis(drop, EX_OK);
2354}
2355/*
2356**  INITMACROS -- initialize the macro system
2357**
2358**	This just involves defining some macros that are actually
2359**	used internally as metasymbols to be themselves.
2360**
2361**	Parameters:
2362**		none.
2363**
2364**	Returns:
2365**		none.
2366**
2367**	Side Effects:
2368**		initializes several macros to be themselves.
2369*/
2370
2371struct metamac	MetaMacros[] =
2372{
2373	/* LHS pattern matching characters */
2374	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
2375	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
2376
2377	/* these are RHS metasymbols */
2378	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
2379	{ '>', CALLSUBR },
2380
2381	/* the conditional operations */
2382	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
2383
2384	/* the hostname lookup characters */
2385	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
2386	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
2387
2388	/* miscellaneous control characters */
2389	{ '&', MACRODEXPAND },
2390
2391	{ '\0', '\0' }
2392};
2393
2394#define MACBINDING(name, mid) \
2395		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
2396		MacroName[mid] = name;
2397
2398void
2399initmacros(e)
2400	register ENVELOPE *e;
2401{
2402	register struct metamac *m;
2403	register int c;
2404	char buf[5];
2405	extern char *MacroName[MAXMACROID + 1];
2406
2407	for (m = MetaMacros; m->metaname != '\0'; m++)
2408	{
2409		buf[0] = m->metaval;
2410		buf[1] = '\0';
2411		define(m->metaname, newstr(buf), e);
2412	}
2413	buf[0] = MATCHREPL;
2414	buf[2] = '\0';
2415	for (c = '0'; c <= '9'; c++)
2416	{
2417		buf[1] = c;
2418		define(c, newstr(buf), e);
2419	}
2420
2421	/* set defaults for some macros sendmail will use later */
2422	define('n', "MAILER-DAEMON", e);
2423
2424	/* set up external names for some internal macros */
2425	MACBINDING("opMode", MID_OPMODE);
2426	/*XXX should probably add equivalents for all short macros here XXX*/
2427}
2428/*
2429**  DISCONNECT -- remove our connection with any foreground process
2430**
2431**	Parameters:
2432**		droplev -- how "deeply" we should drop the line.
2433**			0 -- ignore signals, mail back errors, make sure
2434**			     output goes to stdout.
2435**			1 -- also, make stdout go to /dev/null.
2436**			2 -- also, disconnect from controlling terminal
2437**			     (only for daemon mode).
2438**		e -- the current envelope.
2439**
2440**	Returns:
2441**		none
2442**
2443**	Side Effects:
2444**		Trys to insure that we are immune to vagaries of
2445**		the controlling tty.
2446*/
2447
2448void
2449disconnect(droplev, e)
2450	int droplev;
2451	register ENVELOPE *e;
2452{
2453	int fd;
2454
2455	if (tTd(52, 1))
2456		dprintf("disconnect: In %d Out %d, e=%lx\n",
2457			fileno(InChannel), fileno(OutChannel), (u_long) e);
2458	if (tTd(52, 100))
2459	{
2460		dprintf("don't\n");
2461		return;
2462	}
2463	if (LogLevel > 93)
2464		sm_syslog(LOG_DEBUG, e->e_id,
2465			  "disconnect level %d",
2466			  droplev);
2467
2468	/* be sure we don't get nasty signals */
2469	(void) setsignal(SIGINT, SIG_IGN);
2470	(void) setsignal(SIGQUIT, SIG_IGN);
2471
2472	/* we can't communicate with our caller, so.... */
2473	HoldErrs = TRUE;
2474	CurEnv->e_errormode = EM_MAIL;
2475	Verbose = 0;
2476	DisConnected = TRUE;
2477
2478	/* all input from /dev/null */
2479	if (InChannel != stdin)
2480	{
2481		(void) fclose(InChannel);
2482		InChannel = stdin;
2483	}
2484	if (freopen("/dev/null", "r", stdin) == NULL)
2485		sm_syslog(LOG_ERR, e->e_id,
2486			  "disconnect: freopen(\"/dev/null\") failed: %s",
2487			  errstring(errno));
2488
2489	/* output to the transcript */
2490	if (OutChannel != stdout)
2491	{
2492		(void) fclose(OutChannel);
2493		OutChannel = stdout;
2494	}
2495	if (droplev > 0)
2496	{
2497		fd = open("/dev/null", O_WRONLY, 0666);
2498		if (fd == -1)
2499			sm_syslog(LOG_ERR, e->e_id,
2500				  "disconnect: open(\"/dev/null\") failed: %s",
2501				  errstring(errno));
2502		(void) fflush(stdout);
2503		(void) dup2(fd, STDOUT_FILENO);
2504		(void) dup2(fd, STDERR_FILENO);
2505		(void) close(fd);
2506	}
2507
2508	/* drop our controlling TTY completely if possible */
2509	if (droplev > 1)
2510	{
2511		(void) setsid();
2512		errno = 0;
2513	}
2514
2515#if XDEBUG
2516	checkfd012("disconnect");
2517#endif /* XDEBUG */
2518
2519	if (LogLevel > 71)
2520		sm_syslog(LOG_DEBUG, e->e_id,
2521			  "in background, pid=%d",
2522			  (int) getpid());
2523
2524	errno = 0;
2525}
2526
2527static void
2528obsolete(argv)
2529	char *argv[];
2530{
2531	register char *ap;
2532	register char *op;
2533
2534	while ((ap = *++argv) != NULL)
2535	{
2536		/* Return if "--" or not an option of any form. */
2537		if (ap[0] != '-' || ap[1] == '-')
2538			return;
2539
2540		/* skip over options that do have a value */
2541		op = strchr(OPTIONS, ap[1]);
2542		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
2543		    ap[1] != 'd' &&
2544#if defined(sony_news)
2545		    ap[1] != 'E' && ap[1] != 'J' &&
2546#endif /* defined(sony_news) */
2547		    argv[1] != NULL && argv[1][0] != '-')
2548		{
2549			argv++;
2550			continue;
2551		}
2552
2553		/* If -C doesn't have an argument, use sendmail.cf. */
2554#define	__DEFPATH	"sendmail.cf"
2555		if (ap[1] == 'C' && ap[2] == '\0')
2556		{
2557			*argv = xalloc(sizeof(__DEFPATH) + 2);
2558			(void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
2559					__DEFPATH);
2560		}
2561
2562		/* If -q doesn't have an argument, run it once. */
2563		if (ap[1] == 'q' && ap[2] == '\0')
2564			*argv = "-q0";
2565
2566		/* if -d doesn't have an argument, use 0-99.1 */
2567		if (ap[1] == 'd' && ap[2] == '\0')
2568			*argv = "-d0-99.1";
2569
2570#if defined(sony_news)
2571		/* if -E doesn't have an argument, use -EC */
2572		if (ap[1] == 'E' && ap[2] == '\0')
2573			*argv = "-EC";
2574
2575		/* if -J doesn't have an argument, use -JJ */
2576		if (ap[1] == 'J' && ap[2] == '\0')
2577			*argv = "-JJ";
2578#endif /* defined(sony_news) */
2579	}
2580}
2581/*
2582**  AUTH_WARNING -- specify authorization warning
2583**
2584**	Parameters:
2585**		e -- the current envelope.
2586**		msg -- the text of the message.
2587**		args -- arguments to the message.
2588**
2589**	Returns:
2590**		none.
2591*/
2592
2593void
2594#ifdef __STDC__
2595auth_warning(register ENVELOPE *e, const char *msg, ...)
2596#else /* __STDC__ */
2597auth_warning(e, msg, va_alist)
2598	register ENVELOPE *e;
2599	const char *msg;
2600	va_dcl
2601#endif /* __STDC__ */
2602{
2603	char buf[MAXLINE];
2604	VA_LOCAL_DECL
2605
2606	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2607	{
2608		register char *p;
2609		static char hostbuf[48];
2610
2611		if (hostbuf[0] == '\0')
2612		{
2613			struct hostent *hp;
2614
2615			hp = myhostname(hostbuf, sizeof hostbuf);
2616#if _FFR_FREEHOSTENT && NETINET6
2617			if (hp != NULL)
2618			{
2619				freehostent(hp);
2620				hp = NULL;
2621			}
2622#endif /* _FFR_FREEHOSTENT && NETINET6 */
2623		}
2624
2625		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2626		p = &buf[strlen(buf)];
2627		VA_START(msg);
2628		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2629		VA_END;
2630		addheader("X-Authentication-Warning", buf, 0, &e->e_header);
2631		if (LogLevel > 3)
2632			sm_syslog(LOG_INFO, e->e_id,
2633				  "Authentication-Warning: %.400s",
2634				  buf);
2635	}
2636}
2637/*
2638**  GETEXTENV -- get from external environment
2639**
2640**	Parameters:
2641**		envar -- the name of the variable to retrieve
2642**
2643**	Returns:
2644**		The value, if any.
2645*/
2646
2647char *
2648getextenv(envar)
2649	const char *envar;
2650{
2651	char **envp;
2652	int l;
2653
2654	l = strlen(envar);
2655	for (envp = ExternalEnviron; *envp != NULL; envp++)
2656	{
2657		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2658			return &(*envp)[l + 1];
2659	}
2660	return NULL;
2661}
2662/*
2663**  SETUSERENV -- set an environment in the propogated environment
2664**
2665**	Parameters:
2666**		envar -- the name of the environment variable.
2667**		value -- the value to which it should be set.  If
2668**			null, this is extracted from the incoming
2669**			environment.  If that is not set, the call
2670**			to setuserenv is ignored.
2671**
2672**	Returns:
2673**		none.
2674*/
2675
2676void
2677setuserenv(envar, value)
2678	const char *envar;
2679	const char *value;
2680{
2681	int i, l;
2682	char **evp = UserEnviron;
2683	char *p;
2684
2685	if (value == NULL)
2686	{
2687		value = getextenv(envar);
2688		if (value == NULL)
2689			return;
2690	}
2691
2692	i = strlen(envar) + 1;
2693	l = strlen(value) + i + 1;
2694	p = (char *) xalloc(l);
2695	(void) snprintf(p, l, "%s=%s", envar, value);
2696
2697	while (*evp != NULL && strncmp(*evp, p, i) != 0)
2698		evp++;
2699	if (*evp != NULL)
2700	{
2701		*evp++ = p;
2702	}
2703	else if (evp < &UserEnviron[MAXUSERENVIRON])
2704	{
2705		*evp++ = p;
2706		*evp = NULL;
2707	}
2708
2709	/* make sure it is in our environment as well */
2710	if (putenv(p) < 0)
2711		syserr("setuserenv: putenv(%s) failed", p);
2712}
2713/*
2714**  DUMPSTATE -- dump state
2715**
2716**	For debugging.
2717*/
2718
2719void
2720dumpstate(when)
2721	char *when;
2722{
2723	register char *j = macvalue('j', CurEnv);
2724	int rs;
2725	extern int NextMacroId;
2726
2727	sm_syslog(LOG_DEBUG, CurEnv->e_id,
2728		  "--- dumping state on %s: $j = %s ---",
2729		  when,
2730		  j == NULL ? "<NULL>" : j);
2731	if (j != NULL)
2732	{
2733		if (!wordinclass(j, 'w'))
2734			sm_syslog(LOG_DEBUG, CurEnv->e_id,
2735				  "*** $j not in $=w ***");
2736	}
2737	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2738	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n",
2739		  NextMacroId, MAXMACROID);
2740	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2741	printopenfds(TRUE);
2742	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2743	mci_dump_all(TRUE);
2744	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2745	if (rs > 0)
2746	{
2747		int status;
2748		register char **pvp;
2749		char *pv[MAXATOM + 1];
2750
2751		pv[0] = NULL;
2752		status = rewrite(pv, rs, 0, CurEnv);
2753		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2754			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2755			  status);
2756		for (pvp = pv; *pvp != NULL; pvp++)
2757			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2758	}
2759	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2760}
2761/*
2762**  SIGUSR1 -- Signal a request to dump state.
2763**
2764**	Parameters:
2765**		sig -- calling signal.
2766**
2767**	Returns:
2768**		none.
2769**
2770**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2771**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2772**		DOING.
2773**
2774**		XXX: More work is needed for this signal handler.
2775*/
2776
2777/* ARGSUSED */
2778static SIGFUNC_DECL
2779sigusr1(sig)
2780	int sig;
2781{
2782	int save_errno = errno;
2783
2784	FIX_SYSV_SIGNAL(sig, sigusr1);
2785	errno = save_errno;
2786	CHECK_CRITICAL(sig);
2787	dumpstate("user signal");
2788	errno = save_errno;
2789	return SIGFUNC_RETURN;
2790}
2791/*
2792**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2793**
2794**	Parameters:
2795**		to_real_uid -- if set, drop to the real uid instead
2796**			of the RunAsUser.
2797**
2798**	Returns:
2799**		EX_OSERR if the setuid failed.
2800**		EX_OK otherwise.
2801*/
2802
2803int
2804drop_privileges(to_real_uid)
2805	bool to_real_uid;
2806{
2807	int rval = EX_OK;
2808	GIDSET_T emptygidset[1];
2809
2810	if (tTd(47, 1))
2811		dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2812			(int)to_real_uid, (int)RealUid,
2813			(int)RealGid, (int)RunAsUid, (int)RunAsGid);
2814
2815	if (to_real_uid)
2816	{
2817		RunAsUserName = RealUserName;
2818		RunAsUid = RealUid;
2819		RunAsGid = RealGid;
2820	}
2821
2822	/* make sure no one can grab open descriptors for secret files */
2823	endpwent();
2824
2825	/* reset group permissions; these can be set later */
2826	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2827	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2828	{
2829		syserr("drop_privileges: setgroups(1, %d) failed",
2830		       (int)emptygidset[0]);
2831		rval = EX_OSERR;
2832	}
2833
2834	/* reset primary group and user id */
2835	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2836	{
2837		syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
2838		rval = EX_OSERR;
2839	}
2840	if (to_real_uid || RunAsUid != 0)
2841	{
2842		uid_t euid = geteuid();
2843
2844		if (setuid(RunAsUid) < 0)
2845		{
2846			syserr("drop_privileges: setuid(%d) failed",
2847			       (int)RunAsUid);
2848			rval = EX_OSERR;
2849		}
2850		else if (RunAsUid != 0 && setuid(0) == 0)
2851		{
2852			/*
2853			**  Believe it or not, the Linux capability model
2854			**  allows a non-root process to override setuid()
2855			**  on a process running as root and prevent that
2856			**  process from dropping privileges.
2857			*/
2858
2859			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
2860			rval = EX_OSERR;
2861		}
2862		else if (RunAsUid != euid && setuid(euid) == 0)
2863		{
2864			/*
2865			**  Some operating systems will keep the saved-uid
2866			**  if a non-root effective-uid calls setuid(real-uid)
2867			**  making it possible to set it back again later.
2868			*/
2869
2870			syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
2871			rval = EX_OSERR;
2872		}
2873	}
2874	if (tTd(47, 5))
2875	{
2876		dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2877			(int)geteuid(), (int)getuid(),
2878			(int)getegid(), (int)getgid());
2879		dprintf("drop_privileges: RunAsUser = %d:%d\n",
2880			(int)RunAsUid, (int)RunAsGid);
2881		if (tTd(47, 10))
2882			dprintf("drop_privileges: rval = %d\n", rval);
2883	}
2884	return rval;
2885}
2886/*
2887**  FILL_FD -- make sure a file descriptor has been properly allocated
2888**
2889**	Used to make sure that stdin/out/err are allocated on startup
2890**
2891**	Parameters:
2892**		fd -- the file descriptor to be filled.
2893**		where -- a string used for logging.  If NULL, this is
2894**			being called on startup, and logging should
2895**			not be done.
2896**
2897**	Returns:
2898**		none
2899*/
2900
2901void
2902fill_fd(fd, where)
2903	int fd;
2904	char *where;
2905{
2906	int i;
2907	struct stat stbuf;
2908
2909	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2910		return;
2911
2912	if (where != NULL)
2913		syserr("fill_fd: %s: fd %d not open", where, fd);
2914	else
2915		MissingFds |= 1 << fd;
2916	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2917	if (i < 0)
2918	{
2919		syserr("!fill_fd: %s: cannot open /dev/null",
2920			where == NULL ? "startup" : where);
2921	}
2922	if (fd != i)
2923	{
2924		(void) dup2(i, fd);
2925		(void) close(i);
2926	}
2927}
2928/*
2929**  TESTMODELINE -- process a test mode input line
2930**
2931**	Parameters:
2932**		line -- the input line.
2933**		e -- the current environment.
2934**	Syntax:
2935**		#  a comment
2936**		.X process X as a configuration line
2937**		=X dump a configuration item (such as mailers)
2938**		$X dump a macro or class
2939**		/X try an activity
2940**		X  normal process through rule set X
2941*/
2942
2943static void
2944testmodeline(line, e)
2945	char *line;
2946	ENVELOPE *e;
2947{
2948	register char *p;
2949	char *q;
2950	auto char *delimptr;
2951	int mid;
2952	int i, rs;
2953	STAB *map;
2954	char **s;
2955	struct rewrite *rw;
2956	ADDRESS a;
2957	static int tryflags = RF_COPYNONE;
2958	char exbuf[MAXLINE];
2959	extern u_char TokTypeNoC[];
2960
2961#if _FFR_ADDR_TYPE
2962	define(macid("{addr_type}", NULL), "e r", e);
2963#endif /* _FFR_ADDR_TYPE */
2964
2965	/* skip leading spaces */
2966	while (*line == ' ')
2967		line++;
2968
2969	switch (line[0])
2970	{
2971	  case '#':
2972	  case '\0':
2973		return;
2974
2975	  case '?':
2976		help("-bt", e);
2977		return;
2978
2979	  case '.':		/* config-style settings */
2980		switch (line[1])
2981		{
2982		  case 'D':
2983			mid = macid(&line[2], &delimptr);
2984			if (mid == 0)
2985				return;
2986			translate_dollars(delimptr);
2987			define(mid, newstr(delimptr), e);
2988			break;
2989
2990		  case 'C':
2991			if (line[2] == '\0')	/* not to call syserr() */
2992				return;
2993
2994			mid = macid(&line[2], &delimptr);
2995			if (mid == 0)
2996				return;
2997			translate_dollars(delimptr);
2998			expand(delimptr, exbuf, sizeof exbuf, e);
2999			p = exbuf;
3000			while (*p != '\0')
3001			{
3002				register char *wd;
3003				char delim;
3004
3005				while (*p != '\0' && isascii(*p) && isspace(*p))
3006					p++;
3007				wd = p;
3008				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3009					p++;
3010				delim = *p;
3011				*p = '\0';
3012				if (wd[0] != '\0')
3013					setclass(mid, wd);
3014				*p = delim;
3015			}
3016			break;
3017
3018		  case '\0':
3019			printf("Usage: .[DC]macro value(s)\n");
3020			break;
3021
3022		  default:
3023			printf("Unknown \".\" command %s\n", line);
3024			break;
3025		}
3026		return;
3027
3028	  case '=':		/* config-style settings */
3029		switch (line[1])
3030		{
3031		  case 'S':		/* dump rule set */
3032			rs = strtorwset(&line[2], NULL, ST_FIND);
3033			if (rs < 0)
3034			{
3035				printf("Undefined ruleset %s\n", &line[2]);
3036				return;
3037			}
3038			rw = RewriteRules[rs];
3039			if (rw == NULL)
3040				return;
3041			do
3042			{
3043				(void) putchar('R');
3044				s = rw->r_lhs;
3045				while (*s != NULL)
3046				{
3047					xputs(*s++);
3048					(void) putchar(' ');
3049				}
3050				(void) putchar('\t');
3051				(void) putchar('\t');
3052				s = rw->r_rhs;
3053				while (*s != NULL)
3054				{
3055					xputs(*s++);
3056					(void) putchar(' ');
3057				}
3058				(void) putchar('\n');
3059			} while ((rw = rw->r_next) != NULL);
3060			break;
3061
3062		  case 'M':
3063			for (i = 0; i < MAXMAILERS; i++)
3064			{
3065				if (Mailer[i] != NULL)
3066					printmailer(Mailer[i]);
3067			}
3068			break;
3069
3070		  case '\0':
3071			printf("Usage: =Sruleset or =M\n");
3072			break;
3073
3074		  default:
3075			printf("Unknown \"=\" command %s\n", line);
3076			break;
3077		}
3078		return;
3079
3080	  case '-':		/* set command-line-like opts */
3081		switch (line[1])
3082		{
3083		  case 'd':
3084			tTflag(&line[2]);
3085			break;
3086
3087		  case '\0':
3088			printf("Usage: -d{debug arguments}\n");
3089			break;
3090
3091		  default:
3092			printf("Unknown \"-\" command %s\n", line);
3093			break;
3094		}
3095		return;
3096
3097	  case '$':
3098		if (line[1] == '=')
3099		{
3100			mid = macid(&line[2], NULL);
3101			if (mid != 0)
3102				stabapply(dump_class, mid);
3103			return;
3104		}
3105		mid = macid(&line[1], NULL);
3106		if (mid == 0)
3107			return;
3108		p = macvalue(mid, e);
3109		if (p == NULL)
3110			printf("Undefined\n");
3111		else
3112		{
3113			xputs(p);
3114			printf("\n");
3115		}
3116		return;
3117
3118	  case '/':		/* miscellaneous commands */
3119		p = &line[strlen(line)];
3120		while (--p >= line && isascii(*p) && isspace(*p))
3121			*p = '\0';
3122		p = strpbrk(line, " \t");
3123		if (p != NULL)
3124		{
3125			while (isascii(*p) && isspace(*p))
3126				*p++ = '\0';
3127		}
3128		else
3129			p = "";
3130		if (line[1] == '\0')
3131		{
3132			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
3133			return;
3134		}
3135		if (strcasecmp(&line[1], "quit") == 0)
3136		{
3137			CurEnv->e_id = NULL;
3138			finis(TRUE, ExitStat);
3139		}
3140		if (strcasecmp(&line[1], "mx") == 0)
3141		{
3142#if NAMED_BIND
3143			/* look up MX records */
3144			int nmx;
3145			auto int rcode;
3146			char *mxhosts[MAXMXHOSTS + 1];
3147
3148			if (*p == '\0')
3149			{
3150				printf("Usage: /mx address\n");
3151				return;
3152			}
3153			nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
3154			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
3155			for (i = 0; i < nmx; i++)
3156				printf("\t%s\n", mxhosts[i]);
3157#else /* NAMED_BIND */
3158			printf("No MX code compiled in\n");
3159#endif /* NAMED_BIND */
3160		}
3161		else if (strcasecmp(&line[1], "canon") == 0)
3162		{
3163			char host[MAXHOSTNAMELEN];
3164
3165			if (*p == '\0')
3166			{
3167				printf("Usage: /canon address\n");
3168				return;
3169			}
3170			else if (strlcpy(host, p, sizeof host) >= sizeof host)
3171			{
3172				printf("Name too long\n");
3173				return;
3174			}
3175			(void) getcanonname(host, sizeof host, HasWildcardMX);
3176			printf("getcanonname(%s) returns %s\n", p, host);
3177		}
3178		else if (strcasecmp(&line[1], "map") == 0)
3179		{
3180			auto int rcode = EX_OK;
3181			char *av[2];
3182
3183			if (*p == '\0')
3184			{
3185				printf("Usage: /map mapname key\n");
3186				return;
3187			}
3188			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
3189				continue;
3190			if (*q == '\0')
3191			{
3192				printf("No key specified\n");
3193				return;
3194			}
3195			*q++ = '\0';
3196			map = stab(p, ST_MAP, ST_FIND);
3197			if (map == NULL)
3198			{
3199				printf("Map named \"%s\" not found\n", p);
3200				return;
3201			}
3202			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
3203			    !openmap(&(map->s_map)))
3204			{
3205				printf("Map named \"%s\" not open\n", p);
3206				return;
3207			}
3208			printf("map_lookup: %s (%s) ", p, q);
3209			av[0] = q;
3210			av[1] = NULL;
3211			p = (*map->s_map.map_class->map_lookup)
3212					(&map->s_map, q, av, &rcode);
3213			if (p == NULL)
3214				printf("no match (%d)\n", rcode);
3215			else
3216				printf("returns %s (%d)\n", p, rcode);
3217		}
3218		else if (strcasecmp(&line[1], "try") == 0)
3219		{
3220			MAILER *m;
3221			STAB *st;
3222			auto int rcode = EX_OK;
3223
3224			q = strpbrk(p, " \t");
3225			if (q != NULL)
3226			{
3227				while (isascii(*q) && isspace(*q))
3228					*q++ = '\0';
3229			}
3230			if (q == NULL || *q == '\0')
3231			{
3232				printf("Usage: /try mailer address\n");
3233				return;
3234			}
3235			st = stab(p, ST_MAILER, ST_FIND);
3236			if (st == NULL)
3237			{
3238				printf("Unknown mailer %s\n", p);
3239				return;
3240			}
3241			m = st->s_mailer;
3242			printf("Trying %s %s address %s for mailer %s\n",
3243				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
3244				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
3245				q, p);
3246			p = remotename(q, m, tryflags, &rcode, CurEnv);
3247			printf("Rcode = %d, addr = %s\n",
3248				rcode, p == NULL ? "<NULL>" : p);
3249			e->e_to = NULL;
3250		}
3251		else if (strcasecmp(&line[1], "tryflags") == 0)
3252		{
3253			if (*p == '\0')
3254			{
3255				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
3256				return;
3257			}
3258			for (; *p != '\0'; p++)
3259			{
3260				switch (*p)
3261				{
3262				  case 'H':
3263				  case 'h':
3264					tryflags |= RF_HEADERADDR;
3265					break;
3266
3267				  case 'E':
3268				  case 'e':
3269					tryflags &= ~RF_HEADERADDR;
3270					break;
3271
3272				  case 'S':
3273				  case 's':
3274					tryflags |= RF_SENDERADDR;
3275					break;
3276
3277				  case 'R':
3278				  case 'r':
3279					tryflags &= ~RF_SENDERADDR;
3280					break;
3281				}
3282			}
3283#if _FFR_ADDR_TYPE
3284			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
3285			exbuf[1] = ' ';
3286			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
3287			exbuf[3] = '\0';
3288			define(macid("{addr_type}", NULL), newstr(exbuf), e);
3289#endif /* _FFR_ADDR_TYPE */
3290		}
3291		else if (strcasecmp(&line[1], "parse") == 0)
3292		{
3293			if (*p == '\0')
3294			{
3295				printf("Usage: /parse address\n");
3296				return;
3297			}
3298			q = crackaddr(p);
3299			printf("Cracked address = ");
3300			xputs(q);
3301			printf("\nParsing %s %s address\n",
3302				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
3303				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
3304			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
3305				printf("Cannot parse\n");
3306			else if (a.q_host != NULL && a.q_host[0] != '\0')
3307				printf("mailer %s, host %s, user %s\n",
3308					a.q_mailer->m_name, a.q_host, a.q_user);
3309			else
3310				printf("mailer %s, user %s\n",
3311					a.q_mailer->m_name, a.q_user);
3312			e->e_to = NULL;
3313		}
3314		else
3315		{
3316			printf("Unknown \"/\" command %s\n", line);
3317		}
3318		return;
3319	}
3320
3321	for (p = line; isascii(*p) && isspace(*p); p++)
3322		continue;
3323	q = p;
3324	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3325		p++;
3326	if (*p == '\0')
3327	{
3328		printf("No address!\n");
3329		return;
3330	}
3331	*p = '\0';
3332	if (invalidaddr(p + 1, NULL))
3333		return;
3334	do
3335	{
3336		register char **pvp;
3337		char pvpbuf[PSBUFSIZE];
3338
3339		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
3340			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
3341		if (pvp == NULL)
3342			continue;
3343		p = q;
3344		while (*p != '\0')
3345		{
3346			int status;
3347
3348			rs = strtorwset(p, NULL, ST_FIND);
3349			if (rs < 0)
3350			{
3351				printf("Undefined ruleset %s\n", p);
3352				break;
3353			}
3354			status = rewrite(pvp, rs, 0, e);
3355			if (status != EX_OK)
3356				printf("== Ruleset %s (%d) status %d\n",
3357					p, rs, status);
3358			while (*p != '\0' && *p++ != ',')
3359				continue;
3360		}
3361	} while (*(p = delimptr) != '\0');
3362}
3363
3364static void
3365dump_class(s, id)
3366	register STAB *s;
3367	int id;
3368{
3369	if (s->s_type != ST_CLASS)
3370		return;
3371	if (bitnset(bitidx(id), s->s_class))
3372		printf("%s\n", s->s_name);
3373}
3374