diff options
Diffstat (limited to 'usr.sbin/sendmail/src/main.c')
-rw-r--r-- | usr.sbin/sendmail/src/main.c | 1479 |
1 files changed, 1479 insertions, 0 deletions
diff --git a/usr.sbin/sendmail/src/main.c b/usr.sbin/sendmail/src/main.c new file mode 100644 index 0000000..e2af9df --- /dev/null +++ b/usr.sbin/sendmail/src/main.c @@ -0,0 +1,1479 @@ +/* + * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.55 (Berkeley) 4/15/94"; +#endif /* not lint */ + +#define _DEFINE + +#include "sendmail.h" +#if NAMED_BIND +#include <arpa/nameser.h> +#include <resolv.h> +#endif +#include <pwd.h> + +# ifdef lint +char edata, end; +# endif /* lint */ + +/* +** 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 tables read in from /usr/lib/sendmail.cf +** (read by readcf.c). Some more static configuration info, +** including some code that you may want to tailor for your +** installation, is in conf.c. You may also want to touch +** daemon.c (if you have some other IPC mechanism), acct.c +** (to change your accounting), names.c (to adjust the name +** server mechanism). +** +** 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 (from 11/81) +** Now back at UCB at the Mammoth project. +** The support of the INGRES Project and Britton-Lee is +** gratefully acknowledged. Britton-Lee in +** particular had absolutely nothing 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 */ +ENVELOPE MainEnvelope; /* the envelope around the basic letter */ +ADDRESS NullAddress = /* a null address */ + { "", "", NULL, "" }; +char *UserEnviron[MAXUSERENVIRON + 2]; + /* saved user environment */ +char RealUserName[256]; /* the actual user id on this host */ +char *CommandLineArgs; /* command line args for pid file */ +bool Warn_Q_option = FALSE; /* warn about Q option use */ + +/* +** Pointers for setproctitle. +** This allows "ps" listings to give more useful information. +*/ + +# ifdef SETPROCTITLE +char **Argv = NULL; /* pointer to argument vector */ +char *LastArgv = NULL; /* end of argv */ +# endif /* SETPROCTITLE */ + +static void obsolete(); + +#ifdef DAEMON +#ifndef SMTP +ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR +#endif /* SMTP */ +#endif /* DAEMON */ + +#define MAXCONFIGLEVEL 5 /* highest config version level known */ + +main(argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + register char *p; + char **av; + extern int finis(); + extern char Version[]; + char *ep, *from; + typedef int (*fnptr)(); + STAB *st; + register int i; + int j; + bool queuemode = FALSE; /* process queue requests */ + bool safecf = TRUE; + bool warn_C_flag = FALSE; + char warn_f_flag = '\0'; + static bool reenter = FALSE; + char *argv0 = argv[0]; + struct passwd *pw; + struct stat stb; + char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ + extern int DtableSize; + extern int optind; + extern time_t convtime(); + extern putheader(), putbody(); + extern void intsig(); + extern char **myhostname(); + extern char *arpadate(); + extern char *getauthinfo(); + extern char *getcfname(); + extern char *optarg; + extern char **environ; + extern void sigusr1(); + + /* + ** 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; + + /* do machine-dependent initializations */ + init_md(argc, argv); + + /* arrange to dump state on signal */ +#ifdef SIGUSR1 + setsignal(SIGUSR1, sigusr1); +#endif + + /* 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. + */ + + i = open("/dev/null", O_RDWR, 0); + if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP) + (void) dup2(i, STDIN_FILENO); + if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP) + (void) dup2(i, STDOUT_FILENO); + if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP) + (void) dup2(i, STDERR_FILENO); + if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) + (void) close(i); + + i = DtableSize; + while (--i > 0) + { + if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) + (void) close(i); + } + errno = 0; + +#ifdef LOG +# ifdef LOG_MAIL + openlog("sendmail", LOG_PID, LOG_MAIL); +# else + openlog("sendmail", LOG_PID); +# endif +#endif + + /* 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 = getpwuid(RealUid); + if (pw != NULL) + (void) strcpy(RealUserName, pw->pw_name); + else + (void) sprintf(RealUserName, "Unknown UID %d", RealUid); + + /* save command line arguments */ + i = 0; + for (av = argv; *av != NULL; ) + i += strlen(*av++) + 1; + CommandLineArgs = xalloc(i); + p = CommandLineArgs; + for (av = argv; *av != NULL; ) + { + if (av != argv) + *p++ = ' '; + strcpy(p, *av++); + p += strlen(p); + } + + /* 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:h:Iimno:p:q:r:sTtvX:x" +#endif +#if defined(ultrix) +# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mno:p:q:r:sTtvX:" +#endif +#if defined(NeXT) +# define OPTIONS "B:b:C:cd:e:F:f:h:IimnOo:p:q:r:sTtvX:" +#endif +#ifndef OPTIONS +# define OPTIONS "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:" +#endif + while ((j = getopt(argc, argv, OPTIONS)) != EOF) + { + switch (j) + { + case 'd': + tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); + tTflag(optarg); + setbuf(stdout, (char *) NULL); + printf("Version %s\n", Version); + break; + } + } + + InChannel = stdin; + OutChannel = stdout; + + /* + ** Move the environment so setproctitle can use the space at + ** the top of memory. + */ + + for (i = j = 0; j < MAXUSERENVIRON && (p = envp[i]) != NULL; i++) + { + if (strncmp(p, "FS=", 3) == 0 || strncmp(p, "LD_", 3) == 0) + continue; + UserEnviron[j++] = newstr(p); + } + UserEnviron[j] = NULL; + environ = UserEnviron; + +# ifdef SETPROCTITLE + /* + ** Save start and extent of argv for setproctitle. + */ + + Argv = argv; + if (i > 0) + LastArgv = envp[i - 1] + strlen(envp[i - 1]); + else + LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); +# endif /* SETPROCTITLE */ + + if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) + (void) setsignal(SIGINT, intsig); + if (setsignal(SIGHUP, SIG_IGN) != SIG_IGN) + (void) setsignal(SIGHUP, intsig); + (void) setsignal(SIGTERM, intsig); + (void) setsignal(SIGPIPE, SIG_IGN); + OldUmask = umask(022); + OpMode = MD_DELIVER; + FullName = getenv("NAME"); + +#if NAMED_BIND + if (tTd(8, 8)) + _res.options |= RES_DEBUG; +#endif + + errno = 0; + from = NULL; + + /* initialize some macros, etc. */ + initmacros(CurEnv); + + /* version */ + define('v', Version, CurEnv); + + /* hostname */ + av = myhostname(jbuf, sizeof jbuf); + if (jbuf[0] != '\0') + { + struct utsname utsname; + + if (tTd(0, 4)) + printf("canonical name: %s\n", 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] != '\0') + { + define('m', newstr(&p[1]), CurEnv); + setclass('m', &p[1]); + } + while (p != NULL && strchr(&p[1], '.') != NULL) + { + *p = '\0'; + setclass('w', jbuf); + *p++ = '.'; + p = strchr(p, '.'); + } + } + + if (uname(&utsname) >= 0) + p = utsname.nodename; + else + { + if (tTd(0, 22)) + printf("uname failed (%s)\n", errstring(errno)); + makelower(jbuf); + p = jbuf; + } + if (tTd(0, 4)) + printf("UUCP nodename: %s\n", p); + p = newstr(p); + define('k', p, CurEnv); + setclass('k', p); + setclass('w', p); + } + while (av != NULL && *av != NULL) + { + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", *av); + setclass('w', *av++); + } + + /* current time */ + define('b', arpadate((char *) NULL), CurEnv); + + /* + ** Find our real host name for future logging. + */ + + p = getauthinfo(STDIN_FILENO); + define('_', p, CurEnv); + + /* + ** 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; + + optind = 1; + while ((j = getopt(argc, argv, OPTIONS)) != EOF) + { + switch (j) + { + case 'b': /* operations mode */ + switch (j = *optarg) + { + case MD_DAEMON: +# ifdef DAEMON + if (RealUid != 0) { + usrerr("Permission denied"); + exit (EX_USAGE); + } + (void) unsetenv("HOSTALIASES"); +# else + usrerr("Daemon mode not implemented"); + ExitStat = EX_USAGE; + break; +# endif /* DAEMON */ + case MD_SMTP: +# ifndef SMTP + usrerr("I don't speak SMTP"); + ExitStat = EX_USAGE; + break; +# endif /* SMTP */ + case MD_DELIVER: + case MD_VERIFY: + case MD_TEST: + case MD_INITALIAS: + case MD_PRINT: +#ifdef MAYBE_NEXT_RELEASE + case MD_ARPAFTP: +#endif + 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 = newstr(optarg); + break; + + case 'C': /* select configuration file (already done) */ + if (RealUid != 0) + warn_C_flag = TRUE; + ConfFile = optarg; + (void) setgid(RealGid); + (void) setuid(RealUid); + 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(optarg); + if (strcmp(RealUserName, from) != 0) + warn_f_flag = j; + break; + + case 'F': /* set full name */ + FullName = newstr(optarg); + break; + + case 'h': /* hop count */ + CurEnv->e_hopcount = strtol(optarg, &ep, 10); + if (*ep) + { + usrerr("Bad hop count (%s)", optarg); + ExitStat = EX_USAGE; + break; + } + break; + + case 'n': /* don't alias */ + NoAlias = TRUE; + break; + + case 'o': /* set option */ + setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); + break; + + case 'p': /* set protocol */ + p = strchr(optarg, ':'); + if (p != NULL) + *p++ = '\0'; + if (*optarg != '\0') + define('r', newstr(optarg), CurEnv); + if (p != NULL && *p != '\0') + define('s', newstr(p), CurEnv); + break; + + case 'q': /* run queue files at intervals */ +# ifdef QUEUE + (void) unsetenv("HOSTALIASES"); + FullName = NULL; + queuemode = TRUE; + switch (optarg[0]) + { + case 'I': + QueueLimitId = newstr(&optarg[1]); + break; + + case 'R': + QueueLimitRecipient = newstr(&optarg[1]); + break; + + case 'S': + QueueLimitSender = newstr(&optarg[1]); + break; + + default: + QueueIntvl = convtime(optarg, 'm'); + break; + } +# else /* QUEUE */ + usrerr("I don't know about queues"); + ExitStat = EX_USAGE; +# endif /* QUEUE */ + break; + + case 't': /* read recipients from message */ + GrabTo = TRUE; + break; + + case 'X': /* traffic log file */ + setuid(RealUid); + TrafficLogFile = fopen(optarg, "a"); + if (TrafficLogFile == NULL) + { + syserr("cannot open %s", optarg); + break; + } +#ifdef HASSETVBUF + setvbuf(TrafficLogFile, NULL, _IOLBF, 0); +#else + setlinebuf(TrafficLogFile); +#endif + 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 */ +# if defined(ultrix) + case 'M': /* define macro */ +# endif + 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 +# if defined(NeXT) + case 'O': /* random flag that NeXT Mail.app passes */ + break; +# endif + + default: + ExitStat = EX_USAGE; + finis(); + break; + } + } + av += optind; + + /* + ** Do basic initialization. + ** Read system control file. + ** Extract special fields for local use. + */ + +#ifdef XDEBUG + checkfd012("before readcf"); +#endif + readcf(getcfname(), safecf, CurEnv); + + if (tTd(0, 1)) + { + printf("SYSTEM IDENTITY (after readcf):"); + printf("\n\t (short domain name) $w = "); + xputs(macvalue('w', CurEnv)); + printf("\n\t(canonical domain name) $j = "); + xputs(macvalue('j', CurEnv)); + printf("\n\t (subdomain name) $m = "); + xputs(macvalue('m', CurEnv)); + printf("\n\t (node name) $k = "); + xputs(macvalue('k', CurEnv)); + printf("\n"); + } + + /* + ** Process authorization warnings from command line. + */ + + if (warn_C_flag) + auth_warning(CurEnv, "Processed by %s with -C %s", + RealUserName, ConfFile); +/* + if (warn_f_flag != '\0') + auth_warning(CurEnv, "%s set sender to %s using -%c", + RealUserName, from, warn_f_flag); +*/ + if (Warn_Q_option) + auth_warning(CurEnv, "Processed from queue %s", QueueDir); + + /* Enforce use of local time (null string overrides this) */ + if (TimeZoneSpec == NULL) + unsetenv("TZ"); + else if (TimeZoneSpec[0] != '\0') + { + char **evp = UserEnviron; + char tzbuf[100]; + + strcpy(tzbuf, "TZ="); + strcpy(&tzbuf[3], TimeZoneSpec); + + while (*evp != NULL && strncmp(*evp, "TZ=", 3) != 0) + evp++; + if (*evp == NULL) + { + *evp++ = newstr(tzbuf); + *evp = NULL; + } + else + *evp++ = newstr(tzbuf); + } + + if (ConfigLevel > MAXCONFIGLEVEL) + { + syserr("Warning: .cf version level (%d) exceeds program functionality (%d)", + ConfigLevel, MAXCONFIGLEVEL); + } + + if (MeToo) + BlankEnvelope.e_flags |= EF_METOO; + +# ifdef QUEUE + if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) + { + struct stat stbuf; + + /* check to see if we own the queue directory */ + if (stat(QueueDir, &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"); + exit (EX_NOPERM); + } + } +# endif /* QUEUE */ + + switch (OpMode) + { + case MD_INITALIAS: + Verbose = TRUE; + break; + + case MD_DAEMON: + /* remove things that don't make sense in daemon mode */ + FullName = NULL; + break; + } + + /* 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); + } + + if (ConfigLevel < 3) + { + UseErrorsTo = TRUE; + } + + /* our name for SMTP codes */ + expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv); + MyHostName = 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) + syserr("No local mailer defined"); + else + LocalMailer = st->s_mailer; + + st = stab("prog", ST_MAILER, ST_FIND); + if (st == NULL) + syserr("No prog mailer defined"); + else + ProgMailer = st->s_mailer; + + st = stab("*file*", ST_MAILER, ST_FIND); + if (st == NULL) + syserr("No *file* mailer defined"); + else + FileMailer = st->s_mailer; + + st = stab("*include*", ST_MAILER, ST_FIND); + if (st == NULL) + syserr("No *include* mailer defined"); + else + InclMailer = st->s_mailer; + + + /* operate in queue directory */ + if (OpMode != MD_TEST && chdir(QueueDir) < 0) + { + syserr("cannot chdir(%s)", QueueDir); + ExitStat = EX_SOFTWARE; + } + + /* if we've had errors so far, exit now */ + if (ExitStat != EX_OK && OpMode != MD_TEST) + { + setuid(RealUid); + exit(ExitStat); + } + +#ifdef XDEBUG + checkfd012("before main() initmaps"); +#endif + + /* + ** Do operation-mode-dependent initialization. + */ + + switch (OpMode) + { + case MD_PRINT: + /* print the queue */ +#ifdef QUEUE + dropenvelope(CurEnv); + printqueue(); + setuid(RealUid); + exit(EX_OK); +#else /* QUEUE */ + usrerr("No queue to print"); + finis(); +#endif /* QUEUE */ + + case MD_INITALIAS: + /* initialize alias database */ + initmaps(TRUE, CurEnv); + setuid(RealUid); + exit(EX_OK); + + case MD_DAEMON: + /* don't open alias database -- done in srvrsmtp */ + break; + + default: + /* open the alias database */ + initmaps(FALSE, CurEnv); + break; + } + + if (tTd(0, 15)) + { + /* print configuration table (or at least part of it) */ + printrules(); + for (i = 0; i < MAXMAILERS; i++) + { + register struct mailer *m = Mailer[i]; + int j; + + if (m == NULL) + continue; + printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld F=", i, m->m_name, + m->m_mailer, m->m_se_rwset, m->m_sh_rwset, + m->m_re_rwset, m->m_rh_rwset, m->m_maxsize); + for (j = '\0'; j <= '\177'; j++) + if (bitnset(j, m->m_flags)) + (void) putchar(j); + printf(" E="); + xputs(m->m_eol); + if (m->m_argv != NULL) + { + char **a = m->m_argv; + + printf(" A="); + while (*a != NULL) + { + if (a != m->m_argv) + printf(" "); + xputs(*a++); + } + } + printf("\n"); + } + } + + /* + ** 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 = TRUE; + + if (Verbose) + { + printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); + printf("Enter <ruleset> <address>\n"); + } + for (;;) + { + register char **pvp; + char *q; + auto char *delimptr; + extern bool invalidaddr(); + extern char *crackaddr(); + + if (Verbose) + printf("> "); + (void) fflush(stdout); + if (fgets(buf, sizeof buf, stdin) == NULL) + finis(); + if (!Verbose) + printf("> %s", buf); + switch (buf[0]) + { + case '#': + continue; + +#ifdef MAYBENEXTRELEASE + case 'C': /* try crackaddr */ + q = crackaddr(&buf[1]); + xputs(q); + printf("\n"); + continue; +#endif + } + + for (p = buf; isascii(*p) && isspace(*p); p++) + continue; + q = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p == '\0') + { + printf("No address!\n"); + continue; + } + *p = '\0'; + if (invalidaddr(p + 1, NULL)) + continue; + do + { + char pvpbuf[PSBUFSIZE]; + + pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, + &delimptr); + if (pvp == NULL) + continue; + p = q; + while (*p != '\0') + { + int stat; + + stat = rewrite(pvp, atoi(p), 0, CurEnv); + if (stat != EX_OK) + printf("== Ruleset %s status %d\n", + p, stat); + while (*p != '\0' && *p++ != ',') + continue; + } + } while (*(p = delimptr) != '\0'); + } + } + +# ifdef QUEUE + /* + ** If collecting stuff from the queue, go start doing that. + */ + + if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0) + { + runqueue(FALSE); + finis(); + } +# 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 (!tTd(0, 1)) + { + /* put us in background */ + i = fork(); + if (i < 0) + syserr("daemon: cannot fork"); + if (i != 0) + exit(0); + + /* disconnect from our controlling tty */ + disconnect(2, CurEnv); + } + + dtype[0] = '\0'; + if (OpMode == MD_DAEMON) + strcat(dtype, "+SMTP"); + if (QueueIntvl != 0) + { + strcat(dtype, "+queueing@"); + strcat(dtype, pintvl(QueueIntvl, TRUE)); + } + if (tTd(0, 1)) + strcat(dtype, "+debugging"); + +#ifdef LOG + syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1); +#endif +#ifdef XLA + xla_create_file(); +#endif + +# ifdef QUEUE + if (queuemode) + { + runqueue(TRUE); + if (OpMode != MD_DAEMON) + for (;;) + pause(); + } +# endif /* QUEUE */ + dropenvelope(CurEnv); + +#ifdef DAEMON + getrequests(); + + /* at this point we are in a child: reset state */ + (void) newenvelope(CurEnv, CurEnv); + + /* + ** Get authentication data + */ + + p = getauthinfo(fileno(InChannel)); + define('_', p, CurEnv); + +#endif /* DAEMON */ + } + +# ifdef SMTP + /* + ** If running SMTP protocol, start collecting and executing + ** commands. This will never return. + */ + + if (OpMode == MD_SMTP || OpMode == MD_DAEMON) + smtp(CurEnv); +# endif /* SMTP */ + + if (OpMode == MD_VERIFY) + { + CurEnv->e_sendmode = SM_VERIFY; + CurEnv->e_errormode = EM_QUIET; + } + else + { + /* interactive -- all errors are global */ + CurEnv->e_flags |= EF_GLOBALERRS; + } + + /* + ** Do basic system initialization and set the sender + */ + + initsys(CurEnv); + setsender(from, CurEnv, NULL, FALSE); + if (macvalue('s', CurEnv) == NULL) + define('s', RealHostName, CurEnv); + + if (*av == NULL && !GrabTo) + { + CurEnv->e_flags |= EF_GLOBALERRS; + usrerr("Recipient names must be specified"); + + /* collect body for UUCP return */ + if (OpMode != MD_VERIFY) + collect(FALSE, FALSE, CurEnv); + finis(); + } + + /* + ** 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; + + /* + ** Read the input mail. + */ + + CurEnv->e_to = NULL; + if (OpMode != MD_VERIFY || GrabTo) + { + CurEnv->e_flags |= EF_GLOBALERRS; + collect(FALSE, FALSE, CurEnv); + } + errno = 0; + + if (tTd(1, 1)) + printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); + + /* + ** Actually send everything. + ** If verifying, just ack. + */ + + CurEnv->e_from.q_flags |= QDONTSEND; + if (tTd(1, 5)) + { + printf("main: QDONTSEND "); + printaddr(&CurEnv->e_from, FALSE); + } + CurEnv->e_to = NULL; + sendall(CurEnv, SM_DEFAULT); + + /* + ** All done. + ** Don't send return error message if in VERIFY mode. + */ + + finis(); +} +/* +** FINIS -- Clean up and exit. +** +** Parameters: +** none +** +** Returns: +** never +** +** Side Effects: +** exits sendmail +*/ + +finis() +{ + if (tTd(2, 1)) + printf("\n====finis: stat %d e_flags %o, e_id=%s\n", + ExitStat, CurEnv->e_flags, + CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); + if (tTd(2, 9)) + printopenfds(FALSE); + + /* clean up temp files */ + CurEnv->e_to = NULL; + dropenvelope(CurEnv); + + /* flush any cached connections */ + mci_flush(TRUE, NULL); + +# ifdef XLA + /* clean up extended load average stuff */ + xla_all_end(); +# endif + + /* and exit */ +# ifdef LOG + if (LogLevel > 78) + syslog(LOG_DEBUG, "finis, pid=%d", getpid()); +# endif /* LOG */ + if (ExitStat == EX_TEMPFAIL) + ExitStat = EX_OK; + + /* reset uid for process accounting */ + setuid(RealUid); + + exit(ExitStat); +} +/* +** INTSIG -- clean up on interrupt +** +** This just arranges to exit. It pessimises in that it +** may resend a message. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Unlocks the current job. +*/ + +void +intsig() +{ + FileName = NULL; + unlockqueue(CurEnv); +#ifdef XLA + xla_all_end(); +#endif + + /* reset uid for process accounting */ + setuid(RealUid); + + exit(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, + + '\0' +}; + +initmacros(e) + register ENVELOPE *e; +{ + register struct metamac *m; + char buf[5]; + register int c; + + for (m = MetaMacros; m->metaname != '\0'; m++) + { + buf[0] = m->metaval; + buf[1] = '\0'; + define(m->metaname, newstr(buf), e); + } + buf[0] = MATCHREPL; + buf[2] = '\0'; + for (c = '0'; c <= '9'; c++) + { + buf[1] = c; + define(c, newstr(buf), e); + } + + /* set defaults for some macros sendmail will use later */ + define('e', "\201j Sendmail \201v ready at \201b", e); + define('l', "From \201g \201d", e); + define('n', "MAILER-DAEMON", e); + define('o', ".:@[]", e); + define('q', "<\201g>", e); +} +/* +** 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 transcript. +** 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. +*/ + +disconnect(droplev, e) + int droplev; + register ENVELOPE *e; +{ + int fd; + + if (tTd(52, 1)) + printf("disconnect: In %d Out %d, e=%x\n", + fileno(InChannel), fileno(OutChannel), e); + if (tTd(52, 5)) + { + printf("don't\n"); + return; + } + + /* be sure we don't get nasty signals */ + (void) setsignal(SIGHUP, SIG_IGN); + (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 = FALSE; + DisConnected = TRUE; + + /* all input from /dev/null */ + if (InChannel != stdin) + { + (void) fclose(InChannel); + InChannel = stdin; + } + (void) freopen("/dev/null", "r", stdin); + + /* output to the transcript */ + if (OutChannel != stdout) + { + (void) fclose(OutChannel); + OutChannel = stdout; + } + if (droplev > 0) + { + if (e->e_xfp == NULL) + fd = open("/dev/null", O_WRONLY, 0666); + else + fd = fileno(e->e_xfp); + (void) fflush(stdout); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (e->e_xfp == NULL) + close(fd); + } + + /* drop our controlling TTY completely if possible */ + if (droplev > 1) + { + (void) setsid(); + errno = 0; + } + +#ifdef XDEBUG + checkfd012("disconnect"); +#endif + +# ifdef LOG + if (LogLevel > 71) + syslog(LOG_DEBUG, "in background, pid=%d", getpid()); +# endif /* LOG */ + + 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] == '\0' && + ap[1] != 'd' && 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] == '\0') + { + *argv = xalloc(sizeof(__DEFPATH) + 2); + argv[0][0] = '-'; + argv[0][1] = 'C'; + (void)strcpy(&argv[0][2], __DEFPATH); + } + + /* If -q doesn't have an argument, run it once. */ + if (ap[1] == 'q' && ap[2] == '\0') + *argv = "-q0"; + + /* if -d doesn't have an argument, use 0-99.1 */ + if (ap[1] == 'd' && ap[2] == '\0') + *argv = "-d0-99.1"; + } +} +/* +** 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 +auth_warning(e, msg, va_alist) + register ENVELOPE *e; + const char *msg; + va_dcl +#endif +{ + char buf[MAXLINE]; + VA_LOCAL_DECL + + if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) + { + register char *p; + static char hostbuf[48]; + extern char **myhostname(); + + if (hostbuf[0] == '\0') + (void) myhostname(hostbuf, sizeof hostbuf); + + (void) sprintf(buf, "%s: ", hostbuf); + p = &buf[strlen(buf)]; + VA_START(msg); + vsprintf(p, msg, ap); + VA_END; + addheader("X-Authentication-Warning", buf, e); + } +} +/* +** DUMPSTATE -- dump state +** +** For debugging. +*/ + +void +dumpstate(when) + char *when; +{ +#ifdef LOG + register char *j = macvalue('j', CurEnv); + register STAB *s; + + syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---", + when, + j == NULL ? "<NULL>" : j); + if (j != NULL) + { + s = stab(j, ST_CLASS, ST_FIND); + if (s == NULL || !bitnset('w', s->s_class)) + syslog(LOG_DEBUG, "*** $j not in $=w ***"); + } + syslog(LOG_DEBUG, "--- open file descriptors: ---"); + printopenfds(TRUE); + syslog(LOG_DEBUG, "--- connection cache: ---"); + mci_dump_all(TRUE); + if (RewriteRules[89] != NULL) + { + int stat; + register char **pvp; + char *pv[MAXATOM + 1]; + + pv[0] = NULL; + stat = rewrite(pv, 89, 0, CurEnv); + syslog(LOG_DEBUG, "--- ruleset 89 returns stat %d, pv: ---", + stat); + for (pvp = pv; *pvp != NULL; pvp++) + syslog(LOG_DEBUG, "%s", *pvp); + } + syslog(LOG_DEBUG, "--- end of state dump ---"); +#endif +} + + +void +sigusr1() +{ + dumpstate("user signal"); +} |