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