Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / sendmail / src / main.c
1 /*
2  * Copyright (c) 1998-2003 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 #define _DEFINE
15 #include <sendmail.h>
16 #include <sm/xtrap.h>
17 #include <sm/signal.h>
18
19 #ifndef lint
20 SM_UNUSED(static char copyright[]) =
21 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
22         All rights reserved.\n\
23      Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
24      Copyright (c) 1988, 1993\n\
25         The Regents of the University of California.  All rights reserved.\n";
26 #endif /* ! lint */
27
28 SM_RCSID("@(#)$Id: main.c,v 8.887.2.22 2003/03/06 18:38:08 ca Exp $")
29
30
31 #if NETINET || NETINET6
32 # include <arpa/inet.h>
33 #endif /* NETINET || NETINET6 */
34
35 /* for getcfname() */
36 #include <sendmail/pathnames.h>
37
38 static SM_DEBUG_T
39 DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
40         "@(#)$Debug: no_persistent_restart - don't restart, log only $");
41
42 static void     dump_class __P((STAB *, int));
43 static void     obsolete __P((char **));
44 static void     testmodeline __P((char *, ENVELOPE *));
45 static char     *getextenv __P((const char *));
46 static void     sm_printoptions __P((char **));
47 static SIGFUNC_DECL     intindebug __P((int));
48 static SIGFUNC_DECL     sighup __P((int));
49 static SIGFUNC_DECL     sigpipe __P((int));
50 static SIGFUNC_DECL     sigterm __P((int));
51 #ifdef SIGUSR1
52 static SIGFUNC_DECL     sigusr1 __P((int));
53 #endif /* SIGUSR1 */
54
55 /*
56 **  SENDMAIL -- Post mail to a set of destinations.
57 **
58 **      This is the basic mail router.  All user mail programs should
59 **      call this routine to actually deliver mail.  Sendmail in
60 **      turn calls a bunch of mail servers that do the real work of
61 **      delivering the mail.
62 **
63 **      Sendmail is driven by settings read in from /etc/mail/sendmail.cf
64 **      (read by readcf.c).
65 **
66 **      Usage:
67 **              /usr/lib/sendmail [flags] addr ...
68 **
69 **              See the associated documentation for details.
70 **
71 **      Authors:
72 **              Eric Allman, UCB/INGRES (until 10/81).
73 **                           Britton-Lee, Inc., purveyors of fine
74 **                              database computers (11/81 - 10/88).
75 **                           International Computer Science Institute
76 **                              (11/88 - 9/89).
77 **                           UCB/Mammoth Project (10/89 - 7/95).
78 **                           InReference, Inc. (8/95 - 1/97).
79 **                           Sendmail, Inc. (1/98 - present).
80 **              The support of my employers is gratefully acknowledged.
81 **                      Few of them (Britton-Lee in particular) have had
82 **                      anything to gain from my involvement in this project.
83 **
84 **              Gregory Neil Shapiro,
85 **                      Worcester Polytechnic Institute (until 3/98).
86 **                      Sendmail, Inc. (3/98 - present).
87 **
88 **              Claus Assmann,
89 **                      Sendmail, Inc. (12/98 - present).
90 */
91
92 char            *FullName;      /* sender's full name */
93 ENVELOPE        BlankEnvelope;  /* a "blank" envelope */
94 static ENVELOPE MainEnvelope;   /* the envelope around the basic letter */
95 ADDRESS         NullAddress =   /* a null address */
96                 { "", "", NULL, "" };
97 char            *CommandLineArgs;       /* command line args for pid file */
98 bool            Warn_Q_option = false;  /* warn about Q option use */
99 static int      MissingFds = 0; /* bit map of fds missing on startup */
100 char            *Mbdb = "pw";   /* mailbox database defaults to /etc/passwd */
101
102 #ifdef NGROUPS_MAX
103 GIDSET_T        InitialGidSet[NGROUPS_MAX];
104 #endif /* NGROUPS_MAX */
105
106 #define MAXCONFIGLEVEL  10      /* highest config version level known */
107
108 #if SASL
109 static sasl_callback_t srvcallbacks[] =
110 {
111         {       SASL_CB_VERIFYFILE,     &safesaslfile,  NULL    },
112         {       SASL_CB_PROXY_POLICY,   &proxy_policy,  NULL    },
113         {       SASL_CB_LIST_END,       NULL,           NULL    }
114 };
115 #endif /* SASL */
116
117 unsigned int    SubmitMode;
118 int             SyslogPrefixLen; /* estimated length of syslog prefix */
119 #define PIDLEN          6       /* pid length for computing SyslogPrefixLen */
120 #ifndef SL_FUDGE
121 # define SL_FUDGE       10      /* fudge offset for SyslogPrefixLen */
122 #endif /* ! SL_FUDGE */
123 #define SLDLL           8       /* est. length of default syslog label */
124
125
126 /* Some options are dangerous to allow users to use in non-submit mode */
127 #define CHECK_AGAINST_OPMODE(cmd)                                       \
128 {                                                                       \
129         if (extraprivs &&                                               \
130             OpMode != MD_DELIVER && OpMode != MD_SMTP &&                \
131             OpMode != MD_ARPAFTP &&                                     \
132             OpMode != MD_VERIFY && OpMode != MD_TEST)                   \
133         {                                                               \
134                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,          \
135                                      "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
136                        (cmd));                                          \
137                 break;                                                  \
138         }                                                               \
139         if (extraprivs && queuerun)                                     \
140         {                                                               \
141                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,          \
142                                      "WARNING: Ignoring submission mode -%c option with -q\n", \
143                        (cmd));                                          \
144                 break;                                                  \
145         }                                                               \
146 }
147
148 int
149 main(argc, argv, envp)
150         int argc;
151         char **argv;
152         char **envp;
153 {
154         register char *p;
155         char **av;
156         extern char Version[];
157         char *ep, *from;
158         STAB *st;
159         register int i;
160         int j;
161         int dp;
162         int fill_errno;
163         int qgrp = NOQGRP;              /* queue group to process */
164         bool safecf = true;
165         BITMAP256 *p_flags = NULL;      /* daemon flags */
166         bool warn_C_flag = false;
167         bool auth = true;               /* whether to set e_auth_param */
168         char warn_f_flag = '\0';
169         bool run_in_foreground = false; /* -bD mode */
170         bool queuerun = false, debug = false;
171         struct passwd *pw;
172         struct hostent *hp;
173         char *nullserver = NULL;
174         char *authinfo = NULL;
175         char *sysloglabel = NULL;       /* label for syslog */
176         char *conffile = NULL;          /* name of .cf file */
177         char *queuegroup = NULL;        /* queue group to process */
178 #if _FFR_QUARANTINE
179         char *quarantining = NULL;      /* quarantine queue items? */
180 #endif /* _FFR_QUARANTINE */
181         bool extraprivs;
182         bool forged, negate;
183         bool queuepersistent = false;   /* queue runner process runs forever */
184         bool foregroundqueue = false;   /* queue run in foreground */
185         bool save_val;                  /* to save some bool var. */
186         int cftype;                     /* which cf file to use? */
187         static time_t starttime = 0;    /* when was process started */
188         struct stat traf_st;            /* for TrafficLog FIFO check */
189         char buf[MAXLINE];
190         char jbuf[MAXHOSTNAMELEN];      /* holds MyHostName */
191         static char rnamebuf[MAXNAME];  /* holds RealUserName */
192         char *emptyenviron[1];
193 #if STARTTLS
194         bool tls_ok;
195 #endif /* STARTTLS */
196         QUEUE_CHAR *new;
197         ENVELOPE *e;
198         extern int DtableSize;
199         extern int optind;
200         extern int opterr;
201         extern char *optarg;
202         extern char **environ;
203 #if SASL
204         extern void sm_sasl_init __P((void));
205 #endif /* SASL */
206
207 #if USE_ENVIRON
208         envp = environ;
209 #endif /* USE_ENVIRON */
210
211         /* turn off profiling */
212         SM_PROF(0);
213
214         /* install default exception handler */
215         sm_exc_newthread(fatal_error);
216
217         /* set the default in/out channel so errors reported to screen */
218         InChannel = smioin;
219         OutChannel = smioout;
220
221         /*
222         **  Check to see if we reentered.
223         **      This would normally happen if e_putheader or e_putbody
224         **      were NULL when invoked.
225         */
226
227         if (starttime != 0)
228         {
229                 syserr("main: reentered!");
230                 abort();
231         }
232         starttime = curtime();
233
234         /* avoid null pointer dereferences */
235         TermEscape.te_rv_on = TermEscape.te_rv_off = "";
236
237         RealUid = getuid();
238         RealGid = getgid();
239
240         /* Check if sendmail is running with extra privs */
241         extraprivs = (RealUid != 0 &&
242                       (geteuid() != getuid() || getegid() != getgid()));
243
244         CurrentPid = getpid();
245
246         /* get whatever .cf file is right for the opmode */
247         cftype = SM_GET_RIGHT_CF;
248
249         /* in 4.4BSD, the table can be huge; impose a reasonable limit */
250         DtableSize = getdtsize();
251         if (DtableSize > 256)
252                 DtableSize = 256;
253
254         /*
255         **  Be sure we have enough file descriptors.
256         **      But also be sure that 0, 1, & 2 are open.
257         */
258
259         /* reset errno and fill_errno; the latter is used way down below */
260         errno = fill_errno = 0;
261         fill_fd(STDIN_FILENO, NULL);
262         if (errno != 0)
263                 fill_errno = errno;
264         fill_fd(STDOUT_FILENO, NULL);
265         if (errno != 0)
266                 fill_errno = errno;
267         fill_fd(STDERR_FILENO, NULL);
268         if (errno != 0)
269                 fill_errno = errno;
270
271         i = DtableSize;
272         while (--i > 0)
273         {
274                 if (i != STDIN_FILENO && i != STDOUT_FILENO &&
275                     i != STDERR_FILENO)
276                         (void) close(i);
277         }
278         errno = 0;
279
280 #if LOG
281 # ifndef SM_LOG_STR
282 #  define SM_LOG_STR    "sendmail"
283 # endif /* ! SM_LOG_STR */
284 #  ifdef LOG_MAIL
285         openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
286 #  else /* LOG_MAIL */
287         openlog(SM_LOG_STR, LOG_PID);
288 #  endif /* LOG_MAIL */
289 #endif /* LOG */
290
291         /*
292         **  Seed the random number generator.
293         **  Used for queue file names, picking a queue directory, and
294         **  MX randomization.
295         */
296
297         seed_random();
298
299         /* do machine-dependent initializations */
300         init_md(argc, argv);
301
302
303         SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
304
305         /* reset status from syserr() calls for missing file descriptors */
306         Errors = 0;
307         ExitStat = EX_OK;
308
309         SubmitMode = SUBMIT_UNKNOWN;
310 #if XDEBUG
311         checkfd012("after openlog");
312 #endif /* XDEBUG */
313
314         tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
315
316 #ifdef NGROUPS_MAX
317         /* save initial group set for future checks */
318         i = getgroups(NGROUPS_MAX, InitialGidSet);
319         if (i <= 0)
320         {
321                 InitialGidSet[0] = (GID_T) -1;
322                 i = 0;
323         }
324         while (i < NGROUPS_MAX)
325                 InitialGidSet[i++] = InitialGidSet[0];
326 #endif /* NGROUPS_MAX */
327
328         /* drop group id privileges (RunAsUser not yet set) */
329         dp = drop_privileges(false);
330         setstat(dp);
331
332 #ifdef SIGUSR1
333         /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
334         if (!extraprivs)
335         {
336                 /* arrange to dump state on user-1 signal */
337                 (void) sm_signal(SIGUSR1, sigusr1);
338         }
339         else
340         {
341                 /* ignore user-1 signal */
342                 (void) sm_signal(SIGUSR1, SIG_IGN);
343         }
344 #endif /* SIGUSR1 */
345
346         /* initialize for setproctitle */
347         initsetproctitle(argc, argv, envp);
348
349         /* Handle any non-getoptable constructions. */
350         obsolete(argv);
351
352         /*
353         **  Do a quick prescan of the argument list.
354         */
355
356
357         /* find initial opMode */
358         OpMode = MD_DELIVER;
359         av = argv;
360         p = strrchr(*av, '/');
361         if (p++ == NULL)
362                 p = *av;
363         if (strcmp(p, "newaliases") == 0)
364                 OpMode = MD_INITALIAS;
365         else if (strcmp(p, "mailq") == 0)
366                 OpMode = MD_PRINT;
367         else if (strcmp(p, "smtpd") == 0)
368                 OpMode = MD_DAEMON;
369         else if (strcmp(p, "hoststat") == 0)
370                 OpMode = MD_HOSTSTAT;
371         else if (strcmp(p, "purgestat") == 0)
372                 OpMode = MD_PURGESTAT;
373
374 #if _FFR_QUARANTINE
375 # if defined(__osf__) || defined(_AIX3)
376 #  define OPTIONS       "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:"
377 # endif /* defined(__osf__) || defined(_AIX3) */
378 # if defined(sony_news)
379 #  define OPTIONS       "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
380 # endif /* defined(sony_news) */
381 # ifndef OPTIONS
382 #  define OPTIONS       "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
383 # endif /* ! OPTIONS */
384 #else /* _FFR_QUARANTINE */
385 # if defined(__osf__) || defined(_AIX3)
386 #  define OPTIONS       "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x"
387 # endif /* defined(__osf__) || defined(_AIX3) */
388 # if defined(sony_news)
389 #  define OPTIONS       "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:"
390 # endif /* defined(sony_news) */
391 # ifndef OPTIONS
392 #  define OPTIONS       "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:"
393 # endif /* ! OPTIONS */
394 #endif /* _FFR_QUARANTINE */
395
396         /* Set to 0 to allow -b; need to check optarg before using it! */
397         opterr = 0;
398         while ((j = getopt(argc, argv, OPTIONS)) != -1)
399         {
400                 switch (j)
401                 {
402                   case 'b':     /* operations mode */
403                         j = (optarg == NULL) ? ' ' : *optarg;
404                         switch (j)
405                         {
406                           case MD_DAEMON:
407                           case MD_FGDAEMON:
408                           case MD_SMTP:
409                           case MD_INITALIAS:
410                           case MD_DELIVER:
411                           case MD_VERIFY:
412                           case MD_TEST:
413                           case MD_PRINT:
414                           case MD_PRINTNQE:
415                           case MD_HOSTSTAT:
416                           case MD_PURGESTAT:
417                           case MD_ARPAFTP:
418                                 OpMode = j;
419                                 break;
420
421                           case MD_FREEZE:
422                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423                                                      "Frozen configurations unsupported\n");
424                                 return EX_USAGE;
425
426                           default:
427                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428                                                      "Invalid operation mode %c\n",
429                                                      j);
430                                 return EX_USAGE;
431                         }
432                         break;
433
434                   case 'd':
435                         debug = true;
436                         tTflag(optarg);
437                         (void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT,
438                                              (char *) NULL, SM_IO_NBF,
439                                              SM_IO_BUFSIZ);
440                         break;
441
442                   case 'G':     /* relay (gateway) submission */
443                         SubmitMode = SUBMIT_MTA;
444                         break;
445
446                   case 'L':
447                         if (optarg == NULL)
448                         {
449                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
450                                                      "option requires an argument -- '%c'",
451                                                      (char) j);
452                                 return EX_USAGE;
453                         }
454                         j = SM_MIN(strlen(optarg), 24) + 1;
455                         sysloglabel = xalloc(j);
456                         (void) sm_strlcpy(sysloglabel, optarg, j);
457                         SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
458                                           SL_FUDGE + j;
459                         break;
460
461 #if _FFR_QUARANTINE
462                   case 'Q':
463 #endif /* _FFR_QUARANTINE */
464                   case 'q':
465                         /* just check if it is there */
466                         queuerun = true;
467                         break;
468                 }
469         }
470         opterr = 1;
471
472         /* Don't leak queue information via debug flags */
473         if (extraprivs && queuerun && debug)
474         {
475                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
476                                      "WARNING: Can not use -d with -q.  Disabling debugging.\n");
477                 sm_debug_setfile(NULL);
478                 (void) memset(tTdvect, '\0', sizeof tTdvect);
479         }
480
481 #if LOG
482         if (sysloglabel != NULL)
483         {
484                 /* Sanitize the string */
485                 for (p = sysloglabel; *p != '\0'; p++)
486                 {
487                         if (!isascii(*p) || !isprint(*p) || *p == '%')
488                                 *p = '*';
489                 }
490                 closelog();
491 #  ifdef LOG_MAIL
492                 openlog(sysloglabel, LOG_PID, LOG_MAIL);
493 #  else /* LOG_MAIL */
494                 openlog(sysloglabel, LOG_PID);
495 #  endif /* LOG_MAIL */
496         }
497 #endif /* LOG */
498
499         /* set up the blank envelope */
500         BlankEnvelope.e_puthdr = putheader;
501         BlankEnvelope.e_putbody = putbody;
502         BlankEnvelope.e_xfp = NULL;
503         STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
504         CurEnv = &BlankEnvelope;
505         STRUCTCOPY(NullAddress, MainEnvelope.e_from);
506
507         /*
508         **  Set default values for variables.
509         **      These cannot be in initialized data space.
510         */
511
512         setdefaults(&BlankEnvelope);
513         initmacros(&BlankEnvelope);
514
515         /* reset macro */
516         set_op_mode(OpMode);
517
518         pw = sm_getpwuid(RealUid);
519         if (pw != NULL)
520                 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
521         else
522                 (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
523                                    (int) RealUid);
524
525         RealUserName = rnamebuf;
526
527         if (tTd(0, 101))
528         {
529                 sm_dprintf("Version %s\n", Version);
530                 finis(false, true, EX_OK);
531                 /* NOTREACHED */
532         }
533
534         /*
535         **  if running non-set-user-ID binary as non-root, pretend
536         **  we are the RunAsUid
537         */
538
539         if (RealUid != 0 && geteuid() == RealUid)
540         {
541                 if (tTd(47, 1))
542                         sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
543                                    (int) RealUid);
544                 RunAsUid = RealUid;
545         }
546         else if (geteuid() != 0)
547                 RunAsUid = geteuid();
548
549         EffGid = getegid();
550         if (RealUid != 0 && EffGid == RealGid)
551                 RunAsGid = RealGid;
552
553         if (tTd(47, 5))
554         {
555                 sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
556                            (int) geteuid(), (int) getuid(),
557                            (int) getegid(), (int) getgid());
558                 sm_dprintf("main: RunAsUser = %d:%d\n",
559                            (int) RunAsUid, (int) RunAsGid);
560         }
561
562         /* save command line arguments */
563         j = 0;
564         for (av = argv; *av != NULL; )
565                 j += strlen(*av++) + 1;
566         SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
567         CommandLineArgs = xalloc(j);
568         p = CommandLineArgs;
569         for (av = argv, i = 0; *av != NULL; )
570         {
571                 int h;
572
573                 SaveArgv[i++] = newstr(*av);
574                 if (av != argv)
575                         *p++ = ' ';
576                 (void) sm_strlcpy(p, *av++, j);
577                 h = strlen(p);
578                 p += h;
579                 j -= h + 1;
580         }
581         SaveArgv[i] = NULL;
582
583         if (tTd(0, 1))
584         {
585                 extern char *CompileOptions[];
586
587                 sm_dprintf("Version %s\n Compiled with:", Version);
588                 sm_printoptions(CompileOptions);
589         }
590         if (tTd(0, 10))
591         {
592                 extern char *OsCompileOptions[];
593
594                 sm_dprintf("    OS Defines:");
595                 sm_printoptions(OsCompileOptions);
596 #ifdef _PATH_UNIX
597                 sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
598 #endif /* _PATH_UNIX */
599
600                 sm_dprintf("     Conf file:\t%s (default for MSP)\n",
601                            getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
602                                      conffile));
603                 sm_dprintf("     Conf file:\t%s (default for MTA)\n",
604                            getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
605                                      conffile));
606                 sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
607         }
608
609         if (tTd(0, 12))
610         {
611                 extern char *SmCompileOptions[];
612
613                 sm_dprintf(" libsm Defines:");
614                 sm_printoptions(SmCompileOptions);
615         }
616
617         if (tTd(0, 13))
618         {
619                 extern char *FFRCompileOptions[];
620
621                 sm_dprintf("   FFR Defines:");
622                 sm_printoptions(FFRCompileOptions);
623         }
624
625         /* clear sendmail's environment */
626         ExternalEnviron = environ;
627         emptyenviron[0] = NULL;
628         environ = emptyenviron;
629
630         /*
631         **  restore any original TZ setting until TimeZoneSpec has been
632         **  determined - or early log messages may get bogus time stamps
633         */
634
635         if ((p = getextenv("TZ")) != NULL)
636         {
637                 char *tz;
638                 int tzlen;
639
640                 /* XXX check for reasonable length? */
641                 tzlen = strlen(p) + 4;
642                 tz = xalloc(tzlen);
643                 (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
644
645                 /* XXX check return code? */
646                 (void) putenv(tz);
647         }
648
649         /* prime the child environment */
650         setuserenv("AGENT", "sendmail");
651
652         (void) sm_signal(SIGPIPE, SIG_IGN);
653         OldUmask = umask(022);
654         FullName = getextenv("NAME");
655         if (FullName != NULL)
656                 FullName = newstr(FullName);
657
658         /*
659         **  Initialize name server if it is going to be used.
660         */
661
662 #if NAMED_BIND
663         if (!bitset(RES_INIT, _res.options))
664                 (void) res_init();
665         if (tTd(8, 8))
666                 _res.options |= RES_DEBUG;
667         else
668                 _res.options &= ~RES_DEBUG;
669 # ifdef RES_NOALIASES
670         if (bitset(RES_NOALIASES, _res.options))
671                 ResNoAliases = true;
672         _res.options |= RES_NOALIASES;
673 # endif /* RES_NOALIASES */
674         TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
675         TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
676         TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
677         TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
678         TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
679         TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
680 #endif /* NAMED_BIND */
681
682         errno = 0;
683         from = NULL;
684
685         /* initialize some macros, etc. */
686         init_vendor_macros(&BlankEnvelope);
687
688         /* version */
689         macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
690
691         /* hostname */
692         hp = myhostname(jbuf, sizeof jbuf);
693         if (jbuf[0] != '\0')
694         {
695                 struct utsname utsname;
696
697                 if (tTd(0, 4))
698                         sm_dprintf("Canonical name: %s\n", jbuf);
699                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
700                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
701                 setclass('w', jbuf);
702
703                 p = strchr(jbuf, '.');
704                 if (p != NULL)
705                 {
706                         if (p[1] != '\0')
707                         {
708                                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm',
709                                           &p[1]);
710                         }
711                         while (p != NULL && strchr(&p[1], '.') != NULL)
712                         {
713                                 *p = '\0';
714                                 if (tTd(0, 4))
715                                         sm_dprintf("\ta.k.a.: %s\n", jbuf);
716                                 setclass('w', jbuf);
717                                 *p++ = '.';
718                                 p = strchr(p, '.');
719                         }
720                 }
721
722                 if (uname(&utsname) >= 0)
723                         p = utsname.nodename;
724                 else
725                 {
726                         if (tTd(0, 22))
727                                 sm_dprintf("uname failed (%s)\n",
728                                            sm_errstring(errno));
729                         makelower(jbuf);
730                         p = jbuf;
731                 }
732                 if (tTd(0, 4))
733                         sm_dprintf(" UUCP nodename: %s\n", p);
734                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
735                 setclass('k', p);
736                 setclass('w', p);
737         }
738         if (hp != NULL)
739         {
740                 for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
741                 {
742                         if (tTd(0, 4))
743                                 sm_dprintf("\ta.k.a.: %s\n", *av);
744                         setclass('w', *av);
745                 }
746 #if NETINET || NETINET6
747                 for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
748                 {
749 # if NETINET6
750                         char *addr;
751                         char buf6[INET6_ADDRSTRLEN];
752                         struct in6_addr ia6;
753 # endif /* NETINET6 */
754 # if NETINET
755                         struct in_addr ia;
756 # endif /* NETINET */
757                         char ipbuf[103];
758
759                         ipbuf[0] = '\0';
760                         switch (hp->h_addrtype)
761                         {
762 # if NETINET
763                           case AF_INET:
764                                 if (hp->h_length != INADDRSZ)
765                                         break;
766
767                                 memmove(&ia, hp->h_addr_list[i], INADDRSZ);
768                                 (void) sm_snprintf(ipbuf, sizeof ipbuf,
769                                                    "[%.100s]", inet_ntoa(ia));
770                                 break;
771 # endif /* NETINET */
772
773 # if NETINET6
774                           case AF_INET6:
775                                 if (hp->h_length != IN6ADDRSZ)
776                                         break;
777
778                                 memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
779                                 addr = anynet_ntop(&ia6, buf6, sizeof buf6);
780                                 if (addr != NULL)
781                                         (void) sm_snprintf(ipbuf, sizeof ipbuf,
782                                                            "[%.100s]", addr);
783                                 break;
784 # endif /* NETINET6 */
785                         }
786                         if (ipbuf[0] == '\0')
787                                 break;
788
789                         if (tTd(0, 4))
790                                 sm_dprintf("\ta.k.a.: %s\n", ipbuf);
791                         setclass('w', ipbuf);
792                 }
793 #endif /* NETINET || NETINET6 */
794 #if NETINET6
795                 freehostent(hp);
796                 hp = NULL;
797 #endif /* NETINET6 */
798         }
799
800         /* current time */
801         macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
802
803         /* current load average */
804         sm_getla();
805
806         QueueLimitRecipient = (QUEUE_CHAR *) NULL;
807         QueueLimitSender = (QUEUE_CHAR *) NULL;
808         QueueLimitId = (QUEUE_CHAR *) NULL;
809 #if _FFR_QUARANTINE
810         QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
811 #endif /* _FFR_QUARANTINE */
812
813         /*
814         **  Crack argv.
815         */
816
817         optind = 1;
818         while ((j = getopt(argc, argv, OPTIONS)) != -1)
819         {
820                 switch (j)
821                 {
822                   case 'b':     /* operations mode */
823                         /* already done */
824                         break;
825
826                   case 'A':     /* use Alternate sendmail/submit.cf */
827                         cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
828                                                   : SM_GET_SENDMAIL_CF;
829                         break;
830
831                   case 'B':     /* body type */
832                         CHECK_AGAINST_OPMODE(j);
833                         BlankEnvelope.e_bodytype = newstr(optarg);
834                         break;
835
836                   case 'C':     /* select configuration file (already done) */
837                         if (RealUid != 0)
838                                 warn_C_flag = true;
839                         conffile = newstr(optarg);
840                         dp = drop_privileges(true);
841                         setstat(dp);
842                         safecf = false;
843                         break;
844
845                   case 'd':     /* debugging */
846                         /* already done */
847                         break;
848
849                   case 'f':     /* from address */
850                   case 'r':     /* obsolete -f flag */
851                         CHECK_AGAINST_OPMODE(j);
852                         if (from != NULL)
853                         {
854                                 usrerr("More than one \"from\" person");
855                                 ExitStat = EX_USAGE;
856                                 break;
857                         }
858                         if (optarg[0] == '\0')
859                                 from = newstr("<>");
860                         else
861                                 from = newstr(denlstring(optarg, true, true));
862                         if (strcmp(RealUserName, from) != 0)
863                                 warn_f_flag = j;
864                         break;
865
866                   case 'F':     /* set full name */
867                         CHECK_AGAINST_OPMODE(j);
868                         FullName = newstr(optarg);
869                         break;
870
871                   case 'G':     /* relay (gateway) submission */
872                         /* already set */
873                         CHECK_AGAINST_OPMODE(j);
874                         break;
875
876                   case 'h':     /* hop count */
877                         CHECK_AGAINST_OPMODE(j);
878                         BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
879                                                                   10);
880                         (void) sm_snprintf(buf, sizeof buf, "%d",
881                                            BlankEnvelope.e_hopcount);
882                         macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
883
884                         if (*ep)
885                         {
886                                 usrerr("Bad hop count (%s)", optarg);
887                                 ExitStat = EX_USAGE;
888                         }
889                         break;
890
891                   case 'L':     /* program label */
892                         /* already set */
893                         break;
894
895                   case 'n':     /* don't alias */
896                         CHECK_AGAINST_OPMODE(j);
897                         NoAlias = true;
898                         break;
899
900                   case 'N':     /* delivery status notifications */
901                         CHECK_AGAINST_OPMODE(j);
902                         DefaultNotify |= QHASNOTIFY;
903                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
904                                 macid("{dsn_notify}"), optarg);
905                         if (sm_strcasecmp(optarg, "never") == 0)
906                                 break;
907                         for (p = optarg; p != NULL; optarg = p)
908                         {
909                                 p = strchr(p, ',');
910                                 if (p != NULL)
911                                         *p++ = '\0';
912                                 if (sm_strcasecmp(optarg, "success") == 0)
913                                         DefaultNotify |= QPINGONSUCCESS;
914                                 else if (sm_strcasecmp(optarg, "failure") == 0)
915                                         DefaultNotify |= QPINGONFAILURE;
916                                 else if (sm_strcasecmp(optarg, "delay") == 0)
917                                         DefaultNotify |= QPINGONDELAY;
918                                 else
919                                 {
920                                         usrerr("Invalid -N argument");
921                                         ExitStat = EX_USAGE;
922                                 }
923                         }
924                         break;
925
926                   case 'o':     /* set option */
927                         setoption(*optarg, optarg + 1, false, true,
928                                   &BlankEnvelope);
929                         break;
930
931                   case 'O':     /* set option (long form) */
932                         setoption(' ', optarg, false, true, &BlankEnvelope);
933                         break;
934
935                   case 'p':     /* set protocol */
936                         CHECK_AGAINST_OPMODE(j);
937                         p = strchr(optarg, ':');
938                         if (p != NULL)
939                         {
940                                 *p++ = '\0';
941                                 if (*p != '\0')
942                                 {
943                                         ep = sm_malloc_x(strlen(p) + 1);
944                                         cleanstrcpy(ep, p, MAXNAME);
945                                         macdefine(&BlankEnvelope.e_macro,
946                                                   A_HEAP, 's', ep);
947                                 }
948                         }
949                         if (*optarg != '\0')
950                         {
951                                 ep = sm_malloc_x(strlen(optarg) + 1);
952                                 cleanstrcpy(ep, optarg, MAXNAME);
953                                 macdefine(&BlankEnvelope.e_macro, A_HEAP,
954                                           'r', ep);
955                         }
956                         break;
957
958 #if _FFR_QUARANTINE
959                   case 'Q':     /* change quarantining on queued items */
960                         /* sanity check */
961                         if (OpMode != MD_DELIVER &&
962                             OpMode != MD_QUEUERUN)
963                         {
964                                 usrerr("Can not use -Q with -b%c", OpMode);
965                                 ExitStat = EX_USAGE;
966                                 break;
967                         }
968
969                         if (OpMode == MD_DELIVER)
970                                 set_op_mode(MD_QUEUERUN);
971
972                         FullName = NULL;
973
974                         quarantining = newstr(optarg);
975                         break;
976 #endif /* _FFR_QUARANTINE */
977
978                   case 'q':     /* run queue files at intervals */
979                         /* sanity check */
980                         if (OpMode != MD_DELIVER &&
981                             OpMode != MD_DAEMON &&
982                             OpMode != MD_FGDAEMON &&
983                             OpMode != MD_PRINT &&
984                             OpMode != MD_PRINTNQE &&
985                             OpMode != MD_QUEUERUN)
986                         {
987                                 usrerr("Can not use -q with -b%c", OpMode);
988                                 ExitStat = EX_USAGE;
989                                 break;
990                         }
991
992                         /* don't override -bd, -bD or -bp */
993                         if (OpMode == MD_DELIVER)
994                                 set_op_mode(MD_QUEUERUN);
995
996                         FullName = NULL;
997                         negate = optarg[0] == '!';
998                         if (negate)
999                         {
1000                                 /* negate meaning of pattern match */
1001                                 optarg++; /* skip '!' for next switch */
1002                         }
1003
1004                         switch (optarg[0])
1005                         {
1006                           case 'G': /* Limit by queue group name */
1007                                 if (negate)
1008                                 {
1009                                         usrerr("Can not use -q!G");
1010                                         ExitStat = EX_USAGE;
1011                                         break;
1012                                 }
1013                                 if (queuegroup != NULL)
1014                                 {
1015                                         usrerr("Can not use multiple -qG options");
1016                                         ExitStat = EX_USAGE;
1017                                         break;
1018                                 }
1019                                 queuegroup = newstr(&optarg[1]);
1020                                 break;
1021
1022                           case 'I': /* Limit by ID */
1023                                 new = (QUEUE_CHAR *) xalloc(sizeof *new);
1024                                 new->queue_match = newstr(&optarg[1]);
1025                                 new->queue_negate = negate;
1026                                 new->queue_next = QueueLimitId;
1027                                 QueueLimitId = new;
1028                                 break;
1029
1030                           case 'R': /* Limit by recipient */
1031                                 new = (QUEUE_CHAR *) xalloc(sizeof *new);
1032                                 new->queue_match = newstr(&optarg[1]);
1033                                 new->queue_negate = negate;
1034                                 new->queue_next = QueueLimitRecipient;
1035                                 QueueLimitRecipient = new;
1036                                 break;
1037
1038                           case 'S': /* Limit by sender */
1039                                 new = (QUEUE_CHAR *) xalloc(sizeof *new);
1040                                 new->queue_match = newstr(&optarg[1]);
1041                                 new->queue_negate = negate;
1042                                 new->queue_next = QueueLimitSender;
1043                                 QueueLimitSender = new;
1044                                 break;
1045
1046                           case 'f': /* foreground queue run */
1047                                 foregroundqueue  = true;
1048                                 break;
1049
1050 #if _FFR_QUARANTINE
1051                           case 'Q': /* Limit by quarantine message */
1052                                 if (optarg[1] != '\0')
1053                                 {
1054                                         new = (QUEUE_CHAR *) xalloc(sizeof *new);
1055                                         new->queue_match = newstr(&optarg[1]);
1056                                         new->queue_negate = negate;
1057                                         new->queue_next = QueueLimitQuarantine;
1058                                         QueueLimitQuarantine = new;
1059                                 }
1060                                 QueueMode = QM_QUARANTINE;
1061                                 break;
1062
1063                           case 'L': /* act on lost items */
1064                                 QueueMode = QM_LOST;
1065                                 break;
1066 #endif /* _FFR_QUARANTINE */
1067
1068                           case 'p': /* Persistent queue */
1069                                 queuepersistent = true;
1070                                 if (QueueIntvl == 0)
1071                                         QueueIntvl = 1;
1072                                 if (optarg[1] == '\0')
1073                                         break;
1074                                 ++optarg;
1075                                 /* FALLTHROUGH */
1076
1077                           default:
1078                                 i = Errors;
1079                                 QueueIntvl = convtime(optarg, 'm');
1080                                 if (QueueIntvl < 0)
1081                                 {
1082                                         usrerr("Invalid -q value");
1083                                         ExitStat = EX_USAGE;
1084                                 }
1085
1086                                 /* check for bad conversion */
1087                                 if (i < Errors)
1088                                         ExitStat = EX_USAGE;
1089                                 break;
1090                         }
1091                         break;
1092
1093                   case 'R':     /* DSN RET: what to return */
1094                         CHECK_AGAINST_OPMODE(j);
1095                         if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
1096                         {
1097                                 usrerr("Duplicate -R flag");
1098                                 ExitStat = EX_USAGE;
1099                                 break;
1100                         }
1101                         BlankEnvelope.e_flags |= EF_RET_PARAM;
1102                         if (sm_strcasecmp(optarg, "hdrs") == 0)
1103                                 BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
1104                         else if (sm_strcasecmp(optarg, "full") != 0)
1105                         {
1106                                 usrerr("Invalid -R value");
1107                                 ExitStat = EX_USAGE;
1108                         }
1109                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
1110                                   macid("{dsn_ret}"), optarg);
1111                         break;
1112
1113                   case 't':     /* read recipients from message */
1114                         CHECK_AGAINST_OPMODE(j);
1115                         GrabTo = true;
1116                         break;
1117
1118                   case 'V':     /* DSN ENVID: set "original" envelope id */
1119                         CHECK_AGAINST_OPMODE(j);
1120                         if (!xtextok(optarg))
1121                         {
1122                                 usrerr("Invalid syntax in -V flag");
1123                                 ExitStat = EX_USAGE;
1124                         }
1125                         else
1126                         {
1127                                 BlankEnvelope.e_envid = newstr(optarg);
1128                                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
1129                                           macid("{dsn_envid}"), optarg);
1130                         }
1131                         break;
1132
1133                   case 'X':     /* traffic log file */
1134                         dp = drop_privileges(true);
1135                         setstat(dp);
1136                         if (stat(optarg, &traf_st) == 0 &&
1137                             S_ISFIFO(traf_st.st_mode))
1138                                 TrafficLogFile = sm_io_open(SmFtStdio,
1139                                                             SM_TIME_DEFAULT,
1140                                                             optarg,
1141                                                             SM_IO_WRONLY, NULL);
1142                         else
1143                                 TrafficLogFile = sm_io_open(SmFtStdio,
1144                                                             SM_TIME_DEFAULT,
1145                                                             optarg,
1146                                                             SM_IO_APPEND, NULL);
1147                         if (TrafficLogFile == NULL)
1148                         {
1149                                 syserr("cannot open %s", optarg);
1150                                 ExitStat = EX_CANTCREAT;
1151                                 break;
1152                         }
1153                         (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
1154                                              NULL, SM_IO_LBF, 0);
1155                         break;
1156
1157                         /* compatibility flags */
1158                   case 'c':     /* connect to non-local mailers */
1159                   case 'i':     /* don't let dot stop me */
1160                   case 'm':     /* send to me too */
1161                   case 'T':     /* set timeout interval */
1162                   case 'v':     /* give blow-by-blow description */
1163                         setoption(j, "T", false, true, &BlankEnvelope);
1164                         break;
1165
1166                   case 'e':     /* error message disposition */
1167                   case 'M':     /* define macro */
1168                         setoption(j, optarg, false, true, &BlankEnvelope);
1169                         break;
1170
1171                   case 's':     /* save From lines in headers */
1172                         setoption('f', "T", false, true, &BlankEnvelope);
1173                         break;
1174
1175 #ifdef DBM
1176                   case 'I':     /* initialize alias DBM file */
1177                         set_op_mode(MD_INITALIAS);
1178                         break;
1179 #endif /* DBM */
1180
1181 #if defined(__osf__) || defined(_AIX3)
1182                   case 'x':     /* random flag that OSF/1 & AIX mailx passes */
1183                         break;
1184 #endif /* defined(__osf__) || defined(_AIX3) */
1185 #if defined(sony_news)
1186                   case 'E':
1187                   case 'J':     /* ignore flags for Japanese code conversion
1188                                    implemented on Sony NEWS */
1189                         break;
1190 #endif /* defined(sony_news) */
1191
1192                   default:
1193                         finis(true, true, EX_USAGE);
1194                         /* NOTREACHED */
1195                         break;
1196                 }
1197         }
1198
1199         /* if we've had errors so far, exit now */
1200         if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
1201             ExitStat == EX_OSERR)
1202         {
1203                 finis(false, true, ExitStat);
1204                 /* NOTREACHED */
1205         }
1206
1207         if (bitset(SUBMIT_MTA, SubmitMode))
1208         {
1209                 /* If set daemon_flags on command line, don't reset it */
1210                 if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1211                         macdefine(&BlankEnvelope.e_macro, A_PERM,
1212                                   macid("{daemon_flags}"), "CC f");
1213         }
1214         else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
1215         {
1216                 SubmitMode = SUBMIT_MSA;
1217
1218                 /* If set daemon_flags on command line, don't reset it */
1219                 if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1220                         macdefine(&BlankEnvelope.e_macro, A_PERM,
1221                                   macid("{daemon_flags}"), "c u");
1222         }
1223
1224         /*
1225         **  Do basic initialization.
1226         **      Read system control file.
1227         **      Extract special fields for local use.
1228         */
1229
1230 #if XDEBUG
1231         checkfd012("before readcf");
1232 #endif /* XDEBUG */
1233         vendor_pre_defaults(&BlankEnvelope);
1234
1235         readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
1236                          safecf, &BlankEnvelope);
1237 #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
1238         ConfigFileRead = true;
1239 #endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
1240         vendor_post_defaults(&BlankEnvelope);
1241
1242         /* now we can complain about missing fds */
1243         if (MissingFds != 0 && LogLevel > 8)
1244         {
1245                 char mbuf[MAXLINE];
1246
1247                 mbuf[0] = '\0';
1248                 if (bitset(1 << STDIN_FILENO, MissingFds))
1249                         (void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
1250                 if (bitset(1 << STDOUT_FILENO, MissingFds))
1251                         (void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
1252                 if (bitset(1 << STDERR_FILENO, MissingFds))
1253                         (void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
1254
1255                 /* Notice: fill_errno is from high above: fill_fd() */
1256                 sm_syslog(LOG_WARNING, NOQID,
1257                           "File descriptors missing on startup: %s; %s",
1258                           &mbuf[2], sm_errstring(fill_errno));
1259         }
1260
1261         /* Remove the ability for a normal user to send signals */
1262         if (RealUid != 0 && RealUid != geteuid())
1263         {
1264                 uid_t new_uid = geteuid();
1265
1266 #if HASSETREUID
1267                 /*
1268                 **  Since we can differentiate between uid and euid,
1269                 **  make the uid a different user so the real user
1270                 **  can't send signals.  However, it doesn't need to be
1271                 **  root (euid has root).
1272                 */
1273
1274                 if (new_uid == 0)
1275                         new_uid = DefUid;
1276                 if (tTd(47, 5))
1277                         sm_dprintf("Changing real uid to %d\n", (int) new_uid);
1278                 if (setreuid(new_uid, geteuid()) < 0)
1279                 {
1280                         syserr("main: setreuid(%d, %d) failed",
1281                                (int) new_uid, (int) geteuid());
1282                         finis(false, true, EX_OSERR);
1283                         /* NOTREACHED */
1284                 }
1285                 if (tTd(47, 10))
1286                         sm_dprintf("Now running as e/ruid %d:%d\n",
1287                                    (int) geteuid(), (int) getuid());
1288 #else /* HASSETREUID */
1289                 /*
1290                 **  Have to change both effective and real so need to
1291                 **  change them both to effective to keep privs.
1292                 */
1293
1294                 if (tTd(47, 5))
1295                         sm_dprintf("Changing uid to %d\n", (int) new_uid);
1296                 if (setuid(new_uid) < 0)
1297                 {
1298                         syserr("main: setuid(%d) failed", (int) new_uid);
1299                         finis(false, true, EX_OSERR);
1300                         /* NOTREACHED */
1301                 }
1302                 if (tTd(47, 10))
1303                         sm_dprintf("Now running as e/ruid %d:%d\n",
1304                                    (int) geteuid(), (int) getuid());
1305 #endif /* HASSETREUID */
1306         }
1307
1308 #if NAMED_BIND
1309         if (FallBackMX != NULL)
1310                 (void) getfallbackmxrr(FallBackMX);
1311 #endif /* NAMED_BIND */
1312
1313         if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
1314         {
1315                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1316                                      "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
1317         }
1318
1319         if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
1320         {
1321                 usrerr("Mail submission program cannot be used as daemon");
1322                 finis(false, true, EX_USAGE);
1323         }
1324
1325         if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
1326             OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
1327             OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
1328                 makeworkgroups();
1329
1330         /* set up the basic signal handlers */
1331         if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
1332                 (void) sm_signal(SIGINT, intsig);
1333         (void) sm_signal(SIGTERM, intsig);
1334
1335         /* Enforce use of local time (null string overrides this) */
1336         if (TimeZoneSpec == NULL)
1337                 unsetenv("TZ");
1338         else if (TimeZoneSpec[0] != '\0')
1339                 setuserenv("TZ", TimeZoneSpec);
1340         else
1341                 setuserenv("TZ", NULL);
1342         tzset();
1343
1344         /* initialize mailbox database */
1345         i = sm_mbdb_initialize(Mbdb);
1346         if (i != EX_OK)
1347         {
1348                 usrerr("Can't initialize mailbox database \"%s\": %s",
1349                        Mbdb, sm_strexit(i));
1350                 ExitStat = i;
1351         }
1352
1353         /* avoid denial-of-service attacks */
1354         resetlimits();
1355
1356         if (OpMode == MD_TEST)
1357         {
1358                 /* can't be done after readcf if RunAs* is used */
1359                 dp = drop_privileges(true);
1360                 if (dp != EX_OK)
1361                 {
1362                         finis(false, true, dp);
1363                         /* NOTREACHED */
1364                 }
1365         }
1366         else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
1367         {
1368                 /* drop privileges -- daemon mode done after socket/bind */
1369                 dp = drop_privileges(false);
1370                 setstat(dp);
1371                 if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
1372                 {
1373                         usrerr("Mail submission program must have RunAsUser set to non root user");
1374                         finis(false, true, EX_CONFIG);
1375                         /* NOTREACHED */
1376                 }
1377         }
1378
1379 #if NAMED_BIND
1380         _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
1381         _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1382 #endif /* NAMED_BIND */
1383
1384         /*
1385         **  Find our real host name for future logging.
1386         */
1387
1388         authinfo = getauthinfo(STDIN_FILENO, &forged);
1389         macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
1390
1391         /* suppress error printing if errors mailed back or whatever */
1392         if (BlankEnvelope.e_errormode != EM_PRINT)
1393                 HoldErrs = true;
1394
1395         /* set up the $=m class now, after .cf has a chance to redefine $m */
1396         expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
1397         if (jbuf[0] != '\0')
1398                 setclass('m', jbuf);
1399
1400         /* probe interfaces and locate any additional names */
1401         if (DontProbeInterfaces != DPI_PROBENONE)
1402                 load_if_names();
1403
1404         if (tTd(0, 10))
1405         {
1406                 char pidpath[MAXPATHLEN];
1407
1408                 /* Now we know which .cf file we use */
1409                 sm_dprintf("     Conf file:\t%s (selected)\n",
1410                            getcfname(OpMode, SubmitMode, cftype, conffile));
1411                 expand(PidFile, pidpath, sizeof pidpath, &BlankEnvelope);
1412                 sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
1413         }
1414
1415         if (tTd(0, 1))
1416         {
1417                 sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
1418                 sm_dprintf("\n      (short domain name) $w = ");
1419                 xputs(macvalue('w', &BlankEnvelope));
1420                 sm_dprintf("\n  (canonical domain name) $j = ");
1421                 xputs(macvalue('j', &BlankEnvelope));
1422                 sm_dprintf("\n         (subdomain name) $m = ");
1423                 xputs(macvalue('m', &BlankEnvelope));
1424                 sm_dprintf("\n              (node name) $k = ");
1425                 xputs(macvalue('k', &BlankEnvelope));
1426                 sm_dprintf("\n========================================================\n\n");
1427         }
1428
1429         /*
1430         **  Do more command line checking -- these are things that
1431         **  have to modify the results of reading the config file.
1432         */
1433
1434         /* process authorization warnings from command line */
1435         if (warn_C_flag)
1436                 auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
1437                              RealUserName, conffile);
1438         if (Warn_Q_option && !wordinclass(RealUserName, 't'))
1439                 auth_warning(&BlankEnvelope, "Processed from queue %s",
1440                              QueueDir);
1441         if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
1442             RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
1443                 sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
1444                           (int) RealUid);
1445
1446         /* check body type for legality */
1447         i = check_bodytype(BlankEnvelope.e_bodytype);
1448         if (i == BODYTYPE_ILLEGAL)
1449         {
1450                 usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
1451                 BlankEnvelope.e_bodytype = NULL;
1452         }
1453         else if (i != BODYTYPE_NONE)
1454                 SevenBitInput = (i == BODYTYPE_7BIT);
1455
1456         /* tweak default DSN notifications */
1457         if (DefaultNotify == 0)
1458                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1459
1460         /* be sure we don't pick up bogus HOSTALIASES environment variable */
1461         if (OpMode == MD_QUEUERUN && RealUid != 0)
1462                 (void) unsetenv("HOSTALIASES");
1463
1464         /* check for sane configuration level */
1465         if (ConfigLevel > MAXCONFIGLEVEL)
1466         {
1467                 syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
1468                        ConfigLevel, Version, MAXCONFIGLEVEL);
1469         }
1470
1471         /* need MCI cache to have persistence */
1472         if (HostStatDir != NULL && MaxMciCache == 0)
1473         {
1474                 HostStatDir = NULL;
1475                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1476                                      "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
1477         }
1478
1479         /* need HostStatusDir in order to have SingleThreadDelivery */
1480         if (SingleThreadDelivery && HostStatDir == NULL)
1481         {
1482                 SingleThreadDelivery = false;
1483                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1484                                      "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
1485         }
1486
1487         /* check for permissions */
1488         if (RealUid != 0 &&
1489             RealUid != TrustedUid)
1490         {
1491                 char *action = NULL;
1492
1493                 switch (OpMode)
1494                 {
1495                   case MD_QUEUERUN:
1496 #if _FFR_QUARANTINE
1497                         if (quarantining != NULL)
1498                                 action = "quarantine jobs";
1499                         else
1500 #endif /* _FFR_QUARANTINE */
1501                         /* Normal users can do a single queue run */
1502                         if (QueueIntvl == 0)
1503                                 break;
1504
1505                         /* but not persistent queue runners */
1506                         if (action == NULL)
1507                                 action = "start a queue runner daemon";
1508                         /* FALLTHROUGH */
1509
1510                   case MD_PURGESTAT:
1511                         if (action == NULL)
1512                                 action = "purge host status";
1513                         /* FALLTHROUGH */
1514
1515                   case MD_DAEMON:
1516                   case MD_FGDAEMON:
1517                         if (action == NULL)
1518                                 action = "run daemon";
1519
1520                         if (tTd(65, 1))
1521                                 sm_dprintf("Deny user %d attempt to %s\n",
1522                                            (int) RealUid, action);
1523
1524                         if (LogLevel > 1)
1525                                 sm_syslog(LOG_ALERT, NOQID,
1526                                           "user %d attempted to %s",
1527                                           (int) RealUid, action);
1528                         HoldErrs = false;
1529                         usrerr("Permission denied (real uid not trusted)");
1530                         finis(false, true, EX_USAGE);
1531                         /* NOTREACHED */
1532                         break;
1533
1534                   case MD_VERIFY:
1535                         if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
1536                         {
1537                                 /*
1538                                 **  If -bv and RestrictExpand,
1539                                 **  drop privs to prevent normal
1540                                 **  users from reading private
1541                                 **  aliases/forwards/:include:s
1542                                 */
1543
1544                                 if (tTd(65, 1))
1545                                         sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
1546                                                    (int) RealUid);
1547
1548                                 dp = drop_privileges(true);
1549
1550                                 /* Fake address safety */
1551                                 if (tTd(65, 1))
1552                                         sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
1553                                 setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
1554
1555                                 if (dp != EX_OK)
1556                                 {
1557                                         if (tTd(65, 1))
1558                                                 sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
1559                                                            (int) RealUid);
1560                                         CurEnv->e_id = NULL;
1561                                         finis(true, true, dp);
1562                                         /* NOTREACHED */
1563                                 }
1564                         }
1565                         break;
1566
1567                   case MD_TEST:
1568                   case MD_PRINT:
1569                   case MD_PRINTNQE:
1570                   case MD_FREEZE:
1571                   case MD_HOSTSTAT:
1572                         /* Nothing special to check */
1573                         break;
1574
1575                   case MD_INITALIAS:
1576                         if (!wordinclass(RealUserName, 't'))
1577                         {
1578                                 if (tTd(65, 1))
1579                                         sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
1580                                                    (int) RealUid);
1581                                 if (LogLevel > 1)
1582                                         sm_syslog(LOG_ALERT, NOQID,
1583                                                   "user %d attempted to rebuild the alias map",
1584                                                   (int) RealUid);
1585                                 HoldErrs = false;
1586                                 usrerr("Permission denied (real uid not trusted)");
1587                                 finis(false, true, EX_USAGE);
1588                                 /* NOTREACHED */
1589                         }
1590                         if (UseMSP)
1591                         {
1592                                 HoldErrs = false;
1593                                 usrerr("User %d cannot rebuild aliases in mail submission program",
1594                                        (int) RealUid);
1595                                 finis(false, true, EX_USAGE);
1596                                 /* NOTREACHED */
1597                         }
1598                         /* FALLTHROUGH */
1599
1600                   default:
1601                         if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
1602                             Verbose != 0)
1603                         {
1604                                 /*
1605                                 **  If -v and RestrictExpand, reset
1606                                 **  Verbose to prevent normal users
1607                                 **  from seeing the expansion of
1608                                 **  aliases/forwards/:include:s
1609                                 */
1610
1611                                 if (tTd(65, 1))
1612                                         sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
1613                                                    (int) RealUid);
1614                                 Verbose = 0;
1615                         }
1616                         break;
1617                 }
1618         }
1619
1620         if (MeToo)
1621                 BlankEnvelope.e_flags |= EF_METOO;
1622
1623         switch (OpMode)
1624         {
1625           case MD_TEST:
1626                 /* don't have persistent host status in test mode */
1627                 HostStatDir = NULL;
1628                 if (Verbose == 0)
1629                         Verbose = 2;
1630                 BlankEnvelope.e_errormode = EM_PRINT;
1631                 HoldErrs = false;
1632                 break;
1633
1634           case MD_VERIFY:
1635                 BlankEnvelope.e_errormode = EM_PRINT;
1636                 HoldErrs = false;
1637                 /* arrange to exit cleanly on hangup signal */
1638                 if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1639                         (void) sm_signal(SIGHUP, intsig);
1640                 if (geteuid() != 0)
1641                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1642                                              "Notice: -bv may give misleading output for non-privileged user\n");
1643                 break;
1644
1645           case MD_FGDAEMON:
1646                 run_in_foreground = true;
1647                 set_op_mode(MD_DAEMON);
1648                 /* FALLTHROUGH */
1649
1650           case MD_DAEMON:
1651                 vendor_daemon_setup(&BlankEnvelope);
1652
1653                 /* remove things that don't make sense in daemon mode */
1654                 FullName = NULL;
1655                 GrabTo = false;
1656
1657                 /* arrange to restart on hangup signal */
1658                 if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1659                         sm_syslog(LOG_WARNING, NOQID,
1660                                   "daemon invoked without full pathname; kill -1 won't work");
1661                 break;
1662
1663           case MD_INITALIAS:
1664                 Verbose = 2;
1665                 BlankEnvelope.e_errormode = EM_PRINT;
1666                 HoldErrs = false;
1667                 /* FALLTHROUGH */
1668
1669           default:
1670                 /* arrange to exit cleanly on hangup signal */
1671                 if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1672                         (void) sm_signal(SIGHUP, intsig);
1673                 break;
1674         }
1675
1676         /* special considerations for FullName */
1677         if (FullName != NULL)
1678         {
1679                 char *full = NULL;
1680
1681                 /* full names can't have newlines */
1682                 if (strchr(FullName, '\n') != NULL)
1683                 {
1684                         full = newstr(denlstring(FullName, true, true));
1685                         FullName = full;
1686                 }
1687
1688                 /* check for characters that may have to be quoted */
1689                 if (!rfc822_string(FullName))
1690                 {
1691                         /*
1692                         **  Quote a full name with special characters
1693                         **  as a comment so crackaddr() doesn't destroy
1694                         **  the name portion of the address.
1695                         */
1696
1697                         FullName = addquotes(FullName, NULL);
1698                         if (full != NULL)
1699                                 sm_free(full);  /* XXX */
1700                 }
1701         }
1702
1703         /* do heuristic mode adjustment */
1704         if (Verbose)
1705         {
1706                 /* turn off noconnect option */
1707                 setoption('c', "F", true, false, &BlankEnvelope);
1708
1709                 /* turn on interactive delivery */
1710                 setoption('d', "", true, false, &BlankEnvelope);
1711         }
1712
1713 #ifdef VENDOR_CODE
1714         /* check for vendor mismatch */
1715         if (VendorCode != VENDOR_CODE)
1716         {
1717                 message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1718                         getvendor(VENDOR_CODE), getvendor(VendorCode));
1719         }
1720 #endif /* VENDOR_CODE */
1721
1722         /* check for out of date configuration level */
1723         if (ConfigLevel < MAXCONFIGLEVEL)
1724         {
1725                 message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1726                         Version, MAXCONFIGLEVEL, ConfigLevel);
1727         }
1728
1729         if (ConfigLevel < 3)
1730                 UseErrorsTo = true;
1731
1732         /* set options that were previous macros */
1733         if (SmtpGreeting == NULL)
1734         {
1735                 if (ConfigLevel < 7 &&
1736                     (p = macvalue('e', &BlankEnvelope)) != NULL)
1737                         SmtpGreeting = newstr(p);
1738                 else
1739                         SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1740         }
1741         if (UnixFromLine == NULL)
1742         {
1743                 if (ConfigLevel < 7 &&
1744                     (p = macvalue('l', &BlankEnvelope)) != NULL)
1745                         UnixFromLine = newstr(p);
1746                 else
1747                         UnixFromLine = "From \201g  \201d";
1748         }
1749         SmtpError[0] = '\0';
1750
1751         /* our name for SMTP codes */
1752         expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
1753         if (jbuf[0] == '\0')
1754                 PSTRSET(MyHostName, "localhost");
1755         else
1756                 PSTRSET(MyHostName, jbuf);
1757         if (strchr(MyHostName, '.') == NULL)
1758                 message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1759                         MyHostName);
1760
1761         /* make certain that this name is part of the $=w class */
1762         setclass('w', MyHostName);
1763
1764         /* fill in the structure of the *default* queue */
1765         st = stab("mqueue", ST_QUEUE, ST_FIND);
1766         if (st == NULL)
1767                 syserr("No default queue (mqueue) defined");
1768         else
1769                 set_def_queueval(st->s_quegrp, true);
1770
1771         /* the indices of built-in mailers */
1772         st = stab("local", ST_MAILER, ST_FIND);
1773         if (st != NULL)
1774                 LocalMailer = st->s_mailer;
1775         else if (OpMode != MD_TEST || !warn_C_flag)
1776                 syserr("No local mailer defined");
1777
1778         st = stab("prog", ST_MAILER, ST_FIND);
1779         if (st == NULL)
1780                 syserr("No prog mailer defined");
1781         else
1782         {
1783                 ProgMailer = st->s_mailer;
1784                 clrbitn(M_MUSER, ProgMailer->m_flags);
1785         }
1786
1787         st = stab("*file*", ST_MAILER, ST_FIND);
1788         if (st == NULL)
1789                 syserr("No *file* mailer defined");
1790         else
1791         {
1792                 FileMailer = st->s_mailer;
1793                 clrbitn(M_MUSER, FileMailer->m_flags);
1794         }
1795
1796         st = stab("*include*", ST_MAILER, ST_FIND);
1797         if (st == NULL)
1798                 syserr("No *include* mailer defined");
1799         else
1800                 InclMailer = st->s_mailer;
1801
1802         if (ConfigLevel < 6)
1803         {
1804                 /* heuristic tweaking of local mailer for back compat */
1805                 if (LocalMailer != NULL)
1806                 {
1807                         setbitn(M_ALIASABLE, LocalMailer->m_flags);
1808                         setbitn(M_HASPWENT, LocalMailer->m_flags);
1809                         setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1810                         setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1811                         setbitn(M_CHECKPROG, LocalMailer->m_flags);
1812                         setbitn(M_CHECKFILE, LocalMailer->m_flags);
1813                         setbitn(M_CHECKUDB, LocalMailer->m_flags);
1814                 }
1815                 if (ProgMailer != NULL)
1816                         setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1817                 if (FileMailer != NULL)
1818                         setbitn(M_RUNASRCPT, FileMailer->m_flags);
1819         }
1820         if (ConfigLevel < 7)
1821         {
1822                 if (LocalMailer != NULL)
1823                         setbitn(M_VRFY250, LocalMailer->m_flags);
1824                 if (ProgMailer != NULL)
1825                         setbitn(M_VRFY250, ProgMailer->m_flags);
1826                 if (FileMailer != NULL)
1827                         setbitn(M_VRFY250, FileMailer->m_flags);
1828         }
1829
1830         /* MIME Content-Types that cannot be transfer encoded */
1831         setclass('n', "multipart/signed");
1832
1833         /* MIME message/xxx subtypes that can be treated as messages */
1834         setclass('s', "rfc822");
1835
1836         /* MIME Content-Transfer-Encodings that can be encoded */
1837         setclass('e', "7bit");
1838         setclass('e', "8bit");
1839         setclass('e', "binary");
1840
1841 #ifdef USE_B_CLASS
1842         /* MIME Content-Types that should be treated as binary */
1843         setclass('b', "image");
1844         setclass('b', "audio");
1845         setclass('b', "video");
1846         setclass('b', "application/octet-stream");
1847 #endif /* USE_B_CLASS */
1848
1849         /* MIME headers which have fields to check for overflow */
1850         setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
1851         setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
1852
1853         /* MIME headers to check for length overflow */
1854         setclass(macid("{checkMIMETextHeaders}"), "content-description");
1855
1856         /* MIME headers to check for overflow and rebalance */
1857         setclass(macid("{checkMIMEHeaders}"), "content-disposition");
1858         setclass(macid("{checkMIMEHeaders}"), "content-id");
1859         setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
1860         setclass(macid("{checkMIMEHeaders}"), "content-type");
1861         setclass(macid("{checkMIMEHeaders}"), "mime-version");
1862
1863         /* Macros to save in the queue file -- don't remove any */
1864         setclass(macid("{persistentMacros}"), "r");
1865         setclass(macid("{persistentMacros}"), "s");
1866         setclass(macid("{persistentMacros}"), "_");
1867         setclass(macid("{persistentMacros}"), "{if_addr}");
1868         setclass(macid("{persistentMacros}"), "{daemon_flags}");
1869
1870         /* operate in queue directory */
1871         if (QueueDir == NULL || *QueueDir == '\0')
1872         {
1873                 if (OpMode != MD_TEST)
1874                 {
1875                         syserr("QueueDirectory (Q) option must be set");
1876                         ExitStat = EX_CONFIG;
1877                 }
1878         }
1879         else
1880         {
1881                 if (OpMode != MD_TEST)
1882                         setup_queues(OpMode == MD_DAEMON);
1883         }
1884
1885         /* check host status directory for validity */
1886         if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
1887         {
1888                 /* cannot use this value */
1889                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1890                                      "Warning: Cannot use HostStatusDirectory = %s: %s\n",
1891                                      HostStatDir, sm_errstring(errno));
1892                 HostStatDir = NULL;
1893         }
1894
1895         if (OpMode == MD_QUEUERUN &&
1896             RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1897         {
1898                 struct stat stbuf;
1899
1900                 /* check to see if we own the queue directory */
1901                 if (stat(".", &stbuf) < 0)
1902                         syserr("main: cannot stat %s", QueueDir);
1903                 if (stbuf.st_uid != RealUid)
1904                 {
1905                         /* nope, really a botch */
1906                         HoldErrs = false;
1907                         usrerr("You do not have permission to process the queue");
1908                         finis(false, true, EX_NOPERM);
1909                         /* NOTREACHED */
1910                 }
1911         }
1912
1913 #if MILTER
1914         /* sanity checks on milter filters */
1915         if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
1916         {
1917                 milter_config(InputFilterList, InputFilters, MAXFILTERS);
1918 # if _FFR_MILTER_PERDAEMON
1919                 setup_daemon_milters();
1920 # endif /* _FFR_MILTER_PERDAEMON */
1921         }
1922 #endif /* MILTER */
1923
1924         /* Convert queuegroup string to qgrp number */
1925         if (queuegroup != NULL)
1926         {
1927                 qgrp = name2qid(queuegroup);
1928                 if (qgrp == NOQGRP)
1929                 {
1930                         HoldErrs = false;
1931                         usrerr("Queue group %s unknown", queuegroup);
1932                         finis(false, true, ExitStat);
1933                         /* NOTREACHED */
1934                 }
1935         }
1936
1937         /* if we've had errors so far, exit now */
1938         if (ExitStat != EX_OK && OpMode != MD_TEST)
1939         {
1940                 finis(false, true, ExitStat);
1941                 /* NOTREACHED */
1942         }
1943
1944 #if SASL
1945         /* sendmail specific SASL initialization */
1946         sm_sasl_init();
1947 #endif /* SASL */
1948
1949 #if XDEBUG
1950         checkfd012("before main() initmaps");
1951 #endif /* XDEBUG */
1952
1953         /*
1954         **  Do operation-mode-dependent initialization.
1955         */
1956
1957         switch (OpMode)
1958         {
1959           case MD_PRINT:
1960                 /* print the queue */
1961                 HoldErrs = false;
1962                 dropenvelope(&BlankEnvelope, true, false);
1963                 (void) sm_signal(SIGPIPE, sigpipe);
1964                 if (qgrp != NOQGRP)
1965                 {
1966                         int j;
1967
1968                         /* Selecting a particular queue group to run */
1969                         for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
1970                         {
1971                                 if (StopRequest)
1972                                         stop_sendmail();
1973                                 (void) print_single_queue(qgrp, j);
1974                         }
1975                         finis(false, true, EX_OK);
1976                         /* NOTREACHED */
1977                 }
1978                 printqueue();
1979                 finis(false, true, EX_OK);
1980                 /* NOTREACHED */
1981                 break;
1982
1983           case MD_PRINTNQE:
1984                 /* print number of entries in queue */
1985                 dropenvelope(&BlankEnvelope, true, false);
1986                 (void) sm_signal(SIGPIPE, sigpipe);
1987                 printnqe(smioout, NULL);
1988                 finis(false, true, EX_OK);
1989                 /* NOTREACHED */
1990                 break;
1991
1992 #if _FFR_QUARANTINE
1993           case MD_QUEUERUN:
1994                 /* only handle quarantining here */
1995                 if (quarantining == NULL)
1996                         break;
1997
1998                 if (QueueMode != QM_QUARANTINE &&
1999                     QueueMode != QM_NORMAL)
2000                 {
2001                         HoldErrs = false;
2002                         usrerr("Can not use -Q with -q%c", QueueMode);
2003                         ExitStat = EX_USAGE;
2004                         finis(false, true, ExitStat);
2005                         /* NOTREACHED */
2006                 }
2007                 quarantine_queue(quarantining, qgrp);
2008                 finis(false, true, EX_OK);
2009                 break;
2010 #endif /* _FFR_QUARANTINE */
2011
2012           case MD_HOSTSTAT:
2013                 (void) sm_signal(SIGPIPE, sigpipe);
2014                 (void) mci_traverse_persistent(mci_print_persistent, NULL);
2015                 finis(false, true, EX_OK);
2016                 /* NOTREACHED */
2017                 break;
2018
2019           case MD_PURGESTAT:
2020                 (void) mci_traverse_persistent(mci_purge_persistent, NULL);
2021                 finis(false, true, EX_OK);
2022                 /* NOTREACHED */
2023                 break;
2024
2025           case MD_INITALIAS:
2026                 /* initialize maps */
2027                 initmaps();
2028                 finis(false, true, ExitStat);
2029                 /* NOTREACHED */
2030                 break;
2031
2032           case MD_SMTP:
2033           case MD_DAEMON:
2034                 /* reset DSN parameters */
2035                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
2036                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2037                           macid("{dsn_notify}"), NULL);
2038                 BlankEnvelope.e_envid = NULL;
2039                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2040                           macid("{dsn_envid}"), NULL);
2041                 BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
2042                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2043                           macid("{dsn_ret}"), NULL);
2044
2045                 /* don't open maps for daemon -- done below in child */
2046                 break;
2047         }
2048
2049         if (tTd(0, 15))
2050         {
2051                 /* print configuration table (or at least part of it) */
2052                 if (tTd(0, 90))
2053                         printrules();
2054                 for (i = 0; i < MAXMAILERS; i++)
2055                 {
2056                         if (Mailer[i] != NULL)
2057                                 printmailer(Mailer[i]);
2058                 }
2059         }
2060
2061         /*
2062         **  Switch to the main envelope.
2063         */
2064
2065         CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
2066                              sm_rpool_new_x(NULL));
2067         MainEnvelope.e_flags = BlankEnvelope.e_flags;
2068
2069         /*
2070         **  If test mode, read addresses from stdin and process.
2071         */
2072
2073         if (OpMode == MD_TEST)
2074         {
2075                 if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
2076                         Verbose = 2;
2077
2078                 if (Verbose)
2079                 {
2080                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2081                                      "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
2082                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2083                                      "Enter <ruleset> <address>\n");
2084                 }
2085                 macdefine(&(MainEnvelope.e_macro), A_PERM,
2086                           macid("{addr_type}"), "e r");
2087                 for (;;)
2088                 {
2089                         SM_TRY
2090                         {
2091                                 (void) sm_signal(SIGINT, intindebug);
2092                                 (void) sm_releasesignal(SIGINT);
2093                                 if (Verbose == 2)
2094                                         (void) sm_io_fprintf(smioout,
2095                                                              SM_TIME_DEFAULT,
2096                                                              "> ");
2097                                 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
2098                                 if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
2099                                                 sizeof buf) == NULL)
2100                                         testmodeline("/quit", &MainEnvelope);
2101                                 p = strchr(buf, '\n');
2102                                 if (p != NULL)
2103                                         *p = '\0';
2104                                 if (Verbose < 2)
2105                                         (void) sm_io_fprintf(smioout,
2106                                                              SM_TIME_DEFAULT,
2107                                                              "> %s\n", buf);
2108                                 testmodeline(buf, &MainEnvelope);
2109                         }
2110                         SM_EXCEPT(exc, "[!F]*")
2111                         {
2112                                 /*
2113                                 **  8.10 just prints \n on interrupt.
2114                                 **  I'm printing the exception here in case
2115                                 **  sendmail is extended to raise additional
2116                                 **  exceptions in this context.
2117                                 */
2118
2119                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2120                                                      "\n");
2121                                 sm_exc_print(exc, smioout);
2122                         }
2123                         SM_END_TRY
2124                 }
2125         }
2126
2127 #if STARTTLS
2128         tls_ok = true;
2129         if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)
2130         {
2131                 /* check whether STARTTLS is turned off for the client */
2132                 if (chkclientmodifiers(D_NOTLS))
2133                         tls_ok = false;
2134         }
2135         else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
2136                  OpMode == MD_SMTP)
2137         {
2138                 /* check whether STARTTLS is turned off for the server */
2139                 if (chkdaemonmodifiers(D_NOTLS))
2140                         tls_ok = false;
2141         }
2142         else    /* other modes don't need STARTTLS */
2143                 tls_ok = false;
2144
2145         if (tls_ok)
2146         {
2147                 /* basic TLS initialization */
2148                 tls_ok = init_tls_library();
2149         }
2150
2151         if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
2152         {
2153                 /* disable TLS for client */
2154                 setclttls(false);
2155         }
2156 #endif /* STARTTLS */
2157
2158         /*
2159         **  If collecting stuff from the queue, go start doing that.
2160         */
2161
2162         if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
2163         {
2164                 pid_t pid = -1;
2165
2166 #if STARTTLS
2167                 /* init TLS for client, ignore result for now */
2168                 (void) initclttls(tls_ok);
2169 #endif /* STARTTLS */
2170
2171                 /*
2172                 **  The parent process of the caller of runqueue() needs
2173                 **  to stay around for a possible SIGTERM. The SIGTERM will
2174                 **  tell this process that all of the queue runners children
2175                 **  need to be sent SIGTERM as well. At the same time, we
2176                 **  want to return control to the command line. So we do an
2177                 **  extra fork().
2178                 */
2179
2180                 if (Verbose || foregroundqueue || (pid = fork()) <= 0)
2181                 {
2182                         /*
2183                         **  If the fork() failed we should still try to do
2184                         **  the queue run. If it succeeded then the child
2185                         **  is going to start the run and wait for all
2186                         **  of the children to finish.
2187                         */
2188
2189                         if (pid == 0)
2190                         {
2191                                 /* Reset global flags */
2192                                 RestartRequest = NULL;
2193                                 ShutdownRequest = NULL;
2194                                 PendingSignal = 0;
2195
2196                                 /* disconnect from terminal */
2197                                 disconnect(2, CurEnv);
2198                         }
2199
2200                         CurrentPid = getpid();
2201                         if (qgrp != NOQGRP)
2202                         {
2203                                 int rwgflags = RWG_NONE;
2204
2205                                 /*
2206                                 **  To run a specific queue group mark it to
2207                                 **  be run, select the work group it's in and
2208                                 **  increment the work counter.
2209                                 */
2210
2211                                 for (i = 0; i < NumQueue && Queue[i] != NULL;
2212                                      i++)
2213                                         Queue[i]->qg_nextrun = (time_t) -1;
2214                                 Queue[qgrp]->qg_nextrun = 0;
2215                                 if (Verbose)
2216                                         rwgflags |= RWG_VERBOSE;
2217                                 if (queuepersistent)
2218                                         rwgflags |= RWG_PERSISTENT;
2219                                 rwgflags |= RWG_FORCE;
2220                                 (void) run_work_group(Queue[qgrp]->qg_wgrp,
2221                                                       rwgflags);
2222                         }
2223                         else
2224                                 (void) runqueue(false, Verbose,
2225                                                 queuepersistent, true);
2226
2227                         /* set the title to make it easier to find */
2228                         sm_setproctitle(true, CurEnv, "Queue control");
2229                         (void) sm_signal(SIGCHLD, SIG_DFL);
2230                         while (CurChildren > 0)
2231                         {
2232                                 int status;
2233                                 pid_t ret;
2234
2235                                 while ((ret = sm_wait(&status)) <= 0)
2236                                         continue;
2237
2238                                 /* Only drop when a child gives status */
2239                                 if (WIFSTOPPED(status))
2240                                         continue;
2241
2242                                 proc_list_drop(ret, status, NULL);
2243                         }
2244                 }
2245                 finis(true, true, ExitStat);
2246                 /* NOTREACHED */
2247         }
2248
2249 # if SASL
2250         if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2251         {
2252                 /* check whether AUTH is turned off for the server */
2253                 if (!chkdaemonmodifiers(D_NOAUTH) &&
2254                     (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
2255                         syserr("!sasl_server_init failed! [%s]",
2256                                 sasl_errstring(i, NULL, NULL));
2257         }
2258 # endif /* SASL */
2259
2260         if (OpMode == MD_SMTP)
2261         {
2262                 proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2263                               PROC_DAEMON, 0, -1);
2264
2265                 /* clean up background delivery children */
2266                 (void) sm_signal(SIGCHLD, reapchild);
2267         }
2268
2269         /*
2270         **  If a daemon, wait for a request.
2271         **      getrequests will always return in a child.
2272         **      If we should also be processing the queue, start
2273         **              doing it in background.
2274         **      We check for any errors that might have happened
2275         **              during startup.
2276         */
2277
2278         if (OpMode == MD_DAEMON || QueueIntvl > 0)
2279         {
2280                 char dtype[200];
2281
2282                 if (!run_in_foreground && !tTd(99, 100))
2283                 {
2284                         /* put us in background */
2285                         i = fork();
2286                         if (i < 0)
2287                                 syserr("daemon: cannot fork");
2288                         if (i != 0)
2289                         {
2290                                 finis(false, true, EX_OK);
2291                                 /* NOTREACHED */
2292                         }
2293
2294                         /*
2295                         **  Initialize exception stack and default exception
2296                         **  handler for child process.
2297                         */
2298
2299                         /* Reset global flags */
2300                         RestartRequest = NULL;
2301                         RestartWorkGroup = false;
2302                         ShutdownRequest = NULL;
2303                         PendingSignal = 0;
2304                         CurrentPid = getpid();
2305
2306                         sm_exc_newthread(fatal_error);
2307
2308                         /* disconnect from our controlling tty */
2309                         disconnect(2, &MainEnvelope);
2310                 }
2311
2312                 dtype[0] = '\0';
2313                 if (OpMode == MD_DAEMON)
2314                 {
2315                         (void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
2316                         DaemonPid = CurrentPid;
2317                 }
2318                 if (QueueIntvl > 0)
2319                 {
2320                         (void) sm_strlcat2(dtype,
2321                                            queuepersistent
2322                                            ? "+persistent-queueing@"
2323                                            : "+queueing@",
2324                                            pintvl(QueueIntvl, true),
2325                                            sizeof dtype);
2326                 }
2327                 if (tTd(0, 1))
2328                         (void) sm_strlcat(dtype, "+debugging", sizeof dtype);
2329
2330                 sm_syslog(LOG_INFO, NOQID,
2331                           "starting daemon (%s): %s", Version, dtype + 1);
2332 #if XLA
2333                 xla_create_file();
2334 #endif /* XLA */
2335
2336                 /* save daemon type in a macro for possible PidFile use */
2337                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2338                         macid("{daemon_info}"), dtype + 1);
2339
2340                 /* save queue interval in a macro for possible PidFile use */
2341                 macdefine(&MainEnvelope.e_macro, A_TEMP,
2342                         macid("{queue_interval}"), pintvl(QueueIntvl, true));
2343
2344                 /* workaround: can't seem to release the signal in the parent */
2345                 (void) sm_signal(SIGHUP, sighup);
2346                 (void) sm_releasesignal(SIGHUP);
2347                 (void) sm_signal(SIGTERM, sigterm);
2348
2349                 if (QueueIntvl > 0)
2350                 {
2351                         (void) runqueue(true, false, queuepersistent, true);
2352
2353                         /*
2354                         **  If queuepersistent but not in daemon mode then
2355                         **  we're going to do the queue runner monitoring here.
2356                         **  If in daemon mode then the monitoring will happen
2357                         **  elsewhere.
2358                         */
2359
2360                         if (OpMode != MD_DAEMON && queuepersistent)
2361                         {
2362                                 /* set the title to make it easier to find */
2363                                 sm_setproctitle(true, CurEnv, "Queue control");
2364                                 (void) sm_signal(SIGCHLD, SIG_DFL);
2365                                 while (CurChildren > 0)
2366                                 {
2367                                         int status;
2368                                         pid_t ret;
2369                                         int group;
2370
2371                                         if (ShutdownRequest != NULL)
2372                                                 shutdown_daemon();
2373                                         else if (RestartRequest != NULL)
2374                                                 restart_daemon();
2375                                         else if (RestartWorkGroup)
2376                                                 restart_marked_work_groups();
2377
2378                                         while ((ret = sm_wait(&status)) <= 0)
2379                                                 continue;
2380
2381                                         if (WIFSTOPPED(status))
2382                                                 continue;
2383
2384                                         /* Probe only on a child status */
2385                                         proc_list_drop(ret, status, &group);
2386
2387                                         if (WIFSIGNALED(status))
2388                                         {
2389                                                 if (WCOREDUMP(status))
2390                                                 {
2391                                                         sm_syslog(LOG_ERR, NOQID,
2392                                                                   "persistent queue runner=%d core dumped, signal=%d",
2393                                                                   group, WTERMSIG(status));
2394
2395                                                         /* don't restart this one */
2396                                                         mark_work_group_restart(group, -1);
2397                                                         continue;
2398                                                 }
2399
2400                                                 sm_syslog(LOG_ERR, NOQID,
2401                                                           "persistent queue runner=%d died, signal=%d",
2402                                                           group, WTERMSIG(status));
2403                                         }
2404
2405                                         /*
2406                                         **  When debugging active, don't
2407                                         **  restart the persistent queues.
2408                                         **  But do log this as info.
2409                                         */
2410
2411                                         if (sm_debug_active(&DebugNoPRestart,
2412                                                             1))
2413                                         {
2414                                                 sm_syslog(LOG_DEBUG, NOQID,
2415                                                           "persistent queue runner=%d, exited",
2416                                                           group);
2417                                                 mark_work_group_restart(group, -1);
2418                                         }
2419                                 }
2420                                 finis(true, true, ExitStat);
2421                                 /* NOTREACHED */
2422                         }
2423
2424                         if (OpMode != MD_DAEMON)
2425                         {
2426                                 char qtype[200];
2427
2428                                 /*
2429                                 **  Write the pid to file
2430                                 **  XXX Overwrites sendmail.pid
2431                                 */
2432
2433                                 log_sendmail_pid(&MainEnvelope);
2434
2435                                 /* set the title to make it easier to find */
2436                                 qtype[0] = '\0';
2437                                 (void) sm_strlcpyn(qtype, sizeof qtype, 4,
2438                                                    "Queue runner@",
2439                                                    pintvl(QueueIntvl, true),
2440                                                    " for ",
2441                                                    QueueDir);
2442                                 sm_setproctitle(true, CurEnv, qtype);
2443                                 for (;;)
2444                                 {
2445                                         (void) pause();
2446                                         if (ShutdownRequest != NULL)
2447                                                 shutdown_daemon();
2448                                         else if (RestartRequest != NULL)
2449                                                 restart_daemon();
2450                                         else if (RestartWorkGroup)
2451                                                 restart_marked_work_groups();
2452
2453                                         if (doqueuerun())
2454                                                 (void) runqueue(true, false,
2455                                                                 false, false);
2456                                 }
2457                         }
2458                 }
2459                 dropenvelope(&MainEnvelope, true, false);
2460
2461 #if STARTTLS
2462                 /* init TLS for server, ignore result for now */
2463                 (void) initsrvtls(tls_ok);
2464 #endif /* STARTTLS */
2465
2466         nextreq:
2467                 p_flags = getrequests(&MainEnvelope);
2468
2469                 /* drop privileges */
2470                 (void) drop_privileges(false);
2471
2472                 /*
2473                 **  Get authentication data
2474                 **  Set _ macro in BlankEnvelope before calling newenvelope().
2475                 */
2476
2477                 authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
2478                                                      NULL), &forged);
2479                 macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
2480
2481                 /* at this point we are in a child: reset state */
2482                 sm_rpool_free(MainEnvelope.e_rpool);
2483                 (void) newenvelope(&MainEnvelope, &MainEnvelope,
2484                                    sm_rpool_new_x(NULL));
2485         }
2486
2487         if (LogLevel > 9)
2488         {
2489                 /* log connection information */
2490                 sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
2491         }
2492
2493         /*
2494         **  If running SMTP protocol, start collecting and executing
2495         **  commands.  This will never return.
2496         */
2497
2498         if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2499         {
2500                 char pbuf[20];
2501
2502                 /*
2503                 **  Save some macros for check_* rulesets.
2504                 */
2505
2506                 if (forged)
2507                 {
2508                         char ipbuf[103];
2509
2510                         (void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
2511                                            anynet_ntoa(&RealHostAddr));
2512                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
2513                                   macid("{client_name}"), ipbuf);
2514                 }
2515                 else
2516                         macdefine(&BlankEnvelope.e_macro, A_PERM,
2517                                   macid("{client_name}"), RealHostName);
2518                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2519                           macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
2520                 sm_getla();
2521
2522                 switch (RealHostAddr.sa.sa_family)
2523                 {
2524 #if NETINET
2525                   case AF_INET:
2526                         (void) sm_snprintf(pbuf, sizeof pbuf, "%d",
2527                                            RealHostAddr.sin.sin_port);
2528                         break;
2529 #endif /* NETINET */
2530 #if NETINET6
2531                   case AF_INET6:
2532                         (void) sm_snprintf(pbuf, sizeof pbuf, "%d",
2533                                            RealHostAddr.sin6.sin6_port);
2534                         break;
2535 #endif /* NETINET6 */
2536                   default:
2537                         (void) sm_snprintf(pbuf, sizeof pbuf, "0");
2538                         break;
2539                 }
2540                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2541                         macid("{client_port}"), pbuf);
2542
2543                 if (OpMode == MD_DAEMON)
2544                 {
2545                         /* validate the connection */
2546                         HoldErrs = true;
2547                         nullserver = validate_connection(&RealHostAddr,
2548                                                          RealHostName,
2549                                                          &MainEnvelope);
2550                         HoldErrs = false;
2551                 }
2552                 else if (p_flags == NULL)
2553                 {
2554                         p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
2555                         clrbitmap(p_flags);
2556                 }
2557 #if STARTTLS
2558                 if (OpMode == MD_SMTP)
2559                         (void) initsrvtls(tls_ok);
2560 #endif /* STARTTLS */
2561
2562                 /* turn off profiling */
2563                 SM_PROF(1);
2564                 smtp(nullserver, *p_flags, &MainEnvelope);
2565
2566                 if (tTd(93, 100))
2567                 {
2568                         /* turn off profiling */
2569                         SM_PROF(0);
2570                         if (OpMode == MD_DAEMON)
2571                                 goto nextreq;
2572                 }
2573         }
2574
2575         sm_rpool_free(MainEnvelope.e_rpool);
2576         clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
2577         if (OpMode == MD_VERIFY)
2578         {
2579                 set_delivery_mode(SM_VERIFY, &MainEnvelope);
2580                 PostMasterCopy = NULL;
2581         }
2582         else
2583         {
2584                 /* interactive -- all errors are global */
2585                 MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
2586         }
2587
2588         /*
2589         **  Do basic system initialization and set the sender
2590         */
2591
2592         initsys(&MainEnvelope);
2593         macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
2594         macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
2595         setsender(from, &MainEnvelope, NULL, '\0', false);
2596         if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
2597             (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
2598              strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
2599         {
2600                 auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
2601                              RealUserName, from, warn_f_flag);
2602 #if SASL
2603                 auth = false;
2604 #endif /* SASL */
2605         }
2606         if (auth)
2607         {
2608                 char *fv;
2609
2610                 /* set the initial sender for AUTH= to $f@$j */
2611                 fv = macvalue('f', &MainEnvelope);
2612                 if (fv == NULL || *fv == '\0')
2613                         MainEnvelope.e_auth_param = NULL;
2614                 else
2615                 {
2616                         if (strchr(fv, '@') == NULL)
2617                         {
2618                                 i = strlen(fv) + strlen(macvalue('j',
2619                                                         &MainEnvelope)) + 2;
2620                                 p = sm_malloc_x(i);
2621                                 (void) sm_strlcpyn(p, i, 3, fv, "@",
2622                                                    macvalue('j',
2623                                                             &MainEnvelope));
2624                         }
2625                         else
2626                                 p = sm_strdup_x(fv);
2627                         MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
2628                                                                       xtextify(p, "="));
2629                         sm_free(p);  /* XXX */
2630                 }
2631         }
2632         if (macvalue('s', &MainEnvelope) == NULL)
2633                 macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
2634
2635         av = argv + optind;
2636         if (*av == NULL && !GrabTo)
2637         {
2638                 MainEnvelope.e_to = NULL;
2639                 MainEnvelope.e_flags |= EF_GLOBALERRS;
2640                 HoldErrs = false;
2641                 SuperSafe = SAFE_NO;
2642                 usrerr("Recipient names must be specified");
2643
2644                 /* collect body for UUCP return */
2645                 if (OpMode != MD_VERIFY)
2646                         collect(InChannel, false, NULL, &MainEnvelope);
2647                 finis(true, true, EX_USAGE);
2648                 /* NOTREACHED */
2649         }
2650
2651         /*
2652         **  Scan argv and deliver the message to everyone.
2653         */
2654
2655         save_val = LogUsrErrs;
2656         LogUsrErrs = true;
2657         sendtoargv(av, &MainEnvelope);
2658         LogUsrErrs = save_val;
2659
2660         /* if we have had errors sofar, arrange a meaningful exit stat */
2661         if (Errors > 0 && ExitStat == EX_OK)
2662                 ExitStat = EX_USAGE;
2663
2664 #if _FFR_FIX_DASHT
2665         /*
2666         **  If using -t, force not sending to argv recipients, even
2667         **  if they are mentioned in the headers.
2668         */
2669
2670         if (GrabTo)
2671         {
2672                 ADDRESS *q;
2673
2674                 for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
2675                         q->q_state = QS_REMOVED;
2676         }
2677 #endif /* _FFR_FIX_DASHT */
2678
2679         /*
2680         **  Read the input mail.
2681         */
2682
2683         MainEnvelope.e_to = NULL;
2684         if (OpMode != MD_VERIFY || GrabTo)
2685         {
2686                 int savederrors;
2687                 unsigned long savedflags;
2688
2689                 /*
2690                 **  workaround for compiler warning on Irix:
2691                 **  do not initialize variable in the definition, but
2692                 **  later on:
2693                 **  warning(1548): transfer of control bypasses
2694                 **  initialization of:
2695                 **  variable "savederrors" (declared at line 2570)
2696                 **  variable "savedflags" (declared at line 2571)
2697                 **  goto giveup;
2698                 */
2699
2700                 savederrors = Errors;
2701                 savedflags = MainEnvelope.e_flags & EF_FATALERRS;
2702                 MainEnvelope.e_flags |= EF_GLOBALERRS;
2703                 MainEnvelope.e_flags &= ~EF_FATALERRS;
2704                 Errors = 0;
2705                 buffer_errors();
2706                 collect(InChannel, false, NULL, &MainEnvelope);
2707
2708                 /* header checks failed */
2709                 if (Errors > 0)
2710                 {
2711   giveup:
2712                         if (!GrabTo)
2713                         {
2714                                 /* Log who the mail would have gone to */
2715                                 logundelrcpts(&MainEnvelope,
2716                                               MainEnvelope.e_message,
2717                                               8, false);
2718                         }
2719                         flush_errors(true);
2720                         finis(true, true, ExitStat);
2721                         /* NOTREACHED */
2722                         return -1;
2723                 }
2724
2725                 /* bail out if message too large */
2726                 if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
2727                 {
2728                         finis(true, true, ExitStat != EX_OK ? ExitStat
2729                                                             : EX_DATAERR);
2730                         /* NOTREACHED */
2731                         return -1;
2732                 }
2733
2734                 /* set message size */
2735                 (void) sm_snprintf(buf, sizeof buf, "%ld",
2736                                    MainEnvelope.e_msgsize);
2737                 macdefine(&MainEnvelope.e_macro, A_TEMP,
2738                           macid("{msg_size}"), buf);
2739
2740                 Errors = savederrors;
2741                 MainEnvelope.e_flags |= savedflags;
2742         }
2743         errno = 0;
2744
2745         if (tTd(1, 1))
2746                 sm_dprintf("From person = \"%s\"\n",
2747                            MainEnvelope.e_from.q_paddr);
2748
2749 #if _FFR_QUARANTINE
2750         /* Check if quarantining stats should be updated */
2751         if (MainEnvelope.e_quarmsg != NULL)
2752                 markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
2753 #endif /* _FFR_QUARANTINE */
2754
2755         /*
2756         **  Actually send everything.
2757         **      If verifying, just ack.
2758         */
2759
2760         if (Errors == 0)
2761         {
2762                 if (!split_by_recipient(&MainEnvelope) &&
2763                     bitset(EF_FATALERRS, MainEnvelope.e_flags))
2764                         goto giveup;
2765         }
2766
2767         /* make sure we deliver at least the first envelope */
2768         i = FastSplit > 0 ? 0 : -1;
2769         for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
2770         {
2771                 ENVELOPE *next;
2772
2773                 e->e_from.q_state = QS_SENDER;
2774                 if (tTd(1, 5))
2775                 {
2776                         sm_dprintf("main[%d]: QS_SENDER ", i);
2777                         printaddr(&e->e_from, false);
2778                 }
2779                 e->e_to = NULL;
2780                 sm_getla();
2781                 GrabTo = false;
2782 #if NAMED_BIND
2783                 _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
2784                 _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2785 #endif /* NAMED_BIND */
2786                 next = e->e_sibling;
2787                 e->e_sibling = NULL;
2788
2789                 /* after FastSplit envelopes: queue up */
2790                 sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
2791                 e->e_sibling = next;
2792         }
2793
2794         /*
2795         **  All done.
2796         **      Don't send return error message if in VERIFY mode.
2797         */
2798
2799         finis(true, true, ExitStat);
2800         /* NOTREACHED */
2801         return ExitStat;
2802 }
2803 /*
2804 **  STOP_SENDMAIL -- Stop the running program
2805 **
2806 **      Parameters:
2807 **              none.
2808 **
2809 **      Returns:
2810 **              none.
2811 **
2812 **      Side Effects:
2813 **              exits.
2814 */
2815
2816 void
2817 stop_sendmail()
2818 {
2819         /* reset uid for process accounting */
2820         endpwent();
2821         (void) setuid(RealUid);
2822         exit(EX_OK);
2823 }
2824 /*
2825 **  FINIS -- Clean up and exit.
2826 **
2827 **      Parameters:
2828 **              drop -- whether or not to drop CurEnv envelope
2829 **              cleanup -- call exit() or _exit()?
2830 **              exitstat -- exit status to use for exit() call
2831 **
2832 **      Returns:
2833 **              never
2834 **
2835 **      Side Effects:
2836 **              exits sendmail
2837 */
2838
2839 void
2840 finis(drop, cleanup, exitstat)
2841         bool drop;
2842         bool cleanup;
2843         volatile int exitstat;
2844 {
2845
2846         /* Still want to process new timeouts added below */
2847         sm_clear_events();
2848         (void) sm_releasesignal(SIGALRM);
2849
2850         if (tTd(2, 1))
2851         {
2852                 sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
2853                            exitstat,
2854                            CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
2855                 printenvflags(CurEnv);
2856         }
2857         if (tTd(2, 9))
2858                 printopenfds(false);
2859
2860         SM_TRY
2861                 /*
2862                 **  Clean up.  This might raise E:mta.quickabort
2863                 */
2864
2865                 /* clean up temp files */
2866                 CurEnv->e_to = NULL;
2867                 if (drop)
2868                 {
2869                         if (CurEnv->e_id != NULL)
2870                         {
2871                                 dropenvelope(CurEnv, true, false);
2872                                 sm_rpool_free(CurEnv->e_rpool);
2873                                 CurEnv->e_rpool = NULL;
2874                         }
2875                         else
2876                                 poststats(StatFile);
2877                 }
2878
2879                 /* flush any cached connections */
2880                 mci_flush(true, NULL);
2881
2882                 /* close maps belonging to this pid */
2883                 closemaps(false);
2884
2885 #if USERDB
2886                 /* close UserDatabase */
2887                 _udbx_close();
2888 #endif /* USERDB */
2889
2890 #if SASL
2891                 stop_sasl_client();
2892 #endif /* SASL */
2893
2894 #if XLA
2895                 /* clean up extended load average stuff */
2896                 xla_all_end();
2897 #endif /* XLA */
2898
2899         SM_FINALLY
2900                 /*
2901                 **  And exit.
2902                 */
2903
2904                 if (LogLevel > 78)
2905                         sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
2906                                   (int) CurrentPid);
2907                 if (exitstat == EX_TEMPFAIL ||
2908                     CurEnv->e_errormode == EM_BERKNET)
2909                         exitstat = EX_OK;
2910
2911                 /* XXX clean up queues and related data structures */
2912                 cleanup_queues();
2913 #if SM_CONF_SHM
2914                 cleanup_shm(DaemonPid == getpid());
2915 #endif /* SM_CONF_SHM */
2916
2917                 /* reset uid for process accounting */
2918                 endpwent();
2919                 sm_mbdb_terminate();
2920                 (void) setuid(RealUid);
2921 #if SM_HEAP_CHECK
2922                 /* dump the heap, if we are checking for memory leaks */
2923                 if (sm_debug_active(&SmHeapCheck, 2))
2924                         sm_heap_report(smioout,
2925                                        sm_debug_level(&SmHeapCheck) - 1);
2926 #endif /* SM_HEAP_CHECK */
2927                 if (sm_debug_active(&SmXtrapReport, 1))
2928                         sm_dprintf("xtrap count = %d\n", SmXtrapCount);
2929                 if (cleanup)
2930                         exit(exitstat);
2931                 else
2932                         _exit(exitstat);
2933         SM_END_TRY
2934 }
2935 /*
2936 **  INTINDEBUG -- signal handler for SIGINT in -bt mode
2937 **
2938 **      Parameters:
2939 **              sig -- incoming signal.
2940 **
2941 **      Returns:
2942 **              none.
2943 **
2944 **      Side Effects:
2945 **              longjmps back to test mode loop.
2946 **
2947 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2948 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2949 **              DOING.
2950 */
2951
2952 /* Type of an exception generated on SIGINT during address test mode.  */
2953 static const SM_EXC_TYPE_T EtypeInterrupt =
2954 {
2955         SmExcTypeMagic,
2956         "S:mta.interrupt",
2957         "",
2958         sm_etype_printf,
2959         "interrupt",
2960 };
2961
2962 /* ARGSUSED */
2963 static SIGFUNC_DECL
2964 intindebug(sig)
2965         int sig;
2966 {
2967         int save_errno = errno;
2968
2969         FIX_SYSV_SIGNAL(sig, intindebug);
2970         errno = save_errno;
2971         CHECK_CRITICAL(sig);
2972         errno = save_errno;
2973         sm_exc_raisenew_x(&EtypeInterrupt);
2974         errno = save_errno;
2975         return SIGFUNC_RETURN;
2976 }
2977 /*
2978 **  SIGTERM -- SIGTERM handler for the daemon
2979 **
2980 **      Parameters:
2981 **              sig -- signal number.
2982 **
2983 **      Returns:
2984 **              none.
2985 **
2986 **      Side Effects:
2987 **              Sets ShutdownRequest which will hopefully trigger
2988 **              the daemon to exit.
2989 **
2990 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2991 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2992 **              DOING.
2993 */
2994
2995 /* ARGSUSED */
2996 static SIGFUNC_DECL
2997 sigterm(sig)
2998         int sig;
2999 {
3000         int save_errno = errno;
3001
3002         FIX_SYSV_SIGNAL(sig, sigterm);
3003         ShutdownRequest = "signal";
3004         errno = save_errno;
3005         return SIGFUNC_RETURN;
3006 }
3007 /*
3008 **  SIGHUP -- handle a SIGHUP signal
3009 **
3010 **      Parameters:
3011 **              sig -- incoming signal.
3012 **
3013 **      Returns:
3014 **              none.
3015 **
3016 **      Side Effects:
3017 **              Sets RestartRequest which should cause the daemon
3018 **              to restart.
3019 **
3020 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3021 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3022 **              DOING.
3023 */
3024
3025 /* ARGSUSED */
3026 static SIGFUNC_DECL
3027 sighup(sig)
3028         int sig;
3029 {
3030         int save_errno = errno;
3031
3032         FIX_SYSV_SIGNAL(sig, sighup);
3033         RestartRequest = "signal";
3034         errno = save_errno;
3035         return SIGFUNC_RETURN;
3036 }
3037 /*
3038 **  SIGPIPE -- signal handler for SIGPIPE
3039 **
3040 **      Parameters:
3041 **              sig -- incoming signal.
3042 **
3043 **      Returns:
3044 **              none.
3045 **
3046 **      Side Effects:
3047 **              Sets StopRequest which should cause the mailq/hoststatus
3048 **              display to stop.
3049 **
3050 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3051 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3052 **              DOING.
3053 */
3054
3055 /* ARGSUSED */
3056 static SIGFUNC_DECL
3057 sigpipe(sig)
3058         int sig;
3059 {
3060         int save_errno = errno;
3061
3062         FIX_SYSV_SIGNAL(sig, sigpipe);
3063         StopRequest = true;
3064         errno = save_errno;
3065         return SIGFUNC_RETURN;
3066 }
3067 /*
3068 **  INTSIG -- clean up on interrupt
3069 **
3070 **      This just arranges to exit.  It pessimizes in that it
3071 **      may resend a message.
3072 **
3073 **      Parameters:
3074 **              none.
3075 **
3076 **      Returns:
3077 **              none.
3078 **
3079 **      Side Effects:
3080 **              Unlocks the current job.
3081 **
3082 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3083 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3084 **              DOING.
3085 **
3086 **              XXX: More work is needed for this signal handler.
3087 */
3088
3089 /* ARGSUSED */
3090 SIGFUNC_DECL
3091 intsig(sig)
3092         int sig;
3093 {
3094         bool drop = false;
3095         int save_errno = errno;
3096
3097         FIX_SYSV_SIGNAL(sig, intsig);
3098         errno = save_errno;
3099         CHECK_CRITICAL(sig);
3100         sm_allsignals(true);
3101
3102         if (sig != 0 && LogLevel > 79)
3103                 sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
3104         FileName = NULL;
3105
3106         /* Clean-up on aborted stdin message submission */
3107         if (CurEnv->e_id != NULL &&
3108             (OpMode == MD_SMTP ||
3109              OpMode == MD_DELIVER ||
3110              OpMode == MD_ARPAFTP))
3111         {
3112                 register ADDRESS *q;
3113
3114                 /* don't return an error indication */
3115                 CurEnv->e_to = NULL;
3116                 CurEnv->e_flags &= ~EF_FATALERRS;
3117                 CurEnv->e_flags |= EF_CLRQUEUE;
3118
3119                 /*
3120                 **  Spin through the addresses and
3121                 **  mark them dead to prevent bounces
3122                 */
3123
3124                 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
3125                         q->q_state = QS_DONTSEND;
3126
3127                 drop = true;
3128         }
3129         else if (OpMode != MD_TEST)
3130         {
3131                 unlockqueue(CurEnv);
3132         }
3133
3134         finis(drop, false, EX_OK);
3135         /* NOTREACHED */
3136 }
3137 /*
3138 **  DISCONNECT -- remove our connection with any foreground process
3139 **
3140 **      Parameters:
3141 **              droplev -- how "deeply" we should drop the line.
3142 **                      0 -- ignore signals, mail back errors, make sure
3143 **                           output goes to stdout.
3144 **                      1 -- also, make stdout go to /dev/null.
3145 **                      2 -- also, disconnect from controlling terminal
3146 **                           (only for daemon mode).
3147 **              e -- the current envelope.
3148 **
3149 **      Returns:
3150 **              none
3151 **
3152 **      Side Effects:
3153 **              Trys to insure that we are immune to vagaries of
3154 **              the controlling tty.
3155 */
3156
3157 void
3158 disconnect(droplev, e)
3159         int droplev;
3160         register ENVELOPE *e;
3161 {
3162         int fd;
3163
3164         if (tTd(52, 1))
3165                 sm_dprintf("disconnect: In %d Out %d, e=%p\n",
3166                            sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
3167                            sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
3168         if (tTd(52, 100))
3169         {
3170                 sm_dprintf("don't\n");
3171                 return;
3172         }
3173         if (LogLevel > 93)
3174                 sm_syslog(LOG_DEBUG, e->e_id,
3175                           "disconnect level %d",
3176                           droplev);
3177
3178         /* be sure we don't get nasty signals */
3179         (void) sm_signal(SIGINT, SIG_IGN);
3180         (void) sm_signal(SIGQUIT, SIG_IGN);
3181
3182         /* we can't communicate with our caller, so.... */
3183         HoldErrs = true;
3184         CurEnv->e_errormode = EM_MAIL;
3185         Verbose = 0;
3186         DisConnected = true;
3187
3188         /* all input from /dev/null */
3189         if (InChannel != smioin)
3190         {
3191                 (void) sm_io_close(InChannel, SM_TIME_DEFAULT);
3192                 InChannel = smioin;
3193         }
3194         if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3195                          SM_IO_RDONLY, NULL, smioin) == NULL)
3196                 sm_syslog(LOG_ERR, e->e_id,
3197                           "disconnect: sm_io_reopen(\"%s\") failed: %s",
3198                           SM_PATH_DEVNULL, sm_errstring(errno));
3199
3200         /*
3201         **  output to the transcript
3202         **      We also compare the fd numbers here since OutChannel
3203         **      might be a layer on top of smioout due to encryption
3204         **      (see sfsasl.c).
3205         */
3206
3207         if (OutChannel != smioout &&
3208             sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
3209             sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
3210         {
3211                 (void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
3212                 OutChannel = smioout;
3213
3214 #if 0
3215                 /*
3216                 **  Has smioout been closed? Reopen it.
3217                 **      This shouldn't happen anymore, the code is here
3218                 **      just as a reminder.
3219                 */
3220
3221                 if (smioout->sm_magic == NULL &&
3222                     sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3223                                  SM_IO_WRONLY, NULL, smioout) == NULL)
3224                         sm_syslog(LOG_ERR, e->e_id,
3225                                   "disconnect: sm_io_reopen(\"%s\") failed: %s",
3226                                   SM_PATH_DEVNULL, sm_errstring(errno));
3227 #endif /* 0 */
3228         }
3229         if (droplev > 0)
3230         {
3231                 fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
3232                 if (fd == -1)
3233                         sm_syslog(LOG_ERR, e->e_id,
3234                                   "disconnect: open(\"%s\") failed: %s",
3235                                   SM_PATH_DEVNULL, sm_errstring(errno));
3236                 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
3237                 (void) dup2(fd, STDOUT_FILENO);
3238                 (void) dup2(fd, STDERR_FILENO);
3239                 (void) close(fd);
3240         }
3241
3242         /* drop our controlling TTY completely if possible */
3243         if (droplev > 1)
3244         {
3245                 (void) setsid();
3246                 errno = 0;
3247         }
3248
3249 #if XDEBUG
3250         checkfd012("disconnect");
3251 #endif /* XDEBUG */
3252
3253         if (LogLevel > 71)
3254                 sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
3255                           (int) CurrentPid);
3256
3257         errno = 0;
3258 }
3259
3260 static void
3261 obsolete(argv)
3262         char *argv[];
3263 {
3264         register char *ap;
3265         register char *op;
3266
3267         while ((ap = *++argv) != NULL)
3268         {
3269                 /* Return if "--" or not an option of any form. */
3270                 if (ap[0] != '-' || ap[1] == '-')
3271                         return;
3272
3273 #if _FFR_QUARANTINE
3274                 /* Don't allow users to use "-Q." or "-Q ." */
3275                 if ((ap[1] == 'Q' && ap[2] == '.') ||
3276                     (ap[1] == 'Q' && argv[1] != NULL &&
3277                      argv[1][0] == '.' && argv[1][1] == '\0'))
3278                 {
3279                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3280                                              "Can not use -Q.\n");
3281                         exit(EX_USAGE);
3282                 }
3283 #endif /* _FFR_QUARANTINE */
3284
3285                 /* skip over options that do have a value */
3286                 op = strchr(OPTIONS, ap[1]);
3287                 if (op != NULL && *++op == ':' && ap[2] == '\0' &&
3288                     ap[1] != 'd' &&
3289 #if defined(sony_news)
3290                     ap[1] != 'E' && ap[1] != 'J' &&
3291 #endif /* defined(sony_news) */
3292                     argv[1] != NULL && argv[1][0] != '-')
3293                 {
3294                         argv++;
3295                         continue;
3296                 }
3297
3298                 /* If -C doesn't have an argument, use sendmail.cf. */
3299 #define __DEFPATH       "sendmail.cf"
3300                 if (ap[1] == 'C' && ap[2] == '\0')
3301                 {
3302                         *argv = xalloc(sizeof(__DEFPATH) + 2);
3303                         (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
3304                                            "-C", __DEFPATH);
3305                 }
3306
3307                 /* If -q doesn't have an argument, run it once. */
3308                 if (ap[1] == 'q' && ap[2] == '\0')
3309                         *argv = "-q0";
3310
3311 #if _FFR_QUARANTINE
3312                 /* If -Q doesn't have an argument, disable quarantining */
3313                 if (ap[1] == 'Q' && ap[2] == '\0')
3314                         *argv = "-Q.";
3315 #endif /* _FFR_QUARANTINE */
3316
3317                 /* if -d doesn't have an argument, use 0-99.1 */
3318                 if (ap[1] == 'd' && ap[2] == '\0')
3319                         *argv = "-d0-99.1";
3320
3321 #if defined(sony_news)
3322                 /* if -E doesn't have an argument, use -EC */
3323                 if (ap[1] == 'E' && ap[2] == '\0')
3324                         *argv = "-EC";
3325
3326                 /* if -J doesn't have an argument, use -JJ */
3327                 if (ap[1] == 'J' && ap[2] == '\0')
3328                         *argv = "-JJ";
3329 #endif /* defined(sony_news) */
3330         }
3331 }
3332 /*
3333 **  AUTH_WARNING -- specify authorization warning
3334 **
3335 **      Parameters:
3336 **              e -- the current envelope.
3337 **              msg -- the text of the message.
3338 **              args -- arguments to the message.
3339 **
3340 **      Returns:
3341 **              none.
3342 */
3343
3344 void
3345 #ifdef __STDC__
3346 auth_warning(register ENVELOPE *e, const char *msg, ...)
3347 #else /* __STDC__ */
3348 auth_warning(e, msg, va_alist)
3349         register ENVELOPE *e;
3350         const char *msg;
3351         va_dcl
3352 #endif /* __STDC__ */
3353 {
3354         char buf[MAXLINE];
3355         SM_VA_LOCAL_DECL
3356
3357         if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
3358         {
3359                 register char *p;
3360                 static char hostbuf[48];
3361
3362                 if (hostbuf[0] == '\0')
3363                 {
3364                         struct hostent *hp;
3365
3366                         hp = myhostname(hostbuf, sizeof hostbuf);
3367 #if NETINET6
3368                         if (hp != NULL)
3369                         {
3370                                 freehostent(hp);
3371                                 hp = NULL;
3372                         }
3373 #endif /* NETINET6 */
3374                 }
3375
3376                 (void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
3377                 p = &buf[strlen(buf)];
3378                 SM_VA_START(ap, msg);
3379                 (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
3380                 SM_VA_END(ap);
3381                 addheader("X-Authentication-Warning", buf, 0, e);
3382                 if (LogLevel > 3)
3383                         sm_syslog(LOG_INFO, e->e_id,
3384                                   "Authentication-Warning: %.400s",
3385                                   buf);
3386         }
3387 }
3388 /*
3389 **  GETEXTENV -- get from external environment
3390 **
3391 **      Parameters:
3392 **              envar -- the name of the variable to retrieve
3393 **
3394 **      Returns:
3395 **              The value, if any.
3396 */
3397
3398 static char *
3399 getextenv(envar)
3400         const char *envar;
3401 {
3402         char **envp;
3403         int l;
3404
3405         l = strlen(envar);
3406         for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
3407         {
3408                 if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
3409                         return &(*envp)[l + 1];
3410         }
3411         return NULL;
3412 }
3413 /*
3414 **  SETUSERENV -- set an environment in the propagated environment
3415 **
3416 **      Parameters:
3417 **              envar -- the name of the environment variable.
3418 **              value -- the value to which it should be set.  If
3419 **                      null, this is extracted from the incoming
3420 **                      environment.  If that is not set, the call
3421 **                      to setuserenv is ignored.
3422 **
3423 **      Returns:
3424 **              none.
3425 */
3426
3427 void
3428 setuserenv(envar, value)
3429         const char *envar;
3430         const char *value;
3431 {
3432         int i, l;
3433         char **evp = UserEnviron;
3434         char *p;
3435
3436         if (value == NULL)
3437         {
3438                 value = getextenv(envar);
3439                 if (value == NULL)
3440                         return;
3441         }
3442
3443         /* XXX enforce reasonable size? */
3444         i = strlen(envar) + 1;
3445         l = strlen(value) + i + 1;
3446         p = (char *) xalloc(l);
3447         (void) sm_strlcpyn(p, l, 3, envar, "=", value);
3448
3449         while (*evp != NULL && strncmp(*evp, p, i) != 0)
3450                 evp++;
3451         if (*evp != NULL)
3452         {
3453                 *evp++ = p;
3454         }
3455         else if (evp < &UserEnviron[MAXUSERENVIRON])
3456         {
3457                 *evp++ = p;
3458                 *evp = NULL;
3459         }
3460
3461         /* make sure it is in our environment as well */
3462         if (putenv(p) < 0)
3463                 syserr("setuserenv: putenv(%s) failed", p);
3464 }
3465 /*
3466 **  DUMPSTATE -- dump state
3467 **
3468 **      For debugging.
3469 */
3470
3471 void
3472 dumpstate(when)
3473         char *when;
3474 {
3475         register char *j = macvalue('j', CurEnv);
3476         int rs;
3477         extern int NextMacroId;
3478
3479         sm_syslog(LOG_DEBUG, CurEnv->e_id,
3480                   "--- dumping state on %s: $j = %s ---",
3481                   when,
3482                   j == NULL ? "<NULL>" : j);
3483         if (j != NULL)
3484         {
3485                 if (!wordinclass(j, 'w'))
3486                         sm_syslog(LOG_DEBUG, CurEnv->e_id,
3487                                   "*** $j not in $=w ***");
3488         }
3489         sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
3490         sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
3491                   NextMacroId, MAXMACROID);
3492         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
3493         printopenfds(true);
3494         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3495         mci_dump_all(true);
3496         rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
3497         if (rs > 0)
3498         {
3499                 int status;
3500                 register char **pvp;
3501                 char *pv[MAXATOM + 1];
3502
3503                 pv[0] = NULL;
3504                 status = REWRITE(pv, rs, CurEnv);
3505                 sm_syslog(LOG_DEBUG, CurEnv->e_id,
3506                           "--- ruleset debug_dumpstate returns stat %d, pv: ---",
3507                           status);
3508                 for (pvp = pv; *pvp != NULL; pvp++)
3509                         sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
3510         }
3511         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
3512 }
3513
3514 #ifdef SIGUSR1
3515 /*
3516 **  SIGUSR1 -- Signal a request to dump state.
3517 **
3518 **      Parameters:
3519 **              sig -- calling signal.
3520 **
3521 **      Returns:
3522 **              none.
3523 **
3524 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3525 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3526 **              DOING.
3527 **
3528 **              XXX: More work is needed for this signal handler.
3529 */
3530
3531 /* ARGSUSED */
3532 static SIGFUNC_DECL
3533 sigusr1(sig)
3534         int sig;
3535 {
3536         int save_errno = errno;
3537 # if SM_HEAP_CHECK
3538         extern void dumpstab __P((void));
3539 # endif /* SM_HEAP_CHECK */
3540
3541         FIX_SYSV_SIGNAL(sig, sigusr1);
3542         errno = save_errno;
3543         CHECK_CRITICAL(sig);
3544         dumpstate("user signal");
3545 # if SM_HEAP_CHECK
3546         dumpstab();
3547 # endif /* SM_HEAP_CHECK */
3548         errno = save_errno;
3549         return SIGFUNC_RETURN;
3550 }
3551 #endif /* SIGUSR1 */
3552
3553 /*
3554 **  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
3555 **
3556 **      Parameters:
3557 **              to_real_uid -- if set, drop to the real uid instead
3558 **                      of the RunAsUser.
3559 **
3560 **      Returns:
3561 **              EX_OSERR if the setuid failed.
3562 **              EX_OK otherwise.
3563 */
3564
3565 int
3566 drop_privileges(to_real_uid)
3567         bool to_real_uid;
3568 {
3569         int rval = EX_OK;
3570         GIDSET_T emptygidset[1];
3571
3572         if (tTd(47, 1))
3573                 sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
3574                            (int) to_real_uid,
3575                            (int) RealUid, (int) RealGid,
3576                            (int) getuid(), (int) getgid(),
3577                            (int) geteuid(), (int) getegid(),
3578                            (int) RunAsUid, (int) RunAsGid);
3579
3580         if (to_real_uid)
3581         {
3582                 RunAsUserName = RealUserName;
3583                 RunAsUid = RealUid;
3584                 RunAsGid = RealGid;
3585                 EffGid = RunAsGid;
3586         }
3587
3588         /* make sure no one can grab open descriptors for secret files */
3589         endpwent();
3590         sm_mbdb_terminate();
3591
3592         /* reset group permissions; these can be set later */
3593         emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
3594
3595         /*
3596         **  Notice:  on some OS (Linux...) the setgroups() call causes
3597         **      a logfile entry if sendmail is not run by root.
3598         **      However, it is unclear (no POSIX standard) whether
3599         **      setgroups() can only succeed if executed by root.
3600         **      So for now we keep it as it is; if you want to change it, use
3601         **  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
3602         */
3603
3604         if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
3605         {
3606                 syserr("drop_privileges: setgroups(1, %d) failed",
3607                        (int) emptygidset[0]);
3608                 rval = EX_OSERR;
3609         }
3610
3611         /* reset primary group id */
3612         if (to_real_uid)
3613         {
3614                 /*
3615                 **  Drop gid to real gid.
3616                 **  On some OS we must reset the effective[/real[/saved]] gid,
3617                 **  and then use setgid() to finally drop all group privileges.
3618                 **  Later on we check whether we can get back the
3619                 **  effective gid.
3620                 */
3621
3622 #if HASSETEGID
3623                 if (setegid(RunAsGid) < 0)
3624                 {
3625                         syserr("drop_privileges: setegid(%d) failed",
3626                                (int) RunAsGid);
3627                         rval = EX_OSERR;
3628                 }
3629 #else /* HASSETEGID */
3630 # if HASSETREGID
3631                 if (setregid(RunAsGid, RunAsGid) < 0)
3632                 {
3633                         syserr("drop_privileges: setregid(%d, %d) failed",
3634                                (int) RunAsGid, (int) RunAsGid);
3635                         rval = EX_OSERR;
3636                 }
3637 # else /* HASSETREGID */
3638 #  if HASSETRESGID
3639                 if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
3640                 {
3641                         syserr("drop_privileges: setresgid(%d, %d, %d) failed",
3642                                (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
3643                         rval = EX_OSERR;
3644                 }
3645 #  endif /* HASSETRESGID */
3646 # endif /* HASSETREGID */
3647 #endif /* HASSETEGID */
3648         }
3649         if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
3650         {
3651                 if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
3652                 {
3653                         syserr("drop_privileges: setgid(%d) failed",
3654                                (int) RunAsGid);
3655                         rval = EX_OSERR;
3656                 }
3657                 errno = 0;
3658                 if (rval == EX_OK && getegid() != RunAsGid)
3659                 {
3660                         syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
3661                                (int) getegid(), (int) RunAsGid);
3662                         rval = EX_OSERR;
3663                 }
3664         }
3665
3666         /* fiddle with uid */
3667         if (to_real_uid || RunAsUid != 0)
3668         {
3669                 uid_t euid;
3670
3671                 /*
3672                 **  Try to setuid(RunAsUid).
3673                 **  euid must be RunAsUid,
3674                 **  ruid must be RunAsUid unless (e|r)uid wasn't 0
3675                 **      and we didn't have to drop privileges to the real uid.
3676                 */
3677
3678                 if (setuid(RunAsUid) < 0 ||
3679                     geteuid() != RunAsUid ||
3680                     (getuid() != RunAsUid &&
3681                      (to_real_uid || geteuid() == 0 || getuid() == 0)))
3682                 {
3683 #if HASSETREUID
3684                         /*
3685                         **  if ruid != RunAsUid, euid == RunAsUid, then
3686                         **  try resetting just the real uid, then using
3687                         **  setuid() to drop the saved-uid as well.
3688                         */
3689
3690                         if (geteuid() == RunAsUid)
3691                         {
3692                                 if (setreuid(RunAsUid, -1) < 0)
3693                                 {
3694                                         syserr("drop_privileges: setreuid(%d, -1) failed",
3695                                                (int) RunAsUid);
3696                                         rval = EX_OSERR;
3697                                 }
3698                                 if (setuid(RunAsUid) < 0)
3699                                 {
3700                                         syserr("drop_privileges: second setuid(%d) attempt failed",
3701                                                (int) RunAsUid);
3702                                         rval = EX_OSERR;
3703                                 }
3704                         }
3705                         else
3706 #endif /* HASSETREUID */
3707                         {
3708                                 syserr("drop_privileges: setuid(%d) failed",
3709                                        (int) RunAsUid);
3710                                 rval = EX_OSERR;
3711                         }
3712                 }
3713                 euid = geteuid();
3714                 if (RunAsUid != 0 && setuid(0) == 0)
3715                 {
3716                         /*
3717                         **  Believe it or not, the Linux capability model
3718                         **  allows a non-root process to override setuid()
3719                         **  on a process running as root and prevent that
3720                         **  process from dropping privileges.
3721                         */
3722
3723                         syserr("drop_privileges: setuid(0) succeeded (when it should not)");
3724                         rval = EX_OSERR;
3725                 }
3726                 else if (RunAsUid != euid && setuid(euid) == 0)
3727                 {
3728                         /*
3729                         **  Some operating systems will keep the saved-uid
3730                         **  if a non-root effective-uid calls setuid(real-uid)
3731                         **  making it possible to set it back again later.
3732                         */
3733
3734                         syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
3735                         rval = EX_OSERR;
3736                 }
3737         }
3738
3739         if ((to_real_uid || RunAsGid != 0) &&
3740             rval == EX_OK && RunAsGid != EffGid &&
3741             getuid() != 0 && geteuid() != 0)
3742         {
3743                 errno = 0;
3744                 if (setgid(EffGid) == 0)
3745                 {
3746                         syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
3747                                (int) EffGid);
3748                         rval = EX_OSERR;
3749                 }
3750         }
3751
3752         if (tTd(47, 5))
3753         {
3754                 sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
3755                            (int) geteuid(), (int) getuid(),
3756                            (int) getegid(), (int) getgid());
3757                 sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
3758                            (int) RunAsUid, (int) RunAsGid);
3759                 if (tTd(47, 10))
3760                         sm_dprintf("drop_privileges: rval = %d\n", rval);
3761         }
3762         return rval;
3763 }
3764 /*
3765 **  FILL_FD -- make sure a file descriptor has been properly allocated
3766 **
3767 **      Used to make sure that stdin/out/err are allocated on startup
3768 **
3769 **      Parameters:
3770 **              fd -- the file descriptor to be filled.
3771 **              where -- a string used for logging.  If NULL, this is
3772 **                      being called on startup, and logging should
3773 **                      not be done.
3774 **
3775 **      Returns:
3776 **              none
3777 **
3778 **      Side Effects:
3779 **              possibly changes MissingFds
3780 */
3781
3782 void
3783 fill_fd(fd, where)
3784         int fd;
3785         char *where;
3786 {
3787         int i;
3788         struct stat stbuf;
3789
3790         if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
3791                 return;
3792
3793         if (where != NULL)
3794                 syserr("fill_fd: %s: fd %d not open", where, fd);
3795         else
3796                 MissingFds |= 1 << fd;
3797         i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
3798         if (i < 0)
3799         {
3800                 syserr("!fill_fd: %s: cannot open %s",
3801                        where == NULL ? "startup" : where, SM_PATH_DEVNULL);
3802         }
3803         if (fd != i)
3804         {
3805                 (void) dup2(i, fd);
3806                 (void) close(i);
3807         }
3808 }
3809 /*
3810 **  SM_PRINTOPTIONS -- print options
3811 **
3812 **      Parameters:
3813 **              options -- array of options.
3814 **
3815 **      Returns:
3816 **              none.
3817 */
3818
3819 static void
3820 sm_printoptions(options)
3821         char **options;
3822 {
3823         int ll;
3824         char **av;
3825
3826         av = options;
3827         ll = 7;
3828         while (*av != NULL)
3829         {
3830                 if (ll + strlen(*av) > 63)
3831                 {
3832                         sm_dprintf("\n");
3833                         ll = 0;
3834                 }
3835                 if (ll == 0)
3836                         sm_dprintf("\t\t");
3837                 else
3838                         sm_dprintf(" ");
3839                 sm_dprintf("%s", *av);
3840                 ll += strlen(*av++) + 1;
3841         }
3842         sm_dprintf("\n");
3843 }
3844 /*
3845 **  TESTMODELINE -- process a test mode input line
3846 **
3847 **      Parameters:
3848 **              line -- the input line.
3849 **              e -- the current environment.
3850 **      Syntax:
3851 **              #  a comment
3852 **              .X process X as a configuration line
3853 **              =X dump a configuration item (such as mailers)
3854 **              $X dump a macro or class
3855 **              /X try an activity
3856 **              X  normal process through rule set X
3857 */
3858
3859 static void
3860 testmodeline(line, e)
3861         char *line;
3862         ENVELOPE *e;
3863 {
3864         register char *p;
3865         char *q;
3866         auto char *delimptr;
3867         int mid;
3868         int i, rs;
3869         STAB *map;
3870         char **s;
3871         struct rewrite *rw;
3872         ADDRESS a;
3873         static int tryflags = RF_COPYNONE;
3874         char exbuf[MAXLINE];
3875         extern unsigned char TokTypeNoC[];
3876
3877         /* skip leading spaces */
3878         while (*line == ' ')
3879                 line++;
3880
3881         switch (line[0])
3882         {
3883           case '#':
3884           case '\0':
3885                 return;
3886
3887           case '?':
3888                 help("-bt", e);
3889                 return;
3890
3891           case '.':             /* config-style settings */
3892                 switch (line[1])
3893                 {
3894                   case 'D':
3895                         mid = macid_parse(&line[2], &delimptr);
3896                         if (mid == 0)
3897                                 return;
3898                         translate_dollars(delimptr);
3899                         macdefine(&e->e_macro, A_TEMP, mid, delimptr);
3900                         break;
3901
3902                   case 'C':
3903                         if (line[2] == '\0')    /* not to call syserr() */
3904                                 return;
3905
3906                         mid = macid_parse(&line[2], &delimptr);
3907                         if (mid == 0)
3908                                 return;
3909                         translate_dollars(delimptr);
3910                         expand(delimptr, exbuf, sizeof exbuf, e);
3911                         p = exbuf;
3912                         while (*p != '\0')
3913                         {
3914                                 register char *wd;
3915                                 char delim;
3916
3917                                 while (*p != '\0' && isascii(*p) && isspace(*p))
3918                                         p++;
3919                                 wd = p;
3920                                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3921                                         p++;
3922                                 delim = *p;
3923                                 *p = '\0';
3924                                 if (wd[0] != '\0')
3925                                         setclass(mid, wd);
3926                                 *p = delim;
3927                         }
3928                         break;
3929
3930                   case '\0':
3931                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3932                                              "Usage: .[DC]macro value(s)\n");
3933                         break;
3934
3935                   default:
3936                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3937                                              "Unknown \".\" command %s\n", line);
3938                         break;
3939                 }
3940                 return;
3941
3942           case '=':             /* config-style settings */
3943                 switch (line[1])
3944                 {
3945                   case 'S':             /* dump rule set */
3946                         rs = strtorwset(&line[2], NULL, ST_FIND);
3947                         if (rs < 0)
3948                         {
3949                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3950                                                      "Undefined ruleset %s\n", &line[2]);
3951                                 return;
3952                         }
3953                         rw = RewriteRules[rs];
3954                         if (rw == NULL)
3955                                 return;
3956                         do
3957                         {
3958                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3959                                                   'R');
3960                                 s = rw->r_lhs;
3961                                 while (*s != NULL)
3962                                 {
3963                                         xputs(*s++);
3964                                         (void) sm_io_putc(smioout,
3965                                                           SM_TIME_DEFAULT, ' ');
3966                                 }
3967                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3968                                                   '\t');
3969                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3970                                                   '\t');
3971                                 s = rw->r_rhs;
3972                                 while (*s != NULL)
3973                                 {
3974                                         xputs(*s++);
3975                                         (void) sm_io_putc(smioout,
3976                                                           SM_TIME_DEFAULT, ' ');
3977                                 }
3978                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3979                                                   '\n');
3980                         } while ((rw = rw->r_next) != NULL);
3981                         break;
3982
3983                   case 'M':
3984                         for (i = 0; i < MAXMAILERS; i++)
3985                         {
3986                                 if (Mailer[i] != NULL)
3987                                         printmailer(Mailer[i]);
3988                         }
3989                         break;
3990
3991                   case '\0':
3992                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3993                                              "Usage: =Sruleset or =M\n");
3994                         break;
3995
3996                   default:
3997                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3998                                              "Unknown \"=\" command %s\n", line);
3999                         break;
4000                 }
4001                 return;
4002
4003           case '-':             /* set command-line-like opts */
4004                 switch (line[1])
4005                 {
4006                   case 'd':
4007                         tTflag(&line[2]);
4008                         break;
4009
4010                   case '\0':
4011                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4012                                              "Usage: -d{debug arguments}\n");
4013                         break;
4014
4015                   default:
4016                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4017                                              "Unknown \"-\" command %s\n", line);
4018                         break;
4019                 }
4020                 return;
4021
4022           case '$':
4023                 if (line[1] == '=')
4024                 {
4025                         mid = macid(&line[2]);
4026                         if (mid != 0)
4027                                 stabapply(dump_class, mid);
4028                         return;
4029                 }
4030                 mid = macid(&line[1]);
4031                 if (mid == 0)
4032                         return;
4033                 p = macvalue(mid, e);
4034                 if (p == NULL)
4035                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4036                                              "Undefined\n");
4037                 else
4038                 {
4039                         xputs(p);
4040                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4041                                              "\n");
4042                 }
4043                 return;
4044
4045           case '/':             /* miscellaneous commands */
4046                 p = &line[strlen(line)];
4047                 while (--p >= line && isascii(*p) && isspace(*p))
4048                         *p = '\0';
4049                 p = strpbrk(line, " \t");
4050                 if (p != NULL)
4051                 {
4052                         while (isascii(*p) && isspace(*p))
4053                                 *p++ = '\0';
4054                 }
4055                 else
4056                         p = "";
4057                 if (line[1] == '\0')
4058                 {
4059                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4060                                              "Usage: /[canon|map|mx|parse|try|tryflags]\n");
4061                         return;
4062                 }
4063                 if (sm_strcasecmp(&line[1], "quit") == 0)
4064                 {
4065                         CurEnv->e_id = NULL;
4066                         finis(true, true, ExitStat);
4067                         /* NOTREACHED */
4068                 }
4069                 if (sm_strcasecmp(&line[1], "mx") == 0)
4070                 {
4071 #if NAMED_BIND
4072                         /* look up MX records */
4073                         int nmx;
4074                         auto int rcode;
4075                         char *mxhosts[MAXMXHOSTS + 1];
4076
4077                         if (*p == '\0')
4078                         {
4079                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4080                                                      "Usage: /mx address\n");
4081                                 return;
4082                         }
4083                         nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
4084                                       NULL);
4085                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4086                                              "getmxrr(%s) returns %d value(s):\n",
4087                                 p, nmx);
4088                         for (i = 0; i < nmx; i++)
4089                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4090                                                      "\t%s\n", mxhosts[i]);
4091 #else /* NAMED_BIND */
4092                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4093                                              "No MX code compiled in\n");
4094 #endif /* NAMED_BIND */
4095                 }
4096                 else if (sm_strcasecmp(&line[1], "canon") == 0)
4097                 {
4098                         char host[MAXHOSTNAMELEN];
4099
4100                         if (*p == '\0')
4101                         {
4102                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4103                                                      "Usage: /canon address\n");
4104                                 return;
4105                         }
4106                         else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
4107                         {
4108                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4109                                                      "Name too long\n");
4110                                 return;
4111                         }
4112                         (void) getcanonname(host, sizeof host, !HasWildcardMX,
4113                                             NULL);
4114                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4115                                              "getcanonname(%s) returns %s\n",
4116                                              p, host);
4117                 }
4118                 else if (sm_strcasecmp(&line[1], "map") == 0)
4119                 {
4120                         auto int rcode = EX_OK;
4121                         char *av[2];
4122
4123                         if (*p == '\0')
4124                         {
4125                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4126                                                      "Usage: /map mapname key\n");
4127                                 return;
4128                         }
4129                         for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));                             q++)
4130                                 continue;
4131                         if (*q == '\0')
4132                         {
4133                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4134                                                      "No key specified\n");
4135                                 return;
4136                         }
4137                         *q++ = '\0';
4138                         map = stab(p, ST_MAP, ST_FIND);
4139                         if (map == NULL)
4140                         {
4141                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4142                                                      "Map named \"%s\" not found\n", p);
4143                                 return;
4144                         }
4145                         if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
4146                             !openmap(&(map->s_map)))
4147                         {
4148                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4149                                                      "Map named \"%s\" not open\n", p);
4150                                 return;
4151                         }
4152                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4153                                              "map_lookup: %s (%s) ", p, q);
4154                         av[0] = q;
4155                         av[1] = NULL;
4156                         p = (*map->s_map.map_class->map_lookup)
4157                                         (&map->s_map, q, av, &rcode);
4158                         if (p == NULL)
4159                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4160                                                      "no match (%d)\n",
4161                                                      rcode);
4162                         else
4163                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4164                                                      "returns %s (%d)\n", p,
4165                                                      rcode);
4166                 }
4167                 else if (sm_strcasecmp(&line[1], "try") == 0)
4168                 {
4169                         MAILER *m;
4170                         STAB *st;
4171                         auto int rcode = EX_OK;
4172
4173                         q = strpbrk(p, " \t");
4174                         if (q != NULL)
4175                         {
4176                                 while (isascii(*q) && isspace(*q))
4177                                         *q++ = '\0';
4178                         }
4179                         if (q == NULL || *q == '\0')
4180                         {
4181                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4182                                                      "Usage: /try mailer address\n");
4183                                 return;
4184                         }
4185                         st = stab(p, ST_MAILER, ST_FIND);
4186                         if (st == NULL)
4187                         {
4188                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4189                                                      "Unknown mailer %s\n", p);
4190                                 return;
4191                         }
4192                         m = st->s_mailer;
4193                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4194                                              "Trying %s %s address %s for mailer %s\n",
4195                                      bitset(RF_HEADERADDR, tryflags) ? "header"
4196                                                         : "envelope",
4197                                      bitset(RF_SENDERADDR, tryflags) ? "sender"
4198                                                         : "recipient", q, p);
4199                         p = remotename(q, m, tryflags, &rcode, CurEnv);
4200                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4201                                              "Rcode = %d, addr = %s\n",
4202                                              rcode, p == NULL ? "<NULL>" : p);
4203                         e->e_to = NULL;
4204                 }
4205                 else if (sm_strcasecmp(&line[1], "tryflags") == 0)
4206                 {
4207                         if (*p == '\0')
4208                         {
4209                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4210                                                      "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
4211                                 return;
4212                         }
4213                         for (; *p != '\0'; p++)
4214                         {
4215                                 switch (*p)
4216                                 {
4217                                   case 'H':
4218                                   case 'h':
4219                                         tryflags |= RF_HEADERADDR;
4220                                         break;
4221
4222                                   case 'E':
4223                                   case 'e':
4224                                         tryflags &= ~RF_HEADERADDR;
4225                                         break;
4226
4227                                   case 'S':
4228                                   case 's':
4229                                         tryflags |= RF_SENDERADDR;
4230                                         break;
4231
4232                                   case 'R':
4233                                   case 'r':
4234                                         tryflags &= ~RF_SENDERADDR;
4235                                         break;
4236                                 }
4237                         }
4238                         exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
4239                         exbuf[1] = ' ';
4240                         exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
4241                         exbuf[3] = '\0';
4242                         macdefine(&e->e_macro, A_TEMP,
4243                                 macid("{addr_type}"), exbuf);
4244                 }
4245                 else if (sm_strcasecmp(&line[1], "parse") == 0)
4246                 {
4247                         if (*p == '\0')
4248                         {
4249                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4250                                                      "Usage: /parse address\n");
4251                                 return;
4252                         }
4253                         q = crackaddr(p, e);
4254                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4255                                              "Cracked address = ");
4256                         xputs(q);
4257                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4258                                              "\nParsing %s %s address\n",
4259                                              bitset(RF_HEADERADDR, tryflags) ?
4260                                                         "header" : "envelope",
4261                                              bitset(RF_SENDERADDR, tryflags) ?
4262                                                         "sender" : "recipient");
4263                         if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
4264                             == NULL)
4265                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4266                                                      "Cannot parse\n");
4267                         else if (a.q_host != NULL && a.q_host[0] != '\0')
4268                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4269                                                      "mailer %s, host %s, user %s\n",
4270                                                      a.q_mailer->m_name,
4271                                                      a.q_host,
4272                                                      a.q_user);
4273                         else
4274                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4275                                                      "mailer %s, user %s\n",
4276                                                      a.q_mailer->m_name,
4277                                                      a.q_user);
4278                         e->e_to = NULL;
4279                 }
4280                 else
4281                 {
4282                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4283                                              "Unknown \"/\" command %s\n",
4284                                              line);
4285                 }
4286                 return;
4287         }
4288
4289         for (p = line; isascii(*p) && isspace(*p); p++)
4290                 continue;
4291         q = p;
4292         while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4293                 p++;
4294         if (*p == '\0')
4295         {
4296                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4297                                      "No address!\n");
4298                 return;
4299         }
4300         *p = '\0';
4301         if (invalidaddr(p + 1, NULL, true))
4302                 return;
4303         do
4304         {
4305                 register char **pvp;
4306                 char pvpbuf[PSBUFSIZE];
4307
4308                 pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
4309                               &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
4310                 if (pvp == NULL)
4311                         continue;
4312                 p = q;
4313                 while (*p != '\0')
4314                 {
4315                         int status;
4316
4317                         rs = strtorwset(p, NULL, ST_FIND);
4318                         if (rs < 0)
4319                         {
4320                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4321                                                      "Undefined ruleset %s\n",
4322                                                      p);
4323                                 break;
4324                         }
4325                         status = REWRITE(pvp, rs, e);
4326                         if (status != EX_OK)
4327                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4328                                                      "== Ruleset %s (%d) status %d\n",
4329                                                      p, rs, status);
4330                         while (*p != '\0' && *p++ != ',')
4331                                 continue;
4332                 }
4333         } while (*(p = delimptr) != '\0');
4334 }
4335
4336 static void
4337 dump_class(s, id)
4338         register STAB *s;
4339         int id;
4340 {
4341         if (s->s_symtype != ST_CLASS)
4342                 return;
4343         if (bitnset(bitidx(id), s->s_class))
4344                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4345                                      "%s\n", s->s_name);
4346 }
4347
4348 /*
4349 **  An exception type used to create QuickAbort exceptions.
4350 **  This is my first cut at converting QuickAbort from longjmp to exceptions.
4351 **  These exceptions have a single integer argument, which is the argument
4352 **  to longjmp in the original code (either 1 or 2).  I don't know the
4353 **  significance of 1 vs 2: the calls to setjmp don't care.
4354 */
4355
4356 const SM_EXC_TYPE_T EtypeQuickAbort =
4357 {
4358         SmExcTypeMagic,
4359         "E:mta.quickabort",
4360         "i",
4361         sm_etype_printf,
4362         "quick abort %0",
4363 };