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