main.c revision 38032
138032Speter/* 238032Speter * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 438032Speter * Copyright (c) 1988, 1993 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1338032Speter#ifndef lint 1438032Speterstatic char copyright[] = 1538032Speter"@(#) Copyright (c) 1998 Sendmail, Inc. All rights reserved.\n\ 1638032Speter Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ 1738032Speter Copyright (c) 1988, 1993\n\ 1838032Speter The Regents of the University of California. All rights reserved.\n"; 1938032Speter#endif /* not lint */ 2038032Speter 2138032Speter#ifndef lint 2238032Speterstatic char sccsid[] = "@(#)main.c 8.302 (Berkeley) 6/4/98"; 2338032Speter#endif /* not lint */ 2438032Speter 2538032Speter#define _DEFINE 2638032Speter 2738032Speter#include "sendmail.h" 2838032Speter#include <arpa/inet.h> 2938032Speter#include <grp.h> 3038032Speter#if NAMED_BIND 3138032Speter#include <resolv.h> 3238032Speter#endif 3338032Speter 3438032Speter/* 3538032Speter** SENDMAIL -- Post mail to a set of destinations. 3638032Speter** 3738032Speter** This is the basic mail router. All user mail programs should 3838032Speter** call this routine to actually deliver mail. Sendmail in 3938032Speter** turn calls a bunch of mail servers that do the real work of 4038032Speter** delivering the mail. 4138032Speter** 4238032Speter** Sendmail is driven by settings read in from /etc/sendmail.cf 4338032Speter** (read by readcf.c). 4438032Speter** 4538032Speter** Usage: 4638032Speter** /usr/lib/sendmail [flags] addr ... 4738032Speter** 4838032Speter** See the associated documentation for details. 4938032Speter** 5038032Speter** Author: 5138032Speter** Eric Allman, UCB/INGRES (until 10/81). 5238032Speter** Britton-Lee, Inc., purveyors of fine 5338032Speter** database computers (11/81 - 10/88). 5438032Speter** International Computer Science Institute 5538032Speter** (11/88 - 9/89). 5638032Speter** UCB/Mammoth Project (10/89 - 7/95). 5738032Speter** InReference, Inc. (8/95 - 1/97). 5838032Speter** Sendmail, Inc. (1/98 - present). 5938032Speter** The support of the my employers is gratefully acknowledged. 6038032Speter** Few of them (Britton-Lee in particular) have had 6138032Speter** anything to gain from my involvement in this project. 6238032Speter*/ 6338032Speter 6438032Speter 6538032Speterint NextMailer; /* "free" index into Mailer struct */ 6638032Speterchar *FullName; /* sender's full name */ 6738032SpeterENVELOPE BlankEnvelope; /* a "blank" envelope */ 6838032SpeterENVELOPE MainEnvelope; /* the envelope around the basic letter */ 6938032SpeterADDRESS NullAddress = /* a null address */ 7038032Speter { "", "", NULL, "" }; 7138032Speterchar *CommandLineArgs; /* command line args for pid file */ 7238032Speterbool Warn_Q_option = FALSE; /* warn about Q option use */ 7338032Speterchar **SaveArgv; /* argument vector for re-execing */ 7438032Speterint MissingFds = 0; /* bit map of fds missing on startup */ 7538032Speter 7638032Speter#ifdef NGROUPS_MAX 7738032SpeterGIDSET_T InitialGidSet[NGROUPS_MAX]; 7838032Speter#endif 7938032Speter 8038032Speterstatic void obsolete __P((char **)); 8138032Speterextern void printmailer __P((MAILER *)); 8238032Speterextern void tTflag __P((char *)); 8338032Speter 8438032Speter#if DAEMON && !SMTP 8538032SpeterERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR 8638032Speter#endif /* DAEMON && !SMTP */ 8738032Speter#if SMTP && !QUEUE 8838032SpeterERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR 8938032Speter#endif /* DAEMON && !SMTP */ 9038032Speter 9138032Speter#define MAXCONFIGLEVEL 8 /* highest config version level known */ 9238032Speter 9338032Speterint 9438032Spetermain(argc, argv, envp) 9538032Speter int argc; 9638032Speter char **argv; 9738032Speter char **envp; 9838032Speter{ 9938032Speter register char *p; 10038032Speter char **av; 10138032Speter extern char Version[]; 10238032Speter char *ep, *from; 10338032Speter STAB *st; 10438032Speter register int i; 10538032Speter int j; 10638032Speter bool queuemode = FALSE; /* process queue requests */ 10738032Speter bool safecf = TRUE; 10838032Speter bool warn_C_flag = FALSE; 10938032Speter char warn_f_flag = '\0'; 11038032Speter bool run_in_foreground = FALSE; /* -bD mode */ 11138032Speter static bool reenter = FALSE; 11238032Speter struct passwd *pw; 11338032Speter struct hostent *hp; 11438032Speter char *nullserver = NULL; 11538032Speter bool forged; 11638032Speter char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ 11738032Speter static char rnamebuf[MAXNAME]; /* holds RealUserName */ 11838032Speter char *emptyenviron[1]; 11938032Speter QUEUE_CHAR *new; 12038032Speter extern int DtableSize; 12138032Speter extern int optind; 12238032Speter extern int opterr; 12338032Speter extern char *optarg; 12438032Speter extern char **environ; 12538032Speter extern time_t convtime __P((char *, char)); 12638032Speter extern SIGFUNC_DECL intsig __P((int)); 12738032Speter extern struct hostent *myhostname __P((char *, int)); 12838032Speter extern char *getauthinfo __P((int, bool *)); 12938032Speter extern char *getcfname __P((void)); 13038032Speter extern SIGFUNC_DECL sigusr1 __P((int)); 13138032Speter extern SIGFUNC_DECL sighup __P((int)); 13238032Speter extern void initmacros __P((ENVELOPE *)); 13338032Speter extern void init_md __P((int, char **)); 13438032Speter extern int getdtsize __P((void)); 13538032Speter extern void tTsetup __P((u_char *, int, char *)); 13638032Speter extern void setdefaults __P((ENVELOPE *)); 13738032Speter extern void initsetproctitle __P((int, char **, char **)); 13838032Speter extern void init_vendor_macros __P((ENVELOPE *)); 13938032Speter extern void load_if_names __P((void)); 14038032Speter extern void vendor_pre_defaults __P((ENVELOPE *)); 14138032Speter extern void vendor_post_defaults __P((ENVELOPE *)); 14238032Speter extern void readcf __P((char *, bool, ENVELOPE *)); 14338032Speter extern void printqueue __P((void)); 14438032Speter extern void sendtoargv __P((char **, ENVELOPE *)); 14538032Speter extern void resetlimits __P((void)); 14638032Speter#ifndef HASUNSETENV 14738032Speter extern void unsetenv __P((char *)); 14838032Speter#endif 14938032Speter 15038032Speter /* 15138032Speter ** Check to see if we reentered. 15238032Speter ** This would normally happen if e_putheader or e_putbody 15338032Speter ** were NULL when invoked. 15438032Speter */ 15538032Speter 15638032Speter if (reenter) 15738032Speter { 15838032Speter syserr("main: reentered!"); 15938032Speter abort(); 16038032Speter } 16138032Speter reenter = TRUE; 16238032Speter 16338032Speter /* avoid null pointer dereferences */ 16438032Speter TermEscape.te_rv_on = TermEscape.te_rv_off = ""; 16538032Speter 16638032Speter /* do machine-dependent initializations */ 16738032Speter init_md(argc, argv); 16838032Speter 16938032Speter /* in 4.4BSD, the table can be huge; impose a reasonable limit */ 17038032Speter DtableSize = getdtsize(); 17138032Speter if (DtableSize > 256) 17238032Speter DtableSize = 256; 17338032Speter 17438032Speter /* 17538032Speter ** Be sure we have enough file descriptors. 17638032Speter ** But also be sure that 0, 1, & 2 are open. 17738032Speter */ 17838032Speter 17938032Speter fill_fd(STDIN_FILENO, NULL); 18038032Speter fill_fd(STDOUT_FILENO, NULL); 18138032Speter fill_fd(STDERR_FILENO, NULL); 18238032Speter 18338032Speter i = DtableSize; 18438032Speter while (--i > 0) 18538032Speter { 18638032Speter if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) 18738032Speter (void) close(i); 18838032Speter } 18938032Speter errno = 0; 19038032Speter 19138032Speter#if LOG 19238032Speter# ifdef LOG_MAIL 19338032Speter openlog("sendmail", LOG_PID, LOG_MAIL); 19438032Speter# else 19538032Speter openlog("sendmail", LOG_PID); 19638032Speter# endif 19738032Speter#endif 19838032Speter 19938032Speter if (MissingFds != 0) 20038032Speter { 20138032Speter char mbuf[MAXLINE]; 20238032Speter 20338032Speter mbuf[0] = '\0'; 20438032Speter if (bitset(1 << STDIN_FILENO, MissingFds)) 20538032Speter strcat(mbuf, ", stdin"); 20638032Speter if (bitset(1 << STDOUT_FILENO, MissingFds)) 20738032Speter strcat(mbuf, ", stdout"); 20838032Speter if (bitset(1 << STDERR_FILENO, MissingFds)) 20938032Speter strcat(mbuf, ", stderr"); 21038032Speter syserr("File descriptors missing on startup: %s", &mbuf[2]); 21138032Speter } 21238032Speter 21338032Speter /* reset status from syserr() calls for missing file descriptors */ 21438032Speter Errors = 0; 21538032Speter ExitStat = EX_OK; 21638032Speter 21738032Speter#if XDEBUG 21838032Speter checkfd012("after openlog"); 21938032Speter#endif 22038032Speter 22138032Speter tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 22238032Speter 22338032Speter#ifdef NGROUPS_MAX 22438032Speter /* save initial group set for future checks */ 22538032Speter i = getgroups(NGROUPS_MAX, InitialGidSet); 22638032Speter if (i == 0) 22738032Speter InitialGidSet[0] = (GID_T) -1; 22838032Speter while (i < NGROUPS_MAX) 22938032Speter InitialGidSet[i++] = InitialGidSet[0]; 23038032Speter#endif 23138032Speter 23238032Speter /* drop group id privileges (RunAsUser not yet set) */ 23338032Speter (void) drop_privileges(FALSE); 23438032Speter 23538032Speter#ifdef SIGUSR1 23638032Speter /* arrange to dump state on user-1 signal */ 23738032Speter setsignal(SIGUSR1, sigusr1); 23838032Speter#endif 23938032Speter 24038032Speter /* initialize for setproctitle */ 24138032Speter initsetproctitle(argc, argv, envp); 24238032Speter 24338032Speter /* Handle any non-getoptable constructions. */ 24438032Speter obsolete(argv); 24538032Speter 24638032Speter /* 24738032Speter ** Do a quick prescan of the argument list. 24838032Speter */ 24938032Speter 25038032Speter#if defined(__osf__) || defined(_AIX3) 25138032Speter# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x" 25238032Speter#endif 25338032Speter#if defined(sony_news) 25438032Speter# define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:" 25538032Speter#endif 25638032Speter#ifndef OPTIONS 25738032Speter# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:" 25838032Speter#endif 25938032Speter opterr = 0; 26038032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 26138032Speter { 26238032Speter switch (j) 26338032Speter { 26438032Speter case 'd': 26538032Speter /* hack attack -- see if should use ANSI mode */ 26638032Speter if (strcmp(optarg, "ANSI") == 0) 26738032Speter { 26838032Speter TermEscape.te_rv_on = "\033[7m"; 26938032Speter TermEscape.te_rv_off = "\033[0m"; 27038032Speter break; 27138032Speter } 27238032Speter tTflag(optarg); 27338032Speter setbuf(stdout, (char *) NULL); 27438032Speter break; 27538032Speter } 27638032Speter } 27738032Speter opterr = 1; 27838032Speter 27938032Speter /* set up the blank envelope */ 28038032Speter BlankEnvelope.e_puthdr = putheader; 28138032Speter BlankEnvelope.e_putbody = putbody; 28238032Speter BlankEnvelope.e_xfp = NULL; 28338032Speter STRUCTCOPY(NullAddress, BlankEnvelope.e_from); 28438032Speter CurEnv = &BlankEnvelope; 28538032Speter STRUCTCOPY(NullAddress, MainEnvelope.e_from); 28638032Speter 28738032Speter /* 28838032Speter ** Set default values for variables. 28938032Speter ** These cannot be in initialized data space. 29038032Speter */ 29138032Speter 29238032Speter setdefaults(&BlankEnvelope); 29338032Speter 29438032Speter RealUid = getuid(); 29538032Speter RealGid = getgid(); 29638032Speter 29738032Speter pw = sm_getpwuid(RealUid); 29838032Speter if (pw != NULL) 29938032Speter (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); 30038032Speter else 30138032Speter (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid); 30238032Speter RealUserName = rnamebuf; 30338032Speter 30438032Speter if (tTd(0, 101)) 30538032Speter { 30638032Speter printf("Version %s\n", Version); 30738032Speter endpwent(); 30838032Speter setuid(RealUid); 30938032Speter exit(EX_OK); 31038032Speter } 31138032Speter 31238032Speter /* 31338032Speter ** if running non-setuid binary as non-root, pretend 31438032Speter ** we are the RunAsUid 31538032Speter */ 31638032Speter if (RealUid != 0 && geteuid() == RealUid) 31738032Speter { 31838032Speter if (tTd(47, 1)) 31938032Speter printf("Non-setuid binary: RunAsUid = RealUid = %d\n", 32038032Speter (int)RealUid); 32138032Speter RunAsUid = RealUid; 32238032Speter } 32338032Speter else if (geteuid() != 0) 32438032Speter RunAsUid = geteuid(); 32538032Speter 32638032Speter if (RealUid != 0 && getegid() == RealGid) 32738032Speter RunAsGid = RealGid; 32838032Speter 32938032Speter if (tTd(47, 5)) 33038032Speter { 33138032Speter printf("main: e/ruid = %d/%d e/rgid = %d/%d\n", 33238032Speter (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid()); 33338032Speter printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); 33438032Speter } 33538032Speter 33638032Speter /* save command line arguments */ 33738032Speter i = 0; 33838032Speter for (av = argv; *av != NULL; ) 33938032Speter i += strlen(*av++) + 1; 34038032Speter SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); 34138032Speter CommandLineArgs = xalloc(i); 34238032Speter p = CommandLineArgs; 34338032Speter for (av = argv, i = 0; *av != NULL; ) 34438032Speter { 34538032Speter SaveArgv[i++] = newstr(*av); 34638032Speter if (av != argv) 34738032Speter *p++ = ' '; 34838032Speter strcpy(p, *av++); 34938032Speter p += strlen(p); 35038032Speter } 35138032Speter SaveArgv[i] = NULL; 35238032Speter 35338032Speter if (tTd(0, 1)) 35438032Speter { 35538032Speter int ll; 35638032Speter extern char *CompileOptions[]; 35738032Speter 35838032Speter printf("Version %s\n Compiled with:", Version); 35938032Speter av = CompileOptions; 36038032Speter ll = 7; 36138032Speter while (*av != NULL) 36238032Speter { 36338032Speter if (ll + strlen(*av) > 63) 36438032Speter { 36538032Speter putchar('\n'); 36638032Speter ll = 0; 36738032Speter } 36838032Speter if (ll == 0) 36938032Speter { 37038032Speter putchar('\t'); 37138032Speter putchar('\t'); 37238032Speter } 37338032Speter else 37438032Speter putchar(' '); 37538032Speter printf("%s", *av); 37638032Speter ll += strlen(*av++) + 1; 37738032Speter } 37838032Speter putchar('\n'); 37938032Speter } 38038032Speter if (tTd(0, 10)) 38138032Speter { 38238032Speter int ll; 38338032Speter extern char *OsCompileOptions[]; 38438032Speter 38538032Speter printf(" OS Defines:"); 38638032Speter av = OsCompileOptions; 38738032Speter ll = 7; 38838032Speter while (*av != NULL) 38938032Speter { 39038032Speter if (ll + strlen(*av) > 63) 39138032Speter { 39238032Speter putchar('\n'); 39338032Speter ll = 0; 39438032Speter } 39538032Speter if (ll == 0) 39638032Speter { 39738032Speter putchar('\t'); 39838032Speter putchar('\t'); 39938032Speter } 40038032Speter else 40138032Speter putchar(' '); 40238032Speter printf("%s", *av); 40338032Speter ll += strlen(*av++) + 1; 40438032Speter } 40538032Speter putchar('\n'); 40638032Speter#ifdef _PATH_UNIX 40738032Speter printf("Kernel symbols:\t%s\n", _PATH_UNIX); 40838032Speter#endif 40938032Speter printf(" Def Conf file:\t%s\n", getcfname()); 41038032Speter printf(" Pid file:\t%s\n", PidFile); 41138032Speter } 41238032Speter 41338032Speter InChannel = stdin; 41438032Speter OutChannel = stdout; 41538032Speter 41638032Speter /* clear sendmail's environment */ 41738032Speter ExternalEnviron = environ; 41838032Speter emptyenviron[0] = NULL; 41938032Speter environ = emptyenviron; 42038032Speter 42138032Speter /* 42238032Speter ** restore any original TZ setting until TimeZoneSpec has been 42338032Speter ** determined - or early log messages may get bogus time stamps 42438032Speter */ 42538032Speter if ((p = getextenv("TZ")) != NULL) 42638032Speter { 42738032Speter char *tz; 42838032Speter int tzlen; 42938032Speter 43038032Speter tzlen = strlen(p) + 4; 43138032Speter tz = xalloc(tzlen); 43238032Speter snprintf(tz, tzlen, "TZ=%s", p); 43338032Speter putenv(tz); 43438032Speter } 43538032Speter 43638032Speter /* prime the child environment */ 43738032Speter setuserenv("AGENT", "sendmail"); 43838032Speter 43938032Speter if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) 44038032Speter (void) setsignal(SIGINT, intsig); 44138032Speter (void) setsignal(SIGTERM, intsig); 44238032Speter (void) setsignal(SIGPIPE, SIG_IGN); 44338032Speter OldUmask = umask(022); 44438032Speter OpMode = MD_DELIVER; 44538032Speter FullName = getextenv("NAME"); 44638032Speter 44738032Speter /* 44838032Speter ** Initialize name server if it is going to be used. 44938032Speter */ 45038032Speter 45138032Speter#if NAMED_BIND 45238032Speter if (!bitset(RES_INIT, _res.options)) 45338032Speter res_init(); 45438032Speter if (tTd(8, 8)) 45538032Speter _res.options |= RES_DEBUG; 45638032Speter else 45738032Speter _res.options &= ~RES_DEBUG; 45838032Speter# ifdef RES_NOALIASES 45938032Speter _res.options |= RES_NOALIASES; 46038032Speter# endif 46138032Speter#endif 46238032Speter 46338032Speter errno = 0; 46438032Speter from = NULL; 46538032Speter 46638032Speter /* initialize some macros, etc. */ 46738032Speter initmacros(CurEnv); 46838032Speter init_vendor_macros(CurEnv); 46938032Speter 47038032Speter /* version */ 47138032Speter define('v', Version, CurEnv); 47238032Speter 47338032Speter /* hostname */ 47438032Speter hp = myhostname(jbuf, sizeof jbuf); 47538032Speter if (jbuf[0] != '\0') 47638032Speter { 47738032Speter struct utsname utsname; 47838032Speter 47938032Speter if (tTd(0, 4)) 48038032Speter printf("canonical name: %s\n", jbuf); 48138032Speter define('w', newstr(jbuf), CurEnv); /* must be new string */ 48238032Speter define('j', newstr(jbuf), CurEnv); 48338032Speter setclass('w', jbuf); 48438032Speter 48538032Speter p = strchr(jbuf, '.'); 48638032Speter if (p != NULL) 48738032Speter { 48838032Speter if (p[1] != '\0') 48938032Speter { 49038032Speter define('m', newstr(&p[1]), CurEnv); 49138032Speter } 49238032Speter while (p != NULL && strchr(&p[1], '.') != NULL) 49338032Speter { 49438032Speter *p = '\0'; 49538032Speter if (tTd(0, 4)) 49638032Speter printf("\ta.k.a.: %s\n", jbuf); 49738032Speter setclass('w', jbuf); 49838032Speter *p++ = '.'; 49938032Speter p = strchr(p, '.'); 50038032Speter } 50138032Speter } 50238032Speter 50338032Speter if (uname(&utsname) >= 0) 50438032Speter p = utsname.nodename; 50538032Speter else 50638032Speter { 50738032Speter if (tTd(0, 22)) 50838032Speter printf("uname failed (%s)\n", errstring(errno)); 50938032Speter makelower(jbuf); 51038032Speter p = jbuf; 51138032Speter } 51238032Speter if (tTd(0, 4)) 51338032Speter printf(" UUCP nodename: %s\n", p); 51438032Speter p = newstr(p); 51538032Speter define('k', p, CurEnv); 51638032Speter setclass('k', p); 51738032Speter setclass('w', p); 51838032Speter } 51938032Speter if (hp != NULL) 52038032Speter { 52138032Speter for (av = hp->h_aliases; av != NULL && *av != NULL; av++) 52238032Speter { 52338032Speter if (tTd(0, 4)) 52438032Speter printf("\ta.k.a.: %s\n", *av); 52538032Speter setclass('w', *av); 52638032Speter } 52738032Speter#if NETINET 52838032Speter if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ) 52938032Speter { 53038032Speter for (i = 0; hp->h_addr_list[i] != NULL; i++) 53138032Speter { 53238032Speter char ipbuf[103]; 53338032Speter 53438032Speter snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 53538032Speter inet_ntoa(*((struct in_addr *) hp->h_addr_list[i]))); 53638032Speter if (tTd(0, 4)) 53738032Speter printf("\ta.k.a.: %s\n", ipbuf); 53838032Speter setclass('w', ipbuf); 53938032Speter } 54038032Speter } 54138032Speter#endif 54238032Speter } 54338032Speter 54438032Speter /* current time */ 54538032Speter define('b', arpadate((char *) NULL), CurEnv); 54638032Speter 54738032Speter QueueLimitRecipient = (QUEUE_CHAR *) NULL; 54838032Speter QueueLimitSender = (QUEUE_CHAR *) NULL; 54938032Speter QueueLimitId = (QUEUE_CHAR *) NULL; 55038032Speter 55138032Speter /* 55238032Speter ** Crack argv. 55338032Speter */ 55438032Speter 55538032Speter av = argv; 55638032Speter p = strrchr(*av, '/'); 55738032Speter if (p++ == NULL) 55838032Speter p = *av; 55938032Speter if (strcmp(p, "newaliases") == 0) 56038032Speter OpMode = MD_INITALIAS; 56138032Speter else if (strcmp(p, "mailq") == 0) 56238032Speter OpMode = MD_PRINT; 56338032Speter else if (strcmp(p, "smtpd") == 0) 56438032Speter OpMode = MD_DAEMON; 56538032Speter else if (strcmp(p, "hoststat") == 0) 56638032Speter OpMode = MD_HOSTSTAT; 56738032Speter else if (strcmp(p, "purgestat") == 0) 56838032Speter OpMode = MD_PURGESTAT; 56938032Speter 57038032Speter optind = 1; 57138032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 57238032Speter { 57338032Speter switch (j) 57438032Speter { 57538032Speter case 'b': /* operations mode */ 57638032Speter switch (j = *optarg) 57738032Speter { 57838032Speter case MD_DAEMON: 57938032Speter case MD_FGDAEMON: 58038032Speter# if !DAEMON 58138032Speter usrerr("Daemon mode not implemented"); 58238032Speter ExitStat = EX_USAGE; 58338032Speter break; 58438032Speter# endif /* DAEMON */ 58538032Speter case MD_SMTP: 58638032Speter# if !SMTP 58738032Speter usrerr("I don't speak SMTP"); 58838032Speter ExitStat = EX_USAGE; 58938032Speter break; 59038032Speter# endif /* SMTP */ 59138032Speter 59238032Speter case MD_INITALIAS: 59338032Speter case MD_DELIVER: 59438032Speter case MD_VERIFY: 59538032Speter case MD_TEST: 59638032Speter case MD_PRINT: 59738032Speter case MD_HOSTSTAT: 59838032Speter case MD_PURGESTAT: 59938032Speter case MD_ARPAFTP: 60038032Speter OpMode = j; 60138032Speter break; 60238032Speter 60338032Speter case MD_FREEZE: 60438032Speter usrerr("Frozen configurations unsupported"); 60538032Speter ExitStat = EX_USAGE; 60638032Speter break; 60738032Speter 60838032Speter default: 60938032Speter usrerr("Invalid operation mode %c", j); 61038032Speter ExitStat = EX_USAGE; 61138032Speter break; 61238032Speter } 61338032Speter break; 61438032Speter 61538032Speter case 'B': /* body type */ 61638032Speter CurEnv->e_bodytype = optarg; 61738032Speter break; 61838032Speter 61938032Speter case 'C': /* select configuration file (already done) */ 62038032Speter if (RealUid != 0) 62138032Speter warn_C_flag = TRUE; 62238032Speter ConfFile = optarg; 62338032Speter (void) drop_privileges(TRUE); 62438032Speter safecf = FALSE; 62538032Speter break; 62638032Speter 62738032Speter case 'd': /* debugging -- already done */ 62838032Speter break; 62938032Speter 63038032Speter case 'f': /* from address */ 63138032Speter case 'r': /* obsolete -f flag */ 63238032Speter if (from != NULL) 63338032Speter { 63438032Speter usrerr("More than one \"from\" person"); 63538032Speter ExitStat = EX_USAGE; 63638032Speter break; 63738032Speter } 63838032Speter from = newstr(denlstring(optarg, TRUE, TRUE)); 63938032Speter if (strcmp(RealUserName, from) != 0) 64038032Speter warn_f_flag = j; 64138032Speter break; 64238032Speter 64338032Speter case 'F': /* set full name */ 64438032Speter FullName = newstr(optarg); 64538032Speter break; 64638032Speter 64738032Speter case 'h': /* hop count */ 64838032Speter CurEnv->e_hopcount = strtol(optarg, &ep, 10); 64938032Speter if (*ep) 65038032Speter { 65138032Speter usrerr("Bad hop count (%s)", optarg); 65238032Speter ExitStat = EX_USAGE; 65338032Speter } 65438032Speter break; 65538032Speter 65638032Speter case 'n': /* don't alias */ 65738032Speter NoAlias = TRUE; 65838032Speter break; 65938032Speter 66038032Speter case 'N': /* delivery status notifications */ 66138032Speter DefaultNotify |= QHASNOTIFY; 66238032Speter if (strcasecmp(optarg, "never") == 0) 66338032Speter break; 66438032Speter for (p = optarg; p != NULL; optarg = p) 66538032Speter { 66638032Speter p = strchr(p, ','); 66738032Speter if (p != NULL) 66838032Speter *p++ = '\0'; 66938032Speter if (strcasecmp(optarg, "success") == 0) 67038032Speter DefaultNotify |= QPINGONSUCCESS; 67138032Speter else if (strcasecmp(optarg, "failure") == 0) 67238032Speter DefaultNotify |= QPINGONFAILURE; 67338032Speter else if (strcasecmp(optarg, "delay") == 0) 67438032Speter DefaultNotify |= QPINGONDELAY; 67538032Speter else 67638032Speter { 67738032Speter usrerr("Invalid -N argument"); 67838032Speter ExitStat = EX_USAGE; 67938032Speter } 68038032Speter } 68138032Speter break; 68238032Speter 68338032Speter case 'o': /* set option */ 68438032Speter setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); 68538032Speter break; 68638032Speter 68738032Speter case 'O': /* set option (long form) */ 68838032Speter setoption(' ', optarg, FALSE, TRUE, CurEnv); 68938032Speter break; 69038032Speter 69138032Speter case 'p': /* set protocol */ 69238032Speter p = strchr(optarg, ':'); 69338032Speter if (p != NULL) 69438032Speter { 69538032Speter *p++ = '\0'; 69638032Speter if (*p != '\0') 69738032Speter { 69838032Speter ep = xalloc(strlen(p) + 1); 69938032Speter cleanstrcpy(ep, p, MAXNAME); 70038032Speter define('s', ep, CurEnv); 70138032Speter } 70238032Speter } 70338032Speter if (*optarg != '\0') 70438032Speter { 70538032Speter ep = xalloc(strlen(optarg) + 1); 70638032Speter cleanstrcpy(ep, optarg, MAXNAME); 70738032Speter define('r', ep, CurEnv); 70838032Speter } 70938032Speter break; 71038032Speter 71138032Speter case 'q': /* run queue files at intervals */ 71238032Speter# if QUEUE 71338032Speter FullName = NULL; 71438032Speter queuemode = TRUE; 71538032Speter switch (optarg[0]) 71638032Speter { 71738032Speter case 'I': 71838032Speter if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) 71938032Speter syserr("!Out of memory!!"); 72038032Speter new->queue_match = newstr(&optarg[1]); 72138032Speter new->queue_next = QueueLimitId; 72238032Speter QueueLimitId = new; 72338032Speter break; 72438032Speter 72538032Speter case 'R': 72638032Speter if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) 72738032Speter syserr("!Out of memory!!"); 72838032Speter new->queue_match = newstr(&optarg[1]); 72938032Speter new->queue_next = QueueLimitRecipient; 73038032Speter QueueLimitRecipient = new; 73138032Speter break; 73238032Speter 73338032Speter case 'S': 73438032Speter if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) 73538032Speter syserr("!Out of memory!!"); 73638032Speter new->queue_match = newstr(&optarg[1]); 73738032Speter new->queue_next = QueueLimitSender; 73838032Speter QueueLimitSender = new; 73938032Speter break; 74038032Speter 74138032Speter default: 74238032Speter QueueIntvl = convtime(optarg, 'm'); 74338032Speter break; 74438032Speter } 74538032Speter# else /* QUEUE */ 74638032Speter usrerr("I don't know about queues"); 74738032Speter ExitStat = EX_USAGE; 74838032Speter# endif /* QUEUE */ 74938032Speter break; 75038032Speter 75138032Speter case 'R': /* DSN RET: what to return */ 75238032Speter if (bitset(EF_RET_PARAM, CurEnv->e_flags)) 75338032Speter { 75438032Speter usrerr("Duplicate -R flag"); 75538032Speter ExitStat = EX_USAGE; 75638032Speter break; 75738032Speter } 75838032Speter CurEnv->e_flags |= EF_RET_PARAM; 75938032Speter if (strcasecmp(optarg, "hdrs") == 0) 76038032Speter CurEnv->e_flags |= EF_NO_BODY_RETN; 76138032Speter else if (strcasecmp(optarg, "full") != 0) 76238032Speter { 76338032Speter usrerr("Invalid -R value"); 76438032Speter ExitStat = EX_USAGE; 76538032Speter } 76638032Speter break; 76738032Speter 76838032Speter case 't': /* read recipients from message */ 76938032Speter GrabTo = TRUE; 77038032Speter break; 77138032Speter 77238032Speter case 'U': /* initial (user) submission */ 77338032Speter UserSubmission = TRUE; 77438032Speter break; 77538032Speter 77638032Speter case 'V': /* DSN ENVID: set "original" envelope id */ 77738032Speter if (!xtextok(optarg)) 77838032Speter { 77938032Speter usrerr("Invalid syntax in -V flag"); 78038032Speter ExitStat = EX_USAGE; 78138032Speter } 78238032Speter else 78338032Speter CurEnv->e_envid = newstr(optarg); 78438032Speter break; 78538032Speter 78638032Speter case 'X': /* traffic log file */ 78738032Speter (void) drop_privileges(TRUE); 78838032Speter TrafficLogFile = fopen(optarg, "a"); 78938032Speter if (TrafficLogFile == NULL) 79038032Speter { 79138032Speter syserr("cannot open %s", optarg); 79238032Speter ExitStat = EX_CANTCREAT; 79338032Speter break; 79438032Speter } 79538032Speter#ifdef HASSETVBUF 79638032Speter setvbuf(TrafficLogFile, NULL, _IOLBF, 0); 79738032Speter#else 79838032Speter setlinebuf(TrafficLogFile); 79938032Speter#endif 80038032Speter break; 80138032Speter 80238032Speter /* compatibility flags */ 80338032Speter case 'c': /* connect to non-local mailers */ 80438032Speter case 'i': /* don't let dot stop me */ 80538032Speter case 'm': /* send to me too */ 80638032Speter case 'T': /* set timeout interval */ 80738032Speter case 'v': /* give blow-by-blow description */ 80838032Speter setoption(j, "T", FALSE, TRUE, CurEnv); 80938032Speter break; 81038032Speter 81138032Speter case 'e': /* error message disposition */ 81238032Speter case 'M': /* define macro */ 81338032Speter setoption(j, optarg, FALSE, TRUE, CurEnv); 81438032Speter break; 81538032Speter 81638032Speter case 's': /* save From lines in headers */ 81738032Speter setoption('f', "T", FALSE, TRUE, CurEnv); 81838032Speter break; 81938032Speter 82038032Speter# ifdef DBM 82138032Speter case 'I': /* initialize alias DBM file */ 82238032Speter OpMode = MD_INITALIAS; 82338032Speter break; 82438032Speter# endif /* DBM */ 82538032Speter 82638032Speter# if defined(__osf__) || defined(_AIX3) 82738032Speter case 'x': /* random flag that OSF/1 & AIX mailx passes */ 82838032Speter break; 82938032Speter# endif 83038032Speter# if defined(sony_news) 83138032Speter case 'E': 83238032Speter case 'J': /* ignore flags for Japanese code conversion 83338032Speter impremented on Sony NEWS */ 83438032Speter break; 83538032Speter# endif 83638032Speter 83738032Speter default: 83838032Speter ExitStat = EX_USAGE; 83938032Speter finis(); 84038032Speter break; 84138032Speter } 84238032Speter } 84338032Speter av += optind; 84438032Speter 84538032Speter /* 84638032Speter ** Do basic initialization. 84738032Speter ** Read system control file. 84838032Speter ** Extract special fields for local use. 84938032Speter */ 85038032Speter 85138032Speter /* set up ${opMode} for use in config file */ 85238032Speter { 85338032Speter char mbuf[2]; 85438032Speter 85538032Speter mbuf[0] = OpMode; 85638032Speter mbuf[1] = '\0'; 85738032Speter define(MID_OPMODE, newstr(mbuf), CurEnv); 85838032Speter } 85938032Speter 86038032Speter#if XDEBUG 86138032Speter checkfd012("before readcf"); 86238032Speter#endif 86338032Speter vendor_pre_defaults(CurEnv); 86438032Speter readcf(getcfname(), safecf, CurEnv); 86538032Speter ConfigFileRead = TRUE; 86638032Speter vendor_post_defaults(CurEnv); 86738032Speter 86838032Speter /* Enforce use of local time (null string overrides this) */ 86938032Speter if (TimeZoneSpec == NULL) 87038032Speter unsetenv("TZ"); 87138032Speter else if (TimeZoneSpec[0] != '\0') 87238032Speter setuserenv("TZ", TimeZoneSpec); 87338032Speter else 87438032Speter setuserenv("TZ", NULL); 87538032Speter tzset(); 87638032Speter 87738032Speter /* avoid denial-of-service attacks */ 87838032Speter resetlimits(); 87938032Speter 88038032Speter if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) 88138032Speter { 88238032Speter /* drop privileges -- daemon mode done after socket/bind */ 88338032Speter (void) drop_privileges(FALSE); 88438032Speter } 88538032Speter 88638032Speter /* 88738032Speter ** Find our real host name for future logging. 88838032Speter */ 88938032Speter 89038032Speter p = getauthinfo(STDIN_FILENO, &forged); 89138032Speter define('_', p, CurEnv); 89238032Speter 89338032Speter /* suppress error printing if errors mailed back or whatever */ 89438032Speter if (CurEnv->e_errormode != EM_PRINT) 89538032Speter HoldErrs = TRUE; 89638032Speter 89738032Speter /* set up the $=m class now, after .cf has a chance to redefine $m */ 89838032Speter expand("\201m", jbuf, sizeof jbuf, CurEnv); 89938032Speter setclass('m', jbuf); 90038032Speter 90138032Speter /* probe interfaces and locate any additional names */ 90238032Speter if (!DontProbeInterfaces) 90338032Speter load_if_names(); 90438032Speter 90538032Speter if (tTd(0, 1)) 90638032Speter { 90738032Speter printf("\n============ SYSTEM IDENTITY (after readcf) ============"); 90838032Speter printf("\n (short domain name) $w = "); 90938032Speter xputs(macvalue('w', CurEnv)); 91038032Speter printf("\n (canonical domain name) $j = "); 91138032Speter xputs(macvalue('j', CurEnv)); 91238032Speter printf("\n (subdomain name) $m = "); 91338032Speter xputs(macvalue('m', CurEnv)); 91438032Speter printf("\n (node name) $k = "); 91538032Speter xputs(macvalue('k', CurEnv)); 91638032Speter printf("\n========================================================\n\n"); 91738032Speter } 91838032Speter 91938032Speter /* 92038032Speter ** Do more command line checking -- these are things that 92138032Speter ** have to modify the results of reading the config file. 92238032Speter */ 92338032Speter 92438032Speter /* process authorization warnings from command line */ 92538032Speter if (warn_C_flag) 92638032Speter auth_warning(CurEnv, "Processed by %s with -C %s", 92738032Speter RealUserName, ConfFile); 92838032Speter if (Warn_Q_option) 92938032Speter auth_warning(CurEnv, "Processed from queue %s", QueueDir); 93038032Speter 93138032Speter /* check body type for legality */ 93238032Speter if (CurEnv->e_bodytype == NULL) 93338032Speter /* nothing */ ; 93438032Speter else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) 93538032Speter SevenBitInput = TRUE; 93638032Speter else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) 93738032Speter SevenBitInput = FALSE; 93838032Speter else 93938032Speter { 94038032Speter usrerr("Illegal body type %s", CurEnv->e_bodytype); 94138032Speter CurEnv->e_bodytype = NULL; 94238032Speter } 94338032Speter 94438032Speter /* tweak default DSN notifications */ 94538032Speter if (DefaultNotify == 0) 94638032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 94738032Speter 94838032Speter /* be sure we don't pick up bogus HOSTALIASES environment variable */ 94938032Speter if (queuemode && RealUid != 0) 95038032Speter (void) unsetenv("HOSTALIASES"); 95138032Speter 95238032Speter /* check for sane configuration level */ 95338032Speter if (ConfigLevel > MAXCONFIGLEVEL) 95438032Speter { 95538032Speter syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", 95638032Speter ConfigLevel, Version, MAXCONFIGLEVEL); 95738032Speter } 95838032Speter 95938032Speter /* need MCI cache to have persistence */ 96038032Speter if (HostStatDir != NULL && MaxMciCache == 0) 96138032Speter { 96238032Speter HostStatDir = NULL; 96338032Speter printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); 96438032Speter } 96538032Speter 96638032Speter /* need HostStatusDir in order to have SingleThreadDelivery */ 96738032Speter if (SingleThreadDelivery && HostStatDir == NULL) 96838032Speter { 96938032Speter SingleThreadDelivery = FALSE; 97038032Speter printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); 97138032Speter } 97238032Speter 97338032Speter /* check for permissions */ 97438032Speter if ((OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || 97538032Speter OpMode == MD_PURGESTAT) && RealUid != 0) 97638032Speter { 97738032Speter if (LogLevel > 1) 97838032Speter sm_syslog(LOG_ALERT, NOQID, 97938032Speter "user %d attempted to %s", 98038032Speter RealUid, 98138032Speter OpMode != MD_PURGESTAT ? "run daemon" 98238032Speter : "purge host status"); 98338032Speter usrerr("Permission denied"); 98438032Speter exit(EX_USAGE); 98538032Speter } 98638032Speter 98738032Speter if (MeToo) 98838032Speter BlankEnvelope.e_flags |= EF_METOO; 98938032Speter 99038032Speter switch (OpMode) 99138032Speter { 99238032Speter case MD_TEST: 99338032Speter /* don't have persistent host status in test mode */ 99438032Speter HostStatDir = NULL; 99538032Speter if (Verbose == 0) 99638032Speter Verbose = 2; 99738032Speter CurEnv->e_errormode = EM_PRINT; 99838032Speter HoldErrs = FALSE; 99938032Speter break; 100038032Speter 100138032Speter case MD_VERIFY: 100238032Speter CurEnv->e_errormode = EM_PRINT; 100338032Speter HoldErrs = FALSE; 100438032Speter /* arrange to exit cleanly on hangup signal */ 100538032Speter if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 100638032Speter setsignal(SIGHUP, intsig); 100738032Speter break; 100838032Speter 100938032Speter case MD_FGDAEMON: 101038032Speter run_in_foreground = TRUE; 101138032Speter OpMode = MD_DAEMON; 101238032Speter /* fall through ... */ 101338032Speter 101438032Speter case MD_DAEMON: 101538032Speter vendor_daemon_setup(CurEnv); 101638032Speter 101738032Speter /* remove things that don't make sense in daemon mode */ 101838032Speter FullName = NULL; 101938032Speter GrabTo = FALSE; 102038032Speter 102138032Speter /* arrange to restart on hangup signal */ 102238032Speter if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') 102338032Speter sm_syslog(LOG_WARNING, NOQID, 102438032Speter "daemon invoked without full pathname; kill -1 won't work"); 102538032Speter setsignal(SIGHUP, sighup); 102638032Speter 102738032Speter /* workaround: can't seem to release the signal in the parent */ 102838032Speter releasesignal(SIGHUP); 102938032Speter break; 103038032Speter 103138032Speter case MD_INITALIAS: 103238032Speter Verbose = 2; 103338032Speter CurEnv->e_errormode = EM_PRINT; 103438032Speter HoldErrs = FALSE; 103538032Speter /* fall through... */ 103638032Speter 103738032Speter case MD_PRINT: 103838032Speter /* to handle sendmail -bp -qSfoobar properly */ 103938032Speter queuemode = FALSE; 104038032Speter /* fall through... */ 104138032Speter 104238032Speter default: 104338032Speter /* arrange to exit cleanly on hangup signal */ 104438032Speter if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 104538032Speter setsignal(SIGHUP, intsig); 104638032Speter break; 104738032Speter } 104838032Speter 104938032Speter /* special considerations for FullName */ 105038032Speter if (FullName != NULL) 105138032Speter { 105238032Speter char *full = NULL; 105338032Speter extern bool rfc822_string __P((char *)); 105438032Speter 105538032Speter /* full names can't have newlines */ 105638032Speter if (strchr(FullName, '\n') != NULL) 105738032Speter { 105838032Speter FullName = full = newstr(denlstring(FullName, TRUE, TRUE)); 105938032Speter } 106038032Speter /* check for characters that may have to be quoted */ 106138032Speter if (!rfc822_string(FullName)) 106238032Speter { 106338032Speter extern char *addquotes __P((char *)); 106438032Speter 106538032Speter /* 106638032Speter ** Quote a full name with special characters 106738032Speter ** as a comment so crackaddr() doesn't destroy 106838032Speter ** the name portion of the address. 106938032Speter */ 107038032Speter FullName = addquotes(FullName); 107138032Speter if (full != NULL) 107238032Speter free(full); 107338032Speter } 107438032Speter } 107538032Speter 107638032Speter /* do heuristic mode adjustment */ 107738032Speter if (Verbose) 107838032Speter { 107938032Speter /* turn off noconnect option */ 108038032Speter setoption('c', "F", TRUE, FALSE, CurEnv); 108138032Speter 108238032Speter /* turn on interactive delivery */ 108338032Speter setoption('d', "", TRUE, FALSE, CurEnv); 108438032Speter } 108538032Speter 108638032Speter /* check for out of date configuration level */ 108738032Speter if (ConfigLevel < MAXCONFIGLEVEL) 108838032Speter { 108938032Speter message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d", 109038032Speter Version, MAXCONFIGLEVEL, ConfigLevel); 109138032Speter } 109238032Speter 109338032Speter if (ConfigLevel < 3) 109438032Speter { 109538032Speter UseErrorsTo = TRUE; 109638032Speter } 109738032Speter 109838032Speter /* set options that were previous macros */ 109938032Speter if (SmtpGreeting == NULL) 110038032Speter { 110138032Speter if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) 110238032Speter SmtpGreeting = newstr(p); 110338032Speter else 110438032Speter SmtpGreeting = "\201j Sendmail \201v ready at \201b"; 110538032Speter } 110638032Speter if (UnixFromLine == NULL) 110738032Speter { 110838032Speter if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) 110938032Speter UnixFromLine = newstr(p); 111038032Speter else 111138032Speter UnixFromLine = "From \201g \201d"; 111238032Speter } 111338032Speter 111438032Speter /* our name for SMTP codes */ 111538032Speter expand("\201j", jbuf, sizeof jbuf, CurEnv); 111638032Speter MyHostName = jbuf; 111738032Speter if (strchr(jbuf, '.') == NULL) 111838032Speter message("WARNING: local host name (%s) is not qualified; fix $j in config file", 111938032Speter jbuf); 112038032Speter 112138032Speter /* make certain that this name is part of the $=w class */ 112238032Speter setclass('w', MyHostName); 112338032Speter 112438032Speter /* the indices of built-in mailers */ 112538032Speter st = stab("local", ST_MAILER, ST_FIND); 112638032Speter if (st != NULL) 112738032Speter LocalMailer = st->s_mailer; 112838032Speter else if (OpMode != MD_TEST || !warn_C_flag) 112938032Speter syserr("No local mailer defined"); 113038032Speter 113138032Speter st = stab("prog", ST_MAILER, ST_FIND); 113238032Speter if (st == NULL) 113338032Speter syserr("No prog mailer defined"); 113438032Speter else 113538032Speter { 113638032Speter ProgMailer = st->s_mailer; 113738032Speter clrbitn(M_MUSER, ProgMailer->m_flags); 113838032Speter } 113938032Speter 114038032Speter st = stab("*file*", ST_MAILER, ST_FIND); 114138032Speter if (st == NULL) 114238032Speter syserr("No *file* mailer defined"); 114338032Speter else 114438032Speter { 114538032Speter FileMailer = st->s_mailer; 114638032Speter clrbitn(M_MUSER, FileMailer->m_flags); 114738032Speter } 114838032Speter 114938032Speter st = stab("*include*", ST_MAILER, ST_FIND); 115038032Speter if (st == NULL) 115138032Speter syserr("No *include* mailer defined"); 115238032Speter else 115338032Speter InclMailer = st->s_mailer; 115438032Speter 115538032Speter if (ConfigLevel < 6) 115638032Speter { 115738032Speter /* heuristic tweaking of local mailer for back compat */ 115838032Speter if (LocalMailer != NULL) 115938032Speter { 116038032Speter setbitn(M_ALIASABLE, LocalMailer->m_flags); 116138032Speter setbitn(M_HASPWENT, LocalMailer->m_flags); 116238032Speter setbitn(M_TRYRULESET5, LocalMailer->m_flags); 116338032Speter setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); 116438032Speter setbitn(M_CHECKPROG, LocalMailer->m_flags); 116538032Speter setbitn(M_CHECKFILE, LocalMailer->m_flags); 116638032Speter setbitn(M_CHECKUDB, LocalMailer->m_flags); 116738032Speter } 116838032Speter if (ProgMailer != NULL) 116938032Speter setbitn(M_RUNASRCPT, ProgMailer->m_flags); 117038032Speter if (FileMailer != NULL) 117138032Speter setbitn(M_RUNASRCPT, FileMailer->m_flags); 117238032Speter } 117338032Speter if (ConfigLevel < 7) 117438032Speter { 117538032Speter if (LocalMailer != NULL) 117638032Speter setbitn(M_VRFY250, LocalMailer->m_flags); 117738032Speter if (ProgMailer != NULL) 117838032Speter setbitn(M_VRFY250, ProgMailer->m_flags); 117938032Speter if (FileMailer != NULL) 118038032Speter setbitn(M_VRFY250, FileMailer->m_flags); 118138032Speter } 118238032Speter 118338032Speter /* MIME Content-Types that cannot be transfer encoded */ 118438032Speter setclass('n', "multipart/signed"); 118538032Speter 118638032Speter /* MIME message/xxx subtypes that can be treated as messages */ 118738032Speter setclass('s', "rfc822"); 118838032Speter 118938032Speter /* MIME Content-Transfer-Encodings that can be encoded */ 119038032Speter setclass('e', "7bit"); 119138032Speter setclass('e', "8bit"); 119238032Speter setclass('e', "binary"); 119338032Speter 119438032Speter#ifdef USE_B_CLASS 119538032Speter /* MIME Content-Types that should be treated as binary */ 119638032Speter setclass('b', "image"); 119738032Speter setclass('b', "audio"); 119838032Speter setclass('b', "video"); 119938032Speter setclass('b', "application/octet-stream"); 120038032Speter#endif 120138032Speter 120238032Speter /* operate in queue directory */ 120338032Speter if (QueueDir == NULL) 120438032Speter { 120538032Speter if (OpMode != MD_TEST) 120638032Speter { 120738032Speter syserr("QueueDirectory (Q) option must be set"); 120838032Speter ExitStat = EX_CONFIG; 120938032Speter } 121038032Speter } 121138032Speter else 121238032Speter { 121338032Speter /* test path to get warning messages */ 121438032Speter (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE); 121538032Speter if (OpMode != MD_TEST && chdir(QueueDir) < 0) 121638032Speter { 121738032Speter syserr("cannot chdir(%s)", QueueDir); 121838032Speter ExitStat = EX_CONFIG; 121938032Speter } 122038032Speter } 122138032Speter 122238032Speter /* check host status directory for validity */ 122338032Speter if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) 122438032Speter { 122538032Speter /* cannot use this value */ 122638032Speter if (tTd(0, 2)) 122738032Speter printf("Cannot use HostStatusDirectory = %s: %s\n", 122838032Speter HostStatDir, errstring(errno)); 122938032Speter HostStatDir = NULL; 123038032Speter } 123138032Speter 123238032Speter# if QUEUE 123338032Speter if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) 123438032Speter { 123538032Speter struct stat stbuf; 123638032Speter 123738032Speter /* check to see if we own the queue directory */ 123838032Speter if (stat(".", &stbuf) < 0) 123938032Speter syserr("main: cannot stat %s", QueueDir); 124038032Speter if (stbuf.st_uid != RealUid) 124138032Speter { 124238032Speter /* nope, really a botch */ 124338032Speter usrerr("You do not have permission to process the queue"); 124438032Speter exit (EX_NOPERM); 124538032Speter } 124638032Speter } 124738032Speter# endif /* QUEUE */ 124838032Speter 124938032Speter /* if we've had errors so far, exit now */ 125038032Speter if (ExitStat != EX_OK && OpMode != MD_TEST) 125138032Speter { 125238032Speter endpwent(); 125338032Speter setuid(RealUid); 125438032Speter exit(ExitStat); 125538032Speter } 125638032Speter 125738032Speter#if XDEBUG 125838032Speter checkfd012("before main() initmaps"); 125938032Speter#endif 126038032Speter 126138032Speter /* 126238032Speter ** Do operation-mode-dependent initialization. 126338032Speter */ 126438032Speter 126538032Speter switch (OpMode) 126638032Speter { 126738032Speter case MD_PRINT: 126838032Speter /* print the queue */ 126938032Speter#if QUEUE 127038032Speter dropenvelope(CurEnv, TRUE); 127138032Speter printqueue(); 127238032Speter endpwent(); 127338032Speter setuid(RealUid); 127438032Speter exit(EX_OK); 127538032Speter#else /* QUEUE */ 127638032Speter usrerr("No queue to print"); 127738032Speter finis(); 127838032Speter#endif /* QUEUE */ 127938032Speter 128038032Speter case MD_HOSTSTAT: 128138032Speter mci_traverse_persistent(mci_print_persistent, NULL); 128238032Speter exit(EX_OK); 128338032Speter break; 128438032Speter 128538032Speter case MD_PURGESTAT: 128638032Speter mci_traverse_persistent(mci_purge_persistent, NULL); 128738032Speter exit(EX_OK); 128838032Speter break; 128938032Speter 129038032Speter case MD_INITALIAS: 129138032Speter /* initialize alias database */ 129238032Speter initmaps(TRUE, CurEnv); 129338032Speter endpwent(); 129438032Speter setuid(RealUid); 129538032Speter exit(ExitStat); 129638032Speter 129738032Speter case MD_SMTP: 129838032Speter case MD_DAEMON: 129938032Speter /* reset DSN parameters */ 130038032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 130138032Speter CurEnv->e_envid = NULL; 130238032Speter CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 130338032Speter 130438032Speter /* don't open alias database -- done in srvrsmtp */ 130538032Speter break; 130638032Speter 130738032Speter default: 130838032Speter /* open the alias database */ 130938032Speter initmaps(FALSE, CurEnv); 131038032Speter break; 131138032Speter } 131238032Speter 131338032Speter if (tTd(0, 15)) 131438032Speter { 131538032Speter extern void printrules __P((void)); 131638032Speter 131738032Speter /* print configuration table (or at least part of it) */ 131838032Speter if (tTd(0, 90)) 131938032Speter printrules(); 132038032Speter for (i = 0; i < MAXMAILERS; i++) 132138032Speter { 132238032Speter if (Mailer[i] != NULL) 132338032Speter printmailer(Mailer[i]); 132438032Speter } 132538032Speter } 132638032Speter 132738032Speter /* 132838032Speter ** Switch to the main envelope. 132938032Speter */ 133038032Speter 133138032Speter CurEnv = newenvelope(&MainEnvelope, CurEnv); 133238032Speter MainEnvelope.e_flags = BlankEnvelope.e_flags; 133338032Speter 133438032Speter /* 133538032Speter ** If test mode, read addresses from stdin and process. 133638032Speter */ 133738032Speter 133838032Speter if (OpMode == MD_TEST) 133938032Speter { 134038032Speter char buf[MAXLINE]; 134138032Speter SIGFUNC_DECL intindebug __P((int)); 134238032Speter 134338032Speter if (isatty(fileno(stdin))) 134438032Speter Verbose = 2; 134538032Speter 134638032Speter if (Verbose) 134738032Speter { 134838032Speter printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); 134938032Speter printf("Enter <ruleset> <address>\n"); 135038032Speter } 135138032Speter if (setjmp(TopFrame) > 0) 135238032Speter printf("\n"); 135338032Speter (void) setsignal(SIGINT, intindebug); 135438032Speter for (;;) 135538032Speter { 135638032Speter extern void testmodeline __P((char *, ENVELOPE *)); 135738032Speter 135838032Speter if (Verbose == 2) 135938032Speter printf("> "); 136038032Speter (void) fflush(stdout); 136138032Speter if (fgets(buf, sizeof buf, stdin) == NULL) 136238032Speter finis(); 136338032Speter p = strchr(buf, '\n'); 136438032Speter if (p != NULL) 136538032Speter *p = '\0'; 136638032Speter if (Verbose < 2) 136738032Speter printf("> %s\n", buf); 136838032Speter testmodeline(buf, CurEnv); 136938032Speter } 137038032Speter } 137138032Speter 137238032Speter# if QUEUE 137338032Speter /* 137438032Speter ** If collecting stuff from the queue, go start doing that. 137538032Speter */ 137638032Speter 137738032Speter if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0) 137838032Speter { 137938032Speter (void) runqueue(FALSE, Verbose); 138038032Speter finis(); 138138032Speter } 138238032Speter# endif /* QUEUE */ 138338032Speter 138438032Speter /* 138538032Speter ** If a daemon, wait for a request. 138638032Speter ** getrequests will always return in a child. 138738032Speter ** If we should also be processing the queue, start 138838032Speter ** doing it in background. 138938032Speter ** We check for any errors that might have happened 139038032Speter ** during startup. 139138032Speter */ 139238032Speter 139338032Speter if (OpMode == MD_DAEMON || QueueIntvl != 0) 139438032Speter { 139538032Speter char dtype[200]; 139638032Speter extern void getrequests __P((ENVELOPE *)); 139738032Speter 139838032Speter if (!run_in_foreground && !tTd(99, 100)) 139938032Speter { 140038032Speter /* put us in background */ 140138032Speter i = fork(); 140238032Speter if (i < 0) 140338032Speter syserr("daemon: cannot fork"); 140438032Speter if (i != 0) 140538032Speter exit(EX_OK); 140638032Speter 140738032Speter /* disconnect from our controlling tty */ 140838032Speter disconnect(2, CurEnv); 140938032Speter } 141038032Speter 141138032Speter dtype[0] = '\0'; 141238032Speter if (OpMode == MD_DAEMON) 141338032Speter strcat(dtype, "+SMTP"); 141438032Speter if (QueueIntvl != 0) 141538032Speter { 141638032Speter strcat(dtype, "+queueing@"); 141738032Speter strcat(dtype, pintvl(QueueIntvl, TRUE)); 141838032Speter } 141938032Speter if (tTd(0, 1)) 142038032Speter strcat(dtype, "+debugging"); 142138032Speter 142238032Speter sm_syslog(LOG_INFO, NOQID, 142338032Speter "starting daemon (%s): %s", Version, dtype + 1); 142438032Speter#ifdef XLA 142538032Speter xla_create_file(); 142638032Speter#endif 142738032Speter 142838032Speter# if QUEUE 142938032Speter if (queuemode) 143038032Speter { 143138032Speter (void) runqueue(TRUE, FALSE); 143238032Speter if (OpMode != MD_DAEMON) 143338032Speter { 143438032Speter for (;;) 143538032Speter { 143638032Speter pause(); 143738032Speter if (DoQueueRun) 143838032Speter (void) runqueue(TRUE, FALSE); 143938032Speter } 144038032Speter } 144138032Speter } 144238032Speter# endif /* QUEUE */ 144338032Speter dropenvelope(CurEnv, TRUE); 144438032Speter 144538032Speter#if DAEMON 144638032Speter getrequests(CurEnv); 144738032Speter 144838032Speter /* drop privileges */ 144938032Speter (void) drop_privileges(FALSE); 145038032Speter 145138032Speter /* at this point we are in a child: reset state */ 145238032Speter (void) newenvelope(CurEnv, CurEnv); 145338032Speter 145438032Speter /* 145538032Speter ** Get authentication data 145638032Speter */ 145738032Speter 145838032Speter p = getauthinfo(fileno(InChannel), &forged); 145938032Speter define('_', p, &BlankEnvelope); 146038032Speter#endif /* DAEMON */ 146138032Speter } 146238032Speter 146338032Speter# if SMTP 146438032Speter /* 146538032Speter ** If running SMTP protocol, start collecting and executing 146638032Speter ** commands. This will never return. 146738032Speter */ 146838032Speter 146938032Speter if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 147038032Speter { 147138032Speter char pbuf[20]; 147238032Speter extern void smtp __P((char *, ENVELOPE *)); 147338032Speter 147438032Speter /* 147538032Speter ** Save some macros for check_* rulesets. 147638032Speter */ 147738032Speter 147838032Speter if (forged) 147938032Speter { 148038032Speter char ipbuf[103]; 148138032Speter 148238032Speter snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 148338032Speter inet_ntoa(RealHostAddr.sin.sin_addr)); 148438032Speter 148538032Speter define(macid("{client_name}", NULL), 148638032Speter newstr(ipbuf), &BlankEnvelope); 148738032Speter } 148838032Speter else 148938032Speter define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope); 149038032Speter define(macid("{client_addr}", NULL), 149138032Speter newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); 149238032Speter if (RealHostAddr.sa.sa_family == AF_INET) 149338032Speter snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port); 149438032Speter else 149538032Speter snprintf(pbuf, sizeof pbuf, "0"); 149638032Speter define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope); 149738032Speter 149838032Speter if (OpMode == MD_DAEMON) 149938032Speter { 150038032Speter /* validate the connection */ 150138032Speter HoldErrs = TRUE; 150238032Speter nullserver = validate_connection(&RealHostAddr, 150338032Speter RealHostName, CurEnv); 150438032Speter HoldErrs = FALSE; 150538032Speter } 150638032Speter smtp(nullserver, CurEnv); 150738032Speter } 150838032Speter# endif /* SMTP */ 150938032Speter 151038032Speter clearenvelope(CurEnv, FALSE); 151138032Speter if (OpMode == MD_VERIFY) 151238032Speter { 151338032Speter CurEnv->e_sendmode = SM_VERIFY; 151438032Speter PostMasterCopy = NULL; 151538032Speter } 151638032Speter else 151738032Speter { 151838032Speter /* interactive -- all errors are global */ 151938032Speter CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; 152038032Speter } 152138032Speter 152238032Speter /* 152338032Speter ** Do basic system initialization and set the sender 152438032Speter */ 152538032Speter 152638032Speter initsys(CurEnv); 152738032Speter if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't')) 152838032Speter auth_warning(CurEnv, "%s set sender to %s using -%c", 152938032Speter RealUserName, from, warn_f_flag); 153038032Speter setsender(from, CurEnv, NULL, '\0', FALSE); 153138032Speter if (macvalue('s', CurEnv) == NULL) 153238032Speter define('s', RealHostName, CurEnv); 153338032Speter 153438032Speter if (*av == NULL && !GrabTo) 153538032Speter { 153638032Speter CurEnv->e_flags |= EF_GLOBALERRS; 153738032Speter usrerr("Recipient names must be specified"); 153838032Speter 153938032Speter /* collect body for UUCP return */ 154038032Speter if (OpMode != MD_VERIFY) 154138032Speter collect(InChannel, FALSE, NULL, CurEnv); 154238032Speter finis(); 154338032Speter } 154438032Speter 154538032Speter /* 154638032Speter ** Scan argv and deliver the message to everyone. 154738032Speter */ 154838032Speter 154938032Speter sendtoargv(av, CurEnv); 155038032Speter 155138032Speter /* if we have had errors sofar, arrange a meaningful exit stat */ 155238032Speter if (Errors > 0 && ExitStat == EX_OK) 155338032Speter ExitStat = EX_USAGE; 155438032Speter 155538032Speter#if _FFR_FIX_DASHT 155638032Speter /* 155738032Speter ** If using -t, force not sending to argv recipients, even 155838032Speter ** if they are mentioned in the headers. 155938032Speter */ 156038032Speter 156138032Speter if (GrabTo) 156238032Speter { 156338032Speter ADDRESS *q; 156438032Speter 156538032Speter for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 156638032Speter q->q_flags |= QDONTSEND; 156738032Speter } 156838032Speter#endif 156938032Speter 157038032Speter /* 157138032Speter ** Read the input mail. 157238032Speter */ 157338032Speter 157438032Speter CurEnv->e_to = NULL; 157538032Speter if (OpMode != MD_VERIFY || GrabTo) 157638032Speter { 157738032Speter long savedflags = CurEnv->e_flags & EF_FATALERRS; 157838032Speter 157938032Speter CurEnv->e_flags |= EF_GLOBALERRS; 158038032Speter CurEnv->e_flags &= ~EF_FATALERRS; 158138032Speter collect(InChannel, FALSE, NULL, CurEnv); 158238032Speter 158338032Speter /* bail out if message too large */ 158438032Speter if (bitset(EF_CLRQUEUE, CurEnv->e_flags)) 158538032Speter { 158638032Speter finis(); 158738032Speter /*NOTREACHED*/ 158838032Speter return -1; 158938032Speter } 159038032Speter CurEnv->e_flags |= savedflags; 159138032Speter } 159238032Speter errno = 0; 159338032Speter 159438032Speter if (tTd(1, 1)) 159538032Speter printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); 159638032Speter 159738032Speter /* 159838032Speter ** Actually send everything. 159938032Speter ** If verifying, just ack. 160038032Speter */ 160138032Speter 160238032Speter CurEnv->e_from.q_flags |= QDONTSEND; 160338032Speter if (tTd(1, 5)) 160438032Speter { 160538032Speter printf("main: QDONTSEND "); 160638032Speter printaddr(&CurEnv->e_from, FALSE); 160738032Speter } 160838032Speter CurEnv->e_to = NULL; 160938032Speter CurrentLA = getla(); 161038032Speter GrabTo = FALSE; 161138032Speter sendall(CurEnv, SM_DEFAULT); 161238032Speter 161338032Speter /* 161438032Speter ** All done. 161538032Speter ** Don't send return error message if in VERIFY mode. 161638032Speter */ 161738032Speter 161838032Speter finis(); 161938032Speter /*NOTREACHED*/ 162038032Speter return -1; 162138032Speter} 162238032Speter 162338032Speter 162438032Speter/* ARGSUSED */ 162538032SpeterSIGFUNC_DECL 162638032Speterintindebug(sig) 162738032Speter int sig; 162838032Speter{ 162938032Speter longjmp(TopFrame, 1); 163038032Speter return SIGFUNC_RETURN; 163138032Speter} 163238032Speter 163338032Speter 163438032Speter/* 163538032Speter** FINIS -- Clean up and exit. 163638032Speter** 163738032Speter** Parameters: 163838032Speter** none 163938032Speter** 164038032Speter** Returns: 164138032Speter** never 164238032Speter** 164338032Speter** Side Effects: 164438032Speter** exits sendmail 164538032Speter*/ 164638032Speter 164738032Spetervoid 164838032Speterfinis() 164938032Speter{ 165038032Speter if (tTd(2, 1)) 165138032Speter { 165238032Speter extern void printenvflags __P((ENVELOPE *)); 165338032Speter 165438032Speter printf("\n====finis: stat %d e_id=%s e_flags=", 165538032Speter ExitStat, 165638032Speter CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); 165738032Speter printenvflags(CurEnv); 165838032Speter } 165938032Speter if (tTd(2, 9)) 166038032Speter printopenfds(FALSE); 166138032Speter 166238032Speter /* if we fail in finis(), just exit */ 166338032Speter if (setjmp(TopFrame) != 0) 166438032Speter { 166538032Speter /* failed -- just give it up */ 166638032Speter goto forceexit; 166738032Speter } 166838032Speter 166938032Speter /* clean up temp files */ 167038032Speter CurEnv->e_to = NULL; 167138032Speter if (CurEnv->e_id != NULL) 167238032Speter dropenvelope(CurEnv, TRUE); 167338032Speter 167438032Speter /* flush any cached connections */ 167538032Speter mci_flush(TRUE, NULL); 167638032Speter 167738032Speter# ifdef XLA 167838032Speter /* clean up extended load average stuff */ 167938032Speter xla_all_end(); 168038032Speter# endif 168138032Speter 168238032Speter /* and exit */ 168338032Speter forceexit: 168438032Speter if (LogLevel > 78) 168538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 168638032Speter "finis, pid=%d", 168738032Speter getpid()); 168838032Speter if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) 168938032Speter ExitStat = EX_OK; 169038032Speter 169138032Speter /* reset uid for process accounting */ 169238032Speter endpwent(); 169338032Speter setuid(RealUid); 169438032Speter 169538032Speter exit(ExitStat); 169638032Speter} 169738032Speter/* 169838032Speter** INTSIG -- clean up on interrupt 169938032Speter** 170038032Speter** This just arranges to exit. It pessimises in that it 170138032Speter** may resend a message. 170238032Speter** 170338032Speter** Parameters: 170438032Speter** none. 170538032Speter** 170638032Speter** Returns: 170738032Speter** none. 170838032Speter** 170938032Speter** Side Effects: 171038032Speter** Unlocks the current job. 171138032Speter*/ 171238032Speter 171338032Speter/* ARGSUSED */ 171438032SpeterSIGFUNC_DECL 171538032Speterintsig(sig) 171638032Speter int sig; 171738032Speter{ 171838032Speter if (LogLevel > 79) 171938032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); 172038032Speter FileName = NULL; 172138032Speter unlockqueue(CurEnv); 172238032Speter#ifdef XLA 172338032Speter xla_all_end(); 172438032Speter#endif 172538032Speter 172638032Speter /* reset uid for process accounting */ 172738032Speter endpwent(); 172838032Speter setuid(RealUid); 172938032Speter 173038032Speter exit(EX_OK); 173138032Speter} 173238032Speter/* 173338032Speter** INITMACROS -- initialize the macro system 173438032Speter** 173538032Speter** This just involves defining some macros that are actually 173638032Speter** used internally as metasymbols to be themselves. 173738032Speter** 173838032Speter** Parameters: 173938032Speter** none. 174038032Speter** 174138032Speter** Returns: 174238032Speter** none. 174338032Speter** 174438032Speter** Side Effects: 174538032Speter** initializes several macros to be themselves. 174638032Speter*/ 174738032Speter 174838032Speterstruct metamac MetaMacros[] = 174938032Speter{ 175038032Speter /* LHS pattern matching characters */ 175138032Speter { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, 175238032Speter { '=', MATCHCLASS }, { '~', MATCHNCLASS }, 175338032Speter 175438032Speter /* these are RHS metasymbols */ 175538032Speter { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, 175638032Speter { '>', CALLSUBR }, 175738032Speter 175838032Speter /* the conditional operations */ 175938032Speter { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, 176038032Speter 176138032Speter /* the hostname lookup characters */ 176238032Speter { '[', HOSTBEGIN }, { ']', HOSTEND }, 176338032Speter { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, 176438032Speter 176538032Speter /* miscellaneous control characters */ 176638032Speter { '&', MACRODEXPAND }, 176738032Speter 176838032Speter { '\0' } 176938032Speter}; 177038032Speter 177138032Speter#define MACBINDING(name, mid) \ 177238032Speter stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ 177338032Speter MacroName[mid] = name; 177438032Speter 177538032Spetervoid 177638032Speterinitmacros(e) 177738032Speter register ENVELOPE *e; 177838032Speter{ 177938032Speter register struct metamac *m; 178038032Speter register int c; 178138032Speter char buf[5]; 178238032Speter extern char *MacroName[256]; 178338032Speter 178438032Speter for (m = MetaMacros; m->metaname != '\0'; m++) 178538032Speter { 178638032Speter buf[0] = m->metaval; 178738032Speter buf[1] = '\0'; 178838032Speter define(m->metaname, newstr(buf), e); 178938032Speter } 179038032Speter buf[0] = MATCHREPL; 179138032Speter buf[2] = '\0'; 179238032Speter for (c = '0'; c <= '9'; c++) 179338032Speter { 179438032Speter buf[1] = c; 179538032Speter define(c, newstr(buf), e); 179638032Speter } 179738032Speter 179838032Speter /* set defaults for some macros sendmail will use later */ 179938032Speter define('n', "MAILER-DAEMON", e); 180038032Speter 180138032Speter /* set up external names for some internal macros */ 180238032Speter MACBINDING("opMode", MID_OPMODE); 180338032Speter /*XXX should probably add equivalents for all short macros here XXX*/ 180438032Speter} 180538032Speter/* 180638032Speter** DISCONNECT -- remove our connection with any foreground process 180738032Speter** 180838032Speter** Parameters: 180938032Speter** droplev -- how "deeply" we should drop the line. 181038032Speter** 0 -- ignore signals, mail back errors, make sure 181138032Speter** output goes to stdout. 181238032Speter** 1 -- also, make stdout go to transcript. 181338032Speter** 2 -- also, disconnect from controlling terminal 181438032Speter** (only for daemon mode). 181538032Speter** e -- the current envelope. 181638032Speter** 181738032Speter** Returns: 181838032Speter** none 181938032Speter** 182038032Speter** Side Effects: 182138032Speter** Trys to insure that we are immune to vagaries of 182238032Speter** the controlling tty. 182338032Speter*/ 182438032Speter 182538032Spetervoid 182638032Speterdisconnect(droplev, e) 182738032Speter int droplev; 182838032Speter register ENVELOPE *e; 182938032Speter{ 183038032Speter int fd; 183138032Speter 183238032Speter if (tTd(52, 1)) 183338032Speter printf("disconnect: In %d Out %d, e=%lx\n", 183438032Speter fileno(InChannel), fileno(OutChannel), (u_long) e); 183538032Speter if (tTd(52, 100)) 183638032Speter { 183738032Speter printf("don't\n"); 183838032Speter return; 183938032Speter } 184038032Speter if (LogLevel > 93) 184138032Speter sm_syslog(LOG_DEBUG, e->e_id, 184238032Speter "disconnect level %d", 184338032Speter droplev); 184438032Speter 184538032Speter /* be sure we don't get nasty signals */ 184638032Speter (void) setsignal(SIGINT, SIG_IGN); 184738032Speter (void) setsignal(SIGQUIT, SIG_IGN); 184838032Speter 184938032Speter /* we can't communicate with our caller, so.... */ 185038032Speter HoldErrs = TRUE; 185138032Speter CurEnv->e_errormode = EM_MAIL; 185238032Speter Verbose = 0; 185338032Speter DisConnected = TRUE; 185438032Speter 185538032Speter /* all input from /dev/null */ 185638032Speter if (InChannel != stdin) 185738032Speter { 185838032Speter (void) fclose(InChannel); 185938032Speter InChannel = stdin; 186038032Speter } 186138032Speter if (freopen("/dev/null", "r", stdin) == NULL) 186238032Speter sm_syslog(LOG_ERR, e->e_id, 186338032Speter "disconnect: freopen(\"/dev/null\") failed: %s", 186438032Speter errstring(errno)); 186538032Speter 186638032Speter /* output to the transcript */ 186738032Speter if (OutChannel != stdout) 186838032Speter { 186938032Speter (void) fclose(OutChannel); 187038032Speter OutChannel = stdout; 187138032Speter } 187238032Speter if (droplev > 0) 187338032Speter { 187438032Speter if (e->e_xfp == NULL) 187538032Speter { 187638032Speter fd = open("/dev/null", O_WRONLY, 0666); 187738032Speter if (fd == -1) 187838032Speter sm_syslog(LOG_ERR, e->e_id, 187938032Speter "disconnect: open(\"/dev/null\") failed: %s", 188038032Speter errstring(errno)); 188138032Speter } 188238032Speter else 188338032Speter { 188438032Speter fd = fileno(e->e_xfp); 188538032Speter if (fd == -1) 188638032Speter sm_syslog(LOG_ERR, e->e_id, 188738032Speter "disconnect: fileno(e->e_xfp) failed: %s", 188838032Speter errstring(errno)); 188938032Speter } 189038032Speter (void) fflush(stdout); 189138032Speter dup2(fd, STDOUT_FILENO); 189238032Speter dup2(fd, STDERR_FILENO); 189338032Speter if (e->e_xfp == NULL) 189438032Speter close(fd); 189538032Speter } 189638032Speter 189738032Speter /* drop our controlling TTY completely if possible */ 189838032Speter if (droplev > 1) 189938032Speter { 190038032Speter (void) setsid(); 190138032Speter errno = 0; 190238032Speter } 190338032Speter 190438032Speter#if XDEBUG 190538032Speter checkfd012("disconnect"); 190638032Speter#endif 190738032Speter 190838032Speter if (LogLevel > 71) 190938032Speter sm_syslog(LOG_DEBUG, e->e_id, 191038032Speter "in background, pid=%d", 191138032Speter getpid()); 191238032Speter 191338032Speter errno = 0; 191438032Speter} 191538032Speter 191638032Speterstatic void 191738032Speterobsolete(argv) 191838032Speter char *argv[]; 191938032Speter{ 192038032Speter register char *ap; 192138032Speter register char *op; 192238032Speter 192338032Speter while ((ap = *++argv) != NULL) 192438032Speter { 192538032Speter /* Return if "--" or not an option of any form. */ 192638032Speter if (ap[0] != '-' || ap[1] == '-') 192738032Speter return; 192838032Speter 192938032Speter /* skip over options that do have a value */ 193038032Speter op = strchr(OPTIONS, ap[1]); 193138032Speter if (op != NULL && *++op == ':' && ap[2] == '\0' && 193238032Speter ap[1] != 'd' && 193338032Speter#if defined(sony_news) 193438032Speter ap[1] != 'E' && ap[1] != 'J' && 193538032Speter#endif 193638032Speter argv[1] != NULL && argv[1][0] != '-') 193738032Speter { 193838032Speter argv++; 193938032Speter continue; 194038032Speter } 194138032Speter 194238032Speter /* If -C doesn't have an argument, use sendmail.cf. */ 194338032Speter#define __DEFPATH "sendmail.cf" 194438032Speter if (ap[1] == 'C' && ap[2] == '\0') 194538032Speter { 194638032Speter *argv = xalloc(sizeof(__DEFPATH) + 2); 194738032Speter argv[0][0] = '-'; 194838032Speter argv[0][1] = 'C'; 194938032Speter (void)strcpy(&argv[0][2], __DEFPATH); 195038032Speter } 195138032Speter 195238032Speter /* If -q doesn't have an argument, run it once. */ 195338032Speter if (ap[1] == 'q' && ap[2] == '\0') 195438032Speter *argv = "-q0"; 195538032Speter 195638032Speter /* if -d doesn't have an argument, use 0-99.1 */ 195738032Speter if (ap[1] == 'd' && ap[2] == '\0') 195838032Speter *argv = "-d0-99.1"; 195938032Speter 196038032Speter# if defined(sony_news) 196138032Speter /* if -E doesn't have an argument, use -EC */ 196238032Speter if (ap[1] == 'E' && ap[2] == '\0') 196338032Speter *argv = "-EC"; 196438032Speter 196538032Speter /* if -J doesn't have an argument, use -JJ */ 196638032Speter if (ap[1] == 'J' && ap[2] == '\0') 196738032Speter *argv = "-JJ"; 196838032Speter# endif 196938032Speter } 197038032Speter} 197138032Speter/* 197238032Speter** AUTH_WARNING -- specify authorization warning 197338032Speter** 197438032Speter** Parameters: 197538032Speter** e -- the current envelope. 197638032Speter** msg -- the text of the message. 197738032Speter** args -- arguments to the message. 197838032Speter** 197938032Speter** Returns: 198038032Speter** none. 198138032Speter*/ 198238032Speter 198338032Spetervoid 198438032Speter#ifdef __STDC__ 198538032Speterauth_warning(register ENVELOPE *e, const char *msg, ...) 198638032Speter#else 198738032Speterauth_warning(e, msg, va_alist) 198838032Speter register ENVELOPE *e; 198938032Speter const char *msg; 199038032Speter va_dcl 199138032Speter#endif 199238032Speter{ 199338032Speter char buf[MAXLINE]; 199438032Speter VA_LOCAL_DECL 199538032Speter 199638032Speter if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) 199738032Speter { 199838032Speter register char *p; 199938032Speter static char hostbuf[48]; 200038032Speter extern struct hostent *myhostname __P((char *, int)); 200138032Speter 200238032Speter if (hostbuf[0] == '\0') 200338032Speter (void) myhostname(hostbuf, sizeof hostbuf); 200438032Speter 200538032Speter (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); 200638032Speter p = &buf[strlen(buf)]; 200738032Speter VA_START(msg); 200838032Speter vsnprintf(p, SPACELEFT(buf, p), msg, ap); 200938032Speter VA_END; 201038032Speter addheader("X-Authentication-Warning", buf, &e->e_header); 201138032Speter if (LogLevel > 3) 201238032Speter sm_syslog(LOG_INFO, e->e_id, 201338032Speter "Authentication-Warning: %.400s", 201438032Speter buf); 201538032Speter } 201638032Speter} 201738032Speter/* 201838032Speter** GETEXTENV -- get from external environment 201938032Speter** 202038032Speter** Parameters: 202138032Speter** envar -- the name of the variable to retrieve 202238032Speter** 202338032Speter** Returns: 202438032Speter** The value, if any. 202538032Speter*/ 202638032Speter 202738032Speterchar * 202838032Spetergetextenv(envar) 202938032Speter const char *envar; 203038032Speter{ 203138032Speter char **envp; 203238032Speter int l; 203338032Speter 203438032Speter l = strlen(envar); 203538032Speter for (envp = ExternalEnviron; *envp != NULL; envp++) 203638032Speter { 203738032Speter if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') 203838032Speter return &(*envp)[l + 1]; 203938032Speter } 204038032Speter return NULL; 204138032Speter} 204238032Speter/* 204338032Speter** SETUSERENV -- set an environment in the propogated environment 204438032Speter** 204538032Speter** Parameters: 204638032Speter** envar -- the name of the environment variable. 204738032Speter** value -- the value to which it should be set. If 204838032Speter** null, this is extracted from the incoming 204938032Speter** environment. If that is not set, the call 205038032Speter** to setuserenv is ignored. 205138032Speter** 205238032Speter** Returns: 205338032Speter** none. 205438032Speter*/ 205538032Speter 205638032Spetervoid 205738032Spetersetuserenv(envar, value) 205838032Speter const char *envar; 205938032Speter const char *value; 206038032Speter{ 206138032Speter int i; 206238032Speter char **evp = UserEnviron; 206338032Speter char *p; 206438032Speter 206538032Speter if (value == NULL) 206638032Speter { 206738032Speter value = getextenv(envar); 206838032Speter if (value == NULL) 206938032Speter return; 207038032Speter } 207138032Speter 207238032Speter i = strlen(envar); 207338032Speter p = (char *) xalloc(strlen(value) + i + 2); 207438032Speter strcpy(p, envar); 207538032Speter p[i++] = '='; 207638032Speter strcpy(&p[i], value); 207738032Speter 207838032Speter while (*evp != NULL && strncmp(*evp, p, i) != 0) 207938032Speter evp++; 208038032Speter if (*evp != NULL) 208138032Speter { 208238032Speter *evp++ = p; 208338032Speter } 208438032Speter else if (evp < &UserEnviron[MAXUSERENVIRON]) 208538032Speter { 208638032Speter *evp++ = p; 208738032Speter *evp = NULL; 208838032Speter } 208938032Speter 209038032Speter /* make sure it is in our environment as well */ 209138032Speter if (putenv(p) < 0) 209238032Speter syserr("setuserenv: putenv(%s) failed", p); 209338032Speter} 209438032Speter/* 209538032Speter** DUMPSTATE -- dump state 209638032Speter** 209738032Speter** For debugging. 209838032Speter*/ 209938032Speter 210038032Spetervoid 210138032Speterdumpstate(when) 210238032Speter char *when; 210338032Speter{ 210438032Speter register char *j = macvalue('j', CurEnv); 210538032Speter int rs; 210638032Speter 210738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 210838032Speter "--- dumping state on %s: $j = %s ---", 210938032Speter when, 211038032Speter j == NULL ? "<NULL>" : j); 211138032Speter if (j != NULL) 211238032Speter { 211338032Speter if (!wordinclass(j, 'w')) 211438032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 211538032Speter "*** $j not in $=w ***"); 211638032Speter } 211738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); 211838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); 211938032Speter printopenfds(TRUE); 212038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); 212138032Speter mci_dump_all(TRUE); 212238032Speter rs = strtorwset("debug_dumpstate", NULL, ST_FIND); 212338032Speter if (rs > 0) 212438032Speter { 212538032Speter int stat; 212638032Speter register char **pvp; 212738032Speter char *pv[MAXATOM + 1]; 212838032Speter 212938032Speter pv[0] = NULL; 213038032Speter stat = rewrite(pv, rs, 0, CurEnv); 213138032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 213238032Speter "--- ruleset debug_dumpstate returns stat %d, pv: ---", 213338032Speter stat); 213438032Speter for (pvp = pv; *pvp != NULL; pvp++) 213538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); 213638032Speter } 213738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); 213838032Speter} 213938032Speter 214038032Speter 214138032Speter/* ARGSUSED */ 214238032SpeterSIGFUNC_DECL 214338032Spetersigusr1(sig) 214438032Speter int sig; 214538032Speter{ 214638032Speter dumpstate("user signal"); 214738032Speter return SIGFUNC_RETURN; 214838032Speter} 214938032Speter 215038032Speter 215138032Speter/* ARGSUSED */ 215238032SpeterSIGFUNC_DECL 215338032Spetersighup(sig) 215438032Speter int sig; 215538032Speter{ 215638032Speter if (SaveArgv[0][0] != '/') 215738032Speter { 215838032Speter if (LogLevel > 3) 215938032Speter sm_syslog(LOG_INFO, NOQID, "could not restart: need full path"); 216038032Speter exit(EX_OSFILE); 216138032Speter } 216238032Speter if (LogLevel > 3) 216338032Speter sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]); 216438032Speter alarm(0); 216538032Speter releasesignal(SIGHUP); 216638032Speter if (drop_privileges(TRUE) != EX_OK) 216738032Speter { 216838032Speter if (LogLevel > 0) 216938032Speter sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m", 217038032Speter RunAsUid, RunAsGid); 217138032Speter exit(EX_OSERR); 217238032Speter } 217338032Speter execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); 217438032Speter if (LogLevel > 0) 217538032Speter sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]); 217638032Speter exit(EX_OSFILE); 217738032Speter} 217838032Speter/* 217938032Speter** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option 218038032Speter** 218138032Speter** Parameters: 218238032Speter** to_real_uid -- if set, drop to the real uid instead 218338032Speter** of the RunAsUser. 218438032Speter** 218538032Speter** Returns: 218638032Speter** EX_OSERR if the setuid failed. 218738032Speter** EX_OK otherwise. 218838032Speter*/ 218938032Speter 219038032Speterint 219138032Speterdrop_privileges(to_real_uid) 219238032Speter bool to_real_uid; 219338032Speter{ 219438032Speter int rval = EX_OK; 219538032Speter GIDSET_T emptygidset[1]; 219638032Speter 219738032Speter if (tTd(47, 1)) 219838032Speter printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n", 219938032Speter (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid); 220038032Speter 220138032Speter if (to_real_uid) 220238032Speter { 220338032Speter RunAsUserName = RealUserName; 220438032Speter RunAsUid = RealUid; 220538032Speter RunAsGid = RealGid; 220638032Speter } 220738032Speter 220838032Speter /* make sure no one can grab open descriptors for secret files */ 220938032Speter endpwent(); 221038032Speter 221138032Speter /* reset group permissions; these can be set later */ 221238032Speter emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); 221338032Speter if (setgroups(1, emptygidset) == -1 && geteuid() == 0) 221438032Speter rval = EX_OSERR; 221538032Speter 221638032Speter /* reset primary group and user id */ 221738032Speter if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0) 221838032Speter rval = EX_OSERR; 221938032Speter if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0) 222038032Speter rval = EX_OSERR; 222138032Speter if (tTd(47, 5)) 222238032Speter { 222338032Speter printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", 222438032Speter (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid()); 222538032Speter printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); 222638032Speter } 222738032Speter return rval; 222838032Speter} 222938032Speter/* 223038032Speter** FILL_FD -- make sure a file descriptor has been properly allocated 223138032Speter** 223238032Speter** Used to make sure that stdin/out/err are allocated on startup 223338032Speter** 223438032Speter** Parameters: 223538032Speter** fd -- the file descriptor to be filled. 223638032Speter** where -- a string used for logging. If NULL, this is 223738032Speter** being called on startup, and logging should 223838032Speter** not be done. 223938032Speter** 224038032Speter** Returns: 224138032Speter** none 224238032Speter*/ 224338032Speter 224438032Spetervoid 224538032Speterfill_fd(fd, where) 224638032Speter int fd; 224738032Speter char *where; 224838032Speter{ 224938032Speter int i; 225038032Speter struct stat stbuf; 225138032Speter 225238032Speter if (fstat(fd, &stbuf) >= 0 || errno != EBADF) 225338032Speter return; 225438032Speter 225538032Speter if (where != NULL) 225638032Speter syserr("fill_fd: %s: fd %d not open", where, fd); 225738032Speter else 225838032Speter MissingFds |= 1 << fd; 225938032Speter i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666); 226038032Speter if (i < 0) 226138032Speter { 226238032Speter syserr("!fill_fd: %s: cannot open /dev/null", 226338032Speter where == NULL ? "startup" : where); 226438032Speter } 226538032Speter if (fd != i) 226638032Speter { 226738032Speter (void) dup2(i, fd); 226838032Speter (void) close(i); 226938032Speter } 227038032Speter} 227138032Speter/* 227238032Speter** TESTMODELINE -- process a test mode input line 227338032Speter** 227438032Speter** Parameters: 227538032Speter** line -- the input line. 227638032Speter** e -- the current environment. 227738032Speter** Syntax: 227838032Speter** # a comment 227938032Speter** .X process X as a configuration line 228038032Speter** =X dump a configuration item (such as mailers) 228138032Speter** $X dump a macro or class 228238032Speter** /X try an activity 228338032Speter** X normal process through rule set X 228438032Speter*/ 228538032Speter 228638032Spetervoid 228738032Spetertestmodeline(line, e) 228838032Speter char *line; 228938032Speter ENVELOPE *e; 229038032Speter{ 229138032Speter register char *p; 229238032Speter char *q; 229338032Speter auto char *delimptr; 229438032Speter int mid; 229538032Speter int i, rs; 229638032Speter STAB *map; 229738032Speter char **s; 229838032Speter struct rewrite *rw; 229938032Speter ADDRESS a; 230038032Speter static int tryflags = RF_COPYNONE; 230138032Speter char exbuf[MAXLINE]; 230238032Speter extern bool invalidaddr __P((char *, char *)); 230338032Speter extern char *crackaddr __P((char *)); 230438032Speter extern void dump_class __P((STAB *, int)); 230538032Speter extern void translate_dollars __P((char *)); 230638032Speter extern void help __P((char *)); 230738032Speter 230838032Speter switch (line[0]) 230938032Speter { 231038032Speter case '#': 231138032Speter case 0: 231238032Speter return; 231338032Speter 231438032Speter case '?': 231538032Speter help("-bt"); 231638032Speter return; 231738032Speter 231838032Speter case '.': /* config-style settings */ 231938032Speter switch (line[1]) 232038032Speter { 232138032Speter case 'D': 232238032Speter mid = macid(&line[2], &delimptr); 232338032Speter if (mid == '\0') 232438032Speter return; 232538032Speter translate_dollars(delimptr); 232638032Speter define(mid, newstr(delimptr), e); 232738032Speter break; 232838032Speter 232938032Speter case 'C': 233038032Speter if (line[2] == '\0') /* not to call syserr() */ 233138032Speter return; 233238032Speter 233338032Speter mid = macid(&line[2], &delimptr); 233438032Speter if (mid == '\0') 233538032Speter return; 233638032Speter translate_dollars(delimptr); 233738032Speter expand(delimptr, exbuf, sizeof exbuf, e); 233838032Speter p = exbuf; 233938032Speter while (*p != '\0') 234038032Speter { 234138032Speter register char *wd; 234238032Speter char delim; 234338032Speter 234438032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 234538032Speter p++; 234638032Speter wd = p; 234738032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 234838032Speter p++; 234938032Speter delim = *p; 235038032Speter *p = '\0'; 235138032Speter if (wd[0] != '\0') 235238032Speter setclass(mid, wd); 235338032Speter *p = delim; 235438032Speter } 235538032Speter break; 235638032Speter 235738032Speter case '\0': 235838032Speter printf("Usage: .[DC]macro value(s)\n"); 235938032Speter break; 236038032Speter 236138032Speter default: 236238032Speter printf("Unknown \".\" command %s\n", line); 236338032Speter break; 236438032Speter } 236538032Speter return; 236638032Speter 236738032Speter case '=': /* config-style settings */ 236838032Speter switch (line[1]) 236938032Speter { 237038032Speter case 'S': /* dump rule set */ 237138032Speter rs = strtorwset(&line[2], NULL, ST_FIND); 237238032Speter if (rs < 0) 237338032Speter { 237438032Speter printf("Undefined ruleset %s\n", &line[2]); 237538032Speter return; 237638032Speter } 237738032Speter rw = RewriteRules[rs]; 237838032Speter if (rw == NULL) 237938032Speter return; 238038032Speter do 238138032Speter { 238238032Speter putchar('R'); 238338032Speter s = rw->r_lhs; 238438032Speter while (*s != NULL) 238538032Speter { 238638032Speter xputs(*s++); 238738032Speter putchar(' '); 238838032Speter } 238938032Speter putchar('\t'); 239038032Speter putchar('\t'); 239138032Speter s = rw->r_rhs; 239238032Speter while (*s != NULL) 239338032Speter { 239438032Speter xputs(*s++); 239538032Speter putchar(' '); 239638032Speter } 239738032Speter putchar('\n'); 239838032Speter } while ((rw = rw->r_next) != NULL); 239938032Speter break; 240038032Speter 240138032Speter case 'M': 240238032Speter for (i = 0; i < MAXMAILERS; i++) 240338032Speter { 240438032Speter if (Mailer[i] != NULL) 240538032Speter printmailer(Mailer[i]); 240638032Speter } 240738032Speter break; 240838032Speter 240938032Speter case '\0': 241038032Speter printf("Usage: =Sruleset or =M\n"); 241138032Speter break; 241238032Speter 241338032Speter default: 241438032Speter printf("Unknown \"=\" command %s\n", line); 241538032Speter break; 241638032Speter } 241738032Speter return; 241838032Speter 241938032Speter case '-': /* set command-line-like opts */ 242038032Speter switch (line[1]) 242138032Speter { 242238032Speter case 'd': 242338032Speter tTflag(&line[2]); 242438032Speter break; 242538032Speter 242638032Speter case '\0': 242738032Speter printf("Usage: -d{debug arguments}\n"); 242838032Speter break; 242938032Speter 243038032Speter default: 243138032Speter printf("Unknown \"-\" command %s\n", line); 243238032Speter break; 243338032Speter } 243438032Speter return; 243538032Speter 243638032Speter case '$': 243738032Speter if (line[1] == '=') 243838032Speter { 243938032Speter mid = macid(&line[2], NULL); 244038032Speter if (mid != '\0') 244138032Speter stabapply(dump_class, mid); 244238032Speter return; 244338032Speter } 244438032Speter mid = macid(&line[1], NULL); 244538032Speter if (mid == '\0') 244638032Speter return; 244738032Speter p = macvalue(mid, e); 244838032Speter if (p == NULL) 244938032Speter printf("Undefined\n"); 245038032Speter else 245138032Speter { 245238032Speter xputs(p); 245338032Speter printf("\n"); 245438032Speter } 245538032Speter return; 245638032Speter 245738032Speter case '/': /* miscellaneous commands */ 245838032Speter p = &line[strlen(line)]; 245938032Speter while (--p >= line && isascii(*p) && isspace(*p)) 246038032Speter *p = '\0'; 246138032Speter p = strpbrk(line, " \t"); 246238032Speter if (p != NULL) 246338032Speter { 246438032Speter while (isascii(*p) && isspace(*p)) 246538032Speter *p++ = '\0'; 246638032Speter } 246738032Speter else 246838032Speter p = ""; 246938032Speter if (line[1] == '\0') 247038032Speter { 247138032Speter printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); 247238032Speter return; 247338032Speter } 247438032Speter if (strcasecmp(&line[1], "mx") == 0) 247538032Speter { 247638032Speter#if NAMED_BIND 247738032Speter /* look up MX records */ 247838032Speter int nmx; 247938032Speter auto int rcode; 248038032Speter char *mxhosts[MAXMXHOSTS + 1]; 248138032Speter 248238032Speter if (*p == '\0') 248338032Speter { 248438032Speter printf("Usage: /mx address\n"); 248538032Speter return; 248638032Speter } 248738032Speter nmx = getmxrr(p, mxhosts, FALSE, &rcode); 248838032Speter printf("getmxrr(%s) returns %d value(s):\n", p, nmx); 248938032Speter for (i = 0; i < nmx; i++) 249038032Speter printf("\t%s\n", mxhosts[i]); 249138032Speter#else 249238032Speter printf("No MX code compiled in\n"); 249338032Speter#endif 249438032Speter } 249538032Speter else if (strcasecmp(&line[1], "canon") == 0) 249638032Speter { 249738032Speter char host[MAXHOSTNAMELEN]; 249838032Speter 249938032Speter if (*p == '\0') 250038032Speter { 250138032Speter printf("Usage: /canon address\n"); 250238032Speter return; 250338032Speter } 250438032Speter else if (strlen(p) >= sizeof host) 250538032Speter { 250638032Speter printf("Name too long\n"); 250738032Speter return; 250838032Speter } 250938032Speter strcpy(host, p); 251038032Speter (void) getcanonname(host, sizeof(host), HasWildcardMX); 251138032Speter printf("getcanonname(%s) returns %s\n", p, host); 251238032Speter } 251338032Speter else if (strcasecmp(&line[1], "map") == 0) 251438032Speter { 251538032Speter auto int rcode = EX_OK; 251638032Speter char *av[2]; 251738032Speter 251838032Speter if (*p == '\0') 251938032Speter { 252038032Speter printf("Usage: /map mapname key\n"); 252138032Speter return; 252238032Speter } 252338032Speter for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) 252438032Speter continue; 252538032Speter if (*q == '\0') 252638032Speter { 252738032Speter printf("No key specified\n"); 252838032Speter return; 252938032Speter } 253038032Speter *q++ = '\0'; 253138032Speter map = stab(p, ST_MAP, ST_FIND); 253238032Speter if (map == NULL) 253338032Speter { 253438032Speter printf("Map named \"%s\" not found\n", p); 253538032Speter return; 253638032Speter } 253738032Speter if (!bitset(MF_OPEN, map->s_map.map_mflags)) 253838032Speter { 253938032Speter printf("Map named \"%s\" not open\n", p); 254038032Speter return; 254138032Speter } 254238032Speter printf("map_lookup: %s (%s) ", p, q); 254338032Speter av[0] = q; 254438032Speter av[1] = NULL; 254538032Speter p = (*map->s_map.map_class->map_lookup) 254638032Speter (&map->s_map, q, av, &rcode); 254738032Speter if (p == NULL) 254838032Speter printf("no match (%d)\n", rcode); 254938032Speter else 255038032Speter printf("returns %s (%d)\n", p, rcode); 255138032Speter } 255238032Speter else if (strcasecmp(&line[1], "try") == 0) 255338032Speter { 255438032Speter MAILER *m; 255538032Speter STAB *s; 255638032Speter auto int rcode = EX_OK; 255738032Speter 255838032Speter q = strpbrk(p, " \t"); 255938032Speter if (q != NULL) 256038032Speter { 256138032Speter while (isascii(*q) && isspace(*q)) 256238032Speter *q++ = '\0'; 256338032Speter } 256438032Speter if (q == NULL || *q == '\0') 256538032Speter { 256638032Speter printf("Usage: /try mailer address\n"); 256738032Speter return; 256838032Speter } 256938032Speter s = stab(p, ST_MAILER, ST_FIND); 257038032Speter if (s == NULL) 257138032Speter { 257238032Speter printf("Unknown mailer %s\n", p); 257338032Speter return; 257438032Speter } 257538032Speter m = s->s_mailer; 257638032Speter printf("Trying %s %s address %s for mailer %s\n", 257738032Speter bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 257838032Speter bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", 257938032Speter q, p); 258038032Speter p = remotename(q, m, tryflags, &rcode, CurEnv); 258138032Speter printf("Rcode = %d, addr = %s\n", 258238032Speter rcode, p == NULL ? "<NULL>" : p); 258338032Speter e->e_to = NULL; 258438032Speter } 258538032Speter else if (strcasecmp(&line[1], "tryflags") == 0) 258638032Speter { 258738032Speter if (*p == '\0') 258838032Speter { 258938032Speter printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); 259038032Speter return; 259138032Speter } 259238032Speter for (; *p != '\0'; p++) 259338032Speter { 259438032Speter switch (*p) 259538032Speter { 259638032Speter case 'H': 259738032Speter case 'h': 259838032Speter tryflags |= RF_HEADERADDR; 259938032Speter break; 260038032Speter 260138032Speter case 'E': 260238032Speter case 'e': 260338032Speter tryflags &= ~RF_HEADERADDR; 260438032Speter break; 260538032Speter 260638032Speter case 'S': 260738032Speter case 's': 260838032Speter tryflags |= RF_SENDERADDR; 260938032Speter break; 261038032Speter 261138032Speter case 'R': 261238032Speter case 'r': 261338032Speter tryflags &= ~RF_SENDERADDR; 261438032Speter break; 261538032Speter } 261638032Speter } 261738032Speter } 261838032Speter else if (strcasecmp(&line[1], "parse") == 0) 261938032Speter { 262038032Speter if (*p == '\0') 262138032Speter { 262238032Speter printf("Usage: /parse address\n"); 262338032Speter return; 262438032Speter } 262538032Speter q = crackaddr(p); 262638032Speter printf("Cracked address = "); 262738032Speter xputs(q); 262838032Speter printf("\nParsing %s %s address\n", 262938032Speter bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 263038032Speter bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); 263138032Speter if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) 263238032Speter printf("Cannot parse\n"); 263338032Speter else if (a.q_host != NULL && a.q_host[0] != '\0') 263438032Speter printf("mailer %s, host %s, user %s\n", 263538032Speter a.q_mailer->m_name, a.q_host, a.q_user); 263638032Speter else 263738032Speter printf("mailer %s, user %s\n", 263838032Speter a.q_mailer->m_name, a.q_user); 263938032Speter e->e_to = NULL; 264038032Speter } 264138032Speter else 264238032Speter { 264338032Speter printf("Unknown \"/\" command %s\n", line); 264438032Speter } 264538032Speter return; 264638032Speter } 264738032Speter 264838032Speter for (p = line; isascii(*p) && isspace(*p); p++) 264938032Speter continue; 265038032Speter q = p; 265138032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 265238032Speter p++; 265338032Speter if (*p == '\0') 265438032Speter { 265538032Speter printf("No address!\n"); 265638032Speter return; 265738032Speter } 265838032Speter *p = '\0'; 265938032Speter if (invalidaddr(p + 1, NULL)) 266038032Speter return; 266138032Speter do 266238032Speter { 266338032Speter register char **pvp; 266438032Speter char pvpbuf[PSBUFSIZE]; 266538032Speter 266638032Speter pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, 266738032Speter &delimptr, NULL); 266838032Speter if (pvp == NULL) 266938032Speter continue; 267038032Speter p = q; 267138032Speter while (*p != '\0') 267238032Speter { 267338032Speter int stat; 267438032Speter 267538032Speter rs = strtorwset(p, NULL, ST_FIND); 267638032Speter if (rs < 0) 267738032Speter { 267838032Speter printf("Undefined ruleset %s\n", p); 267938032Speter break; 268038032Speter } 268138032Speter stat = rewrite(pvp, rs, 0, e); 268238032Speter if (stat != EX_OK) 268338032Speter printf("== Ruleset %s (%d) status %d\n", 268438032Speter p, rs, stat); 268538032Speter while (*p != '\0' && *p++ != ',') 268638032Speter continue; 268738032Speter } 268838032Speter } while (*(p = delimptr) != '\0'); 268938032Speter} 269038032Speter 269138032Speter 269238032Spetervoid 269338032Speterdump_class(s, id) 269438032Speter register STAB *s; 269538032Speter int id; 269638032Speter{ 269738032Speter if (s->s_type != ST_CLASS) 269838032Speter return; 269938032Speter if (bitnset(id & 0xff, s->s_class)) 270038032Speter printf("%s\n", s->s_name); 270138032Speter} 2702