summaryrefslogtreecommitdiff
path: root/src/srvrsmtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/srvrsmtp.c')
-rw-r--r--src/srvrsmtp.c1532
1 files changed, 0 insertions, 1532 deletions
diff --git a/src/srvrsmtp.c b/src/srvrsmtp.c
deleted file mode 100644
index f4ffe8c..0000000
--- a/src/srvrsmtp.c
+++ /dev/null
@@ -1,1532 +0,0 @@
-/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-# include "sendmail.h"
-
-#ifndef lint
-#if SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (with SMTP)";
-#else
-static char sccsid[] = "@(#)srvrsmtp.c 8.187 (Berkeley) 10/23/1998 (without SMTP)";
-#endif
-#endif /* not lint */
-
-# include <errno.h>
-
-# if SMTP
-
-/*
-** SMTP -- run the SMTP protocol.
-**
-** Parameters:
-** nullserver -- if non-NULL, rejection message for
-** all SMTP commands.
-** e -- the envelope.
-**
-** Returns:
-** never.
-**
-** Side Effects:
-** Reads commands from the input channel and processes
-** them.
-*/
-
-struct cmd
-{
- char *cmdname; /* command name */
- int cmdcode; /* internal code, see below */
-};
-
-/* values for cmdcode */
-# define CMDERROR 0 /* bad command */
-# define CMDMAIL 1 /* mail -- designate sender */
-# define CMDRCPT 2 /* rcpt -- designate recipient */
-# define CMDDATA 3 /* data -- send message text */
-# define CMDRSET 4 /* rset -- reset state */
-# define CMDVRFY 5 /* vrfy -- verify address */
-# define CMDEXPN 6 /* expn -- expand address */
-# define CMDNOOP 7 /* noop -- do nothing */
-# define CMDQUIT 8 /* quit -- close connection and die */
-# define CMDHELO 9 /* helo -- be polite */
-# define CMDHELP 10 /* help -- give usage info */
-# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
-# define CMDETRN 12 /* etrn -- flush queue */
-/* non-standard commands */
-# define CMDONEX 16 /* onex -- sending one transaction only */
-# define CMDVERB 17 /* verb -- go into verbose mode */
-# define CMDXUSR 18 /* xusr -- initial (user) submission */
-/* use this to catch and log "door handle" attempts on your system */
-# define CMDLOGBOGUS 23 /* bogus command that should be logged */
-/* debugging-only commands, only enabled if SMTPDEBUG is defined */
-# define CMDDBGQSHOW 24 /* showq -- show send queue */
-# define CMDDBGDEBUG 25 /* debug -- set debug mode */
-
-static struct cmd CmdTab[] =
-{
- { "mail", CMDMAIL },
- { "rcpt", CMDRCPT },
- { "data", CMDDATA },
- { "rset", CMDRSET },
- { "vrfy", CMDVRFY },
- { "expn", CMDEXPN },
- { "help", CMDHELP },
- { "noop", CMDNOOP },
- { "quit", CMDQUIT },
- { "helo", CMDHELO },
- { "ehlo", CMDEHLO },
- { "etrn", CMDETRN },
- { "verb", CMDVERB },
- { "onex", CMDONEX },
- { "xusr", CMDXUSR },
- /* remaining commands are here only to trap and log attempts to use them */
- { "showq", CMDDBGQSHOW },
- { "debug", CMDDBGDEBUG },
- { "wiz", CMDLOGBOGUS },
-
- { NULL, CMDERROR }
-};
-
-bool OneXact = FALSE; /* one xaction only this run */
-char *CurSmtpClient; /* who's at the other end of channel */
-
-static char *skipword __P((char *volatile, char *));
-
-
-#define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-#define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
-#define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-#define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-#define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-
-void
-smtp(nullserver, e)
- char *nullserver;
- register ENVELOPE *volatile e;
-{
- register char *volatile p;
- register struct cmd *c;
- char *cmd;
- auto ADDRESS *vrfyqueue;
- ADDRESS *a;
- volatile bool gotmail; /* mail command received */
- volatile bool gothello; /* helo command received */
- bool vrfy; /* set if this is a vrfy command */
- char *volatile protocol; /* sending protocol */
- char *volatile sendinghost; /* sending hostname */
- char *volatile peerhostname; /* name of SMTP peer or "localhost" */
- auto char *delimptr;
- char *id;
- volatile int nrcpts = 0; /* number of RCPT commands */
- bool doublequeue;
- volatile bool discard;
- volatile int badcommands = 0; /* count of bad commands */
- volatile int nverifies = 0; /* count of VRFY/EXPN commands */
- volatile int n_etrn = 0; /* count of ETRN commands */
- volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */
- volatile int n_helo = 0; /* count of HELO/EHLO commands */
- bool ok;
- volatile int lognullconnection = TRUE;
- register char *q;
- QUEUE_CHAR *new;
- char inp[MAXLINE];
- char cmdbuf[MAXLINE];
- extern ENVELOPE BlankEnvelope;
- extern void help __P((char *));
- extern void settime __P((ENVELOPE *));
- extern bool enoughdiskspace __P((long));
- extern int runinchild __P((char *, ENVELOPE *));
- extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
-
- if (fileno(OutChannel) != fileno(stdout))
- {
- /* arrange for debugging output to go to remote host */
- (void) dup2(fileno(OutChannel), fileno(stdout));
- }
- settime(e);
- peerhostname = RealHostName;
- if (peerhostname == NULL)
- peerhostname = "localhost";
- CurHostName = peerhostname;
- CurSmtpClient = macvalue('_', e);
- if (CurSmtpClient == NULL)
- CurSmtpClient = CurHostName;
-
- /* check_relay may have set discard bit, save for later */
- discard = bitset(EF_DISCARD, e->e_flags);
-
- sm_setproctitle(TRUE, "server %s startup", CurSmtpClient);
-#if DAEMON
- if (LogLevel > 11)
- {
- /* log connection information */
- sm_syslog(LOG_INFO, NOQID,
- "SMTP connect from %.100s (%.100s)",
- CurSmtpClient, anynet_ntoa(&RealHostAddr));
- }
-#endif
-
- /* output the first line, inserting "ESMTP" as second word */
- expand(SmtpGreeting, inp, sizeof inp, e);
- p = strchr(inp, '\n');
- if (p != NULL)
- *p++ = '\0';
- id = strchr(inp, ' ');
- if (id == NULL)
- id = &inp[strlen(inp)];
- cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
- message(cmd, id - inp, inp, id);
-
- /* output remaining lines */
- while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
- {
- *p++ = '\0';
- if (isascii(*id) && isspace(*id))
- id++;
- message("220-%s", id);
- }
- if (id != NULL)
- {
- if (isascii(*id) && isspace(*id))
- id++;
- message("220 %s", id);
- }
-
- protocol = NULL;
- sendinghost = macvalue('s', e);
- gothello = FALSE;
- gotmail = FALSE;
- for (;;)
- {
- /* arrange for backout */
- (void) setjmp(TopFrame);
- QuickAbort = FALSE;
- HoldErrs = FALSE;
- SuprErrs = FALSE;
- LogUsrErrs = FALSE;
- OnlyOneError = TRUE;
- e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
-
- /* setup for the read */
- e->e_to = NULL;
- Errors = 0;
- (void) fflush(stdout);
-
- /* read the input line */
- SmtpPhase = "server cmd read";
- sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient);
- p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
- SmtpPhase);
-
- /* handle errors */
- if (p == NULL)
- {
- /* end of file, just die */
- disconnect(1, e);
- message("421 %s Lost input channel from %s",
- MyHostName, CurSmtpClient);
- if (LogLevel > (gotmail ? 1 : 19))
- sm_syslog(LOG_NOTICE, e->e_id,
- "lost input channel from %.100s",
- CurSmtpClient);
- if (lognullconnection && LogLevel > 5)
- sm_syslog(LOG_INFO, NULL,
- "Null connection from %.100s",
- CurSmtpClient);
-
- /*
- ** If have not accepted mail (DATA), do not bounce
- ** bad addresses back to sender.
- */
- if (bitset(EF_CLRQUEUE, e->e_flags))
- e->e_sendqueue = NULL;
-
- if (InChild)
- ExitStat = EX_QUIT;
- finis(TRUE, ExitStat);
- }
-
- /* clean up end of line */
- fixcrlf(inp, TRUE);
-
- /* echo command to transcript */
- if (e->e_xfp != NULL)
- fprintf(e->e_xfp, "<<< %s\n", inp);
-
- if (LogLevel >= 15)
- sm_syslog(LOG_INFO, e->e_id,
- "<-- %s",
- inp);
-
- if (e->e_id == NULL)
- sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp);
- else
- sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
-
- /* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
- continue;
- cmd = cmdbuf;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
- cmd < &cmdbuf[sizeof cmdbuf - 2])
- *cmd++ = *p++;
- *cmd = '\0';
-
- /* throw away leading whitespace */
- while (isascii(*p) && isspace(*p))
- p++;
-
- /* decode command */
- for (c = CmdTab; c->cmdname != NULL; c++)
- {
- if (!strcasecmp(c->cmdname, cmdbuf))
- break;
- }
-
- /* reset errors */
- errno = 0;
-
- /*
- ** Process command.
- **
- ** If we are running as a null server, return 550
- ** to everything.
- */
-
- if (nullserver != NULL)
- {
- switch (c->cmdcode)
- {
- case CMDQUIT:
- case CMDHELO:
- case CMDEHLO:
- case CMDNOOP:
- /* process normally */
- break;
-
- default:
- if (++badcommands > MAXBADCOMMANDS)
- sleep(1);
- usrerr("550 %s", nullserver);
- continue;
- }
- }
-
- /* non-null server */
- switch (c->cmdcode)
- {
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = FALSE;
- }
-
- switch (c->cmdcode)
- {
- case CMDHELO: /* hello -- introduce yourself */
- case CMDEHLO: /* extended hello */
- if (c->cmdcode == CMDEHLO)
- {
- protocol = "ESMTP";
- SmtpPhase = "server EHLO";
- }
- else
- {
- protocol = "SMTP";
- SmtpPhase = "server HELO";
- }
-
- /* avoid denial-of-service */
- checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
-
- /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
- if (gothello)
- {
- usrerr("503 %s Duplicate HELO/EHLO",
- MyHostName);
- break;
- }
-
- /* check for valid domain name (re 1123 5.2.5) */
- if (*p == '\0' && !AllowBogusHELO)
- {
- usrerr("501 %s requires domain address",
- cmdbuf);
- break;
- }
-
- /* check for long domain name (hides Received: info) */
- if (strlen(p) > MAXNAME)
- {
- usrerr("501 Invalid domain name");
- break;
- }
-
- for (q = p; *q != '\0'; q++)
- {
- if (!isascii(*q))
- break;
- if (isalnum(*q))
- continue;
- if (isspace(*q))
- {
- *q = '\0';
- break;
- }
- if (strchr("[].-_#", *q) == NULL)
- break;
- }
- if (*q == '\0')
- {
- q = "pleased to meet you";
- sendinghost = newstr(p);
- }
- else if (!AllowBogusHELO)
- {
- usrerr("501 Invalid domain name");
- break;
- }
- else
- {
- q = "accepting invalid domain name";
- }
-
- gothello = TRUE;
-
- /* print HELO response message */
- if (c->cmdcode != CMDEHLO || nullserver != NULL)
- {
- message("250 %s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
- break;
- }
-
- message("250-%s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
-
- /* print EHLO features list */
- if (!bitset(PRIV_NOEXPN, PrivacyFlags))
- {
- message("250-EXPN");
- if (!bitset(PRIV_NOVERB, PrivacyFlags))
- message("250-VERB");
- }
-#if MIME8TO7
- message("250-8BITMIME");
-#endif
- if (MaxMessageSize > 0)
- message("250-SIZE %ld", MaxMessageSize);
- else
- message("250-SIZE");
-#if DSN
- if (SendMIMEErrors)
- message("250-DSN");
-#endif
- message("250-ONEX");
- if (!bitset(PRIV_NOETRN, PrivacyFlags))
- message("250-ETRN");
- message("250-XUSR");
- message("250 HELP");
- break;
-
- case CMDMAIL: /* mail -- designate sender */
- SmtpPhase = "server MAIL";
-
- /* check for validity of this command */
- if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
- {
- usrerr("503 Polite people say HELO first");
- break;
- }
- if (gotmail)
- {
- usrerr("503 Sender already specified");
- break;
- }
- if (InChild)
- {
- errno = 0;
- syserr("503 Nested MAIL command: MAIL %s", p);
- finis(TRUE, ExitStat);
- }
-
- /* make sure we know who the sending host is */
- if (sendinghost == NULL)
- sendinghost = peerhostname;
-
- p = skipword(p, "from");
- if (p == NULL)
- break;
-
- /* fork a subprocess to process this command */
- if (runinchild("SMTP-MAIL", e) > 0)
- break;
- if (Errors > 0)
- goto undo_subproc_no_pm;
- if (!gothello)
- {
- auth_warning(e,
- "%s didn't use HELO protocol",
- CurSmtpClient);
- }
-#ifdef PICKY_HELO_CHECK
- if (strcasecmp(sendinghost, peerhostname) != 0 &&
- (strcasecmp(peerhostname, "localhost") != 0 ||
- strcasecmp(sendinghost, MyHostName) != 0))
- {
- auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
- }
-#endif
-
- if (protocol == NULL)
- protocol = "SMTP";
- define('r', protocol, e);
- define('s', sendinghost, e);
- initsys(e);
- if (Errors > 0)
- goto undo_subproc_no_pm;
- nrcpts = 0;
- e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
- sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
-
- /* child -- go do the processing */
- if (setjmp(TopFrame) > 0)
- {
- /* this failed -- undo work */
- undo_subproc_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo_subproc:
- if (InChild)
- {
- QuickAbort = FALSE;
- SuprErrs = TRUE;
- e->e_flags &= ~EF_FATALERRS;
- finis(TRUE, ExitStat);
- }
- break;
- }
- QuickAbort = TRUE;
-
- /* must parse sender first */
- delimptr = NULL;
- setsender(p, e, &delimptr, ' ', FALSE);
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
- if (Errors > 0)
- goto undo_subproc_no_pm;
-
- /* do config file checking of the sender */
- if (rscheck("check_mail", p, NULL, e) != EX_OK ||
- Errors > 0)
- goto undo_subproc_no_pm;
-
- /* check for possible spoofing */
- if (RealUid != 0 && OpMode == MD_SMTP &&
- !wordinclass(RealUserName, 't') &&
- !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
- strcmp(e->e_from.q_user, RealUserName) != 0)
- {
- auth_warning(e, "%s owned process doing -bs",
- RealUserName);
- }
-
- /* now parse ESMTP arguments */
- e->e_msgsize = 0;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
-
- /* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- printf("MAIL: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- mail_esmtp_args(kp, vp, e);
- if (Errors > 0)
- goto undo_subproc_no_pm;
- }
- if (Errors > 0)
- goto undo_subproc_no_pm;
-
- if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
- {
- usrerr("552 Message size exceeds fixed maximum message size (%ld)",
- MaxMessageSize);
- goto undo_subproc_no_pm;
- }
-
- if (!enoughdiskspace(e->e_msgsize))
- {
- usrerr("452 Insufficient disk space; try again later");
- goto undo_subproc_no_pm;
- }
- if (Errors > 0)
- goto undo_subproc_no_pm;
- message("250 Sender ok");
- gotmail = TRUE;
- break;
-
- case CMDRCPT: /* rcpt -- designate recipient */
- if (!gotmail)
- {
- usrerr("503 Need MAIL before RCPT");
- break;
- }
- SmtpPhase = "server RCPT";
- if (setjmp(TopFrame) > 0)
- {
- e->e_flags &= ~EF_FATALERRS;
- break;
- }
- QuickAbort = TRUE;
- LogUsrErrs = TRUE;
-
- /* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
- {
- usrerr("452 Too many recipients");
- break;
- }
-
- if (e->e_sendmode != SM_DELIVER)
- e->e_flags |= EF_VRFYONLY;
-
- p = skipword(p, "to");
- if (p == NULL)
- break;
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
- if (Errors > 0)
- break;
- if (a == NULL)
- {
- usrerr("501 Missing recipient");
- break;
- }
-
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
-
- /* do config file checking of the recipient */
- if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
- Errors > 0)
- break;
-
- /* now parse ESMTP arguments */
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
-
- /* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- printf("RCPT: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- rcpt_esmtp_args(a, kp, vp, e);
- if (Errors > 0)
- break;
- }
- if (Errors > 0)
- break;
-
- /* save in recipient list after ESMTP mods */
- a = recipient(a, &e->e_sendqueue, 0, e);
- if (Errors > 0)
- break;
-
- /* no errors during parsing, but might be a duplicate */
- e->e_to = a->q_paddr;
- if (!bitset(QBADADDR, a->q_flags))
- {
- message("250 Recipient ok%s",
- bitset(QQUEUEUP, a->q_flags) ?
- " (will queue)" : "");
- nrcpts++;
- }
- else
- {
- /* punt -- should keep message in ADDRESS.... */
- usrerr("550 Addressee unknown");
- }
- break;
-
- case CMDDATA: /* data -- text of mail */
- SmtpPhase = "server DATA";
- if (!gotmail)
- {
- usrerr("503 Need MAIL command");
- break;
- }
- else if (nrcpts <= 0)
- {
- usrerr("503 Need RCPT (recipient)");
- break;
- }
-
- /* put back discard bit */
- if (discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QBADADDR on already-diagnosted addrs */
- doublequeue = FALSE;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (bitset(QVERIFIED, a->q_flags) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = TRUE;
- }
- if (bitset(QBADADDR, a->q_flags))
- {
- /* make this "go away" */
- a->q_flags |= QDONTSEND;
- a->q_flags &= ~QBADADDR;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
- collect(InChannel, TRUE, NULL, e);
- if (Errors > 0)
- {
- flush_errors(TRUE);
- buffer_errors();
- goto abortmessage;
- }
-
- /* make sure we actually do delivery */
- e->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, we have to operate silently */
- buffer_errors();
- e->e_errormode = EM_MAIL;
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
- id = e->e_id;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(e, FALSE);
- }
- else
- {
- /* send to all recipients */
- sendall(e, SM_DEFAULT);
- }
- e->e_to = NULL;
-
- /* issue success message */
- message("250 %s Message accepted for delivery", id);
-
- /* if we just queued, poke it */
- if (doublequeue &&
- e->e_sendmode != SM_QUEUE &&
- e->e_sendmode != SM_DEFER)
- {
- CurrentLA = getla();
-
- if (!shouldqueue(e->e_msgpriority, e->e_ctime))
- {
- unlockqueue(e);
- (void) dowork(id, TRUE, TRUE, e);
- }
- }
-
- abortmessage:
- /* if in a child, pop back to our parent */
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
- e->e_flags = BlankEnvelope.e_flags;
- break;
-
- case CMDRSET: /* rset -- reset state */
- if (tTd(94, 100))
- message("451 Test failure");
- else
- message("250 Reset state");
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- e->e_flags |= EF_CLRQUEUE;
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- SuprErrs = TRUE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
- break;
-
- case CMDVRFY: /* vrfy -- verify address */
- case CMDEXPN: /* expn -- expand address */
- checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
- c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
- vrfy = c->cmdcode == CMDVRFY;
- if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
- PrivacyFlags))
- {
- if (vrfy)
- message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
- else
- message("502 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- else if (!gothello &&
- bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
- PrivacyFlags))
- {
- usrerr("503 I demand that you introduce yourself first");
- break;
- }
- if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
- break;
- if (Errors > 0)
- goto undo_subproc;
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- if (setjmp(TopFrame) > 0)
- goto undo_subproc;
- QuickAbort = TRUE;
- vrfyqueue = NULL;
- if (vrfy)
- e->e_flags |= EF_VRFYONLY;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- {
- usrerr("501 Argument required");
- }
- else
- {
- (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
- }
- if (Errors > 0)
- goto undo_subproc;
- if (vrfyqueue == NULL)
- {
- usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
- }
- while (vrfyqueue != NULL)
- {
- extern void printvrfyaddr __P((ADDRESS *, bool, bool));
-
- a = vrfyqueue;
- while ((a = a->q_next) != NULL &&
- bitset(QDONTSEND|QBADADDR, a->q_flags))
- continue;
- if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
- printvrfyaddr(vrfyqueue, a == NULL, vrfy);
- vrfyqueue = vrfyqueue->q_next;
- }
- if (InChild)
- finis(TRUE, ExitStat);
- break;
-
- case CMDETRN: /* etrn -- force queue flush */
- if (bitset(PRIV_NOETRN, PrivacyFlags))
- {
- message("502 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
-
- if (strlen(p) <= 0)
- {
- usrerr("500 Parameter required");
- break;
- }
-
- /* crude way to avoid denial-of-service attacks */
- checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
-
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: ETRN %s",
- CurSmtpClient,
- shortenstring(p, MAXSHORTSTR));
-
- id = p;
- if (*id == '@')
- id++;
- else
- *--id = '@';
-
- if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
- {
- syserr("500 ETRN out of memory");
- break;
- }
- new->queue_match = id;
- new->queue_next = NULL;
- QueueLimitRecipient = new;
- ok = runqueue(TRUE, TRUE);
- free(QueueLimitRecipient);
- QueueLimitRecipient = NULL;
- if (ok && Errors == 0)
- message("250 Queuing for node %s started", p);
- break;
-
- case CMDHELP: /* help -- give user info */
- help(p);
- break;
-
- case CMDNOOP: /* noop -- do nothing */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
- message("250 OK");
- break;
-
- case CMDQUIT: /* quit -- leave mail */
- message("221 %s closing connection", MyHostName);
-
-doquit:
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
-
- /* avoid future 050 messages */
- disconnect(1, e);
-
- if (InChild)
- ExitStat = EX_QUIT;
- if (lognullconnection && LogLevel > 5)
- sm_syslog(LOG_INFO, NULL,
- "Null connection from %.100s",
- CurSmtpClient);
- finis(TRUE, ExitStat);
-
- case CMDVERB: /* set verbose mode */
- if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
- bitset(PRIV_NOVERB, PrivacyFlags))
- {
- /* this would give out the same info */
- message("502 Verbose unavailable");
- break;
- }
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
- Verbose = 1;
- e->e_sendmode = SM_DELIVER;
- message("250 Verbose mode");
- break;
-
- case CMDONEX: /* doing one transaction only */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
- OneXact = TRUE;
- message("250 Only one transaction");
- break;
-
- case CMDXUSR: /* initial (user) submission */
- checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
- UserSubmission = TRUE;
- message("250 Initial submission");
- break;
-
-# if SMTPDEBUG
- case CMDDBGQSHOW: /* show queues */
- printf("Send Queue=");
- printaddr(e->e_sendqueue, TRUE);
- break;
-
- case CMDDBGDEBUG: /* set debug mode */
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- tTflag(p);
- message("200 Debug set");
- break;
-
-# else /* not SMTPDEBUG */
- case CMDDBGQSHOW: /* show queues */
- case CMDDBGDEBUG: /* set debug mode */
-# endif /* SMTPDEBUG */
- case CMDLOGBOGUS: /* bogus command */
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, e->e_id,
- "\"%s\" command from %.100s (%.100s)",
- c->cmdname, CurSmtpClient,
- anynet_ntoa(&RealHostAddr));
- /* FALL THROUGH */
-
- case CMDERROR: /* unknown command */
- if (++badcommands > MAXBADCOMMANDS)
- {
- message("421 %s Too many bad commands; closing connection",
- MyHostName);
- goto doquit;
- }
-
- usrerr("500 Command unrecognized: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- default:
- errno = 0;
- syserr("500 smtp: unknown code %d", c->cmdcode);
- break;
- }
- }
-}
- /*
-** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
-**
-** Parameters:
-** pcounter -- pointer to a counter for this command.
-** maxcount -- maximum value for this counter before we
-** slow down.
-** cname -- command name for logging.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Slows down if we seem to be under attack.
-*/
-
-void
-checksmtpattack(pcounter, maxcount, cname, e)
- volatile int *pcounter;
- int maxcount;
- char *cname;
- ENVELOPE *e;
-{
- if (++(*pcounter) >= maxcount)
- {
- if (*pcounter == maxcount && LogLevel > 5)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %.40s attack?",
- CurSmtpClient, cname);
- }
- sleep(*pcounter / maxcount);
- }
-}
- /*
-** SKIPWORD -- skip a fixed word.
-**
-** Parameters:
-** p -- place to start looking.
-** w -- word to skip.
-**
-** Returns:
-** p following w.
-** NULL on error.
-**
-** Side Effects:
-** clobbers the p data area.
-*/
-
-static char *
-skipword(p, w)
- register char *volatile p;
- char *w;
-{
- register char *q;
- char *firstp = p;
-
- /* find beginning of word */
- while (isascii(*p) && isspace(*p))
- p++;
- q = p;
-
- /* find end of word */
- while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- if (*p != ':')
- {
- syntax:
- usrerr("501 Syntax error in parameters scanning \"%s\"",
- shortenstring(firstp, MAXSHORTSTR));
- return (NULL);
- }
- *p++ = '\0';
- while (isascii(*p) && isspace(*p))
- p++;
-
- if (*p == '\0')
- goto syntax;
-
- /* see if the input word matches desired word */
- if (strcasecmp(q, w))
- goto syntax;
-
- return (p);
-}
- /*
-** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
-**
-** Parameters:
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-mail_esmtp_args(kp, vp, e)
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (strcasecmp(kp, "size") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 SIZE requires a value");
- /* NOTREACHED */
- }
-# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
- e->e_msgsize = strtoul(vp, (char **) NULL, 10);
-# else
- e->e_msgsize = strtol(vp, (char **) NULL, 10);
-# endif
- }
- else if (strcasecmp(kp, "body") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 BODY requires a value");
- /* NOTREACHED */
- }
- else if (strcasecmp(vp, "8bitmime") == 0)
- {
- SevenBitInput = FALSE;
- }
- else if (strcasecmp(vp, "7bit") == 0)
- {
- SevenBitInput = TRUE;
- }
- else
- {
- usrerr("501 Unknown BODY type %s",
- vp);
- /* NOTREACHED */
- }
- e->e_bodytype = newstr(vp);
- }
- else if (strcasecmp(kp, "envid") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 ENVID requires a value");
- /* NOTREACHED */
- }
- if (!xtextok(vp))
- {
- usrerr("501 Syntax error in ENVID parameter value");
- /* NOTREACHED */
- }
- if (e->e_envid != NULL)
- {
- usrerr("501 Duplicate ENVID parameter");
- /* NOTREACHED */
- }
- e->e_envid = newstr(vp);
- }
- else if (strcasecmp(kp, "ret") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 RET requires a value");
- /* NOTREACHED */
- }
- if (bitset(EF_RET_PARAM, e->e_flags))
- {
- usrerr("501 Duplicate RET parameter");
- /* NOTREACHED */
- }
- e->e_flags |= EF_RET_PARAM;
- if (strcasecmp(vp, "hdrs") == 0)
- e->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(vp, "full") != 0)
- {
- usrerr("501 Bad argument \"%s\" to RET", vp);
- /* NOTREACHED */
- }
- }
- else
- {
- usrerr("501 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
- /*
-** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
-**
-** Parameters:
-** a -- the address corresponding to the To: parameter.
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-rcpt_esmtp_args(a, kp, vp, e)
- ADDRESS *a;
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (strcasecmp(kp, "notify") == 0)
- {
- char *p;
-
- if (vp == NULL)
- {
- usrerr("501 NOTIFY requires a value");
- /* NOTREACHED */
- }
- a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
- a->q_flags |= QHASNOTIFY;
- if (strcasecmp(vp, "never") == 0)
- return;
- for (p = vp; p != NULL; vp = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (strcasecmp(vp, "success") == 0)
- a->q_flags |= QPINGONSUCCESS;
- else if (strcasecmp(vp, "failure") == 0)
- a->q_flags |= QPINGONFAILURE;
- else if (strcasecmp(vp, "delay") == 0)
- a->q_flags |= QPINGONDELAY;
- else
- {
- usrerr("501 Bad argument \"%s\" to NOTIFY",
- vp);
- /* NOTREACHED */
- }
- }
- }
- else if (strcasecmp(kp, "orcpt") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 ORCPT requires a value");
- /* NOTREACHED */
- }
- if (strchr(vp, ';') == NULL || !xtextok(vp))
- {
- usrerr("501 Syntax error in ORCPT parameter value");
- /* NOTREACHED */
- }
- if (a->q_orcpt != NULL)
- {
- usrerr("501 Duplicate ORCPT parameter");
- /* NOTREACHED */
- }
- a->q_orcpt = newstr(vp);
- }
- else
- {
- usrerr("501 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
- /*
-** PRINTVRFYADDR -- print an entry in the verify queue
-**
-** Parameters:
-** a -- the address to print
-** last -- set if this is the last one.
-** vrfy -- set if this is a VRFY command.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Prints the appropriate 250 codes.
-*/
-
-void
-printvrfyaddr(a, last, vrfy)
- register ADDRESS *a;
- bool last;
- bool vrfy;
-{
- char fmtbuf[20];
-
- if (vrfy && a->q_mailer != NULL &&
- !bitnset(M_VRFY250, a->q_mailer->m_flags))
- strcpy(fmtbuf, "252");
- else
- strcpy(fmtbuf, "250");
- fmtbuf[3] = last ? ' ' : '-';
-
- if (a->q_fullname == NULL)
- {
- if (strchr(a->q_user, '@') == NULL)
- strcpy(&fmtbuf[4], "<%s@%s>");
- else
- strcpy(&fmtbuf[4], "<%s>");
- message(fmtbuf, a->q_user, MyHostName);
- }
- else
- {
- if (strchr(a->q_user, '@') == NULL)
- strcpy(&fmtbuf[4], "%s <%s@%s>");
- else
- strcpy(&fmtbuf[4], "%s <%s>");
- message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
- }
-}
- /*
-** RUNINCHILD -- return twice -- once in the child, then in the parent again
-**
-** Parameters:
-** label -- a string used in error messages
-**
-** Returns:
-** zero in the child
-** one in the parent
-**
-** Side Effects:
-** none.
-*/
-
-int
-runinchild(label, e)
- char *label;
- register ENVELOPE *e;
-{
- pid_t childpid;
-
- if (!OneXact)
- {
- /*
- ** Disable child process reaping, in case ETRN has preceeded
- ** MAIL command, and then fork.
- */
-
- (void) blocksignal(SIGCHLD);
-
- childpid = dofork();
- if (childpid < 0)
- {
- syserr("451 %s: cannot fork", label);
- (void) releasesignal(SIGCHLD);
- return (1);
- }
- if (childpid > 0)
- {
- auto int st;
-
- /* parent -- wait for child to complete */
- sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient);
- st = waitfor(childpid);
- if (st == -1)
- syserr("451 %s: lost child", label);
- else if (!WIFEXITED(st))
- syserr("451 %s: died on signal %d",
- label, st & 0177);
-
- /* if we exited on a QUIT command, complete the process */
- if (WEXITSTATUS(st) == EX_QUIT)
- {
- disconnect(1, e);
- finis(TRUE, ExitStat);
- }
-
- /* restore the child signal */
- (void) releasesignal(SIGCHLD);
-
- return (1);
- }
- else
- {
- /* child */
- InChild = TRUE;
- QuickAbort = FALSE;
- clearenvelope(e, FALSE);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) releasesignal(SIGCHLD);
- }
- }
- return (0);
-}
-
-# endif /* SMTP */
- /*
-** HELP -- implement the HELP command.
-**
-** Parameters:
-** topic -- the topic we want help for.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** outputs the help file to message output.
-*/
-
-void
-help(topic)
- char *topic;
-{
- register FILE *hf;
- int len;
- bool noinfo;
- int sff = SFF_OPENASROOT|SFF_REGONLY;
- char buf[MAXLINE];
- extern char Version[];
-
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
-
- if (HelpFile == NULL ||
- (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
- {
- /* no help */
- errno = 0;
- message("502 Sendmail %s -- HELP not implemented", Version);
- return;
- }
-
- if (topic == NULL || *topic == '\0')
- {
- topic = "smtp";
- message("214-This is Sendmail version %s", Version);
- noinfo = FALSE;
- }
- else
- {
- makelower(topic);
- noinfo = TRUE;
- }
-
- len = strlen(topic);
-
- while (fgets(buf, sizeof buf, hf) != NULL)
- {
- if (strncmp(buf, topic, len) == 0)
- {
- register char *p;
-
- p = strchr(buf, '\t');
- if (p == NULL)
- p = buf;
- else
- p++;
- fixcrlf(p, TRUE);
- message("214-%s", p);
- noinfo = FALSE;
- }
- }
-
- if (noinfo)
- message("504 HELP topic \"%.10s\" unknown", topic);
- else
- message("214 End of HELP info");
- (void) fclose(hf);
-}