Code/Resource
Windows Develop
Linux-Unix program
Internet-Socket-Network
Web Server
Browser Client
Ftp Server
Ftp Client
Browser Plugins
Proxy Server
Email Server
Email Client
WEB Mail
Firewall-Security
Telnet Server
Telnet Client
ICQ-IM-Chat
Search Engine
Sniffer Package capture
Remote Control
xml-soap-webservice
P2P
WEB(ASP,PHP,...)
TCP/IP Stack
SNMP
Grid Computing
SilverLight
DNS
Cluster Service
Network Security
Communication-Mobile
Game Program
Editor
Multimedia program
Graph program
Compiler program
Compress-Decompress algrithms
Crypt_Decrypt algrithms
Mathimatics-Numerical algorithms
MultiLanguage
Disk/Storage
Java Develop
assembly language
Applications
Other systems
Database system
Embeded-SCM Develop
FlashMX/Flex
source in ebook
Delphi VCL
OS Develop
MiddleWare
MPI
MacOS develop
LabView
ELanguage
Software/Tools
E-Books
Artical/Document
main.c
Package: sendmail.8.10.0.Beta10.tar.Z [view]
Upload User: xu_441
Upload Date: 2007-01-04
Package Size: 1640k
Code Size: 67k
Category:
Email Client
Development Platform:
Unix_Linux
- /*
- * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
- #ifndef lint
- static char copyright[] =
- "@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.n
- All rights reserved.n
- Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.n
- Copyright (c) 1988, 1993n
- The Regents of the University of California. All rights reserved.n";
- #endif /* ! lint */
- #ifndef lint
- static char id[] = "@(#)$Id: main.c,v 8.469 1999/11/24 08:44:36 gshapiro Exp $";
- #endif /* ! lint */
- #define _DEFINE
- #include <sendmail.h>
- #if NETINET || NETINET6
- # include <arpa/inet.h>
- #endif /* NETINET || NETINET6 */
- static void dump_class __P((STAB *, int));
- static void obsolete __P((char **));
- static void testmodeline __P((char *, ENVELOPE *));
- /*
- ** SENDMAIL -- Post mail to a set of destinations.
- **
- ** This is the basic mail router. All user mail programs should
- ** call this routine to actually deliver mail. Sendmail in
- ** turn calls a bunch of mail servers that do the real work of
- ** delivering the mail.
- **
- ** Sendmail is driven by settings read in from /etc/mail/sendmail.cf
- ** (read by readcf.c).
- **
- ** Usage:
- ** /usr/lib/sendmail [flags] addr ...
- **
- ** See the associated documentation for details.
- **
- ** Author:
- ** Eric Allman, UCB/INGRES (until 10/81).
- ** Britton-Lee, Inc., purveyors of fine
- ** database computers (11/81 - 10/88).
- ** International Computer Science Institute
- ** (11/88 - 9/89).
- ** UCB/Mammoth Project (10/89 - 7/95).
- ** InReference, Inc. (8/95 - 1/97).
- ** Sendmail, Inc. (1/98 - present).
- ** The support of the my employers is gratefully acknowledged.
- ** Few of them (Britton-Lee in particular) have had
- ** anything to gain from my involvement in this project.
- */
- int NextMailer; /* "free" index into Mailer struct */
- char *FullName; /* sender's full name */
- ENVELOPE BlankEnvelope; /* a "blank" envelope */
- static ENVELOPE MainEnvelope; /* the envelope around the basic letter */
- ADDRESS NullAddress = /* a null address */
- { "", "", NULL, "" };
- char *CommandLineArgs; /* command line args for pid file */
- bool Warn_Q_option = FALSE; /* warn about Q option use */
- char **SaveArgv; /* argument vector for re-execing */
- static int MissingFds = 0; /* bit map of fds missing on startup */
- #ifdef NGROUPS_MAX
- GIDSET_T InitialGidSet[NGROUPS_MAX];
- #endif /* NGROUPS_MAX */
- #if DAEMON && !SMTP
- ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR
- #endif /* DAEMON && !SMTP */
- #if SMTP && !QUEUE
- ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR
- #endif /* SMTP && !QUEUE */
- #define MAXCONFIGLEVEL 9 /* highest config version level known */
- /* Flags for submitmode */
- #define SUBMIT_UNKNOWN 0x0000 /* unknown agent type */
- #define SUBMIT_MTA 0x0001 /* act like a message transfer agent */
- #define SUBMIT_MSA 0x0002 /* act like a message submission agent */
- #if SASL
- static sasl_callback_t srvcallbacks[] = {
- { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
- { SASL_CB_PROXY_POLICY, &proxy_policy, NULL },
- { SASL_CB_LIST_END, NULL, NULL }
- };
- #endif /* SASL */
- int
- main(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
- {
- register char *p;
- char **av;
- extern char Version[];
- char *ep, *from;
- STAB *st;
- register int i;
- int j;
- int submitmode = SUBMIT_UNKNOWN;
- bool safecf = TRUE;
- BITMAP256 *p_flags = NULL; /* daemon flags */
- bool warn_C_flag = FALSE;
- bool auth = TRUE; /* whether to set e_auth_param */
- char warn_f_flag = '';
- bool run_in_foreground = FALSE; /* -bD mode */
- static bool reenter = FALSE;
- struct passwd *pw;
- struct hostent *hp;
- char *nullserver = NULL;
- char *authinfo = NULL;
- char *sysloglabel = NULL; /* label for syslog */
- bool forged;
- struct stat traf_st; /* for TrafficLog FIFO check */
- char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
- static char rnamebuf[MAXNAME]; /* holds RealUserName */
- char *emptyenviron[1];
- QUEUE_CHAR *new;
- extern int DtableSize;
- extern int optind;
- extern int opterr;
- extern char *optarg;
- extern char **environ;
- /*
- ** Check to see if we reentered.
- ** This would normally happen if e_putheader or e_putbody
- ** were NULL when invoked.
- */
- if (reenter)
- {
- syserr("main: reentered!");
- abort();
- }
- reenter = TRUE;
- /* avoid null pointer dereferences */
- TermEscape.te_rv_on = TermEscape.te_rv_off = "";
- /* do machine-dependent initializations */
- init_md(argc, argv);
- /* in 4.4BSD, the table can be huge; impose a reasonable limit */
- DtableSize = getdtsize();
- if (DtableSize > 256)
- DtableSize = 256;
- /*
- ** Be sure we have enough file descriptors.
- ** But also be sure that 0, 1, & 2 are open.
- */
- fill_fd(STDIN_FILENO, NULL);
- fill_fd(STDOUT_FILENO, NULL);
- fill_fd(STDERR_FILENO, NULL);
- i = DtableSize;
- while (--i > 0)
- {
- if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
- (void) close(i);
- }
- errno = 0;
- #if LOG
- # ifdef LOG_MAIL
- openlog("sendmail", LOG_PID, LOG_MAIL);
- # else /* LOG_MAIL */
- openlog("sendmail", LOG_PID);
- # endif /* LOG_MAIL */
- #endif /* LOG */
- if (MissingFds != 0)
- {
- char mbuf[MAXLINE];
- mbuf[0] = '';
- if (bitset(1 << STDIN_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stdin", sizeof mbuf);
- if (bitset(1 << STDOUT_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stdout", sizeof mbuf);
- if (bitset(1 << STDERR_FILENO, MissingFds))
- (void) strlcat(mbuf, ", stderr", sizeof mbuf);
- syserr("File descriptors missing on startup: %s", &mbuf[2]);
- }
- /* reset status from syserr() calls for missing file descriptors */
- Errors = 0;
- ExitStat = EX_OK;
- #if XDEBUG
- checkfd012("after openlog");
- #endif /* XDEBUG */
- /*
- ** Seed the random number generator.
- ** Used for queue file names, picking a queue directory, and
- ** MX randomization.
- */
- seed_random();
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- #ifdef NGROUPS_MAX
- /* save initial group set for future checks */
- i = getgroups(NGROUPS_MAX, InitialGidSet);
- if (i == 0)
- InitialGidSet[0] = (GID_T) -1;
- while (i < NGROUPS_MAX)
- InitialGidSet[i++] = InitialGidSet[0];
- #endif /* NGROUPS_MAX */
- /* drop group id privileges (RunAsUser not yet set) */
- (void) drop_privileges(FALSE);
- #ifdef SIGUSR1
- /* arrange to dump state on user-1 signal */
- (void) setsignal(SIGUSR1, sigusr1);
- #endif /* SIGUSR1 */
- /* initialize for setproctitle */
- initsetproctitle(argc, argv, envp);
- /* Handle any non-getoptable constructions. */
- obsolete(argv);
- /*
- ** Do a quick prescan of the argument list.
- */
- #if defined(__osf__) || defined(_AIX3)
- # define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x"
- #endif /* defined(__osf__) || defined(_AIX3) */
- #if defined(sony_news)
- # define OPTIONS "B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
- #endif /* defined(sony_news) */
- #ifndef OPTIONS
- # define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
- #endif /* ! OPTIONS */
- opterr = 0;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'd':
- /* hack attack -- see if should use ANSI mode */
- if (strcmp(optarg, "ANSI") == 0)
- {
- TermEscape.te_rv_on = "33[7m";
- TermEscape.te_rv_off = "33[0m";
- break;
- }
- tTflag(optarg);
- setbuf(stdout, (char *) NULL);
- break;
- case 'L':
- sysloglabel = optarg;
- break;
- }
- }
- opterr = 1;
- if (sysloglabel != NULL)
- {
- if (strlen(sysloglabel) > 24)
- sysloglabel[24] = '';
- #if LOG
- closelog();
- # ifdef LOG_MAIL
- openlog(sysloglabel, LOG_PID, LOG_MAIL);
- # else /* LOG_MAIL */
- openlog(sysloglabel, LOG_PID);
- # endif /* LOG_MAIL */
- #endif /* LOG */
- }
- /* set up the blank envelope */
- BlankEnvelope.e_puthdr = putheader;
- BlankEnvelope.e_putbody = putbody;
- BlankEnvelope.e_xfp = NULL;
- STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
- CurEnv = &BlankEnvelope;
- STRUCTCOPY(NullAddress, MainEnvelope.e_from);
- /*
- ** Set default values for variables.
- ** These cannot be in initialized data space.
- */
- setdefaults(&BlankEnvelope);
- RealUid = getuid();
- RealGid = getgid();
- pw = sm_getpwuid(RealUid);
- if (pw != NULL)
- (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
- else
- (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
- (int) RealUid);
- RealUserName = rnamebuf;
- if (tTd(0, 101))
- {
- dprintf("Version %sn", Version);
- finis(FALSE, EX_OK);
- }
- /*
- ** if running non-setuid binary as non-root, pretend
- ** we are the RunAsUid
- */
- if (RealUid != 0 && geteuid() == RealUid)
- {
- if (tTd(47, 1))
- dprintf("Non-setuid binary: RunAsUid = RealUid = %dn",
- (int)RealUid);
- RunAsUid = RealUid;
- }
- else if (geteuid() != 0)
- RunAsUid = geteuid();
- if (RealUid != 0 && getegid() == RealGid)
- RunAsGid = RealGid;
- if (tTd(47, 5))
- {
- dprintf("main: e/ruid = %d/%d e/rgid = %d/%dn",
- (int)geteuid(), (int)getuid(),
- (int)getegid(), (int)getgid());
- dprintf("main: RunAsUser = %d:%dn",
- (int)RunAsUid, (int)RunAsGid);
- }
- /* save command line arguments */
- j = 0;
- for (av = argv; *av != NULL; )
- j += strlen(*av++) + 1;
- SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
- CommandLineArgs = xalloc(j);
- p = CommandLineArgs;
- for (av = argv, i = 0; *av != NULL; )
- {
- int h;
- SaveArgv[i++] = newstr(*av);
- if (av != argv)
- *p++ = ' ';
- (void) strlcpy(p, *av++, j);
- h = strlen(p);
- p += h;
- j -= h + 1;
- }
- SaveArgv[i] = NULL;
- if (tTd(0, 1))
- {
- int ll;
- extern char *CompileOptions[];
- dprintf("Version %sn Compiled with:", Version);
- av = CompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- dprintf("n");
- ll = 0;
- }
- if (ll == 0)
- dprintf("tt");
- else
- dprintf(" ");
- dprintf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- dprintf("n");
- }
- if (tTd(0, 10))
- {
- int ll;
- extern char *OsCompileOptions[];
- dprintf(" OS Defines:");
- av = OsCompileOptions;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- dprintf("n");
- ll = 0;
- }
- if (ll == 0)
- dprintf("tt");
- else
- dprintf(" ");
- dprintf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- dprintf("n");
- #ifdef _PATH_UNIX
- dprintf("Kernel symbols:t%sn", _PATH_UNIX);
- #endif /* _PATH_UNIX */
- dprintf(" Def Conf file:t%sn", getcfname());
- dprintf(" Def Pid file:t%sn", PidFile);
- }
- InChannel = stdin;
- OutChannel = stdout;
- /* clear sendmail's environment */
- ExternalEnviron = environ;
- emptyenviron[0] = NULL;
- environ = emptyenviron;
- /*
- ** restore any original TZ setting until TimeZoneSpec has been
- ** determined - or early log messages may get bogus time stamps
- */
- if ((p = getextenv("TZ")) != NULL)
- {
- char *tz;
- int tzlen;
- tzlen = strlen(p) + 4;
- tz = xalloc(tzlen);
- (void) snprintf(tz, tzlen, "TZ=%s", p);
- (void) putenv(tz);
- }
- /* prime the child environment */
- setuserenv("AGENT", "sendmail");
- if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
- (void) setsignal(SIGINT, intsig);
- (void) setsignal(SIGTERM, intsig);
- (void) setsignal(SIGPIPE, SIG_IGN);
- OldUmask = umask(022);
- OpMode = MD_DELIVER;
- FullName = getextenv("NAME");
- /*
- ** Initialize name server if it is going to be used.
- */
- #if NAMED_BIND
- if (!bitset(RES_INIT, _res.options))
- (void) res_init();
- if (tTd(8, 8))
- _res.options |= RES_DEBUG;
- else
- _res.options &= ~RES_DEBUG;
- # ifdef RES_NOALIASES
- _res.options |= RES_NOALIASES;
- # endif /* RES_NOALIASES */
- TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
- TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
- TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
- TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
- TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
- TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
- #endif /* NAMED_BIND */
- errno = 0;
- from = NULL;
- /* initialize some macros, etc. */
- initmacros(CurEnv);
- init_vendor_macros(CurEnv);
- /* version */
- define('v', Version, CurEnv);
- /* hostname */
- hp = myhostname(jbuf, sizeof jbuf);
- if (jbuf[0] != '')
- {
- struct utsname utsname;
- if (tTd(0, 4))
- dprintf("canonical name: %sn", jbuf);
- define('w', newstr(jbuf), CurEnv); /* must be new string */
- define('j', newstr(jbuf), CurEnv);
- setclass('w', jbuf);
- p = strchr(jbuf, '.');
- if (p != NULL)
- {
- if (p[1] != '')
- {
- define('m', newstr(&p[1]), CurEnv);
- }
- while (p != NULL && strchr(&p[1], '.') != NULL)
- {
- *p = '';
- if (tTd(0, 4))
- dprintf("ta.k.a.: %sn", jbuf);
- setclass('w', jbuf);
- *p++ = '.';
- p = strchr(p, '.');
- }
- }
- if (uname(&utsname) >= 0)
- p = utsname.nodename;
- else
- {
- if (tTd(0, 22))
- dprintf("uname failed (%s)n",
- errstring(errno));
- makelower(jbuf);
- p = jbuf;
- }
- if (tTd(0, 4))
- dprintf(" UUCP nodename: %sn", p);
- p = newstr(p);
- define('k', p, CurEnv);
- setclass('k', p);
- setclass('w', p);
- }
- if (hp != NULL)
- {
- for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
- {
- if (tTd(0, 4))
- dprintf("ta.k.a.: %sn", *av);
- setclass('w', *av);
- }
- #if NETINET || NETINET6
- for (i = 0; hp->h_addr_list[i] != NULL; i++)
- {
- # if NETINET6
- char *addr;
- char buf6[INET6_ADDRSTRLEN];
- struct in6_addr ia6;
- # endif /* NETINET6 */
- # if NETINET
- struct in_addr ia;
- # endif /* NETINET */
- char ipbuf[103];
- ipbuf[0] = '';
- switch (hp->h_addrtype)
- {
- # if NETINET
- case AF_INET:
- if (hp->h_length != INADDRSZ)
- break;
- memmove(&ia, hp->h_addr_list[i], INADDRSZ);
- (void) snprintf(ipbuf, sizeof ipbuf,
- "[%.100s]", inet_ntoa(ia));
- break;
- # endif /* NETINET */
- # if NETINET6
- case AF_INET6:
- if (hp->h_length != IN6ADDRSZ)
- break;
- memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
- addr = anynet_ntop(&ia6, buf6, sizeof buf6);
- if (addr != NULL)
- (void) snprintf(ipbuf, sizeof ipbuf,
- "[%.100s]", addr);
- break;
- # endif /* NETINET6 */
- }
- if (ipbuf[0] == '')
- break;
- if (tTd(0, 4))
- dprintf("ta.k.a.: %sn", ipbuf);
- setclass('w', ipbuf);
- }
- #endif /* NETINET || NETINET6 */
- }
- /* current time */
- define('b', arpadate((char *) NULL), CurEnv);
- /* current load average */
- CurrentLA = sm_getla(CurEnv);
- QueueLimitRecipient = (QUEUE_CHAR *) NULL;
- QueueLimitSender = (QUEUE_CHAR *) NULL;
- QueueLimitId = (QUEUE_CHAR *) NULL;
- /*
- ** Crack argv.
- */
- av = argv;
- p = strrchr(*av, '/');
- if (p++ == NULL)
- p = *av;
- if (strcmp(p, "newaliases") == 0)
- OpMode = MD_INITALIAS;
- else if (strcmp(p, "mailq") == 0)
- OpMode = MD_PRINT;
- else if (strcmp(p, "smtpd") == 0)
- OpMode = MD_DAEMON;
- else if (strcmp(p, "hoststat") == 0)
- OpMode = MD_HOSTSTAT;
- else if (strcmp(p, "purgestat") == 0)
- OpMode = MD_PURGESTAT;
- optind = 1;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'b': /* operations mode */
- switch (j = *optarg)
- {
- case MD_DAEMON:
- case MD_FGDAEMON:
- #if !DAEMON
- usrerr("Daemon mode not implemented");
- ExitStat = EX_USAGE;
- break;
- #endif /* !DAEMON */
- case MD_SMTP:
- #if !SMTP
- usrerr("I don't speak SMTP");
- ExitStat = EX_USAGE;
- break;
- #endif /* !SMTP */
- case MD_INITALIAS:
- case MD_DELIVER:
- case MD_VERIFY:
- case MD_TEST:
- case MD_PRINT:
- case MD_HOSTSTAT:
- case MD_PURGESTAT:
- case MD_ARPAFTP:
- OpMode = j;
- break;
- case MD_FREEZE:
- usrerr("Frozen configurations unsupported");
- ExitStat = EX_USAGE;
- break;
- default:
- usrerr("Invalid operation mode %c", j);
- ExitStat = EX_USAGE;
- break;
- }
- break;
- case 'B': /* body type */
- CurEnv->e_bodytype = optarg;
- break;
- case 'C': /* select configuration file (already done) */
- if (RealUid != 0)
- warn_C_flag = TRUE;
- ConfFile = optarg;
- (void) drop_privileges(TRUE);
- safecf = FALSE;
- break;
- case 'd': /* debugging -- already done */
- break;
- case 'f': /* from address */
- case 'r': /* obsolete -f flag */
- if (from != NULL)
- {
- usrerr("More than one "from" person");
- ExitStat = EX_USAGE;
- break;
- }
- from = newstr(denlstring(optarg, TRUE, TRUE));
- if (strcmp(RealUserName, from) != 0)
- warn_f_flag = j;
- break;
- case 'F': /* set full name */
- FullName = newstr(optarg);
- break;
- case 'G': /* relay (gateway) submission */
- submitmode |= SUBMIT_MTA;
- break;
- case 'h': /* hop count */
- CurEnv->e_hopcount = strtol(optarg, &ep, 10);
- if (*ep)
- {
- usrerr("Bad hop count (%s)", optarg);
- ExitStat = EX_USAGE;
- }
- break;
- case 'L': /* program label */
- /* already set */
- break;
- case 'n': /* don't alias */
- NoAlias = TRUE;
- break;
- case 'N': /* delivery status notifications */
- DefaultNotify |= QHASNOTIFY;
- if (strcasecmp(optarg, "never") == 0)
- break;
- for (p = optarg; p != NULL; optarg = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '';
- if (strcasecmp(optarg, "success") == 0)
- DefaultNotify |= QPINGONSUCCESS;
- else if (strcasecmp(optarg, "failure") == 0)
- DefaultNotify |= QPINGONFAILURE;
- else if (strcasecmp(optarg, "delay") == 0)
- DefaultNotify |= QPINGONDELAY;
- else
- {
- usrerr("Invalid -N argument");
- ExitStat = EX_USAGE;
- }
- }
- break;
- case 'o': /* set option */
- setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
- break;
- case 'O': /* set option (long form) */
- setoption(' ', optarg, FALSE, TRUE, CurEnv);
- break;
- case 'p': /* set protocol */
- p = strchr(optarg, ':');
- if (p != NULL)
- {
- *p++ = '';
- if (*p != '')
- {
- ep = xalloc(strlen(p) + 1);
- cleanstrcpy(ep, p, MAXNAME);
- define('s', ep, CurEnv);
- }
- }
- if (*optarg != '')
- {
- ep = xalloc(strlen(optarg) + 1);
- cleanstrcpy(ep, optarg, MAXNAME);
- define('r', ep, CurEnv);
- }
- break;
- case 'q': /* run queue files at intervals */
- #if QUEUE
- /* sanity check */
- if (OpMode != MD_DELIVER &&
- OpMode != MD_DAEMON &&
- OpMode != MD_FGDAEMON &&
- OpMode != MD_PRINT &&
- OpMode != MD_QUEUERUN)
- {
- usrerr("Can not use -q with -b%c", OpMode);
- ExitStat = EX_USAGE;
- break;
- }
- /* don't override -bd, -bD or -bp */
- if (OpMode == MD_DELIVER)
- OpMode = MD_QUEUERUN;
- FullName = NULL;
- switch (optarg[0])
- {
- case 'I':
- new = (QUEUE_CHAR *) xalloc(sizeof *new);
- new->queue_match = newstr(&optarg[1]);
- new->queue_next = QueueLimitId;
- QueueLimitId = new;
- break;
- case 'R':
- new = (QUEUE_CHAR *) xalloc(sizeof *new);
- new->queue_match = newstr(&optarg[1]);
- new->queue_next = QueueLimitRecipient;
- QueueLimitRecipient = new;
- break;
- case 'S':
- new = (QUEUE_CHAR *) xalloc(sizeof *new);
- new->queue_match = newstr(&optarg[1]);
- new->queue_next = QueueLimitSender;
- QueueLimitSender = new;
- break;
- default:
- i = Errors;
- QueueIntvl = convtime(optarg, 'm');
- /* check for bad conversion */
- if (i < Errors)
- ExitStat = EX_USAGE;
- break;
- }
- #else /* QUEUE */
- usrerr("I don't know about queues");
- ExitStat = EX_USAGE;
- #endif /* QUEUE */
- break;
- case 'R': /* DSN RET: what to return */
- if (bitset(EF_RET_PARAM, CurEnv->e_flags))
- {
- usrerr("Duplicate -R flag");
- ExitStat = EX_USAGE;
- break;
- }
- CurEnv->e_flags |= EF_RET_PARAM;
- if (strcasecmp(optarg, "hdrs") == 0)
- CurEnv->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(optarg, "full") != 0)
- {
- usrerr("Invalid -R value");
- ExitStat = EX_USAGE;
- }
- define(macid("{dsn_ret}", NULL),
- newstr(optarg), CurEnv);
- break;
- case 't': /* read recipients from message */
- GrabTo = TRUE;
- break;
- case 'U': /* initial (user) submission */
- submitmode |= SUBMIT_MSA;
- break;
- case 'V': /* DSN ENVID: set "original" envelope id */
- if (!xtextok(optarg))
- {
- usrerr("Invalid syntax in -V flag");
- ExitStat = EX_USAGE;
- }
- else
- {
- CurEnv->e_envid = newstr(optarg);
- define(macid("{dsn_envid}", NULL),
- newstr(optarg), CurEnv);
- }
- break;
- case 'X': /* traffic log file */
- (void) drop_privileges(TRUE);
- if (stat(optarg, &traf_st) == 0 &&
- S_ISFIFO(traf_st.st_mode))
- TrafficLogFile = fopen(optarg, "w");
- else
- TrafficLogFile = fopen(optarg, "a");
- if (TrafficLogFile == NULL)
- {
- syserr("cannot open %s", optarg);
- ExitStat = EX_CANTCREAT;
- break;
- }
- #if HASSETVBUF
- (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
- #else /* HASSETVBUF */
- (void) setlinebuf(TrafficLogFile);
- #endif /* HASSETVBUF */
- break;
- /* compatibility flags */
- case 'c': /* connect to non-local mailers */
- case 'i': /* don't let dot stop me */
- case 'm': /* send to me too */
- case 'T': /* set timeout interval */
- case 'v': /* give blow-by-blow description */
- setoption(j, "T", FALSE, TRUE, CurEnv);
- break;
- case 'e': /* error message disposition */
- case 'M': /* define macro */
- setoption(j, optarg, FALSE, TRUE, CurEnv);
- break;
- case 's': /* save From lines in headers */
- setoption('f', "T", FALSE, TRUE, CurEnv);
- break;
- #ifdef DBM
- case 'I': /* initialize alias DBM file */
- OpMode = MD_INITALIAS;
- break;
- #endif /* DBM */
- #if defined(__osf__) || defined(_AIX3)
- case 'x': /* random flag that OSF/1 & AIX mailx passes */
- break;
- #endif /* defined(__osf__) || defined(_AIX3) */
- #if defined(sony_news)
- case 'E':
- case 'J': /* ignore flags for Japanese code conversion
- implemented on Sony NEWS */
- break;
- #endif /* defined(sony_news) */
- default:
- finis(TRUE, EX_USAGE);
- break;
- }
- }
- av += optind;
- if (bitset(SUBMIT_MTA, submitmode) &&
- bitset(SUBMIT_MSA, submitmode))
- {
- /* sanity check */
- usrerr("Cannot use both -G and -U together");
- }
- else if (bitset(SUBMIT_MTA, submitmode))
- define(macid("{daemon_flags}", NULL), "CCf", CurEnv);
- else if (bitset(SUBMIT_MSA, submitmode))
- define(macid("{daemon_flags}", NULL), "cu", CurEnv);
- else
- {
- #if _FFR_DEFAULT_SUBMIT_TO_MSA
- define(macid("{daemon_flags}", NULL), "cu", CurEnv);
- #else /* _FFR_DEFAULT_SUBMIT_TO_MSA */
- /* EMPTY */
- #endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */
- }
- /*
- ** Do basic initialization.
- ** Read system control file.
- ** Extract special fields for local use.
- */
- /* set up ${opMode} for use in config file */
- {
- char mbuf[2];
- mbuf[0] = OpMode;
- mbuf[1] = '';
- define(MID_OPMODE, newstr(mbuf), CurEnv);
- }
- #if XDEBUG
- checkfd012("before readcf");
- #endif /* XDEBUG */
- vendor_pre_defaults(CurEnv);
- readcf(getcfname(), safecf, CurEnv);
- ConfigFileRead = TRUE;
- vendor_post_defaults(CurEnv);
- /* Enforce use of local time (null string overrides this) */
- if (TimeZoneSpec == NULL)
- unsetenv("TZ");
- else if (TimeZoneSpec[0] != '')
- setuserenv("TZ", TimeZoneSpec);
- else
- setuserenv("TZ", NULL);
- tzset();
- /* avoid denial-of-service attacks */
- resetlimits();
- if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
- {
- /* drop privileges -- daemon mode done after socket/bind */
- (void) drop_privileges(FALSE);
- }
- #if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
- _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
- #endif /* NAMED_BIND */
- /*
- ** Find our real host name for future logging.
- */
- authinfo = getauthinfo(STDIN_FILENO, &forged);
- define('_', authinfo, CurEnv);
- /* suppress error printing if errors mailed back or whatever */
- if (CurEnv->e_errormode != EM_PRINT)
- HoldErrs = TRUE;
- /* set up the $=m class now, after .cf has a chance to redefine $m */
- expand("201m", jbuf, sizeof jbuf, CurEnv);
- setclass('m', jbuf);
- /* probe interfaces and locate any additional names */
- if (!DontProbeInterfaces)
- load_if_names();
- if (tTd(0, 1))
- {
- dprintf("n============ SYSTEM IDENTITY (after readcf) ============");
- dprintf("n (short domain name) $w = ");
- xputs(macvalue('w', CurEnv));
- dprintf("n (canonical domain name) $j = ");
- xputs(macvalue('j', CurEnv));
- dprintf("n (subdomain name) $m = ");
- xputs(macvalue('m', CurEnv));
- dprintf("n (node name) $k = ");
- xputs(macvalue('k', CurEnv));
- dprintf("n========================================================nn");
- }
- /*
- ** Do more command line checking -- these are things that
- ** have to modify the results of reading the config file.
- */
- /* process authorization warnings from command line */
- if (warn_C_flag)
- auth_warning(CurEnv, "Processed by %s with -C %s",
- RealUserName, ConfFile);
- if (Warn_Q_option && !wordinclass(RealUserName, 't'))
- auth_warning(CurEnv, "Processed from queue %s", QueueDir);
- /* check body type for legality */
- if (CurEnv->e_bodytype == NULL)
- /* EMPTY */
- /* nothing */ ;
- else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
- SevenBitInput = TRUE;
- else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
- SevenBitInput = FALSE;
- else
- {
- usrerr("Illegal body type %s", CurEnv->e_bodytype);
- CurEnv->e_bodytype = NULL;
- }
- /* tweak default DSN notifications */
- if (DefaultNotify == 0)
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
- /* be sure we don't pick up bogus HOSTALIASES environment variable */
- if (OpMode == MD_QUEUERUN && RealUid != 0)
- (void) unsetenv("HOSTALIASES");
- /* check for sane configuration level */
- if (ConfigLevel > MAXCONFIGLEVEL)
- {
- syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
- ConfigLevel, Version, MAXCONFIGLEVEL);
- }
- /* need MCI cache to have persistence */
- if (HostStatDir != NULL && MaxMciCache == 0)
- {
- HostStatDir = NULL;
- printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0n");
- }
- /* need HostStatusDir in order to have SingleThreadDelivery */
- if (SingleThreadDelivery && HostStatDir == NULL)
- {
- SingleThreadDelivery = FALSE;
- printf("Warning: HostStatusDirectory required for SingleThreadDeliveryn");
- }
- /* check for permissions */
- if ((OpMode == MD_DAEMON ||
- OpMode == MD_FGDAEMON ||
- OpMode == MD_PURGESTAT) &&
- RealUid != 0 &&
- RealUid != TrustedUid)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to %s",
- RealUid,
- OpMode != MD_PURGESTAT ? "run daemon"
- : "purge host status");
- usrerr("Permission denied");
- finis(FALSE, EX_USAGE);
- }
- if (OpMode == MD_INITALIAS &&
- RealUid != 0 &&
- RealUid != TrustedUid &&
- !wordinclass(RealUserName, 't'))
- {
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to rebuild the alias map",
- RealUid);
- usrerr("Permission denied");
- finis(FALSE, EX_USAGE);
- }
- if (MeToo)
- BlankEnvelope.e_flags |= EF_METOO;
- switch (OpMode)
- {
- case MD_TEST:
- /* don't have persistent host status in test mode */
- HostStatDir = NULL;
- if (Verbose == 0)
- Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
- break;
- case MD_VERIFY:
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
- /* arrange to exit cleanly on hangup signal */
- if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) setsignal(SIGHUP, intsig);
- break;
- case MD_FGDAEMON:
- run_in_foreground = TRUE;
- OpMode = MD_DAEMON;
- /* FALLTHROUGH */
- case MD_DAEMON:
- vendor_daemon_setup(CurEnv);
- /* remove things that don't make sense in daemon mode */
- FullName = NULL;
- GrabTo = FALSE;
- /* arrange to restart on hangup signal */
- if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
- sm_syslog(LOG_WARNING, NOQID,
- "daemon invoked without full pathname; kill -1 won't work");
- (void) setsignal(SIGHUP, sighup);
- /* workaround: can't seem to release the signal in the parent */
- (void) releasesignal(SIGHUP);
- break;
- case MD_INITALIAS:
- Verbose = 2;
- CurEnv->e_errormode = EM_PRINT;
- HoldErrs = FALSE;
- /* FALLTHROUGH */
- default:
- /* arrange to exit cleanly on hangup signal */
- if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) setsignal(SIGHUP, intsig);
- break;
- }
- /* special considerations for FullName */
- if (FullName != NULL)
- {
- char *full = NULL;
- /* full names can't have newlines */
- if (strchr(FullName, 'n') != NULL)
- {
- FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
- }
- /* check for characters that may have to be quoted */
- if (!rfc822_string(FullName))
- {
- /*
- ** Quote a full name with special characters
- ** as a comment so crackaddr() doesn't destroy
- ** the name portion of the address.
- */
- FullName = addquotes(FullName);
- if (full != NULL)
- free(full);
- }
- }
- /* do heuristic mode adjustment */
- if (Verbose)
- {
- /* turn off noconnect option */
- setoption('c', "F", TRUE, FALSE, CurEnv);
- /* turn on interactive delivery */
- setoption('d', "", TRUE, FALSE, CurEnv);
- }
- #ifdef VENDOR_CODE
- /* check for vendor mismatch */
- if (VendorCode != VENDOR_CODE)
- {
- message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
- getvendor(VENDOR_CODE), getvendor(VendorCode));
- }
- #endif /* VENDOR_CODE */
- /* check for out of date configuration level */
- if (ConfigLevel < MAXCONFIGLEVEL)
- {
- message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
- Version, MAXCONFIGLEVEL, ConfigLevel);
- }
- if (ConfigLevel < 3)
- {
- UseErrorsTo = TRUE;
- }
- /* set options that were previous macros */
- if (SmtpGreeting == NULL)
- {
- if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
- SmtpGreeting = newstr(p);
- else
- SmtpGreeting = "201j Sendmail 201v ready at 201b";
- }
- if (UnixFromLine == NULL)
- {
- if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
- UnixFromLine = newstr(p);
- else
- UnixFromLine = "From 201g 201d";
- }
- /* our name for SMTP codes */
- expand("201j", jbuf, sizeof jbuf, CurEnv);
- MyHostName = jbuf;
- if (strchr(jbuf, '.') == NULL)
- message("WARNING: local host name (%s) is not qualified; fix $j in config file",
- jbuf);
- /* make certain that this name is part of the $=w class */
- setclass('w', MyHostName);
- /* the indices of built-in mailers */
- st = stab("local", ST_MAILER, ST_FIND);
- if (st != NULL)
- LocalMailer = st->s_mailer;
- else if (OpMode != MD_TEST || !warn_C_flag)
- syserr("No local mailer defined");
- st = stab("prog", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No prog mailer defined");
- else
- {
- ProgMailer = st->s_mailer;
- clrbitn(M_MUSER, ProgMailer->m_flags);
- }
- st = stab("*file*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *file* mailer defined");
- else
- {
- FileMailer = st->s_mailer;
- clrbitn(M_MUSER, FileMailer->m_flags);
- }
- st = stab("*include*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *include* mailer defined");
- else
- InclMailer = st->s_mailer;
- if (ConfigLevel < 6)
- {
- /* heuristic tweaking of local mailer for back compat */
- if (LocalMailer != NULL)
- {
- setbitn(M_ALIASABLE, LocalMailer->m_flags);
- setbitn(M_HASPWENT, LocalMailer->m_flags);
- setbitn(M_TRYRULESET5, LocalMailer->m_flags);
- setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
- setbitn(M_CHECKPROG, LocalMailer->m_flags);
- setbitn(M_CHECKFILE, LocalMailer->m_flags);
- setbitn(M_CHECKUDB, LocalMailer->m_flags);
- }
- if (ProgMailer != NULL)
- setbitn(M_RUNASRCPT, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_RUNASRCPT, FileMailer->m_flags);
- }
- if (ConfigLevel < 7)
- {
- if (LocalMailer != NULL)
- setbitn(M_VRFY250, LocalMailer->m_flags);
- if (ProgMailer != NULL)
- setbitn(M_VRFY250, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_VRFY250, FileMailer->m_flags);
- }
- /* MIME Content-Types that cannot be transfer encoded */
- setclass('n', "multipart/signed");
- /* MIME message/xxx subtypes that can be treated as messages */
- setclass('s', "rfc822");
- /* MIME Content-Transfer-Encodings that can be encoded */
- setclass('e', "7bit");
- setclass('e', "8bit");
- setclass('e', "binary");
- #ifdef USE_B_CLASS
- /* MIME Content-Types that should be treated as binary */
- setclass('b', "image");
- setclass('b', "audio");
- setclass('b', "video");
- setclass('b', "application/octet-stream");
- #endif /* USE_B_CLASS */
- /* MIME headers which have fields to check for overflow */
- setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
- setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
- /* MIME headers to check for length overflow */
- setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
- /* MIME headers to check for overflow and rebalance */
- setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
- setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
- setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
- /* Macros to save in the qf file -- don't remove any */
- setclass(macid("{persistentMacros}", NULL), "r");
- setclass(macid("{persistentMacros}", NULL), "s");
- setclass(macid("{persistentMacros}", NULL), "_");
- setclass(macid("{persistentMacros}", NULL), "{if_addr}");
- setclass(macid("{persistentMacros}", NULL), "{if_family}");
- setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
- setclass(macid("{persistentMacros}", NULL), "{client_flags}");
- /* operate in queue directory */
- if (QueueDir == NULL)
- {
- if (OpMode != MD_TEST)
- {
- syserr("QueueDirectory (Q) option must be set");
- ExitStat = EX_CONFIG;
- }
- }
- else
- {
- /*
- ** If multiple queues wildcarded, use one for
- ** the daemon's home. Note that this preconditions
- ** a wildcarded QueueDir to a real pathname.
- */
- if (OpMode != MD_TEST)
- multiqueue_cache();
- }
- /* check host status directory for validity */
- if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
- {
- /* cannot use this value */
- if (tTd(0, 2))
- dprintf("Cannot use HostStatusDirectory = %s: %sn",
- HostStatDir, errstring(errno));
- HostStatDir = NULL;
- }
- #if QUEUE
- if (OpMode == MD_QUEUERUN && RealUid != 0 &&
- bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
- {
- struct stat stbuf;
- /* check to see if we own the queue directory */
- if (stat(".", &stbuf) < 0)
- syserr("main: cannot stat %s", QueueDir);
- if (stbuf.st_uid != RealUid)
- {
- /* nope, really a botch */
- usrerr("You do not have permission to process the queue");
- finis(FALSE, EX_NOPERM);
- }
- }
- #endif /* QUEUE */
- /* if we've had errors so far, exit now */
- if (ExitStat != EX_OK && OpMode != MD_TEST)
- finis(FALSE, ExitStat);
- #if XDEBUG
- checkfd012("before main() initmaps");
- #endif /* XDEBUG */
- /*
- ** Do operation-mode-dependent initialization.
- */
- switch (OpMode)
- {
- case MD_PRINT:
- /* print the queue */
- #if QUEUE
- dropenvelope(CurEnv, TRUE);
- (void) setsignal(SIGPIPE, quiesce);
- printqueue();
- finis(FALSE, EX_OK);
- #else /* QUEUE */
- usrerr("No queue to print");
- finis(FALSE, EX_UNAVAILABLE);
- #endif /* QUEUE */
- break;
- case MD_HOSTSTAT:
- (void) setsignal(SIGPIPE, quiesce);
- (void) mci_traverse_persistent(mci_print_persistent, NULL);
- finis(FALSE, EX_OK);
- break;
- case MD_PURGESTAT:
- (void) mci_traverse_persistent(mci_purge_persistent, NULL);
- finis(FALSE, EX_OK);
- break;
- case MD_INITALIAS:
- /* initialize maps */
- initmaps();
- finis(FALSE, ExitStat);
- break;
- case MD_SMTP:
- case MD_DAEMON:
- /* reset DSN parameters */
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
- CurEnv->e_envid = NULL;
- define(macid("{dsn_envid}", NULL), NULL, CurEnv);
- CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
- define(macid("{dsn_ret}", NULL), NULL, CurEnv);
- /* don't open maps for daemon -- done below in child */
- break;
- }
- if (tTd(0, 15))
- {
- /* print configuration table (or at least part of it) */
- if (tTd(0, 90))
- printrules();
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(Mailer[i]);
- }
- }
- /*
- ** Switch to the main envelope.
- */
- CurEnv = newenvelope(&MainEnvelope, CurEnv);
- MainEnvelope.e_flags = BlankEnvelope.e_flags;
- /*
- ** If test mode, read addresses from stdin and process.
- */
- if (OpMode == MD_TEST)
- {
- char buf[MAXLINE];
- if (isatty(fileno(stdin)))
- Verbose = 2;
- if (Verbose)
- {
- printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)n");
- printf("Enter <ruleset> <address>n");
- }
- if (setjmp(TopFrame) > 0)
- printf("n");
- (void) setsignal(SIGINT, intindebug);
- for (;;)
- {
- if (Verbose == 2)
- printf("> ");
- (void) fflush(stdout);
- if (fgets(buf, sizeof buf, stdin) == NULL)
- testmodeline("/quit", CurEnv);
- p = strchr(buf, 'n');
- if (p != NULL)
- *p = '';
- if (Verbose < 2)
- printf("> %sn", buf);
- testmodeline(buf, CurEnv);
- }
- }
- #if SMTP
- #endif /* SMTP */
- #if QUEUE
- /*
- ** If collecting stuff from the queue, go start doing that.
- */
- if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
- {
- # if SMTP
- # endif /* SMTP */
- (void) runqueue(!Verbose, Verbose);
- finis(TRUE, ExitStat);
- }
- #endif /* QUEUE */
- /*
- ** If a daemon, wait for a request.
- ** getrequests will always return in a child.
- ** If we should also be processing the queue, start
- ** doing it in background.
- ** We check for any errors that might have happened
- ** during startup.
- */
- if (OpMode == MD_DAEMON || QueueIntvl != 0)
- {
- char dtype[200];
- if (!run_in_foreground && !tTd(99, 100))
- {
- /* put us in background */
- i = fork();
- if (i < 0)
- syserr("daemon: cannot fork");
- if (i != 0)
- finis(FALSE, EX_OK);
- /* disconnect from our controlling tty */
- disconnect(2, CurEnv);
- }
- dtype[0] = '';
- if (OpMode == MD_DAEMON)
- (void) strlcat(dtype, "+SMTP", sizeof dtype);
- if (QueueIntvl != 0)
- {
- (void) strlcat(dtype, "+queueing@", sizeof dtype);
- (void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
- sizeof dtype);
- }
- if (tTd(0, 1))
- (void) strlcat(dtype, "+debugging", sizeof dtype);
- sm_syslog(LOG_INFO, NOQID,
- "starting daemon (%s): %s", Version, dtype + 1);
- #ifdef XLA
- xla_create_file();
- #endif /* XLA */
- /* save daemon type in a macro for possible PidFile use */
- define(macid("{daemon_info}", NULL),
- newstr(dtype + 1), &BlankEnvelope);
- /* save queue interval in a macro for possible PidFile use */
- define(macid("{queue_interval}", NULL),
- newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
- #if QUEUE
- if (QueueIntvl != 0)
- {
- (void) runqueue(TRUE, FALSE);
- if (OpMode != MD_DAEMON)
- {
- /* write the pid to file */
- log_sendmail_pid(CurEnv);
- for (;;)
- {
- (void) pause();
- if (DoQueueRun)
- (void) runqueue(TRUE, FALSE);
- }
- }
- }
- #endif /* QUEUE */
- dropenvelope(CurEnv, TRUE);
- #if DAEMON
- p_flags = getrequests(CurEnv);
- /* drop privileges */
- (void) drop_privileges(FALSE);
- /* at this point we are in a child: reset state */
- (void) newenvelope(CurEnv, CurEnv);
- /*
- ** Get authentication data
- */
- authinfo = getauthinfo(fileno(InChannel), &forged);
- define('_', authinfo, &BlankEnvelope);
- #endif /* DAEMON */
- }
- if (LogLevel > 9)
- {
- /* log connection information */
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "connect from %.100s", authinfo);
- }
- #if SMTP
- /*
- ** If running SMTP protocol, start collecting and executing
- ** commands. This will never return.
- */
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- {
- char pbuf[20];
- /*
- ** Save some macros for check_* rulesets.
- */
- if (forged)
- {
- char ipbuf[103];
- (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
- anynet_ntoa(&RealHostAddr));
- define(macid("{client_name}", NULL),
- newstr(ipbuf), &BlankEnvelope);
- define(macid("{client_resolve}", NULL),
- "FORGED", &BlankEnvelope);
- }
- else
- define(macid("{client_name}", NULL), RealHostName,
- &BlankEnvelope);
- define(macid("{client_addr}", NULL),
- newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
- (void)sm_getla(&BlankEnvelope);
- switch(RealHostAddr.sa.sa_family)
- {
- # if NETINET
- case AF_INET:
- (void) snprintf(pbuf, sizeof pbuf, "%d",
- RealHostAddr.sin.sin_port);
- break;
- # endif /* NETINET */
- # if NETINET6
- case AF_INET6:
- (void) snprintf(pbuf, sizeof pbuf, "%d",
- RealHostAddr.sin6.sin6_port);
- break;
- # endif /* NETINET6 */
- default:
- (void) snprintf(pbuf, sizeof pbuf, "0");
- break;
- }
- define(macid("{client_port}", NULL),
- newstr(pbuf), &BlankEnvelope);
- #if SASL
- /* give a syserr or just disable AUTH ? */
- if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK)
- syserr("!sasl_server_init failed!");
- #endif /* SASL */
- if (OpMode == MD_DAEMON)
- {
- /* validate the connection */
- HoldErrs = TRUE;
- nullserver = validate_connection(&RealHostAddr,
- RealHostName, CurEnv);
- HoldErrs = FALSE;
- }
- else if (p_flags == NULL)
- {
- p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
- clrbitmap(p_flags);
- }
- smtp(nullserver, *p_flags, CurEnv);
- }
- #endif /* SMTP */
- clearenvelope(CurEnv, FALSE);
- if (OpMode == MD_VERIFY)
- {
- set_delivery_mode(SM_VERIFY, CurEnv);
- PostMasterCopy = NULL;
- }
- else
- {
- /* interactive -- all errors are global */
- CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
- }
- /*
- ** Do basic system initialization and set the sender
- */
- initsys(CurEnv);
- define(macid("{ntries}", NULL), "0", CurEnv);
- setsender(from, CurEnv, NULL, '', FALSE);
- if (warn_f_flag != '' && !wordinclass(RealUserName, 't') &&
- (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) ||
- strcmp(CurEnv->e_from.q_user, RealUserName) != 0))
- {
- auth_warning(CurEnv, "%s set sender to %s using -%c",
- RealUserName, from, warn_f_flag);
- #if SASL
- auth = FALSE;
- #endif /* SASL */
- }
- if (auth)
- {
- char *fv;
- /* set the initial sender for AUTH= to $f@$j */
- fv = macvalue('f', CurEnv);
- if (fv == NULL || *fv == '')
- CurEnv->e_auth_param = NULL;
- else
- {
- if (strchr(fv, '@') == NULL)
- {
- i = strlen(fv) + strlen(macvalue('j', CurEnv))
- + 2;
- p = xalloc(i);
- (void) snprintf(p, i, "%s@%s", fv,
- macvalue('j', CurEnv));
- }
- else
- p = newstr(fv);
- CurEnv->e_auth_param = newstr(xtextify(p, NULL));
- }
- }
- if (macvalue('s', CurEnv) == NULL)
- define('s', RealHostName, CurEnv);
- if (*av == NULL && !GrabTo)
- {
- CurEnv->e_to = NULL;
- CurEnv->e_flags |= EF_GLOBALERRS;
- HoldErrs = FALSE;
- usrerr("Recipient names must be specified");
- /* collect body for UUCP return */
- if (OpMode != MD_VERIFY)
- collect(InChannel, FALSE, NULL, CurEnv);
- finis(TRUE, EX_USAGE);
- }
- /*
- ** Scan argv and deliver the message to everyone.
- */
- sendtoargv(av, CurEnv);
- /* if we have had errors sofar, arrange a meaningful exit stat */
- if (Errors > 0 && ExitStat == EX_OK)
- ExitStat = EX_USAGE;
- #if _FFR_FIX_DASHT
- /*
- ** If using -t, force not sending to argv recipients, even
- ** if they are mentioned in the headers.
- */
- if (GrabTo)
- {
- ADDRESS *q;
- for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
- q->q_state = QS_REMOVED;
- }
- #endif /* _FFR_FIX_DASHT */
- /*
- ** Read the input mail.
- */
- CurEnv->e_to = NULL;
- if (OpMode != MD_VERIFY || GrabTo)
- {
- int savederrors = Errors;
- long savedflags = CurEnv->e_flags & EF_FATALERRS;
- CurEnv->e_flags |= EF_GLOBALERRS;
- CurEnv->e_flags &= ~EF_FATALERRS;
- Errors = 0;
- buffer_errors();
- collect(InChannel, FALSE, NULL, CurEnv);
- /* header checks failed */
- if (Errors > 0)
- {
- /* Log who the mail would have gone to */
- if (LogLevel > 8 && CurEnv->e_message != NULL &&
- !GrabTo)
- {
- ADDRESS *a;
- for (a = CurEnv->e_sendqueue;
- a != NULL;
- a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state))
- continue;
- CurEnv->e_to = a->q_paddr;
- logdelivery(NULL, NULL,
- CurEnv->e_message,
- NULL, (time_t) 0, CurEnv);
- }
- CurEnv->e_to = NULL;
- }
- flush_errors(TRUE);
- finis(TRUE, ExitStat);
- /* NOTREACHED */
- return -1;
- }
- /* bail out if message too large */
- if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
- {
- finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
- /* NOTREACHED */
- return -1;
- }
- Errors = savederrors;
- CurEnv->e_flags |= savedflags;
- }
- errno = 0;
- if (tTd(1, 1))
- dprintf("From person = "%s"n", CurEnv->e_from.q_paddr);
- /*
- ** Actually send everything.
- ** If verifying, just ack.
- */
- CurEnv->e_from.q_state = QS_SENDER;
- if (tTd(1, 5))
- {
- dprintf("main: QS_SENDER ");
- printaddr(&CurEnv->e_from, FALSE);
- }
- CurEnv->e_to = NULL;
- CurrentLA = sm_getla(CurEnv);
- GrabTo = FALSE;
- #if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
- #endif /* NAMED_BIND */
- sendall(CurEnv, SM_DEFAULT);
- /*
- ** All done.
- ** Don't send return error message if in VERIFY mode.
- */
- finis(TRUE, ExitStat);
- /* NOTREACHED */
- return ExitStat;
- }
- /* ARGSUSED */
- SIGFUNC_DECL
- quiesce(sig)
- int sig;
- {
- clear_events();
- finis(FALSE, EX_OK);
- }
- /* ARGSUSED */
- SIGFUNC_DECL
- intindebug(sig)
- int sig;
- {
- longjmp(TopFrame, 1);
- return SIGFUNC_RETURN;
- }
- /*
- ** FINIS -- Clean up and exit.
- **
- ** Parameters:
- ** drop -- whether or not to drop CurEnv envelope
- ** exitstat -- exit status to use for exit() call
- **
- ** Returns:
- ** never
- **
- ** Side Effects:
- ** exits sendmail
- */
- void
- finis(drop, exitstat)
- bool drop;
- volatile int exitstat;
- {
- if (tTd(2, 1))
- {
- dprintf("n====finis: stat %d e_id=%s e_flags=",
- exitstat,
- CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
- printenvflags(CurEnv);
- }
- if (tTd(2, 9))
- printopenfds(FALSE);
- /* if we fail in finis(), just exit */
- if (setjmp(TopFrame) != 0)
- {
- /* failed -- just give it up */
- goto forceexit;
- }
- /* clean up temp files */
- CurEnv->e_to = NULL;
- if (drop)
- {
- if (CurEnv->e_id != NULL)
- dropenvelope(CurEnv, TRUE);
- else
- poststats(StatFile);
- }
- /* flush any cached connections */
- mci_flush(TRUE, NULL);
- /* close maps belonging to this pid */
- closemaps();
- #if USERDB
- /* close UserDatabase */
- _udbx_close();
- #endif /* USERDB */
- #ifdef XLA
- /* clean up extended load average stuff */
- xla_all_end();
- #endif /* XLA */
- /* and exit */
- forceexit:
- if (LogLevel > 78)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "finis, pid=%d",
- getpid());
- if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
- exitstat = EX_OK;
- sync_queue_time();
- /* reset uid for process accounting */
- endpwent();
- (void) setuid(RealUid);
- exit(exitstat);
- }
- /*
- ** INTSIG -- clean up on interrupt
- **
- ** This just arranges to exit. It pessimizes in that it
- ** may resend a message.
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** Unlocks the current job.
- */
- /* ARGSUSED */
- SIGFUNC_DECL
- intsig(sig)
- int sig;
- {
- bool drop = FALSE;
- clear_events();
- if (sig != 0 && LogLevel > 79)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
- FileName = NULL;
- closecontrolsocket(TRUE);
- #ifdef XLA
- xla_all_end();
- #endif /* XLA */
- /* Clean-up on aborted stdin message submission */
- if (CurEnv->e_id != NULL &&
- (OpMode == MD_SMTP ||
- OpMode == MD_DELIVER ||
- OpMode == MD_ARPAFTP))
- {
- register ADDRESS *q;
- /* don't return an error indication */
- CurEnv->e_to = NULL;
- CurEnv->e_flags &= ~EF_FATALERRS;
- CurEnv->e_flags |= EF_CLRQUEUE;
- /*
- ** Spin through the addresses and
- ** mark them dead to prevent bounces
- */
- for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
- q->q_state = QS_DONTSEND;
- /* and don't try to deliver the partial message either */
- if (InChild)
- ExitStat = EX_QUIT;
- drop = TRUE;
- }
- else
- unlockqueue(CurEnv);
- finis(drop, EX_OK);
- }
- /*
- ** INITMACROS -- initialize the macro system
- **
- ** This just involves defining some macros that are actually
- ** used internally as metasymbols to be themselves.
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** initializes several macros to be themselves.
- */
- struct metamac MetaMacros[] =
- {
- /* LHS pattern matching characters */
- { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
- { '=', MATCHCLASS }, { '~', MATCHNCLASS },
- /* these are RHS metasymbols */
- { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
- { '>', CALLSUBR },
- /* the conditional operations */
- { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
- /* the hostname lookup characters */
- { '[', HOSTBEGIN }, { ']', HOSTEND },
- { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
- /* miscellaneous control characters */
- { '&', MACRODEXPAND },
- { '' }
- };
- #define MACBINDING(name, mid)
- stab(name, ST_MACRO, ST_ENTER)->s_macro = mid;
- MacroName[mid] = name;
- void
- initmacros(e)
- register ENVELOPE *e;
- {
- register struct metamac *m;
- register int c;
- char buf[5];
- extern char *MacroName[256];
- for (m = MetaMacros; m->metaname != ''; m++)
- {
- buf[0] = m->metaval;
- buf[1] = '';
- define(m->metaname, newstr(buf), e);
- }
- buf[0] = MATCHREPL;
- buf[2] = '';
- for (c = '0'; c <= '9'; c++)
- {
- buf[1] = c;
- define(c, newstr(buf), e);
- }
- /* set defaults for some macros sendmail will use later */
- define('n', "MAILER-DAEMON", e);
- /* set up external names for some internal macros */
- MACBINDING("opMode", MID_OPMODE);
- /*XXX should probably add equivalents for all short macros here XXX*/
- }
- /*
- ** DISCONNECT -- remove our connection with any foreground process
- **
- ** Parameters:
- ** droplev -- how "deeply" we should drop the line.
- ** 0 -- ignore signals, mail back errors, make sure
- ** output goes to stdout.
- ** 1 -- also, make stdout go to /dev/null.
- ** 2 -- also, disconnect from controlling terminal
- ** (only for daemon mode).
- ** e -- the current envelope.
- **
- ** Returns:
- ** none
- **
- ** Side Effects:
- ** Trys to insure that we are immune to vagaries of
- ** the controlling tty.
- */
- void
- disconnect(droplev, e)
- int droplev;
- register ENVELOPE *e;
- {
- int fd;
- if (tTd(52, 1))
- dprintf("disconnect: In %d Out %d, e=%lxn",
- fileno(InChannel), fileno(OutChannel), (u_long) e);
- if (tTd(52, 100))
- {
- dprintf("don'tn");
- return;
- }
- if (LogLevel > 93)
- sm_syslog(LOG_DEBUG, e->e_id,
- "disconnect level %d",
- droplev);
- /* be sure we don't get nasty signals */
- (void) setsignal(SIGINT, SIG_IGN);
- (void) setsignal(SIGQUIT, SIG_IGN);
- /* we can't communicate with our caller, so.... */
- HoldErrs = TRUE;
- CurEnv->e_errormode = EM_MAIL;
- Verbose = 0;
- DisConnected = TRUE;
- /* all input from /dev/null */
- if (InChannel != stdin)
- {
- (void) fclose(InChannel);
- InChannel = stdin;
- }
- if (freopen("/dev/null", "r", stdin) == NULL)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: freopen("/dev/null") failed: %s",
- errstring(errno));
- /* output to the transcript */
- if (OutChannel != stdout)
- {
- (void) fclose(OutChannel);
- OutChannel = stdout;
- }
- if (droplev > 0)
- {
- fd = open("/dev/null", O_WRONLY, 0666);
- if (fd == -1)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: open("/dev/null") failed: %s",
- errstring(errno));
- (void) fflush(stdout);
- (void) dup2(fd, STDOUT_FILENO);
- (void) dup2(fd, STDERR_FILENO);
- (void) close(fd);
- }
- /* drop our controlling TTY completely if possible */
- if (droplev > 1)
- {
- (void) setsid();
- errno = 0;
- }
- #if XDEBUG
- checkfd012("disconnect");
- #endif /* XDEBUG */
- if (LogLevel > 71)
- sm_syslog(LOG_DEBUG, e->e_id,
- "in background, pid=%d",
- getpid());
- errno = 0;
- }
- static void
- obsolete(argv)
- char *argv[];
- {
- register char *ap;
- register char *op;
- while ((ap = *++argv) != NULL)
- {
- /* Return if "--" or not an option of any form. */
- if (ap[0] != '-' || ap[1] == '-')
- return;
- /* skip over options that do have a value */
- op = strchr(OPTIONS, ap[1]);
- if (op != NULL && *++op == ':' && ap[2] == '' &&
- ap[1] != 'd' &&
- #if defined(sony_news)
- ap[1] != 'E' && ap[1] != 'J' &&
- #endif /* defined(sony_news) */
- argv[1] != NULL && argv[1][0] != '-')
- {
- argv++;
- continue;
- }
- /* If -C doesn't have an argument, use sendmail.cf. */
- #define __DEFPATH "sendmail.cf"
- if (ap[1] == 'C' && ap[2] == '')
- {
- *argv = xalloc(sizeof(__DEFPATH) + 2);
- (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
- __DEFPATH);
- }
- /* If -q doesn't have an argument, run it once. */
- if (ap[1] == 'q' && ap[2] == '')
- *argv = "-q0";
- /* if -d doesn't have an argument, use 0-99.1 */
- if (ap[1] == 'd' && ap[2] == '')
- *argv = "-d0-99.1";
- #if defined(sony_news)
- /* if -E doesn't have an argument, use -EC */
- if (ap[1] == 'E' && ap[2] == '')
- *argv = "-EC";
- /* if -J doesn't have an argument, use -JJ */
- if (ap[1] == 'J' && ap[2] == '')
- *argv = "-JJ";
- #endif /* defined(sony_news) */
- }
- }
- /*
- ** AUTH_WARNING -- specify authorization warning
- **
- ** Parameters:
- ** e -- the current envelope.
- ** msg -- the text of the message.
- ** args -- arguments to the message.
- **
- ** Returns:
- ** none.
- */
- void
- #ifdef __STDC__
- auth_warning(register ENVELOPE *e, const char *msg, ...)
- #else /* __STDC__ */
- auth_warning(e, msg, va_alist)
- register ENVELOPE *e;
- const char *msg;
- va_dcl
- #endif /* __STDC__ */
- {
- char buf[MAXLINE];
- VA_LOCAL_DECL
- if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
- {
- register char *p;
- static char hostbuf[48];
- if (hostbuf[0] == '')
- (void) myhostname(hostbuf, sizeof hostbuf);
- (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
- p = &buf[strlen(buf)];
- VA_START(msg);
- vsnprintf(p, SPACELEFT(buf, p), msg, ap);
- VA_END;
- addheader("X-Authentication-Warning", buf, &e->e_header);
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Authentication-Warning: %.400s",
- buf);
- }
- }
- /*
- ** GETEXTENV -- get from external environment
- **
- ** Parameters:
- ** envar -- the name of the variable to retrieve
- **
- ** Returns:
- ** The value, if any.
- */
- char *
- getextenv(envar)
- const char *envar;
- {
- char **envp;
- int l;
- l = strlen(envar);
- for (envp = ExternalEnviron; *envp != NULL; envp++)
- {
- if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
- return &(*envp)[l + 1];
- }
- return NULL;
- }
- /*
- ** SETUSERENV -- set an environment in the propogated environment
- **
- ** Parameters:
- ** envar -- the name of the environment variable.
- ** value -- the value to which it should be set. If
- ** null, this is extracted from the incoming
- ** environment. If that is not set, the call
- ** to setuserenv is ignored.
- **
- ** Returns:
- ** none.
- */
- void
- setuserenv(envar, value)
- const char *envar;
- const char *value;
- {
- int i, l;
- char **evp = UserEnviron;
- char *p;
- if (value == NULL)
- {
- value = getextenv(envar);
- if (value == NULL)
- return;
- }
- i = strlen(envar) + 1;
- l = strlen(value) + i + 1;
- p = (char *) xalloc(l);
- (void) snprintf(p, l, "%s=%s", envar, value);
- while (*evp != NULL && strncmp(*evp, p, i) != 0)
- evp++;
- if (*evp != NULL)
- {
- *evp++ = p;
- }
- else if (evp < &UserEnviron[MAXUSERENVIRON])
- {
- *evp++ = p;
- *evp = NULL;
- }
- /* make sure it is in our environment as well */
- if (putenv(p) < 0)
- syserr("setuserenv: putenv(%s) failed", p);
- }
- /*
- ** DUMPSTATE -- dump state
- **
- ** For debugging.
- */
- void
- dumpstate(when)
- char *when;
- {
- register char *j = macvalue('j', CurEnv);
- int rs;
- extern int NextMacroId;
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- dumping state on %s: $j = %s ---",
- when,
- j == NULL ? "<NULL>" : j);
- if (j != NULL)
- {
- if (!wordinclass(j, 'w'))
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "*** $j not in $=w ***");
- }
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)n",
- NextMacroId, MAXMACROID);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
- printopenfds(TRUE);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
- mci_dump_all(TRUE);
- rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
- if (rs > 0)
- {
- int status;
- register char **pvp;
- char *pv[MAXATOM + 1];
- pv[0] = NULL;
- status = rewrite(pv, rs, 0, CurEnv);
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- ruleset debug_dumpstate returns stat %d, pv: ---",
- status);
- for (pvp = pv; *pvp != NULL; pvp++)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
- }
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
- }
- /* ARGSUSED */
- SIGFUNC_DECL
- sigusr1(sig)
- int sig;
- {
- dumpstate("user signal");
- return SIGFUNC_RETURN;
- }
- /* ARGSUSED */
- SIGFUNC_DECL
- sighup(sig)
- int sig;
- {
- int i;
- extern int DtableSize;
- clear_events();
- (void) alarm(0);
- if (SaveArgv[0][0] != '/')
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "could not restart: need full path");
- finis(FALSE, EX_OSFILE);
- }
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "restarting %s %s",
- sig == 0 ? "due to control command" : "on signal",
- SaveArgv[0]);
- /* Control socket restart? */
- if (sig != 0)
- (void) releasesignal(SIGHUP);
- closecontrolsocket(TRUE);
- if (drop_privileges(TRUE) != EX_OK)
- {
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "could not set[ug]id(%d, %d): %m",
- RunAsUid, RunAsGid);
- finis(FALSE, EX_OSERR);
- }
- /* arrange for all the files to be closed */
- for (i = 3; i < DtableSize; i++)
- {
- register int j;
- if ((j = fcntl(i, F_GETFD, 0)) != -1)
- (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
- }
- (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
- SaveArgv[0]);
- finis(FALSE, EX_OSFILE);
- }
- /*
- ** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
- **
- ** Parameters:
- ** to_real_uid -- if set, drop to the real uid instead
- ** of the RunAsUser.
- **
- ** Returns:
- ** EX_OSERR if the setuid failed.
- ** EX_OK otherwise.
- */
- int
- drop_privileges(to_real_uid)
- bool to_real_uid;
- {
- int rval = EX_OK;
- GIDSET_T emptygidset[1];
- if (tTd(47, 1))
- dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%dn",
- (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
- if (to_real_uid)
- {
- RunAsUserName = RealUserName;
- RunAsUid = RealUid;
- RunAsGid = RealGid;
- }
- /* make sure no one can grab open descriptors for secret files */
- endpwent();
- /* reset group permissions; these can be set later */
- emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
- if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
- rval = EX_OSERR;
- /* reset primary group and user id */
- if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
- rval = EX_OSERR;
- if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
- rval = EX_OSERR;
- if (tTd(47, 5))
- {
- dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%dn",
- (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
- dprintf("drop_privileges: RunAsUser = %d:%dn",
- (int)RunAsUid, (int)RunAsGid);
- }
- return rval;
- }
- /*
- ** FILL_FD -- make sure a file descriptor has been properly allocated
- **
- ** Used to make sure that stdin/out/err are allocated on startup
- **
- ** Parameters:
- ** fd -- the file descriptor to be filled.
- ** where -- a string used for logging. If NULL, this is
- ** being called on startup, and logging should
- ** not be done.
- **
- ** Returns:
- ** none
- */
- void
- fill_fd(fd, where)
- int fd;
- char *where;
- {
- int i;
- struct stat stbuf;
- if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
- return;
- if (where != NULL)
- syserr("fill_fd: %s: fd %d not open", where, fd);
- else
- MissingFds |= 1 << fd;
- i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
- if (i < 0)
- {
- syserr("!fill_fd: %s: cannot open /dev/null",
- where == NULL ? "startup" : where);
- }
- if (fd != i)
- {
- (void) dup2(i, fd);
- (void) close(i);
- }
- }
- /*
- ** TESTMODELINE -- process a test mode input line
- **
- ** Parameters:
- ** line -- the input line.
- ** e -- the current environment.
- ** Syntax:
- ** # a comment
- ** .X process X as a configuration line
- ** =X dump a configuration item (such as mailers)
- ** $X dump a macro or class
- ** /X try an activity
- ** X normal process through rule set X
- */
- static void
- testmodeline(line, e)
- char *line;
- ENVELOPE *e;
- {
- register char *p;
- char *q;
- auto char *delimptr;
- int mid;
- int i, rs;
- STAB *map;
- char **s;
- struct rewrite *rw;
- ADDRESS a;
- static int tryflags = RF_COPYNONE;
- char exbuf[MAXLINE];
- extern u_char TokTypeNoC[];
- #if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
- #endif /* _FFR_ADDR_TYPE */
- switch (line[0])
- {
- case '#':
- case '':
- return;
- case '?':
- help("-bt", e);
- return;
- case '.': /* config-style settings */
- switch (line[1])
- {
- case 'D':
- mid = macid(&line[2], &delimptr);
- if (mid == '')
- return;
- translate_dollars(delimptr);
- define(mid, newstr(delimptr), e);
- break;
- case 'C':
- if (line[2] == '') /* not to call syserr() */
- return;
- mid = macid(&line[2], &delimptr);
- if (mid == '')
- return;
- translate_dollars(delimptr);
- expand(delimptr, exbuf, sizeof exbuf, e);
- p = exbuf;
- while (*p != '')
- {
- register char *wd;
- char delim;
- while (*p != '' && isascii(*p) && isspace(*p))
- p++;
- wd = p;
- while (*p != '' && !(isascii(*p) && isspace(*p)))
- p++;
- delim = *p;
- *p = '';
- if (wd[0] != '')
- setclass(mid, wd);
- *p = delim;
- }
- break;
- case '':
- printf("Usage: .[DC]macro value(s)n");
- break;
- default:
- printf("Unknown "." command %sn", line);
- break;
- }
- return;
- case '=': /* config-style settings */
- switch (line[1])
- {
- case 'S': /* dump rule set */
- rs = strtorwset(&line[2], NULL, ST_FIND);
- if (rs < 0)
- {
- printf("Undefined ruleset %sn", &line[2]);
- return;
- }
- rw = RewriteRules[rs];
- if (rw == NULL)
- return;
- do
- {
- (void) putchar('R');
- s = rw->r_lhs;
- while (*s != NULL)
- {
- xputs(*s++);
- (void) putchar(' ');
- }
- (void) putchar('t');
- (void) putchar('t');
- s = rw->r_rhs;
- while (*s != NULL)
- {
- xputs(*s++);
- (void) putchar(' ');
- }
- (void) putchar('n');
- } while ((rw = rw->r_next) != NULL);
- break;
- case 'M':
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(Mailer[i]);
- }
- break;
- case '':
- printf("Usage: =Sruleset or =Mn");
- break;
- default:
- printf("Unknown "=" command %sn", line);
- break;
- }
- return;
- case '-': /* set command-line-like opts */
- switch (line[1])
- {
- case 'd':
- tTflag(&line[2]);
- break;
- case '':
- printf("Usage: -d{debug arguments}n");
- break;
- default:
- printf("Unknown "-" command %sn", line);
- break;
- }
- return;
- case '$':
- if (line[1] == '=')
- {
- mid = macid(&line[2], NULL);
- if (mid != '')
- stabapply(dump_class, mid);
- return;
- }
- mid = macid(&line[1], NULL);
- if (mid == '')
- return;
- p = macvalue(mid, e);
- if (p == NULL)
- printf("Undefinedn");
- else
- {
- xputs(p);
- printf("n");
- }
- return;
- case '/': /* miscellaneous commands */
- p = &line[strlen(line)];
- while (--p >= line && isascii(*p) && isspace(*p))
- *p = '';
- p = strpbrk(line, " t");
- if (p != NULL)
- {
- while (isascii(*p) && isspace(*p))
- *p++ = '';
- }
- else
- p = "";
- if (line[1] == '')
- {
- printf("Usage: /[canon|map|mx|parse|try|tryflags]n");
- return;
- }
- if (strcasecmp(&line[1], "quit") == 0)
- {
- CurEnv->e_id = NULL;
- finis(TRUE, ExitStat);
- }
- if (strcasecmp(&line[1], "mx") == 0)
- {
- #if NAMED_BIND
- /* look up MX records */
- int nmx;
- auto int rcode;
- char *mxhosts[MAXMXHOSTS + 1];
- if (*p == '')
- {
- printf("Usage: /mx addressn");
- return;
- }
- nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
- printf("getmxrr(%s) returns %d value(s):n", p, nmx);
- for (i = 0; i < nmx; i++)
- printf("t%sn", mxhosts[i]);
- #else /* NAMED_BIND */
- printf("No MX code compiled inn");
- #endif /* NAMED_BIND */
- }
- else if (strcasecmp(&line[1], "canon") == 0)
- {
- char host[MAXHOSTNAMELEN];
- if (*p == '')
- {
- printf("Usage: /canon addressn");
- return;
- }
- else if (strlcpy(host, p, sizeof host) >= sizeof host)
- {
- printf("Name too longn");
- return;
- }
- (void) getcanonname(host, sizeof host, HasWildcardMX);
- printf("getcanonname(%s) returns %sn", p, host);
- }
- else if (strcasecmp(&line[1], "map") == 0)
- {
- auto int rcode = EX_OK;
- char *av[2];
- if (*p == '')
- {
- printf("Usage: /map mapname keyn");
- return;
- }
- for (q = p; *q != '' && !(isascii(*q) && isspace(*q)); q++)
- continue;
- if (*q == '')
- {
- printf("No key specifiedn");
- return;
- }
- *q++ = '';
- map = stab(p, ST_MAP, ST_FIND);
- if (map == NULL)
- {
- printf("Map named "%s" not foundn", p);
- return;
- }
- if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
- !openmap(&(map->s_map)))
- {
- printf("Map named "%s" not openn", p);
- return;
- }
- printf("map_lookup: %s (%s) ", p, q);
- av[0] = q;
- av[1] = NULL;
- p = (*map->s_map.map_class->map_lookup)
- (&map->s_map, q, av, &rcode);
- if (p == NULL)
- printf("no match (%d)n", rcode);
- else
- printf("returns %s (%d)n", p, rcode);
- }
- else if (strcasecmp(&line[1], "try") == 0)
- {
- MAILER *m;
- STAB *st;
- auto int rcode = EX_OK;
- q = strpbrk(p, " t");
- if (q != NULL)
- {
- while (isascii(*q) && isspace(*q))
- *q++ = '';
- }
- if (q == NULL || *q == '')
- {
- printf("Usage: /try mailer addressn");
- return;
- }
- st = stab(p, ST_MAILER, ST_FIND);
- if (st == NULL)
- {
- printf("Unknown mailer %sn", p);
- return;
- }
- m = st->s_mailer;
- printf("Trying %s %s address %s for mailer %sn",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
- q, p);
- p = remotename(q, m, tryflags, &rcode, CurEnv);
- printf("Rcode = %d, addr = %sn",
- rcode, p == NULL ? "<NULL>" : p);
- e->e_to = NULL;
- }
- else if (strcasecmp(&line[1], "tryflags") == 0)
- {
- if (*p == '')
- {
- printf("Usage: /tryflags [Hh|Ee][Ss|Rr]n");
- return;
- }
- for (; *p != ''; p++)
- {
- switch (*p)
- {
- case 'H':
- case 'h':
- tryflags |= RF_HEADERADDR;
- break;
- case 'E':
- case 'e':
- tryflags &= ~RF_HEADERADDR;
- break;
- case 'S':
- case 's':
- tryflags |= RF_SENDERADDR;
- break;
- case 'R':
- case 'r':
- tryflags &= ~RF_SENDERADDR;
- break;
- }
- }
- #if _FFR_ADDR_TYPE
- exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
- exbuf[1] = ' ';
- exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
- exbuf[3] = '';
- define(macid("{addr_type}", NULL), newstr(exbuf), e);
- #endif /* _FFR_ADDR_TYPE */
- }
- else if (strcasecmp(&line[1], "parse") == 0)
- {
- if (*p == '')
- {
- printf("Usage: /parse addressn");
- return;
- }
- q = crackaddr(p);
- printf("Cracked address = ");
- xputs(q);
- printf("nParsing %s %s addressn",
- bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
- if (parseaddr(p, &a, tryflags, '', NULL, e) == NULL)
- printf("Cannot parsen");
- else if (a.q_host != NULL && a.q_host[0] != '')
- printf("mailer %s, host %s, user %sn",
- a.q_mailer->m_name, a.q_host, a.q_user);
- else
- printf("mailer %s, user %sn",
- a.q_mailer->m_name, a.q_user);
- e->e_to = NULL;
- }
- else
- {
- printf("Unknown "/" command %sn", line);
- }
- return;
- }
- for (p = line; isascii(*p) && isspace(*p); p++)
- continue;
- q = p;
- while (*p != '' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p == '')
- {
- printf("No address!n");
- return;
- }
- *p = '';
- if (invalidaddr(p + 1, NULL))
- return;
- do
- {
- register char **pvp;
- char pvpbuf[PSBUFSIZE];
- pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
- &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
- if (pvp == NULL)
- continue;
- p = q;
- while (*p != '')
- {
- int status;
- rs = strtorwset(p, NULL, ST_FIND);
- if (rs < 0)
- {
- printf("Undefined ruleset %sn", p);
- break;
- }
- status = rewrite(pvp, rs, 0, e);
- if (status != EX_OK)
- printf("== Ruleset %s (%d) status %dn",
- p, rs, status);
- while (*p != '' && *p++ != ',')
- continue;
- }
- } while (*(p = delimptr) != '');
- }
- static void
- dump_class(s, id)
- register STAB *s;
- int id;
- {
- if (s->s_type != ST_CLASS)
- return;
- if (bitnset(id & 0xff, s->s_class))
- printf("%sn", s->s_name);
- }