Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

smtpcmds.c

/*
 *    Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *      This will be free software, but only when it is finished.
 */
/*
 *    Several extensive changes by Matti Aarnio <mea@nic.funet.fi>
 *      Copyright 1991-2003.
 */
/*
 * Zmailer SMTP-server divided into bits
 *
 * The basic commands:
 *
 *  - HELO/EHLO
 *  - MAIL 
 *  - RCPT
 *  - VRFY
 *  - EXPN
 *
 */

#include "smtpserver.h"

extern int do_whoson;

static const char *orcpt_string __((const char *));

static const char *orcpt_string(str)
const char *str;
{
    /* Verify that the input is valid RFC 1981 XTEXT string! */
    const char *stro = rfc822atom(str);
    const char *s2;

    if (stro == str)
      return NULL;
    if (*stro != ';') {
      rfc821_error_ptr = stro - 1;
      return NULL;
    }
    s2 = xtext_string(++stro);
    if (s2 == stro)
      return NULL;
    return s2;
}


static void rfc822commentprint __((FILE *, const char *));
static void
rfc822commentprint(mfp, str)
FILE *mfp;
const char *str;
{
  fputc('"', mfp);
  for ( ; *str ; ++str ) {
    int c = (*str) & 0xFF;
    if (c < 0x20 || c > 0x7e)
      fprintf(mfp,"\\%03o", c);
    else {
      if (c == '"' || c == '\\' || c == '(' || c == ')')
      fputc('\\', mfp);
      fputc(c, mfp);
    }
  }
  fputc('"', mfp);
}


/* If the argument is not in valid domain name syntax, return 1 */

static int partridge __((SmtpState *, const char *));

static int partridge(SS, s)
SmtpState *SS;
const char *s;
{
    const char *p = rfc821_domain(s, STYLE(SS->cfinfo, 'R'));
    if (p == s)
      return 1;
    if (!strict_protocol)
      while (*p == ' ' || *p == '\n')
      ++p;
    if (*p == 0)
      return 0;

    rfc821_error_ptr = p;
    rfc821_error = "Spurious junk after the DOMAIN in HELO/EHLO argument";

    return 1;
}

/* The input this macro gets is already verified to be in RFC-821 form.
   In it we can have double-quotes in the input, but only \ -quoted!
   Therefore it is highly unlikely to need even more than two chars
   longer form for the RFC-822 format string. */

static void rfc821to822quote(cpp,newcpp,alenp)
     char **cpp, **newcpp;
     int *alenp;
{
      char *cp    = *cpp;
      char *newcp = *newcpp;
      int alen    = *alenp;
      if (cp && memchr(cp,'\\',alen) != NULL && *cp != '"') {
        int i;
        const char *s1 = cp;
        char *s2 = emalloc(alen+8);
        newcp = s2;
        *s2++ = '"';
        for (i = 0; i < alen; ++i) {
          if (*s1 == '@')
            break; /* Unquoted AT --> move to plain copying! */
          if (*s1 == '\\' && s1[1] != 0)
            { *s2++ = *s1++; ++i; }
          /* Normal copy */
          *s2++ = *s1++;
        }
        *s2++ = '"';
        /* The possible final copying of '@hostpart' */
        for ( ; i < alen; ++i)
          *s2++ = *s1++;
        alen = s2 - newcp;
        cp = newcp;
      }
      *cpp    = cp;
      *newcpp = newcp;
      *alenp  = alen;
}
#define RFC821_822QUOTE(cp,newcp,alen) \
      rfc821to822quote(&cp,&newcp,&alen)


/* SMTP-server verb subroutines */

void smtp_helo(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
    const char *msg = NULL;

    switch (SS->carp->cmd) {
    case Hello2:
      MIBMtaEntry->ss.IncomingSMTP_EHLO += 1;
      break;
    case Hello:
      MIBMtaEntry->ss.IncomingSMTP_HELO += 1;
      break;
    default: /* Should not happen... */
      break;
    }

    if (SS->state != Hello && SS->state != MailOrHello) {
      switch (SS->state) {
      case Mail:
          msg = "Waiting for MAIL command";
          break;
      case Recipient:
          msg = "Waiting for RCPT command";
          break;
      default:
          break;
      }
    }

    /* HELO/EHLO is also implicite RSET ! */
    if (SS->mfp != NULL) {
      clearerr(SS->mfp);
      mail_abort(SS->mfp);
      SS->mfp = NULL;
    }

    strncpy(SS->helobuf, buf, sizeof(SS->helobuf));
    SS->helobuf[sizeof(SS->helobuf)-1] = 0;

    if (strict_protocol && *cp == ' ')
      ++cp;
    else
      while (*cp == ' ' || *cp == '\t') ++cp;

    if (debug) typeflush(SS);
    if (SS->netconnected_flg)
      SS->policyresult = policytest(policydb, &SS->policystate,
                            POLICY_HELONAME, cp, strlen(cp),
                            SS->authuser);
    else
      SS->policyresult = 0;

    if (logfp || logfp_to_syslog) time( & now );

    if (logfp) {
      char *s = policymsg(policydb, &SS->policystate);
      if (SS->policyresult != 0 || s != NULL) {
      fprintf(logfp, "%s%04d#\t-- policy result=%d, msg: %s\n", logtag,
            (int)(now - logtagepoch),
            SS->policyresult, (s ? s : "<NONE!>"));
      fflush(logfp);
      }
    }
    if (logfp_to_syslog) {
      char *s = policymsg(policydb, &SS->policystate);
      if (SS->policyresult != 0 || s != NULL) {
      zsyslog((LOG_DEBUG, "%s%04d # policy result=%d, msg: %s", logtag,
            (int)(now - logtagepoch),
            SS->policyresult, (s ? s : "<NONE!>")));
      }
    }

    /* actually we accept here about anything, and mark
       our decissions up into the 'policystate' variables */


    /*
     * Craig P. says we have to spit back syntactically
     * invalid helo parameters at this stage, which is
     * hard to do right since it requires a full '822
     * tokenizer.  We do a half-hearted attempt here.
     */
    /*
     * Matti A. says we have a more-than-half-hearted
     * tokenizer -- RFC821SCN.C, lets use it :)
     * We need it for proper handling of ESMTP anyway
     */
    if (checkhelo && skeptical && partridge(SS, cp)) {
      smtp_tarpit(SS);
      type821err(SS, -501, "", buf, "Invalid `%.200s' parameter!", buf);
      if (msg != NULL)
        type(SS, -501, "", "%s", msg);
      type(SS, 501, "", "Err: %s", rfc821_error);
      strcpy(SS->helobuf, "Bad.Helo.Input");
      return;
    }
    /* At least check the input, though say "Ok, Master" while
       complaining.. */
    if (!checkhelo && partridge(SS, cp)) {
      if (SS->carp->cmd != Hello) {
          type821err(SS, -250, "", buf, "Invalid `%.200s' parameter!", buf);
          type(SS, -250, NULL, "Err: %s", rfc821_error);
          if (msg != NULL)
            type(SS, -250, "", "%s", msg);
      }
    }
    SS->cfinfo = findcf(cp);
    if (SS->cfinfo != NULL && *(SS->cfinfo->flags) == '!') {
      smtp_tarpit(SS);
      if (SS->cfinfo->flags[1] != '\0')
          type(SS, 501, NULL, "%s", (SS->cfinfo->flags) + 1);
      else
          type(SS, 501, NULL, "Sorry, access denied.");
      return;
    }

    /* Router interacting policy analysis functions */
    /* Note, these are orthogonal to those of smtp-server
       internal policy functions! */

#if 0 /* Original security reviewer reported here a potential
       for a buffer overflow, however there are potentially
       more severe things lurking in the interactive router
       call with these parameters, than mere buffer overflow.
       Observe the quotation issues...
       This code is now buried into '#if 0', until the
       real fix is written -- if ever..
       This code has never been in active use...
      */

    if (STYLE(SS->cfinfo, 'h')) {
      char argbuf[100+100];
      char *s;
      sprintf(argbuf,"%.99s %.99s", SS->rhostname,
            ((SS->ihostaddr && (SS->ihostaddr[0] != '\0'))
             ? SS->ihostaddr : "[0.0.0.0]"));
      if ((s = router(SS, RKEY_HELLO, 1, argbuf, strlen(argbuf))) == NULL)
      /* the error was printed in router() */
      return;
      if (atoi(s) / 100 != 2) {
      /* verification failed */
      smtp_tarpit(SS);
      type(SS, atoi(s), s+4, "Failed", "Failed");
      free(s);
      return;
      }
      else {
      type(SS, -atoi(s), s+4, "Ok", "Ok");
      free(s);
      }
    }
#endif

    /* Check `cp' corresponds to the reverse address */
    if (skeptical && SS->ihostaddr[0] != '\0'
      && SS->rhostname[0] != '\0' && SS->rhostname[0] != '['
      && !CISTREQ(cp, SS->rhostname)) {
      if (checkhelo) {
          type(SS, -250, NULL, "%s That hostname is inconsistent with",
             SS->myhostname);
          type(SS, -250, NULL, "%s your address to name mapping.",
             SS->myhostname);
      }
      if (msg != NULL)
        type(SS, -250, "", "%s", msg);
      type(SS, SS->carp->cmd == Hello2 ? -250 : 250, NULL,
           "%s expected \"%s %s\"", SS->myhostname,
           SS->carp->verb, SS->rhostname);
    } else {
      if (msg != NULL)
        type(SS, -250, "", "%s", msg);
      type(SS, SS->carp->cmd == Hello2 ? -250 : 250, NULL,
           "%s Hello %s", SS->myhostname, cp);
    }
    if (SS->carp->cmd == Hello2) {

      /* ESMTP -- RFC 1651,1652,1653,1428 thingies */
      char sizebuf[20];
      long policyinlimit = policyinsizelimit(policydb, &SS->policystate);
      long maxinlimit = maxsize;
      int multiline = multilinereplies;
      multilinereplies = 1;

      if (policyinlimit >= 0)  /* defined if non-negative value */
        maxinlimit = policyinlimit;
      if (maxsize != 0 && maxsize < maxinlimit)
        maxinlimit = maxsize;
      if (maxsize != 0 && maxinlimit == 0)
        maxinlimit = maxsize; /* Lower from infinite */

      sprintf(sizebuf, "SIZE %ld", maxinlimit); /* 0: No fixed max size
                                         in force, else:
                                         The FIXED maximum */

      type(SS, -250, NULL, sizebuf);            /* RFC 1427/1653/1870 */
      if (mime8bitok)
        type(SS, -250, NULL, "8BITMIME"); /* RFC 1426/1652 */
      if (pipeliningok)
        type(SS, -250, NULL, "PIPELINING");     /* RFC 1854/2197 */
      if (chunkingok)
        type(SS, -250, NULL, "CHUNKING"); /* RFC 1830: BDAT */
      if (enhancedstatusok)
        type(SS, -250, NULL, "ENHANCEDSTATUSCODES"); /* RFC 2034 */
      if (expncmdok && STYLE(SS->cfinfo, 'e'))
        type(SS, -250, NULL, "EXPN");           /* RFC 821 */
      if (vrfycmdok && STYLE(SS->cfinfo, 'v'))
        type(SS, -250, NULL, "VRFY");           /* RFC 821 */
      if (dsn_ok)
        type(SS, -250, NULL, "DSN");            /* RFC 1891 */

      if (deliverby_ok == 0)
        type(SS, -250, NULL, "DELIVERBY");      /* RFC 2852 */
      else if (deliverby_ok > 0)
        type(SS, -250, NULL, "DELIVERBY %d", deliverby_ok);

      if (rcptlimitcnt > 100)
        type(SS, -250, NULL, "X-RCPTLIMIT %d", rcptlimitcnt);

      smtpauth_ehloresponse(SS);

#ifdef HAVE_OPENSSL
      /* NOTE: This seems to require TLS and STARTTLS facilities,
         better known as SSL..  TLS: RFC 2246, STARTTLS: RFC 2487 */
      if (starttls_ok && !SS->sslmode) {
        type (SS, -250, NULL, "STARTTLS"); /* RFC 2487 */
      }
#endif /* - HAVE_OPENSSL */
      if (etrn_ok && !msa_mode)
        type(SS, -250, NULL, "ETRN");           /* RFC 1985 */
      type(SS, 250, NULL, "HELP");        /* RFC 821 ? */
      SS->with_protocol = WITH_ESMTP;
      multilinereplies = multiline;
    }
    SS->state = MailOrHello;
}

int smtp_mail(SS, buf, cp, insecure)
SmtpState *SS;
const char *buf, *cp;
int insecure;
{
    const char *s, *p;
    int rc;
    const char *drpt_envid;
    const char *drpt_ret;
    const char *bodytype = NULL;
    const char *newcp = NULL;
    const char *srcrtestatus = "";
    const char *auth_param;
    int addrlen, drptret_len, drptenvid_len, authparam_len;
    int strict = STYLE(SS->cfinfo, 'R');
    int sloppy = STYLE(SS->cfinfo, 'S');


    if (strict && sloppy) /* If misconfigured, SLOPPY takes precedence! */
      strict = 0;

    addrlen = 0;
    drptret_len = 0;
    drptenvid_len = 0;
    authparam_len = 0;

    SS->sender_ok = 0;        /* Set it, when we are sure.. */

    SS->deliverby_time  = 0;        /* RFC 2852 */
    SS->deliverby_flags = 0;

    /* For ESMTP SIZE-option use we need to know
       how much space we have, it is easiest by
       opening the spool file here, and asking
       filesystem to report free space..
       We use at most half of the free space,
       and each recipient will use the claimed
       size, thus marking up its reception..        */

    if (SS->carp->cmd == Mail2 || SS->carp->cmd == Send2) {
      SS->with_protocol = WITH_ESMTP;
    }
    if (SS->state != Mail && SS->state != MailOrHello) {
      switch (SS->state) {
      case Hello:
          cp = "Waiting for HELO/EHLO command";
          break;
      case Recipient:
          cp = "Waiting for RCPT command";
          break;
      default:
          cp = NULL;
          break;
      }
      smtp_tarpit(SS);
      type(SS, 503, m551, cp);
      return -1;
    }

    if (*cp == ' ') ++cp;
    if (!strict_protocol || sloppy) while (*cp == ' ' || *cp == '\t') ++cp;
    if (!CISTREQN(cp, "From:", 5)) {
      smtp_tarpit(SS);
      type(SS, 501, m552, "where is From: in that?");
      return -1;
    }
    cp += 5;
    if (!strict_protocol || sloppy)
      for (; *cp != '\0' && *cp != '<'; ++cp)
      /* Skip white-space */
      if (!isascii(*cp) || !isspace(*cp)) {
        if (!sloppy) {
          smtp_tarpit(SS);
          type(SS, 501, m517, "where is <...> in that?");
          return -1;
        }
        break; /* Sigh, be sloppy.. */
      }
    if (*cp == '\0') {
      smtp_tarpit(SS);
      type(SS, 501, m517, "where is <...> in this: %s", cp);
      return -1;
    } else if (*cp != '<' && !sloppy) {
      smtp_tarpit(SS);
      type(SS, 501, m517, "strangeness between ':' and '<': %s", cp);
      return -1;
    }
    if (*(cp + 1) == '<') {
      smtp_tarpit(SS);
      type(SS, 501, m517, "there are too many <'s in this: %s", cp);
      return -1;
    }
    /* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
    if (*cp == '<') {
      if (!sloppy) {
      s = rfc821_path(cp, strict || strict_protocol);
      if (s == cp) {
        /* Failure.. ? */
        type(SS, -501, m517, "For input: %s", cp);
        type821err(SS, 501, m517, buf, "Path data: %.200s", rfc821_error);
        return -1;
      }
      if (*s == '>') {
        smtp_tarpit(SS);
        type(SS, 501, m517, "there are too many >'s in this: <%s", cp);
        return -1;
      }
      /* Ok, now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (cp[1] == '@') {
        /* Yup, Starting with an "@" ..  scan until ":", which must be
           in there as this is valid RFC-821 object. */
        if (!allow_source_route) {
          while (*cp != ':') ++cp; 
          srcrtestatus = ", source route ignored";
        }
      }
      ++cp;             /* Skip the initial '<' */
      addrlen = s - 1 - cp;   /* Length until final  '>' */
      } else { /* Sloppy processing */
      ++cp;
      while (*cp == ' ' || *cp == '\t') ++cp;
      s = rfc821_path2(cp, 0);
      if (s == cp && *s != '>') {
        /* Failure.. ? */
        type(SS, -501, m517, "For input: %s", cp);
        type821err(SS, 501, m517, buf, "Path data: %.200s", rfc821_error);
        return -1;
      }
      /* Now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (*cp == '@') {
        /* Yup, Starting with an "@" ..  scan until ":", which must be
           in there as this is valid RFC-821 object. */
        if (!allow_source_route) {
          while (*cp != ':') ++cp; 
          if (*cp == ':') ++cp; /* Should be ALWAYS */
          srcrtestatus = ", source route ignored";
        }
      }
      addrlen = s - cp;
      while (*s == ' ' || *s == '\t') ++s;
      if (*s != '>') {
        rfc821_error_ptr = s;
        type(SS, -501, m517, "For input: %s", cp);
        type821err(SS, 501, m517, buf, "Missing ending '>' bracket");
        return -1;
      }
      ++s;
      }
    } else {
      /* We can be here only with non-strict mode (i.e. Sloppy..) */

      s = rfc821_path2(cp, strict);
      if (s == cp) {
      /* Failure.. */
      type(SS, -501, m517, "For input: %s", cp);
      type821err(SS, 501, m517, buf, "Path data: %.200s", rfc821_error);
      return -1;
      }

      if (*s == '>') {
      smtp_tarpit(SS);
      type(SS, 501, m517, "there are too many >'s in that!");
      return -1;
      }

      /* Ok, now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (cp[0] == '@') {
      /* Yup, Starting with an "@" ..  scan until ":", which must be
         in there as this is valid RFC-821 object. */
      if (!allow_source_route) {
        while (*cp != ':') ++cp; 
        srcrtestatus = ", source route ignored";
      }
      }
      addrlen = s - cp; /* Length */
    }

    /* BODY=8BITMIME SIZE=nnnn ENVID=xxxxxx RET=xxxx BY=nnnn;FF */
    SS->sizeoptval = -1;
    SS->sizeoptsum = -1;
    drpt_envid = NULL;
    drpt_ret   = NULL;
    auth_param = NULL;
    rc = 0;
    while (*s) {
      while (*s == ' ' || (sloppy && *s == '\t')) {
          ++s;
          if (strict_protocol) break;
          if (strict && !sloppy) break;
      }
      if (dsn_ok && CISTREQN("RET=", s, 4)) {
          if (drpt_ret) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "RET-param double defined!");
            return -1;
          }
          s += 4;
          drpt_ret = s;
          if (CISTREQN("FULL", s, 4) ||
            CISTREQN("HDRS", s, 4))
            s += 4;
          if (*s && *s != ' ' && *s == '\t') {
            smtp_tarpit(SS);
            type(SS, 501, m454, "RET-param data error");
            return -1;
          }
          drptret_len = (s - drpt_ret);
          MIBMtaEntry->ss.IncomingSMTP_OPT_RET ++;
          continue;
      }
      if (mime8bitok && CISTREQN("BODY=", s, 5)) {
          /* Actually we do not use this data... */
          s += 5;
          if (bodytype != NULL) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "BODY= double definition!");
            rc = 1;
            break;
          }
          if (CISTREQN(s, "8BITMIME", 8)) {
            bodytype = "8BITMIME";
            s += 8;
            MIBMtaEntry->ss.IncomingSMTP_OPT_BODY_8BITMIME ++;
          } else if (CISTREQN(s, "BINARYMIME", 10)) {
            bodytype = "BINARYMIME";
            s += 10;
            MIBMtaEntry->ss.IncomingSMTP_OPT_BODY_BINARYMIME ++;
          } else if (CISTREQN(s, "7BIT", 4)) {
            bodytype = "7BIT";
            s += 4;
            MIBMtaEntry->ss.IncomingSMTP_OPT_BODY_7BIT ++;
          }
          if (*s && *s != ' ' && *s != '\t') {
            smtp_tarpit(SS);
            type(SS, 501, m554, "BODY-param data error, must be one of: 8BITMIME/BINARYMIME/7BIT");
            rc = 1;
            break;
          }
          continue;
      }
      if (CISTREQN("SIZE=", s, 5)) {
          s += 5;
          if (SS->sizeoptval != -1) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "SIZE-param double definition!");
            rc = 1;
            break;
          }
          /* This data we use, gather the value */
          SS->sizeoptval = 0;
          while (isascii(*s) && isdigit(*s)) {
              if (SS->sizeoptval > 100000000) /* Next to exceed 1G ? */
              break;
            SS->sizeoptval *= 10;
            SS->sizeoptval += (*s - '0');
            ++s;
          }
          if ((*s && *s != ' ' && *s != '\t') ||
            (SS->sizeoptval > 1000000000) /* 1GB */ ) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "SIZE-param data error");
            rc = 1;
            break;
          }
          MIBMtaEntry->ss.IncomingSMTP_OPT_SIZE ++;
          continue;
      }
      /* IETF-NOTARY  SMTP-DSN extensions */
      if (dsn_ok && CISTREQN("ENVID=", s, 6)) {
          if (drpt_envid != NULL) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "ENVID double definition!");
            rc = 1;
            break;
          }
          drpt_envid = s + 6;
          p = xtext_string(s + 6);
          if (p == (s + 6)) {
            smtp_tarpit(SS);
            type821err(SS, -501, m554, buf, "Invalid ENVID value '%.200s'", drpt_envid);
            type(SS, 501, m554, "ENVID data contains illegal characters!");
            rc = 1;
            break;
          }
          s = p;
          drptenvid_len = s - drpt_envid;
          ++s;
          if (drptenvid_len == 0) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "ENVID= without data!");
            rc = 1;
            break;
          }
          MIBMtaEntry->ss.IncomingSMTP_OPT_ENVID ++;
          continue;
      }
      if (auth_ok && CISTREQN("AUTH=", s, 5)) {
          /* RFC 2554 AUTH extension */
          auth_param = s + 5;
          p = xtext_string(s + 5);
          if (p == (s + 5)) {
            smtp_tarpit(SS);
            type821err(SS, -501, m554, buf, "Invalid AUTH value '%.200s'", auth_param);
            type(SS, 501, m554, "AUTH data contains illegal characters!");
            rc = 1;
            break;
          }
          s = p;
          authparam_len = s - auth_param;
          ++s;
          if (authparam_len == 0) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "AUTH= without data!");
            rc = 1;
            break;
          }
          MIBMtaEntry->ss.IncomingSMTP_OPT_AUTH ++;
          continue;
      }
      if (deliverby_ok >= 0 && CISTREQN("BY=", s, 3)) {
          /* RFC 2852: DELIVERBY extension */
          int neg = 0;
          int val = 0;
          int cnt = 0;
          p = s + 3;
          if (*p == '-') neg = *p++; /* non-zero flag */
          while ('0' <= *p && *p <= '9') {
            val = val * 10 + (*p - '0');
            ++p; ++cnt;
          }
          if (cnt > 9) {
          invalid_by_data:
            smtp_tarpit(SS);
            type821err(SS, 501, m554, buf, "Invalid data at BY= parameter: '%.200s'", s);
            rc = 1;
            break;
          }
          if (neg) val = -val;
          if (*p != ';') goto invalid_by_data;
          ++p;
          neg = 0;
          while (*p && *p != ' ' && *p != '\t') {
            switch(*p) {
            case 'T': case 't':
            if (neg & DELIVERBY_T) goto invalid_by_data;
            neg |= DELIVERBY_T;
            break;
            case 'N': case 'n':
            if (neg & (DELIVERBY_R|DELIVERBY_N)) goto invalid_by_data;
            /* N and R are exclusive */
            neg |= DELIVERBY_N;
            break;
            case 'R': case 'r':
            if (neg & (DELIVERBY_R|DELIVERBY_N)) goto invalid_by_data;
            /* R and N are exclusive */
            neg |= DELIVERBY_R;
            break;
            default:
            goto invalid_by_data;
            break;
            }
            ++p;
          }
          if ((neg & (DELIVERBY_N|DELIVERBY_R)) == 0)
            goto invalid_by_data; /* Neither N or R ?! */
          if ((neg & DELIVERBY_R) && val <= 0) {
            type(SS, 501, m554,
               "The strict delivery deadline is already past: BY=%d;R%s",
               val, (neg & DELIVERBY_T) ? "T":"");
            rc = 1;
            break;
          }
          if ((neg & DELIVERBY_R) &&
            deliverby_ok > 0 && val < deliverby_ok) {
            type(SS, 553, m571, "Too small short delivery deadline value given: %d\n", val);
            rc = 1;
            break;
          }
          SS->deliverby_time  = time(NULL) + val;
          SS->deliverby_flags = neg;
          s = p;
          MIBMtaEntry->ss.IncomingSMTP_OPT_DELIVERBY ++;
          continue;
      }

      smtp_tarpit(SS);
      type(SS, 501, m554, "Unknown MAIL FROM:<> parameter: %s", s);
      rc = 1;
      break;
    }
    if (rc != 0)
      return -1;        /* Error(s) in previous loop.. */

    /*printf("  <path>: len=%d \"%s\"\n",cp-s,cp); */

    RFC821_822QUOTE(cp, newcp, addrlen);

    if (debug) typeflush(SS);
    if (SS->netconnected_flg)
      SS->policyresult = policytest(policydb, &SS->policystate,
                            POLICY_MAILFROM, cp, addrlen,
                            SS->authuser);
    else
      SS->policyresult = 0;
    if (logfp || logfp_to_syslog) {
      char *ss = policymsg(policydb, &SS->policystate);
      if (SS->policyresult != 0 || ss != NULL) {
      type(NULL,0,NULL,"-- policy result=%d, msg: %s",
           SS->policyresult, (ss ? ss : "<NONE!>"));
      if (logfp)
        fflush(logfp);
      }
    }

    if (SS->policyresult < 0) {
      char *ss = policymsg(policydb, &SS->policystate);
      if (ss != NULL) {
      int code = 450;
      const char *mcode = m471;
      if (SS->policyresult >= -99) {
        code = 553;
        mcode = m571;
      }
      smtp_tarpit(SS);
      type(SS, code, mcode, "Hello %s, for your MAIL FROM address <%.*s> policy analysis reported: %s",
           SS->ihostaddr, addrlen, cp, ss);
      } else if (SS->policyresult < -99) {
      smtp_tarpit(SS);
      if (SS->policyresult < -103) { /* -104 */
        type(SS,450,m443, "Hello %s, for your input: <%.*s> policy analysis reports temporary DNS error with your source domain.",
             SS->ihostaddr, addrlen, cp);
      } else if (SS->policyresult < -100) {
        type(SS,450,m443, "Hello %s, for your input: <%.*s> policy analysis reports DNS error with your source domain.",
             SS->ihostaddr, addrlen, cp);
      } else {
        type(SS,450,m471, "Hello %s, for address <%.*s> access denied by the policy analysis functions.", SS->ihostaddr, addrlen, cp);
      }
      } else {
      char *ss = policymsg(policydb, &SS->policystate);
      smtp_tarpit(SS);
      if (ss != NULL) {
        type(SS, 553, m571, "Hello %s, for your input address <%.*s> Policy analysis reported: %s", SS->ihostaddr, addrlen, cp, ss);
      } else if (SS->policyresult < -1) {
        type(SS,553,m543,"Hello %s, for MAIL FROM address <%.*s> the policy analysis reports DNS error with your source domain.", SS->ihostaddr, addrlen, cp);
      } else {
        type(SS,553,m571,"Hello %s, for MAIL FROM address <%.*s> access is denied by the policy analysis functions.", SS->ihostaddr, addrlen, cp);
      }
      }
      if (newcp)
      free((void*)newcp);
      return -1;
    }
    s = NULL;
    if ( enable_router && /* addrlen > 0 && */ STYLE(SS->cfinfo, 'f')) {
      s = router(SS, RKEY_FROM, 1, cp, addrlen);
      if (s == NULL) {
          /* the error was printed in router() */
          if (newcp)
            free((void*)newcp);
          return -1;
      }
      if (atoi(s) / 100 != 2) {
          /* verification failed */
          smtp_tarpit(SS);
          type(SS, atoi(s), s + 4, "Failed", "Failed");
          free((void *) s);
          if (newcp)
            free((void *) newcp);
          return -1;
      }
      /* The 's' goes to use below */
    }

    if (SS->mfp == NULL &&
      (SS->mfp = mail_open(MSG_RFC822)) == NULL) {
      if (s)
          free((void *) s);
      smtp_tarpit(SS);
      type(SS, 452, m430, (char *) NULL);
      if (newcp)
          free((void *) newcp);
      return -1;
    }
    SS->messagesize = 0;
    fflush(SS->mfp);
    rewind(SS->mfp);
#ifdef HAVE_FTRUNCATE
    while (ftruncate(FILENO(SS->mfp), 0) < 0)
      if (errno != EINTR && errno != EAGAIN)
      break;
#endif
    if (insecure)
      fprintf(SS->mfp, "external\n");

    if (SS->netconnected_flg) {

      /* Produce the 'rcvdfrom' header only when connected
       to network socket */

      fprintf(SS->mfp, "rcvdfrom %.200s (", SS->rhostname);
      if (SS->ihostaddr[0] != 0)
      fprintf(SS->mfp, "%s:%d ", SS->ihostaddr, SS->rport);
      rfc822commentprint(SS->mfp, SS->helobuf);

      if (ident_flag && log_rcvd_ident) {
      fprintf(SS->mfp, " ident: ");
      rfc822commentprint(SS->mfp, SS->ident_username);
      }
#ifdef HAVE_WHOSON_H
      if (log_rcvd_whoson && do_whoson) {
      fprintf(SS->mfp, " whoson: ");
      rfc822commentprint(SS->mfp,
                     ((SS->whoson_result == 0) ? SS->whoson_data :
                      ((SS->whoson_result == 1) ? "-unregistered-" : 
                       "-unavailable-")));
      }
#endif
      if (log_rcvd_authuser) {
      fprintf(SS->mfp, " smtp-auth: ");
      if (SS->authuser) {
        rfc822commentprint(SS->mfp,SS->authuser);
      } else {
        fprintf(SS->mfp, "<none>");
      }
      }
#ifdef HAVE_OPENSSL
      if (SS->sslmode) {
      if (log_rcvd_tls_mode) {
        fprintf(SS->mfp, " TLS-CIPHER: ");
        if (SS->TLS.cipher_info)
          rfc822commentprint(SS->mfp, SS->TLS.cipher_info);
        else
          fprintf(SS->mfp, "<none>");
      }
      if (log_rcvd_tls_peer) {
        fprintf(SS->mfp, " TLS-PEER-CN1: ");
        if (SS->TLS.peer_subject)
          rfc822commentprint(SS->mfp, SS->TLS.peer_subject);
        else
          fprintf(SS->mfp, "<none>");
      }
      } else {
      if (log_rcvd_tls_mode)
        fprintf(SS->mfp, " TLS-CIPHER: <none>");
      if (log_rcvd_tls_peer)
        fprintf(SS->mfp, " TLS-PEER-CN1: <none>");
      }
#endif /* - HAVE_OPENSSL */
      fprintf(SS->mfp, ")\n");

      /* COMMENT SECTION GETTING IT ALL IN EVERY CASE! */

      fprintf(SS->mfp, "comment %s ", SS->rhostname);
      if (SS->ihostaddr[0] != 0)
      fprintf(SS->mfp, "%s:%d ", SS->ihostaddr, SS->rport);
      rfc822commentprint(SS->mfp, SS->helobuf);

#ifdef HAVE_WHOSON_H
      if (do_whoson) {
      fprintf(SS->mfp, " whoson: ");
      rfc822commentprint(SS->mfp,
                     ((SS->whoson_result == 0) ? SS->whoson_data :
                      ((SS->whoson_result == 1) ? "-unregistered-" : 
                       "-unavailable-")));
      }
#endif
      fprintf(SS->mfp, " smtp-auth: ");
      if (SS->authuser) {
      rfc822commentprint(SS->mfp,SS->authuser);
      } else {
      fprintf(SS->mfp, "<none>");
      }
#ifdef HAVE_OPENSSL
      if (SS->sslmode) {
      fprintf(SS->mfp, " TLS-CIPHER: ");
      if (SS->TLS.cipher_info)
        rfc822commentprint(SS->mfp, SS->TLS.cipher_info);
      else
        fprintf(SS->mfp, "<none>");

      fprintf(SS->mfp, " TLS-PEER: ");
      if (SS->TLS.peer_subject)
        rfc822commentprint(SS->mfp, SS->TLS.peer_subject);
      else
        fprintf(SS->mfp, "<none>");
      } else {
      fprintf(SS->mfp, " TLS-CIPHER: <none>");
      fprintf(SS->mfp, " TLS-PEER: <none>");
      }
#endif /* - HAVE_OPENSSL */
      fprintf(SS->mfp, ")\n");

    }

    if (bodytype != NULL)
      fprintf(SS->mfp, "bodytype %s\n", bodytype);
    fprintf(SS->mfp, "with %s\n", SS->with_protocol);
    if (ident_flag)
      fprintf(SS->mfp, "identinfo %s\n", SS->ident_username);

    if (SS->smtpfrom) free((void*)SS->smtpfrom);
    SS->smtpfrom = malloc(addrlen+1);
    memcpy((void*)(SS->smtpfrom), cp, addrlen);
    *((char*)(&SS->smtpfrom[addrlen])) = 0;

    if (addrlen == 0)
      fputs("channel error\n", SS->mfp);  /* Empty  MAIL FROM:<> */
    else {
      fputs("from <", SS->mfp);
      fwrite(cp, 1, addrlen, SS->mfp);
      fputs(">\n", SS->mfp);
    }
    if (SS->policyresult > 0)
      fprintf(SS->mfp,"comment policytest() reported freeze state\n");

    if (newcp)
      free((void *)newcp);

    if (drpt_envid != NULL) {
      fprintf(SS->mfp, "envid ");
      fwrite(drpt_envid, 1, drptenvid_len, SS->mfp);
      fputs("\n", SS->mfp);
    }
    if (drpt_ret != NULL) {
      fprintf(SS->mfp, "notaryret ");
      fwrite(drpt_ret, 1, drptret_len, SS->mfp);
      fputs("\n", SS->mfp);
    }

    availspace = used_fd_statfs(FILENO(SS->mfp));
    if (availspace >= 0)
      MIBMtaEntry->sys.SpoolUsedSpace = availspace;
    availspace = free_fd_statfs(FILENO(SS->mfp));
    if (availspace < 0)
      availspace = LONG_MAX / 1024; /* Over 2G ? */
    if (availspace >= 0)
      MIBMtaEntry->sys.SpoolFreeSpace = availspace;
    availspace -= minimum_availspace;
    if (availspace > (LONG_MAX / 1024))
      availspace = LONG_MAX / 1024;
    availspace *= 1024;


    if (ferror(SS->mfp)) {
      smtp_tarpit(SS);
      type(SS, 452, m430, (char *) NULL);
      mail_abort(SS->mfp);
      SS->mfp = NULL;
    } else if (SS->sizeoptval > maxsize && maxsize > 0) {
      smtp_tarpit(SS);
      type(SS, 552, m534, "This message is larger, than our maximum acceptable incoming message size of  %d  chars.", maxsize);
      mail_abort(SS->mfp);
      SS->mfp = NULL;
    } else if (SS->sizeoptval > availspace) {
      smtp_tarpit(SS);
      type(SS, 452, m431, "Try again later, insufficient storage available at the moment");
      mail_abort(SS->mfp);
      SS->mfp = NULL;
    } else {
      if (s) {
          int rrc = atoi(s);
          if (rrc >= 400) {
            smtp_tarpit(SS);
            mail_abort(SS->mfp);
            SS->mfp = NULL;
          }
          type(SS, rrc, s + 4, "Ok");
      } else
          type(SS, 250, "2.1.0", "Sender syntax Ok%s", srcrtestatus);
      SS->sender_ok = 1;
    }
    if (s)
      free((void *) s);

    if (SS->mfp) /* State change only, if we still have the mfp */
      SS->state = Recipient;

    SS->rcpt_count = 0;
    SS->ok_rcpt_count = 0;
    SS->from_box = (addrlen == 0);

    return 0; /* Is ok */
}


int smtp_rcpt(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
    const char *s;
    const char *drpt_notify, *drpt_orcpt;
    const char *newcp = NULL;
    const char *srcrtestatus = "";
    int addrlen = 0, notifylen = 0, orcptlen = 0, notifyflgs;
    int strict = STYLE(SS->cfinfo, 'R');
    int sloppy = STYLE(SS->cfinfo, 'S');
    int err;

    if (strict && sloppy) /* If misconfigured, SLOPPY takes precedence! */
      strict = 0;

    /* some smtp clients don't get the 503 right and try again, so
       tell the spammers exactly what's happening. */
    if ( (SS->state == MailOrHello || SS->state == Mail) &&
       policydb != NULL && SS->policyresult < 0 ) {
      smtp_tarpit(SS);
      type(SS, 550, m571, "Hello %s, access denied by the policy analysis functions by earlier rejection", SS->ihostaddr);
      return -1;
    }

    if (SS->state != Recipient && SS->state != RecipientOrData) {
      switch (SS->state) {
      case Hello:
          cp = "Waiting for HELO command";
          break;
      case Mail:
      case MailOrHello:
          cp = "Waiting for MAIL command";
          break;
      default:
          cp = NULL;
          break;
      }
      smtp_tarpit(SS);
      type(SS, 503, m551, cp);
      return -1;
    }

    if (*cp == ' ') ++cp;
    if (!strict_protocol) while (*cp == ' ' || *cp == '\t') ++cp;

    if (!CISTREQN(cp, "To:", 3)) {
      smtp_tarpit(SS);
      type(SS, 501, m552, "where is To: in this?  %s", cp);
      return -1;
    }
    cp += 3;
    if (!strict_protocol || sloppy)
      for (; *cp != '\0' && *cp != '<'; ++cp)
      if (!isspace(*cp)) {
        if (!sloppy) {
          smtp_tarpit(SS);
          type(SS, 501, m513, "where is <...> in this?  %s", cp);
          return -1;
        }
        break; /* Sigh, be sloppy.. */
      }
    if (*cp == '\0') {
      smtp_tarpit(SS);
      type(SS, 501, m513, "where is <...> in this?  %s", cp);
      return -1;
    } else if (*cp != '<' && !sloppy) {
      smtp_tarpit(SS);
      type(SS, 501, m513, "strangeness between ':' and '<': %s", cp);
      return -1;
    } else if (*(cp+1) == '>') {
      smtp_tarpit(SS);
      type(SS, 501, m513, "Null address valid only as source: %s", cp);
      return -1;
    }
    if (*(cp + 1) == '<') {
      smtp_tarpit(SS);
      type(SS, 501, m513, "there are too many <'s in this: %s", cp);
      return -1;
    }
    if (*cp == '<') {
      /* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
      s = rfc821_path(cp, strict || strict_protocol);
      if (!sloppy) {
      if (s == cp) {
        /* Failure ?  Perhaps we are RESTRICTIVE, and the address
           is '<postmaster>' without domain ? */
        if (CISTREQN(cp, "<POSTMASTER>", 12)) {
          s += 12;
        } else {
          /* Genuine failure.. */
          type821err(SS, 501, m513, buf, "Path data: %s", rfc821_error);
          return -1;
        }
      }
      if (*s == '>') {
        smtp_tarpit(SS);
        type(SS, 501, m513, "there are too many >'s in this: %s", cp);
        return -1;
      }
      /* Ok, now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (cp[1] == '@') {
        /* Yup, Starting with an "@" ..  scan until ":", which must be
           in there as this is valid RFC-821 object. */
        if (!allow_source_route) {
          while (*cp != ':') ++cp; 
          srcrtestatus = ", source route ignored";
        }
      }
      ++cp;             /* Skip the initial '<' */
      addrlen = s - 1 - cp;   /* Length until final  '>' */
      } else { /* Sloppy processing */
      /* Sigh, lets try recovery... */
      ++cp;
      while (*cp == ' ' || *cp == '\t') ++cp;
      s = rfc821_path2(cp, 0);
      if (s == cp) {
        /* Failure.. ? */
        type821err(SS, 501, m517, buf, "Path data: %.200s", rfc821_error);
        return -1;
      }
      /* Now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (*cp == '@') {
        /* Yup, Starting with an "@" ..  scan until ":", which must be
           in there as this is valid RFC-821 object. */
        if (!allow_source_route) {
          while (*cp != ':') ++cp; 
          if (*cp == ':') ++cp; /* Should be ALWAYS */
          srcrtestatus = ", source route ignored";
        }
      }
      addrlen = s - cp;
      while (*s == ' ' || *s == '\t') ++s;
      if (*s != '>') {
        rfc821_error_ptr = s;
        type821err(SS, 501, m517, buf, "Missing ending '>' bracket: %s", cp);
        return -1;
      }
      ++s;
      }
    } else {
      /* We can be here only with non-strict mode (i.e. Sloppy..) */

      s = rfc821_path2(cp, strict || strict_protocol);
      if (s == cp) {
      /* Failure.. */
      type821err(SS, 501, m513, buf, "Path data: %.200s", rfc821_error);
      return -1;
      }

      if (*s == '>') {
      smtp_tarpit(SS);
      type(SS, 501, m513, "there are too many >'s in that!");
      return -1;
      }

      /* Ok, now it is a moment to see, if we have source routes: @a,@b:c@d */
      if (cp[0] == '@') {
      /* Yup, Starting with an "@" ..  scan until ":", which must be
         in there as this is valid RFC-821 object. */
      if (!allow_source_route) {
        while (*cp != ':') ++cp; 
        srcrtestatus = ", source route ignored";
      }
      }
      addrlen = s - cp; /* Length */
    }

#if 0
    if (debug)
      type(SS, 000, "", "<path>: len=%d \"%.*s\"\r\n", addrlen, addrlen, cp);
#endif

    if (addrlen < 1) {
      smtp_tarpit(SS);
      type(SS, 501, m513, "What is an empty recipient?");
      return -1;
    }
    drpt_notify = NULL;
    notifyflgs  = 0;
    drpt_orcpt = NULL;

    while (*s) {
      while (*s == ' ' || (sloppy && *s == '\t')) {
          ++s;
          if (strict_protocol) break;
          if (strict && !sloppy) break;
      }
      /* IETF-NOTARY  SMTP-DSN extensions */

#define NOTIFY_SUCCESS 1
#define NOTIFY_FAILURE 2
#define NOTIFY_DELAY   4
#define NOTIFY_NEVER   8

      if (dsn_ok && CISTREQN("NOTIFY=", s, 7)) {
          if (drpt_notify) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "NOTIFY-param double defined!");
            return -1;
          }
          drpt_notify = s;
          s += 7;
          while (*s) {
            if (CISTREQN("SUCCESS", s, 7))
                s += 7, notifyflgs |= NOTIFY_SUCCESS;
            else if (CISTREQN("FAILURE", s, 7))
                s += 7, notifyflgs |= NOTIFY_FAILURE;
            else if (CISTREQN("DELAY", s, 5))
                s += 5, notifyflgs |= NOTIFY_DELAY;
            else if (CISTREQN("NEVER", s, 5))
                s += 5, notifyflgs |= NOTIFY_NEVER;
            if (*s != ',')
                break;
            ++s;
          }
          if (*s && *s != ' ' && *s != '\t') {
            smtp_tarpit(SS);
            type(SS, 455, m454, "NOTIFY-param data error");
            return -1;
          }
          notifylen = s - drpt_notify;
          MIBMtaEntry->ss.IncomingSMTP_OPT_NOTIFY ++;
          continue;
      }
      if (dsn_ok && CISTREQN("ORCPT=", s, 6)) {
          if (drpt_orcpt) {
            smtp_tarpit(SS);
            type(SS, 501, m554, "ORCPT-param double defined!");
            return -1;
          }
          drpt_orcpt = s;
          s = orcpt_string(s + 6);
          if (s == NULL) {
            smtp_tarpit(SS);
            type821err(SS, -501, m454, buf, "Invalid ORCPT value '%s'", drpt_orcpt);
            type(SS, 501, m454, "ORCPT-param data error!");
            return -1;
          }
          orcptlen = s - drpt_orcpt;
          MIBMtaEntry->ss.IncomingSMTP_OPT_ORCPT ++;
          continue;
      }
      smtp_tarpit(SS);
      type(SS, 555, m554, "Unknown RCPT TO:<> parameter: %s", s);
      return -1;
    }

    if (SS->rcpt_count >= rcptlimitcnt) {
      smtp_tarpit(SS);
      type(SS, 452, "4.5.2", "Too many recipients in one go!");
      return -1;
    }

    RFC821_822QUOTE(cp, newcp, addrlen);

    if (debug) typeflush(SS);
    if (SS->netconnected_flg)
      SS->policyresult = policytest(policydb, &SS->policystate,
                            POLICY_RCPTTO, cp, addrlen,
                            SS->authuser);
    else
      SS->policyresult = 0;
    if (logfp || logfp_to_syslog) {
      char *ss = policymsg(policydb, &SS->policystate);
      if (SS->policyresult != 0 || ss != NULL) {
      type(NULL,0,NULL,"-- policy result=%d, msg: %s",
           SS->policyresult, (ss ? ss : "<NONE!>"));
      if (logfp)
        fflush(logfp);
      }
    }

    if (SS->postmasteronly || SS->policyresult < 0) {
      if (CISTREQN(cp, "postmaster", 10)) {
      /* Rejected, but it seems to be a postmaster ??? */
      if (addrlen == 10)
        SS->policyresult = 0; /* Plain <postmaster> */
      else
        if (policydb != NULL && SS->policyresult > -100) {
          int rc;
          if (debug) typeflush(SS);
          if (SS->netconnected_flg)
            rc = policytest(policydb, &SS->policystate,
                        POLICY_RCPTPOSTMASTER, cp, addrlen,
                        SS->authuser);
          else
            rc = 0;
          if (rc == 0)
            SS->policyresult = 0;

          if (logfp || logfp_to_syslog) {
            char *ss = policymsg(policydb, &SS->policystate);
            if (SS->policyresult != 0 || ss != NULL) {
            type(NULL,0,NULL,"-- policy result=%d, msg: %s",
                 SS->policyresult, (ss ? ss : "<NONE!>"));
            if (logfp)
              fflush(logfp);
            }
          }
        }
      }
    }

    if (SS->policyresult < 0) {
      char *ss = policymsg(policydb, &SS->policystate);

      fprintf(SS->mfp, "comment policytest() rejected rcptaddr: <");
      fwrite(cp, 1, addrlen, SS->mfp);
      fprintf(SS->mfp,">\n");

      smtp_tarpit(SS);

      if (SS->policyresult < -99) { /* "soft error, 4XX code */
        if (ss != NULL) {
          type(SS, 450, m471, "Hello %s, for recipient address <%.*s> the policy analysis reported: %s", SS->ihostaddr, addrlen, cp, ss);

        } else if (SS->policyresult < -103) { /* -104 */
          type(SS, 450, m443, "Hello %s, policy analysis reports temporary DNS error with the target domain: <%.*s>", SS->ihostaddr, addrlen, cp);

        } else if (SS->policyresult < -102) {
          /* Code: -103 */
          type(SS,450, m471, "Your IP address %s is not allowed to relay to email address <%.*s> via our server; MX rule",
             SS->ihostaddr, addrlen, cp);

        } else if (SS->policyresult < -100) {
          /* Code: -102 */
          type(SS, 450, m443, "Hello %s, Policy analysis found DNS error on the target address: <%.*s>", SS->ihostaddr, addrlen, cp);

        } else {
          type(SS,450,m443, "Hello %s, Policy rejection on the target address: <%.*s>",
             SS->ihostaddr, addrlen, cp);
        }
      } else {
        if (ss != NULL) {
          type(SS, 550, m571, "Hello %s, Policy analysis reported: %s rcpt=<%.*s>",
             SS->ihostaddr, ss, addrlen, cp);

        } else if (SS->policyresult < -2) {
          /* Code: -3 */
          type(SS,550, m571, "Your IP address %s is not allowed to relay to email address <%.*s> via our server; MX rule",
             SS->ihostaddr, addrlen, cp);

        } else if (SS->policyresult < -1) {
          /* Code: -2 */
          type(SS,550,m543, "Hello %s, Policy analysis found DNS error on the target domain: <%.*s>", SS->ihostaddr, addrlen, cp);

        } else {
          type(SS,550,m571, "Hello %s, Policy rejection on the target address: <%.*s>",
             SS->ihostaddr, addrlen, cp);
        }
      }
      if (newcp)
          free((void *) newcp);
      return -1;
    }

    s = NULL;
    if (enable_router && STYLE(SS->cfinfo, 't')) {
      s = router(SS, RKEY_TO, 1, cp, addrlen);
      if (s == NULL) {
          /* the error was printed in router() */
          if (newcp)
            free((void *) newcp);
          return -1;
      }
      if (atoi(s) / 100 != 2) {
          /* verification failed */
          smtp_tarpit(SS);
          type(SS, atoi(s), s + 4, "Failed", "Failed");
          free((void *) s);
          if (newcp)
            free((void *) newcp);
          return -1;
      }
      /* The 's' goes to use below */
    }
    /* FIRST 'todsn', THEN 'to' -HEADER */

    /* IETF-NOTARY DSN data: */
    fputs("todsn", SS->mfp);
    if (force_rcpt_notify_never) {
      fwrite(" NOTIFY=NEVER", 1, 13, SS->mfp);
    } else if (drpt_notify) {
      fputc(' ', SS->mfp);
      fwrite(drpt_notify, 1, notifylen, SS->mfp);
      if (!(notifyflgs & NOTIFY_NEVER) /* Not 'NEVER' */ &&
          !(notifyflgs & NOTIFY_DELAY) /* Not 'DELAY' */ &&
          (SS->deliverby_flags & DELIVERBY_N) /* 'N' mode */)
        /* RFC 2852: 4.1.4.2: */
        fwrite(",DELAY", 1, 6, SS->mfp);
      if (SS->deliverby_flags & DELIVERBY_T)
        fwrite(",TRACE", 1, 6, SS->mfp);
    } else {
      fprintf(SS->mfp, " NOTIFY=FAILURE,DELAY");
      if (SS->deliverby_flags & DELIVERBY_T)
        fwrite(",TRACE", 1, 6, SS->mfp);
    }
    if (SS->deliverby_time) {
      fprintf(SS->mfp, " BY=%ld;", (long)SS->deliverby_time);
      if (SS->deliverby_flags & DELIVERBY_R)
      fputc('R', SS->mfp);
      if (SS->deliverby_flags & DELIVERBY_N)
      fputc('N', SS->mfp);
      if (SS->deliverby_flags & DELIVERBY_T)
      fputc('T', SS->mfp);
    }
    if (drpt_orcpt) {
      fputc(' ', SS->mfp);
      fwrite(drpt_orcpt, 1, orcptlen, SS->mfp);
    } else {
      const char *p = cp;
      const char *ep = cp + addrlen;
      fputs(" ORCPT=rfc822;", SS->mfp);
      while (*p && p < ep) {
          char c = (*p) & 0xFF;
          if ('!' <= c && c <= '~' && c != '+' && c != '=')
            fputc(c, SS->mfp);
          else
            fprintf(SS->mfp, "+%02X", c);
          ++p;
      }
    }
    { /* Our received inbound RCPT value */
      const char *p = cp;
      const char *ep = cp + addrlen;
      fputs(" INRCPT=rfc822;", SS->mfp);
      while (*p && p < ep) {
          char c = (*p) & 0xFF;
          if ('!' <= c && c <= '~' && c != '+' && c != '=')
            fputc(c, SS->mfp);
          else
            fprintf(SS->mfp, "+%02X", c);
          ++p;
      }

      fputs(" INFROM=rfc822;", SS->mfp);
      p = SS->smtpfrom;
      while (*p) {
          char c = (*p) & 0xFF;
          if ('!' <= c && c <= '~' && c != '+' && c != '=')
            fputc(c, SS->mfp);
          else
            fprintf(SS->mfp, "+%02X", c);
          ++p;
      }
    }
    fputc('\n', SS->mfp);

    /* Normal "RCPT TO:<>" data: */
    fprintf(SS->mfp, "to <");
    fwrite(cp, 1, addrlen, SS->mfp);
    fprintf(SS->mfp, ">\n");

    if (SS->policyresult > 0)
      fprintf(SS->mfp,"comment policytest() reported freeze state %d\n",
            SS->policyresult);

    if (newcp)
      free((void *)newcp);

    if (SS->sizeoptval < 0)
      SS->sizeoptval = 0;

    if (sum_sizeoption_value)
      SS->sizeoptsum += SS->sizeoptval;
    else
      SS->sizeoptsum = SS->sizeoptval;

    err = 1;
    SS->rcpt_count += 1;

    if (ferror(SS->mfp)) {
      smtp_tarpit(SS);
      type(SS, 452, m430, (char *) NULL);
    } else if (maxsize > 0 && SS->sizeoptval > maxsize) {
      smtp_tarpit(SS);
      type(SS, 552, m534, "Message size exceeds fixed maximum size of %ld chars for acceptable email; rcpt=<%.*s>", maxsize, addrlen, cp);
    } else if (SS->sizeoptsum > availspace) {
      smtp_tarpit(SS);
      type(SS, 452, m431, "insufficient storage space, try again later");
    } else if (s) {
      if (SS->from_box && SS->rcpt_count > MaxErrorRecipients) {
          smtp_tarpit(SS);
          type(SS, 552, m571, "SPAM trap -- too many recipients for an empty source address!");
      } else {
          err = atoi(s);
          if (err >= 400) {
            smtp_tarpit(SS);
            mail_abort(SS->mfp);
            SS->mfp = NULL;
          }
          type(SS, err, s + 4, "Ok");
          if (err < 400)
            err = 0;
      }
    } else {
      if (SS->from_box && SS->rcpt_count > MaxErrorRecipients) {
          smtp_tarpit(SS);
          type(SS, 552, m571, "SPAM trap -- too many recipients for an empty source address!");
      } else if (SS->sizeoptval)
          type(SS, 250, "2.1.5", "Ok; can accomodate %d byte message%s for <%.*s>",
             SS->sizeoptval, srcrtestatus, addrlen, cp);
      else
          type(SS, 250, "2.1.5", "Recipient address syntax Ok%s; rcpt=<%.*s>",
             srcrtestatus, addrlen, cp);
      err = 0;
    }
    if (s)
      free((void *) s);

    if (!err) {
      SS->ok_rcpt_count += 1;
      SS->state = RecipientOrData;
    }
    return err;
}


void smtp_verify(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
    char *s;
    int cfi;
    const char *newcp = NULL;
    int addrlen;

    MIBMtaEntry->ss.IncomingSMTP_VRFY ++;

    if (SS->state == Hello) {
      type(SS, 503, m551, "Waiting for HELO/EHLO command");
      return;
    }
    while (*cp == ' ' || *cp == '\t')
      ++cp;

    cfi = STYLE(SS->cfinfo, 'v');

#if 0                   /* The input string is ARBITRARY STRING (shudder..) */
    if (*cp == '<')
      s = rfc821_path(cp, cfi);     /* with < > */
    else
      s = rfc821_path2(cp, cfi);    /* Without < > */
    if (s == cp) {
      type821err(SS, 501, m552, buf, "Path data: %s", rfc821_error);
      return;
    }
    while (*s == ' ' || *s == '\t')
      ++s;
    if (*s != 0) {
      type(SS, 501, m552, "Growl! Extra junk after the VRFY argument!");
      return;
    }
    addrlen = s - cp;
#else
    addrlen = strlen(cp);
#endif

    RFC821_822QUOTE(cp, newcp, addrlen);

    if (cfi && enable_router) {
      s = router(SS, RKEY_VERIFY, 0, cp, addrlen);
      if (s != NULL) {
          /* printf("%s\r\n", s); */
          free(s);
      } else
          type(SS, 501, m540, "Unable to verify that address");
    } else
      type(SS, 252, "2.5.2", (char *) NULL);    /* Syntax ok */

    if (newcp)
      free((void *)newcp);
}

void smtp_expand(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
    char *s;
    int cfi, addrlen;
    char *newcp = NULL;

    MIBMtaEntry->ss.IncomingSMTP_EXPN ++;

    if (SS->state == Hello) {
      type(SS, 503, m551, "Waiting for HELO/EHLO command");
      return;
    }
    while (*cp == ' ' || *cp == '\t')
      ++cp;

    cfi = STYLE(SS->cfinfo, 'e');
    if (cfi && enable_router) {
#if 0                   /* The input string is an arbitrary string! */
      if (*cp == '<')
          s = rfc821_path(cp, cfi); /* with < > */
      else
          s = rfc821_path2(cp, cfi);      /* Without < > */
      if (s == cp) {
          type821err(SS, 501, m552, buf, "Path data: %s", rfc821_error);
          return;
      }
      while (*s == ' ' || *s == '\t')
          ++s;
      if (*s != 0) {
          type(SS, 501, m552, "Growl! Extra junk after the EXPN argument!");
          return;
      }
      addrlen = s - cp;
#else
      addrlen = strlen(cp);
#endif

      RFC821_822QUOTE(cp, newcp, addrlen);

      s = router(SS, RKEY_EXPAND, 0, cp, addrlen);
      if (s != NULL) {
          /* printf("%s\r\n", s); */
          free(s);
      } else
          type(SS, 501, m540, "Unable to expand that address");
      if (newcp)
          free((void *)newcp);
    } else
      type(SS, 502, m540, (char *) NULL);
}

Generated by  Doxygen 1.6.0   Back to index