more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / srvrsmtp.c
index 25f3c15..5280aac 100644 (file)
@@ -10,9 +10,9 @@
 
 #ifndef lint
 #ifdef SMTP
 
 #ifndef lint
 #ifdef SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 8.10 (Berkeley) %G% (with SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.62 (Berkeley) %G% (with SMTP)";
 #else
 #else
-static char sccsid[] = "@(#)srvrsmtp.c 8.10 (Berkeley) %G% (without SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.62 (Berkeley) %G% (without SMTP)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
@@ -57,6 +57,8 @@ struct cmd
 /* non-standard commands */
 # define CMDONEX       16      /* onex -- sending one transaction only */
 # define CMDVERB       17      /* verb -- go into verbose mode */
 /* non-standard commands */
 # define CMDONEX       16      /* onex -- sending one transaction only */
 # define CMDVERB       17      /* verb -- go into verbose mode */
+/* 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 */
 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
 # define CMDDBGQSHOW   24      /* showq -- show send queue */
 # define CMDDBGDEBUG   25      /* debug -- set debug mode */
@@ -84,15 +86,18 @@ static struct cmd   CmdTab[] =
         */
        "showq",        CMDDBGQSHOW,
        "debug",        CMDDBGDEBUG,
         */
        "showq",        CMDDBGQSHOW,
        "debug",        CMDDBGDEBUG,
+       "wiz",          CMDLOGBOGUS,
        NULL,           CMDERROR,
 };
 
        NULL,           CMDERROR,
 };
 
-bool   InChild = FALSE;                /* true if running in a subprocess */
 bool   OneXact = FALSE;                /* one xaction only this run */
 bool   OneXact = FALSE;                /* one xaction only this run */
-
-#define EX_QUIT                22              /* special code for QUIT command */
+char   *CurSmtpClient;                 /* who's at the other end of channel */
 
 static char    *skipword();
 
 static char    *skipword();
+extern char    RealUserName[];
+
+
+#define MAXBADCOMMANDS 25              /* maximum number of bad commands */
 
 smtp(e)
        register ENVELOPE *e;
 
 smtp(e)
        register ENVELOPE *e;
@@ -110,11 +115,37 @@ smtp(e)
                (void) dup2(fileno(OutChannel), fileno(stdout));
        }
        settime(e);
                (void) dup2(fileno(OutChannel), fileno(stdout));
        }
        settime(e);
-       CurHostName = RealHostName;
-       setproctitle("server %s startup", CurHostName);
-       expand("\201e", inp, &inp[sizeof inp], e);
-       message("220-%s", inp);
-       message("220 ESMTP spoken here");
+       peerhostname = RealHostName;
+       if (peerhostname == NULL)
+               peerhostname = "localhost";
+       CurHostName = peerhostname;
+       CurSmtpClient = macvalue('_', e);
+       if (CurSmtpClient == NULL)
+               CurSmtpClient = CurHostName;
+
+       setproctitle("server %s startup", CurSmtpClient);
+       expand("\201e", inp, sizeof inp, e);
+       if (BrokenSmtpPeers)
+       {
+               p = strchr(inp, '\n');
+               if (p != NULL)
+                       *p = '\0';
+               message("220 %s", inp);
+       }
+       else
+       {
+               char *q = inp;
+
+               while (q != NULL)
+               {
+                       p = strchr(q, '\n');
+                       if (p != NULL)
+                               *p++ = '\0';
+                       message("220-%s", q);
+                       q = p;
+               }
+               message("220 ESMTP spoken here");
+       }
        protocol = NULL;
        sendinghost = macvalue('s', e);
        gothello = FALSE;
        protocol = NULL;
        sendinghost = macvalue('s', e);
        gothello = FALSE;
@@ -122,11 +153,15 @@ smtp(e)
        for (;;)
        {
                /* arrange for backout */
        for (;;)
        {
                /* arrange for backout */
-               if (setjmp(TopFrame) > 0 && InChild)
+               if (setjmp(TopFrame) > 0)
                {
                {
-                       QuickAbort = FALSE;
-                       SuprErrs = TRUE;
-                       finis();
+                       /* if() nesting is necessary for Cray UNICOS */
+                       if (InChild)
+                       {
+                               QuickAbort = FALSE;
+                               SuprErrs = TRUE;
+                               finis();
+                       }
                }
                QuickAbort = FALSE;
                HoldErrs = FALSE;
                }
                QuickAbort = FALSE;
                HoldErrs = FALSE;
@@ -148,12 +183,13 @@ smtp(e)
                if (p == NULL)
                {
                        /* end of file, just die */
                if (p == NULL)
                {
                        /* end of file, just die */
+                       disconnect(1, e);
                        message("421 %s Lost input channel from %s",
                        message("421 %s Lost input channel from %s",
-                               MyHostName, CurHostName);
+                               MyHostName, CurSmtpClient);
 #ifdef LOG
                        if (LogLevel > (gotmail ? 1 : 19))
                                syslog(LOG_NOTICE, "lost input channel from %s",
 #ifdef LOG
                        if (LogLevel > (gotmail ? 1 : 19))
                                syslog(LOG_NOTICE, "lost input channel from %s",
-                                       CurHostName);
+                                       CurSmtpClient);
 #endif
                        if (InChild)
                                ExitStat = EX_QUIT;
 #endif
                        if (InChild)
                                ExitStat = EX_QUIT;
@@ -168,9 +204,9 @@ smtp(e)
                        fprintf(e->e_xfp, "<<< %s\n", inp);
 
                if (e->e_id == NULL)
                        fprintf(e->e_xfp, "<<< %s\n", inp);
 
                if (e->e_id == NULL)
-                       setproctitle("%s: %s", CurHostName, inp);
+                       setproctitle("%s: %.80s", CurSmtpClient, inp);
                else
                else
-                       setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
+                       setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                /* break off command */
                for (p = inp; isascii(*p) && isspace(*p); p++)
 
                /* break off command */
                for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -211,15 +247,35 @@ smtp(e)
                                protocol = "SMTP";
                                SmtpPhase = "server HELO";
                        }
                                protocol = "SMTP";
                                SmtpPhase = "server HELO";
                        }
-                       sendinghost = newstr(p);
-                       if (strcasecmp(p, RealHostName) != 0)
+
+                       /* check for valid domain name (re 1123 5.2.5) */
+                       if (*p == '\0')
                        {
                        {
-                               auth_warning(e, "Host %s claimed to be %s",
-                                       RealHostName, p);
+                               message("501 %s requires domain address",
+                                       cmdbuf);
+                               break;
                        }
                        }
-                       p = macvalue('_', e);
-                       if (p == NULL)
-                               p = RealHostName;
+                       else
+                       {
+                               register char *q;
+
+                               for (q = p; *q != '\0'; q++)
+                               {
+                                       if (!isascii(*q))
+                                               break;
+                                       if (isalnum(*q))
+                                               continue;
+                                       if (strchr("[].-_#", *q) == NULL)
+                                               break;
+                               }
+                               if (*q != '\0')
+                               {
+                                       message("501 Invalid domain name");
+                                       break;
+                               }
+                       }
+
+                       sendinghost = newstr(p);
                        message("250", "%s Hello %s, pleased to meet you", HostName, p);
                        break;
 
                        message("250", "%s Hello %s, pleased to meet you", HostName, p);
                        break;
 
@@ -231,7 +287,7 @@ smtp(e)
                        {
                                /* set sending host to our known value */
                                if (sendinghost == NULL)
                        {
                                /* set sending host to our known value */
                                if (sendinghost == NULL)
-                                       sendinghost = RealHostName;
+                                       sendinghost = peerhostname;
 
                                if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
                                {
 
                                if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
                                {
@@ -260,15 +316,26 @@ smtp(e)
                        {
                                auth_warning(e,
                                        "Host %s didn't use HELO protocol",
                        {
                                auth_warning(e,
                                        "Host %s didn't use HELO protocol",
-                                       RealHostName);
+                                       peerhostname);
+                       }
+#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",
+                                       peerhostname, sendinghost);
                        }
                        }
+#endif
+
                        if (protocol == NULL)
                                protocol = "SMTP";
                        define('r', protocol, e);
                        define('s', sendinghost, e);
                        initsys(e);
                        nrcpts = 0;
                        if (protocol == NULL)
                                protocol = "SMTP";
                        define('r', protocol, e);
                        define('s', sendinghost, e);
                        initsys(e);
                        nrcpts = 0;
-                       setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
+                       e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
+                       setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                        /* child -- go do the processing */
                        p = skipword(p, "from");
 
                        /* child -- go do the processing */
                        p = skipword(p, "from");
@@ -295,12 +362,21 @@ smtp(e)
                        if (p != NULL && *p != '\0')
                                *p++ = '\0';
 
                        if (p != NULL && *p != '\0')
                                *p++ = '\0';
 
+                       /* check for possible spoofing */
+                       if (RealUid != 0 && OpMode == MD_SMTP &&
+                           !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 */
                        /* now parse ESMTP arguments */
-                       msize = 0;
-                       for (; p != NULL && *p != '\0'; p++)
+                       e->e_msgsize = 0;
+                       while (p != NULL && *p != '\0')
                        {
                                char *kp;
                        {
                                char *kp;
-                               char *vp;
+                               char *vp = NULL;
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
 
                                /* locate the beginning of the keyword */
                                while (isascii(*p) && isspace(*p))
@@ -328,58 +404,20 @@ smtp(e)
                                        *p++ = '\0';
 
                                if (tTd(19, 1))
                                        *p++ = '\0';
 
                                if (tTd(19, 1))
-                                       printf("MAIL: got arg %s=%s\n", kp,
+                                       printf("MAIL: got arg %s=\"%s\"\n", kp,
                                                vp == NULL ? "<null>" : vp);
 
                                                vp == NULL ? "<null>" : vp);
 
-                               if (strcasecmp(kp, "size") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 SIZE requires a value");
-                                               /* NOTREACHED */
-                                       }
-                                       msize = atol(vp);
-                               }
-                               else if (strcasecmp(kp, "body") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 BODY requires a value");
-                                               /* NOTREACHED */
-                                       }
-# ifdef MIME
-                                       if (strcasecmp(vp, "8bitmime") == 0)
-                                       {
-                                               e->e_bodytype = "8BITMIME";
-                                               SevenBit = FALSE;
-                                       }
-                                       else if (strcasecmp(vp, "7bit") == 0)
-                                       {
-                                               e->e_bodytype = "7BIT";
-                                               SevenBit = TRUE;
-                                       }
-                                       else
-                                       {
-                                               usrerr("501 Unknown BODY type %s",
-                                                       vp);
-                                       }
-# endif
-                               }
-                               else
-                               {
-                                       usrerr("501 %s parameter unrecognized", kp);
-                                       /* NOTREACHED */
-                               }
+                               mail_esmtp_args(kp, vp, e);
                        }
 
                        }
 
-                       if (MaxMessageSize > 0 && msize > MaxMessageSize)
+                       if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
                        {
                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
                                        MaxMessageSize);
                                /* NOTREACHED */
                        }
                                
                        {
                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
                                        MaxMessageSize);
                                /* NOTREACHED */
                        }
                                
-                       if (!enoughspace(msize))
+                       if (!enoughspace(e->e_msgsize))
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
@@ -422,7 +460,9 @@ smtp(e)
                        e->e_to = p;
                        if (!bitset(QBADADDR, a->q_flags))
                        {
                        e->e_to = p;
                        if (!bitset(QBADADDR, a->q_flags))
                        {
-                               message("250 Recipient ok");
+                               message("250 Recipient ok%s",
+                                       bitset(QQUEUEUP, a->q_flags) ?
+                                               " (will queue)" : "");
                                nrcpts++;
                        }
                        else
                                nrcpts++;
                        }
                        else
@@ -440,7 +480,7 @@ smtp(e)
                                message("503 Need MAIL command");
                                break;
                        }
                                message("503 Need MAIL command");
                                break;
                        }
-                       else if (e->e_nrcpts <= 0)
+                       else if (nrcpts <= 0)
                        {
                                message("503 Need RCPT (recipient)");
                                break;
                        {
                                message("503 Need RCPT (recipient)");
                                break;
@@ -466,8 +506,16 @@ smtp(e)
 
                        /* collect the text of the message */
                        SmtpPhase = "collect";
 
                        /* collect the text of the message */
                        SmtpPhase = "collect";
+                       collect(InChannel, TRUE, doublequeue, NULL, e);
+                       if (Errors != 0)
+                               goto abortmessage;
+
+                       /* make sure we actually do delivery */
+                       e->e_flags &= ~EF_CLRQUEUE;
+
+                       /* from now on, we have to operate silently */
                        HoldErrs = TRUE;
                        HoldErrs = TRUE;
-                       collect(TRUE, doublequeue, e);
+                       e->e_errormode = EM_MAIL;
 
                        /*
                        **  Arrange to send to everyone.
 
                        /*
                        **  Arrange to send to everyone.
@@ -488,43 +536,33 @@ smtp(e)
                        */
 
                        SmtpPhase = "delivery";
                        */
 
                        SmtpPhase = "delivery";
-                       if (nrcpts != 1 && !doublequeue)
-                       {
-                               HoldErrs = TRUE;
-                               e->e_errormode = EM_MAIL;
-                       }
                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
                        id = e->e_id;
 
                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
                        id = e->e_id;
 
-                       /* send to all recipients */
-                       sendall(e, Verbose ? SM_DELIVER : SM_QUEUE);
-                       e->e_to = NULL;
-
-                       unlockqueue(e);
-
-                       /* issue success if appropriate and reset */
-                       if (Errors == 0 || HoldErrs)
-                               message("250 %s Message accepted for delivery", id);
-
-                       if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
+                       if (doublequeue)
                        {
                        {
-                               /* avoid sending back an extra message */
-                               e->e_flags &= ~EF_FATALERRS;
-                               e->e_flags |= EF_CLRQUEUE;
+                               /* make sure it is in the queue */
+                               queueup(e, TRUE, FALSE);
+                               if (e->e_sendmode == SM_QUEUE)
+                                       e->e_flags |= EF_KEEPQUEUE;
                        }
                        else
                        {
                        }
                        else
                        {
-                               /* from now on, we have to operate silently */
-                               HoldErrs = TRUE;
-                               e->e_errormode = EM_MAIL;
+                               /* send to all recipients */
+                               sendall(e, SM_DEFAULT);
+                       }
+                       e->e_to = NULL;
 
 
-                               /* if we just queued, poke it */
-                               if (doublequeue && e->e_sendmode != SM_QUEUE)
-                               {
-                                       unlockqueue(e);
-                                       dowork(id, TRUE, TRUE, e);
-                                       e->e_id = 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)
+                       {
+                               extern pid_t dowork();
+
+                               unlockqueue(e);
+                               (void) dowork(id, TRUE, TRUE, e);
                        }
 
                        /* now make it really happen */
                        }
 
                        /* now make it really happen */
@@ -545,6 +583,10 @@ smtp(e)
 
                  case CMDRSET:         /* rset -- reset state */
                        message("250 Reset state");
 
                  case CMDRSET:         /* rset -- reset state */
                        message("250 Reset state");
+
+                       /* arrange to ignore any current send list */
+                       e->e_sendqueue = NULL;
+                       e->e_flags |= EF_CLRQUEUE;
                        if (InChild)
                                finis();
 
                        if (InChild)
                                finis();
 
@@ -561,9 +603,14 @@ smtp(e)
                                                PrivacyFlags))
                        {
                                if (vrfy)
                                                PrivacyFlags))
                        {
                                if (vrfy)
-                                       message("252 Who's to say?");
+                                       message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
                                else
                                else
-                                       message("502 That's none of your business");
+                                       message("502 Sorry, we do not allow this operation");
+#ifdef LOG
+                               if (LogLevel > 5)
+                                       syslog(LOG_INFO, "%s: %s [rejected]",
+                                               CurSmtpClient, inp);
+#endif
                                break;
                        }
                        else if (!gothello &&
                                break;
                        }
                        else if (!gothello &&
@@ -577,7 +624,7 @@ smtp(e)
                                break;
 #ifdef LOG
                        if (LogLevel > 5)
                                break;
 #ifdef LOG
                        if (LogLevel > 5)
-                               syslog(LOG_INFO, "%s: %s", CurHostName, inp);
+                               syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
 #endif
                                paddrtree(a);
                        break;
 #endif
                                paddrtree(a);
                        break;
@@ -587,14 +634,18 @@ smtp(e)
                        break;
 
                  case CMDNOOP:         /* noop -- do nothing */
                        break;
 
                  case CMDNOOP:         /* noop -- do nothing */
-                       message("200 OK");
+                       message("250 OK");
                        break;
 
                  case CMDQUIT:         /* quit -- leave mail */
                        message("221 %s closing connection", MyHostName);
 
                        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 */
                        /* avoid future 050 messages */
-                       Verbose = FALSE;
+                       disconnect(1, e);
 
                        if (InChild)
                                ExitStat = EX_QUIT;
 
                        if (InChild)
                                ExitStat = EX_QUIT;
@@ -630,20 +681,27 @@ smtp(e)
                        break;
 
 # else /* not SMTPDEBUG */
                        break;
 
 # else /* not SMTPDEBUG */
-
                  case CMDDBGQSHOW:     /* show queues */
                  case CMDDBGDEBUG:     /* set debug mode */
                  case CMDDBGQSHOW:     /* show queues */
                  case CMDDBGDEBUG:     /* set debug mode */
+# endif /* SMTPDEBUG */
+                 case CMDLOGBOGUS:     /* bogus command */
 # ifdef LOG
                        if (LogLevel > 0)
 # ifdef LOG
                        if (LogLevel > 0)
-                               syslog(LOG_NOTICE,
+                               syslog(LOG_CRIT,
                                    "\"%s\" command from %s (%s)",
                                    "\"%s\" command from %s (%s)",
-                                   c->cmdname, RealHostName,
+                                   c->cmdname, peerhostname,
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
-# endif /* SMTPDEBUG */
 
                  case CMDERROR:        /* unknown command */
 
                  case CMDERROR:        /* unknown command */
+                       if (++badcommands > MAXBADCOMMANDS)
+                       {
+                               message("421 %s Too many bad commands; closing connection",
+                                       MyHostName);
+                               goto doquit;
+                       }
+
                        message("500 Command unrecognized");
                        break;
 
                        message("500 Command unrecognized");
                        break;
 
@@ -675,6 +733,7 @@ skipword(p, w)
        char *w;
 {
        register char *q;
        char *w;
 {
        register char *q;
+       char *firstp = p;
 
        /* find beginning of word */
        while (isascii(*p) && isspace(*p))
 
        /* find beginning of word */
        while (isascii(*p) && isspace(*p))
@@ -689,7 +748,8 @@ skipword(p, w)
        if (*p != ':')
        {
          syntax:
        if (*p != ':')
        {
          syntax:
-               message("501 Syntax error in parameters");
+               message("501 Syntax error in parameters scanning \"%s\"",
+                       firstp);
                Errors++;
                return (NULL);
        }
                Errors++;
                return (NULL);
        }
@@ -707,6 +767,181 @@ skipword(p, w)
        return (p);
 }
 \f/*
        return (p);
 }
 \f/*
+**  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.
+*/
+
+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 */
+               }
+# ifdef __STDC__
+               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 */
+               }
+               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 */
+       }
+}
+\f/*
+**  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.
+*/
+
+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 (!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 */
+       }
+}
+\f/*
 **  PRINTVRFYADDR -- print an entry in the verify queue
 **
 **     Parameters:
 **  PRINTVRFYADDR -- print an entry in the verify queue
 **
 **     Parameters:
@@ -843,10 +1078,16 @@ runinchild(label, e)
                        st = waitfor(childpid);
                        if (st == -1)
                                syserr("%s: lost child", label);
                        st = waitfor(childpid);
                        if (st == -1)
                                syserr("%s: lost child", label);
+                       else if (!WIFEXITED(st))
+                               syserr("%s: died on signal %d",
+                                       label, st & 0177);
 
                        /* if we exited on a QUIT command, complete the process */
 
                        /* if we exited on a QUIT command, complete the process */
-                       if (st == (EX_QUIT << 8))
+                       if (WEXITSTATUS(st) == EX_QUIT)
+                       {
+                               disconnect(1, e);
                                finis();
                                finis();
+                       }
 
                        return (1);
                }
 
                        return (1);
                }