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