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