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