Add control=suppress_local_fixups to complete the quartet.
[exim.git] / src / src / smtp_in.c
index 063d74a9691a502a88a644409da308ce210e480d..c08d17972a292dd952b96e1fce68337ba0904f14 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.9 2005/01/13 16:15:53 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.25 2005/09/12 10:08:54 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -34,9 +34,12 @@ int deny_severity  = LOG_NOTICE;
 #endif
 
 
 #endif
 
 
-/* Size of buffer for reading SMTP commands */
+/* Size of buffer for reading SMTP commands. We used to use 512, as defined
+by RFC 821. However, RFC 1869 specifies that this must be increased for SMTP
+commands that accept arguments, and this in particular applies to AUTH, where
+the data can be quite long. */
 
 
-#define cmd_buffer_size  512      /* Ref. RFC 821 */
+#define cmd_buffer_size  2048
 
 /* Size of buffer for reading SMTP incoming packets */
 
 
 /* Size of buffer for reading SMTP incoming packets */
 
@@ -116,8 +119,6 @@ static int  unknown_command_count;
 static int  sync_cmd_limit;
 static int  smtp_write_error = 0;
 
 static int  sync_cmd_limit;
 static int  smtp_write_error = 0;
 
-static uschar *smtp_data;
-
 static uschar *cmd_buffer;
 
 /* We need to know the position of RSET, HELO, EHLO, AUTH, and STARTTLS. Their
 static uschar *cmd_buffer;
 
 /* We need to know the position of RSET, HELO, EHLO, AUTH, and STARTTLS. Their
@@ -532,7 +533,7 @@ for (p = cmd_list; p < cmd_list_end; p++)
     the following test, so that if it fails, the command name can easily be
     logged. */
 
     the following test, so that if it fails, the command name can easily be
     logged. */
 
-    smtp_data = cmd_buffer + p->len;
+    smtp_command_argument = cmd_buffer + p->len;
 
     /* Count non-mail commands from those hosts that are controlled in this
     way. The default is all hosts. We don't waste effort checking the list
 
     /* Count non-mail commands from those hosts that are controlled in this
     way. The default is all hosts. We don't waste effort checking the list
@@ -550,11 +551,11 @@ for (p = cmd_list; p < cmd_list_end; p++)
         return TOO_MANY_NONMAIL_CMD;
       }
 
         return TOO_MANY_NONMAIL_CMD;
       }
 
-    /* Get the data pointer over leading spaces and return; if there is no data
-    for a command that expects it, we give the error centrally here. */
+    /* Get the data pointer over leading spaces and return; if there is data
+    for a command that does not expect it, give the error centrally here. */
 
 
-    while (isspace(*smtp_data)) smtp_data++;
-    return (p->has_arg || *smtp_data == 0)? p->cmd : BADARG_CMD;
+    while (isspace(*smtp_command_argument)) smtp_command_argument++;
+    return (p->has_arg || *smtp_command_argument == 0)? p->cmd : BADARG_CMD;
     }
   }
 
     }
   }
 
@@ -742,7 +743,7 @@ return yield;
 *         Extract SMTP command option            *
 *************************************************/
 
 *         Extract SMTP command option            *
 *************************************************/
 
-/* This function picks the next option setting off the end of smtp_data. It
+/* This function picks the next option setting off the end of smtp_command_argument. It
 is called for MAIL FROM and RCPT TO commands, to pick off the optional ESMTP
 things that can appear there.
 
 is called for MAIL FROM and RCPT TO commands, to pick off the optional ESMTP
 things that can appear there.
 
@@ -757,11 +758,11 @@ static BOOL
 extract_option(uschar **name, uschar **value)
 {
 uschar *n;
 extract_option(uschar **name, uschar **value)
 {
 uschar *n;
-uschar *v = smtp_data + Ustrlen(smtp_data) -1;
+uschar *v = smtp_command_argument + Ustrlen(smtp_command_argument) -1;
 while (isspace(*v)) v--;
 v[1] = 0;
 
 while (isspace(*v)) v--;
 v[1] = 0;
 
-while (v > smtp_data && *v != '=' && !isspace(*v)) v--;
+while (v > smtp_command_argument && *v != '=' && !isspace(*v)) v--;
 if (*v != '=') return FALSE;
 
 n = v;
 if (*v != '=') return FALSE;
 
 n = v;
@@ -801,18 +802,21 @@ store_reset(reset_point);
 recipients_list = NULL;
 rcpt_count = rcpt_defer_count = rcpt_fail_count =
   raw_recipients_count = recipients_count = recipients_list_max = 0;
 recipients_list = NULL;
 rcpt_count = rcpt_defer_count = rcpt_fail_count =
   raw_recipients_count = recipients_count = recipients_list_max = 0;
+message_linecount = 0;
 message_size = -1;
 acl_warn_headers = NULL;
 queue_only_policy = FALSE;
 deliver_freeze = FALSE;                              /* Can be set by ACL */
 message_size = -1;
 acl_warn_headers = NULL;
 queue_only_policy = FALSE;
 deliver_freeze = FALSE;                              /* Can be set by ACL */
-fake_reject = FALSE;                                 /* Can be set by ACL */
+fake_response = OK;                                  /* Can be set by ACL */
 #ifdef WITH_CONTENT_SCAN
 no_mbox_unspool = FALSE;                             /* Can be set by ACL */
 #endif
 submission_mode = FALSE;                             /* Can be set by ACL */
 #ifdef WITH_CONTENT_SCAN
 no_mbox_unspool = FALSE;                             /* Can be set by ACL */
 #endif
 submission_mode = FALSE;                             /* Can be set by ACL */
+suppress_local_fixups = FALSE;                       /* Can be set by ACL */
 active_local_from_check = local_from_check;          /* Can be set by ACL */
 active_local_sender_retain = local_sender_retain;    /* Can be set by ACL */
 sender_address = NULL;
 active_local_from_check = local_from_check;          /* Can be set by ACL */
 active_local_sender_retain = local_sender_retain;    /* Can be set by ACL */
 sender_address = NULL;
+submission_name = NULL;                              /* Can be set by ACL */
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
 sender_address_unrewritten = NULL;  /* Set only after verify rewrite */
 sender_verified_list = NULL;        /* No senders verified */
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
 sender_address_unrewritten = NULL;  /* Set only after verify rewrite */
 sender_verified_list = NULL;        /* No senders verified */
@@ -823,14 +827,21 @@ authenticated_sender = NULL;
 bmi_run = 0;
 bmi_verdicts = NULL;
 #endif
 bmi_run = 0;
 bmi_verdicts = NULL;
 #endif
+#ifdef EXPERIMENTAL_DOMAINKEYS
+dk_do_verify = 0;
+#endif
 #ifdef EXPERIMENTAL_SPF
 spf_header_comment = NULL;
 spf_received = NULL;
 #ifdef EXPERIMENTAL_SPF
 spf_header_comment = NULL;
 spf_received = NULL;
-spf_result = NULL;  
+spf_result = NULL;
 spf_smtp_comment = NULL;
 #endif
 body_linecount = body_zerocount = 0;
 
 spf_smtp_comment = NULL;
 #endif
 body_linecount = body_zerocount = 0;
 
+sender_rate = sender_rate_limit = sender_rate_period = NULL;
+ratelimiters_mail = NULL;           /* Updated by ratelimit ACL condition */
+                   /* Note that ratelimiters_conn persists across resets. */
+
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
 
 /* The message body variables use malloc store. They may be set if this is
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
 
 /* The message body variables use malloc store. They may be set if this is
@@ -915,7 +926,7 @@ while (done <= 0)
     case HELO_CMD:
     case EHLO_CMD:
 
     case HELO_CMD:
     case EHLO_CMD:
 
-    check_helo(smtp_data);
+    check_helo(smtp_command_argument);
     /* Fall through */
 
     case RSET_CMD:
     /* Fall through */
 
     case RSET_CMD:
@@ -935,7 +946,7 @@ while (done <= 0)
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "503 Sender already given");
 
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "503 Sender already given");
 
-    if (smtp_data[0] == 0)
+    if (smtp_command_argument[0] == 0)
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "501 MAIL FROM must have an address operand");
 
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "501 MAIL FROM must have an address operand");
 
@@ -946,8 +957,8 @@ while (done <= 0)
     /* Apply SMTP rewrite */
 
     raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
     /* Apply SMTP rewrite */
 
     raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
-      rewrite_one(smtp_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
-        US"", global_rewrite_rules) : smtp_data;
+      rewrite_one(smtp_command_argument, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
+        US"", global_rewrite_rules) : smtp_command_argument;
 
     /* Extract the address; the TRUE flag allows <> as valid */
 
 
     /* Extract the address; the TRUE flag allows <> as valid */
 
@@ -990,7 +1001,7 @@ while (done <= 0)
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "503 No sender yet given");
 
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "503 No sender yet given");
 
-    if (smtp_data[0] == 0)
+    if (smtp_command_argument[0] == 0)
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "501 RCPT TO must have an address operand");
 
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(cmd_buffer, "501 RCPT TO must have an address operand");
 
@@ -1005,8 +1016,8 @@ while (done <= 0)
     recipient address */
 
     recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
     recipient address */
 
     recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
-      rewrite_one(smtp_data, rewrite_smtp, NULL, FALSE, US"",
-        global_rewrite_rules) : smtp_data;
+      rewrite_one(smtp_command_argument, rewrite_smtp, NULL, FALSE, US"",
+        global_rewrite_rules) : smtp_command_argument;
 
     /* rfc821_domains = TRUE; << no longer needed */
     recipient = parse_extract_address(recipient, &errmess, &start, &end,
 
     /* rfc821_domains = TRUE; << no longer needed */
     recipient = parse_extract_address(recipient, &errmess, &start, &end,
@@ -1121,11 +1132,13 @@ int size = 256;
 int i, ptr;
 uschar *p, *s, *ss;
 
 int i, ptr;
 uschar *p, *s, *ss;
 
-/* If we are running in the test harness, and the incoming call is from 
-127.0.0.2 (sic), have a short delay. This makes it possible to test handling of 
+/* If we are running in the test harness, and the incoming call is from
+127.0.0.2 (sic), have a short delay. This makes it possible to test handling of
 input sent too soon (before the banner is output). */
 
 input sent too soon (before the banner is output). */
 
-if (running_in_test_harness && Ustrcmp(sender_host_address, "127.0.0.2") == 0)
+if (running_in_test_harness &&
+    sender_host_address != NULL &&
+    Ustrcmp(sender_host_address, "127.0.0.2") == 0)
   sleep(1);
 
 /* Default values for certain variables */
   sleep(1);
 
 /* Default values for certain variables */
@@ -1257,16 +1270,16 @@ if (!sender_host_unknown)
   if (!host_checking && !sender_host_notsocket)
     {
     #if OPTSTYLE == 1
   if (!host_checking && !sender_host_notsocket)
     {
     #if OPTSTYLE == 1
-    SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
+    EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
     struct ip_options *ipopt = store_get(optlen);
     #elif OPTSTYLE == 2
     struct ip_opts ipoptblock;
     struct ip_opts *ipopt = &ipoptblock;
     struct ip_options *ipopt = store_get(optlen);
     #elif OPTSTYLE == 2
     struct ip_opts ipoptblock;
     struct ip_opts *ipopt = &ipoptblock;
-    SOCKLEN_T optlen = sizeof(ipoptblock);
+    EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
     #else
     struct ipoption ipoptblock;
     struct ipoption *ipopt = &ipoptblock;
     #else
     struct ipoption ipoptblock;
     struct ipoption *ipopt = &ipoptblock;
-    SOCKLEN_T optlen = sizeof(ipoptblock);
+    EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
     #endif
 
     /* Occasional genuine failures of getsockopt() have been seen - for
     #endif
 
     /* Occasional genuine failures of getsockopt() have been seen - for
@@ -1533,8 +1546,7 @@ if (acl_smtp_connect != NULL)
   {
   int rc;
   uschar *user_msg, *log_msg;
   {
   int rc;
   uschar *user_msg, *log_msg;
-  smtp_data = US"in \"connect\" ACL";    /* For logged failure message */
-  rc = acl_check(ACL_WHERE_CONNECT, US"", acl_smtp_connect, &user_msg,
+  rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg,
     &log_msg);
   if (rc != OK)
     {
     &log_msg);
   if (rc != OK)
     {
@@ -1608,14 +1620,17 @@ if (smtp_enforce_sync && sender_host_address != NULL && !sender_host_notsocket)
       &tzero) > 0)
     {
     int rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
       &tzero) > 0)
     {
     int rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
-    if (rc > 150) rc = 150;
-    smtp_inbuffer[rc] = 0; 
-    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol violation: "
-      "synchronization error (input sent without waiting for greeting): "
-      "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
-      string_printing(smtp_inbuffer));
-    smtp_printf("554 SMTP synchronization error\r\n");
-    return FALSE;
+    if (rc > 0)
+      {
+      if (rc > 150) rc = 150;
+      smtp_inbuffer[rc] = 0;
+      log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+        "synchronization error (input sent without waiting for greeting): "
+        "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+        string_printing(smtp_inbuffer));
+      smtp_printf("554 SMTP synchronization error\r\n");
+      return FALSE;
+      }
     }
   }
 
     }
   }
 
@@ -1800,12 +1815,15 @@ int code = acl_wherecodes[where];
 BOOL drop = rc == FAIL_DROP;
 uschar *lognl;
 uschar *sender_info = US"";
 BOOL drop = rc == FAIL_DROP;
 uschar *lognl;
 uschar *sender_info = US"";
-uschar *what = (where == ACL_WHERE_PREDATA)? US"DATA" :
+uschar *what =
 #ifdef WITH_CONTENT_SCAN
 #ifdef WITH_CONTENT_SCAN
-               (where == ACL_WHERE_MIME)? US"during MIME ACL checks" :
-#endif  
-               (where == ACL_WHERE_DATA)? US"after DATA" :
-  string_sprintf("%s %s", acl_wherenames[where], smtp_data);
+  (where == ACL_WHERE_MIME)? US"during MIME ACL checks" :
+#endif
+  (where == ACL_WHERE_PREDATA)? US"DATA" :
+  (where == ACL_WHERE_DATA)? US"after DATA" :
+  (smtp_command_argument == NULL)?
+    string_sprintf("%s in \"connect\" ACL", acl_wherenames[where]) :
+    string_sprintf("%s %s", acl_wherenames[where], smtp_command_argument);
 
 if (drop) rc = FAIL;
 
 
 if (drop) rc = FAIL;
 
@@ -1916,6 +1934,130 @@ return 2;
 
 
 
 
 
 
+/*************************************************
+*             Verify HELO argument               *
+*************************************************/
+
+/* This function is called if helo_verify_hosts or helo_try_verify_hosts is
+matched. It is also called from ACL processing if verify = helo is used and
+verification was not previously tried (i.e. helo_try_verify_hosts was not
+matched). The result of its processing is to set helo_verified and
+helo_verify_failed. These variables should both be FALSE for this function to
+be called.
+
+Note that EHLO/HELO is legitimately allowed to quote an address literal. Allow
+for IPv6 ::ffff: literals.
+
+Argument:   none
+Returns:    TRUE if testing was completed;
+            FALSE on a temporary failure
+*/
+
+BOOL
+smtp_verify_helo(void)
+{
+BOOL yield = TRUE;
+
+HDEBUG(D_receive) debug_printf("verifying EHLO/HELO argument \"%s\"\n",
+  sender_helo_name);
+
+if (sender_helo_name == NULL)
+  {
+  HDEBUG(D_receive) debug_printf("no EHLO/HELO command was issued\n");
+  }
+
+else if (sender_helo_name[0] == '[')
+  {
+  helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
+    Ustrlen(sender_host_address)) == 0;
+
+  #if HAVE_IPV6
+  if (!helo_verified)
+    {
+    if (strncmpic(sender_host_address, US"::ffff:", 7) == 0)
+      helo_verified = Ustrncmp(sender_helo_name + 1,
+        sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0;
+    }
+  #endif
+
+  HDEBUG(D_receive)
+    { if (helo_verified) debug_printf("matched host address\n"); }
+  }
+
+/* Do a reverse lookup if one hasn't already given a positive or negative
+response. If that fails, or the name doesn't match, try checking with a forward
+lookup. */
+
+else
+  {
+  if (sender_host_name == NULL && !host_lookup_failed)
+    yield = host_name_lookup() != DEFER;
+
+  /* If a host name is known, check it and all its aliases. */
+
+  if (sender_host_name != NULL)
+    {
+    helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0;
+
+    if (helo_verified)
+      {
+      HDEBUG(D_receive) debug_printf("matched host name\n");
+      }
+    else
+      {
+      uschar **aliases = sender_host_aliases;
+      while (*aliases != NULL)
+        {
+        helo_verified = strcmpic(*aliases++, sender_helo_name) == 0;
+        if (helo_verified) break;
+        }
+      HDEBUG(D_receive)
+        {
+        if (helo_verified)
+          debug_printf("matched alias %s\n", *(--aliases));
+        }
+      }
+    }
+
+  /* Final attempt: try a forward lookup of the helo name */
+
+  if (!helo_verified)
+    {
+    int rc;
+    host_item h;
+    h.name = sender_helo_name;
+    h.address = NULL;
+    h.mx = MX_NONE;
+    h.next = NULL;
+    HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
+      sender_helo_name);
+    rc = host_find_byname(&h, NULL, NULL, TRUE);
+    if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
+      {
+      host_item *hh = &h;
+      while (hh != NULL)
+        {
+        if (Ustrcmp(hh->address, sender_host_address) == 0)
+          {
+          helo_verified = TRUE;
+          HDEBUG(D_receive)
+            debug_printf("IP address for %s matches calling address\n",
+              sender_helo_name);
+          break;
+          }
+        hh = hh->next;
+        }
+      }
+    }
+  }
+
+if (!helo_verified) helo_verify_failed = FALSE;  /* We've tried ... */
+return yield;
+}
+
+
+
+
 /*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
 /*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
@@ -2039,8 +2181,7 @@ while (done <= 0)
 
     if (acl_smtp_auth != NULL)
       {
 
     if (acl_smtp_auth != NULL)
       {
-      rc = acl_check(ACL_WHERE_AUTH, smtp_data, acl_smtp_auth, &user_msg,
-        &log_msg);
+      rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg);
       if (rc != OK)
         {
         done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
       if (rc != OK)
         {
         done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
@@ -2050,8 +2191,8 @@ while (done <= 0)
 
     /* Find the name of the requested authentication mechanism. */
 
 
     /* Find the name of the requested authentication mechanism. */
 
-    s = smtp_data;
-    while ((c = *smtp_data) != 0 && !isspace(c))
+    s = smtp_command_argument;
+    while ((c = *smtp_command_argument) != 0 && !isspace(c))
       {
       if (!isalnum(c) && c != '-' && c != '_')
         {
       {
       if (!isalnum(c) && c != '-' && c != '_')
         {
@@ -2059,16 +2200,16 @@ while (done <= 0)
           US"invalid character in authentication mechanism name");
         goto COMMAND_LOOP;
         }
           US"invalid character in authentication mechanism name");
         goto COMMAND_LOOP;
         }
-      smtp_data++;
+      smtp_command_argument++;
       }
 
     /* If not at the end of the line, we must be at white space. Terminate the
     name and move the pointer on to any data that may be present. */
 
       }
 
     /* If not at the end of the line, we must be at white space. Terminate the
     name and move the pointer on to any data that may be present. */
 
-    if (*smtp_data != 0)
+    if (*smtp_command_argument != 0)
       {
       {
-      *smtp_data++ = 0;
-      while (isspace(*smtp_data)) smtp_data++;
+      *smtp_command_argument++ = 0;
+      while (isspace(*smtp_command_argument)) smtp_command_argument++;
       }
 
     /* Search for an authentication mechanism which is configured for use
       }
 
     /* Search for an authentication mechanism which is configured for use
@@ -2098,10 +2239,18 @@ while (done <= 0)
     expand_nmax = 0;
     expand_nlength[0] = 0;   /* $0 contains nothing */
 
     expand_nmax = 0;
     expand_nlength[0] = 0;   /* $0 contains nothing */
 
-    c = (au->info->servercode)(au, smtp_data);
+    c = (au->info->servercode)(au, smtp_command_argument);
     if (au->set_id != NULL) set_id = expand_string(au->set_id);
     expand_nmax = -1;        /* Reset numeric variables */
 
     if (au->set_id != NULL) set_id = expand_string(au->set_id);
     expand_nmax = -1;        /* Reset numeric variables */
 
+    /* The value of authenticated_id is stored in the spool file and printed in
+    log lines. It must not contain binary zeros or newline characters. In
+    normal use, it never will, but when playing around or testing, this error
+    can (did) happen. To guard against this, ensure that the id contains only
+    printing characters. */
+
+    if (set_id != NULL) set_id = string_printing(set_id);
+
     /* For the non-OK cases, set up additional logging data if set_id
     is not empty. */
 
     /* For the non-OK cases, set up additional logging data if set_id
     is not empty. */
 
@@ -2206,14 +2355,14 @@ while (done <= 0)
     /* Reject the HELO if its argument was invalid or non-existent. A
     successful check causes the argument to be saved in malloc store. */
 
     /* Reject the HELO if its argument was invalid or non-existent. A
     successful check causes the argument to be saved in malloc store. */
 
-    if (!check_helo(smtp_data))
+    if (!check_helo(smtp_command_argument))
       {
       smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
 
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
         "invalid argument(s): %s", hello, host_and_ident(FALSE),
       {
       smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
 
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
         "invalid argument(s): %s", hello, host_and_ident(FALSE),
-        (*smtp_data == 0)? US"(no argument given)" :
-                           string_printing(smtp_data));
+        (*smtp_command_argument == 0)? US"(no argument given)" :
+                           string_printing(smtp_command_argument));
 
       if (++synprot_error_count > smtp_max_synprot_errors)
         {
 
       if (++synprot_error_count > smtp_max_synprot_errors)
         {
@@ -2236,7 +2385,7 @@ while (done <= 0)
     if (!sender_host_unknown)
       {
       BOOL old_helo_verified = helo_verified;
     if (!sender_host_unknown)
       {
       BOOL old_helo_verified = helo_verified;
-      uschar *p = smtp_data;
+      uschar *p = smtp_command_argument;
 
       while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; }
       *p = 0;
 
       while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; }
       *p = 0;
@@ -2258,104 +2407,16 @@ while (done <= 0)
         (tls_active >= 0)? " TLS" : "", host_and_ident(FALSE));
 
       /* Verify if configured. This doesn't give much security, but it does
         (tls_active >= 0)? " TLS" : "", host_and_ident(FALSE));
 
       /* Verify if configured. This doesn't give much security, but it does
-      make some people happy to be able to do it. Note that HELO is legitimately
-      allowed to quote an address literal. Allow for IPv6 ::ffff: literals. */
+      make some people happy to be able to do it. If helo_required is set,
+      (host matches helo_verify_hosts) failure forces rejection. If helo_verify
+      is set (host matches helo_try_verify_hosts), it does not. This is perhaps
+      now obsolescent, since the verification can now be requested selectively
+      at ACL time. */
 
 
-      helo_verified = FALSE;
+      helo_verified = helo_verify_failed = FALSE;
       if (helo_required || helo_verify)
         {
       if (helo_required || helo_verify)
         {
-        BOOL tempfail = FALSE;
-
-        HDEBUG(D_receive) debug_printf("verifying %s %s\n", hello,
-          sender_helo_name);
-        if (sender_helo_name[0] == '[')
-          {
-          helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
-            Ustrlen(sender_host_address)) == 0;
-
-          #if HAVE_IPV6
-          if (!helo_verified)
-            {
-            if (strncmpic(sender_host_address, US"::ffff:", 7) == 0)
-              helo_verified = Ustrncmp(sender_helo_name + 1,
-                sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0;
-            }
-          #endif
-
-          HDEBUG(D_receive)
-            { if (helo_verified) debug_printf("matched host address\n"); }
-          }
-
-        /* Do a reverse lookup if one hasn't already given a positive or
-        negative response. If that fails, or the name doesn't match, try
-        checking with a forward lookup. */
-
-        else
-          {
-          if (sender_host_name == NULL && !host_lookup_failed)
-            tempfail = host_name_lookup() == DEFER;
-
-          /* If a host name is known, check it and all its aliases. */
-
-          if (sender_host_name != NULL)
-            {
-            helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0;
-
-            if (helo_verified)
-              {
-              HDEBUG(D_receive) debug_printf("matched host name\n");
-              }
-            else
-              {
-              uschar **aliases = sender_host_aliases;
-              while (*aliases != NULL)
-                {
-                helo_verified = strcmpic(*aliases++, sender_helo_name) == 0;
-                if (helo_verified) break;
-                }
-              HDEBUG(D_receive)
-                {
-                if (helo_verified)
-                  debug_printf("matched alias %s\n", *(--aliases));
-                }
-              }
-            }
-
-          /* Final attempt: try a forward lookup of the helo name */
-
-          if (!helo_verified)
-            {
-            int rc;
-            host_item h;
-            h.name = sender_helo_name;
-            h.address = NULL;
-            h.mx = MX_NONE;
-            h.next = NULL;
-            HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
-              sender_helo_name);
-            rc = host_find_byname(&h, NULL, NULL, TRUE);
-            if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
-              {
-              host_item *hh = &h;
-              while (hh != NULL)
-                {
-                if (Ustrcmp(hh->address, sender_host_address) == 0)
-                  {
-                  helo_verified = TRUE;
-                  HDEBUG(D_receive)
-                    debug_printf("IP address for %s matches calling address\n",
-                      sender_helo_name);
-                  break;
-                  }
-                hh = hh->next;
-                }
-              }
-            }
-          }
-
-        /* Verification failed. A temporary lookup failure gives a temporary
-        error. */
-
+        BOOL tempfail = !smtp_verify_helo();
         if (!helo_verified)
           {
           if (helo_required)
         if (!helo_verified)
           {
           if (helo_required)
@@ -2383,8 +2444,7 @@ while (done <= 0)
 
     if (acl_smtp_helo != NULL)
       {
 
     if (acl_smtp_helo != NULL)
       {
-      rc = acl_check(ACL_WHERE_HELO, smtp_data, acl_smtp_helo, &user_msg,
-        &log_msg);
+      rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg);
       if (rc != OK)
         {
         done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
       if (rc != OK)
         {
         done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
@@ -2569,7 +2629,7 @@ while (done <= 0)
     if (tls_active >= 0) (void)tls_write(s, ptr); else
     #endif
 
     if (tls_active >= 0) (void)tls_write(s, ptr); else
     #endif
 
-    fwrite(s, 1, ptr, smtp_out);
+    (void)fwrite(s, 1, ptr, smtp_out);
     DEBUG(D_receive) debug_printf("SMTP>> %s", s);
     helo_seen = TRUE;
     break;   /* HELO/EHLO */
     DEBUG(D_receive) debug_printf("SMTP>> %s", s);
     helo_seen = TRUE;
     break;   /* HELO/EHLO */
@@ -2600,7 +2660,7 @@ while (done <= 0)
       break;
       }
 
       break;
       }
 
-    if (smtp_data[0] == 0)
+    if (smtp_command_argument[0] == 0)
       {
       done = synprot_error(L_smtp_protocol_error, 501, NULL,
         US"MAIL must have an address operand");
       {
       done = synprot_error(L_smtp_protocol_error, 501, NULL,
         US"MAIL must have an address operand");
@@ -2759,8 +2819,8 @@ while (done <= 0)
     TRUE flag allows "<>" as a sender address. */
 
     raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
     TRUE flag allows "<>" as a sender address. */
 
     raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
-      rewrite_one(smtp_data, rewrite_smtp, NULL, FALSE, US"",
-        global_rewrite_rules) : smtp_data;
+      rewrite_one(smtp_command_argument, rewrite_smtp, NULL, FALSE, US"",
+        global_rewrite_rules) : smtp_command_argument;
 
     /* rfc821_domains = TRUE; << no longer needed */
     raw_sender =
 
     /* rfc821_domains = TRUE; << no longer needed */
     raw_sender =
@@ -2770,7 +2830,7 @@ while (done <= 0)
 
     if (raw_sender == NULL)
       {
 
     if (raw_sender == NULL)
       {
-      done = synprot_error(L_smtp_syntax_error, 501, smtp_data, errmess);
+      done = synprot_error(L_smtp_syntax_error, 501, smtp_command_argument, errmess);
       break;
       }
 
       break;
       }
 
@@ -2830,7 +2890,7 @@ while (done <= 0)
       else
         {
         smtp_printf("501 %s: sender address must contain a domain\r\n",
       else
         {
         smtp_printf("501 %s: sender address must contain a domain\r\n",
-          smtp_data);
+          smtp_command_argument);
         log_write(L_smtp_syntax_error,
           LOG_MAIN|LOG_REJECT,
           "unqualified sender rejected: <%s> %s%s",
         log_write(L_smtp_syntax_error,
           LOG_MAIN|LOG_REJECT,
           "unqualified sender rejected: <%s> %s%s",
@@ -2898,7 +2958,7 @@ while (done <= 0)
 
     /* Check for an operand */
 
 
     /* Check for an operand */
 
-    if (smtp_data[0] == 0)
+    if (smtp_command_argument[0] == 0)
       {
       done = synprot_error(L_smtp_syntax_error, 501, NULL,
         US"RCPT must have an address operand");
       {
       done = synprot_error(L_smtp_syntax_error, 501, NULL,
         US"RCPT must have an address operand");
@@ -2910,8 +2970,8 @@ while (done <= 0)
     as a recipient address */
 
     recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
     as a recipient address */
 
     recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
-      rewrite_one(smtp_data, rewrite_smtp, NULL, FALSE, US"",
-        global_rewrite_rules) : smtp_data;
+      rewrite_one(smtp_command_argument, rewrite_smtp, NULL, FALSE, US"",
+        global_rewrite_rules) : smtp_command_argument;
 
     /* rfc821_domains = TRUE; << no longer needed */
     recipient = parse_extract_address(recipient, &errmess, &start, &end,
 
     /* rfc821_domains = TRUE; << no longer needed */
     recipient = parse_extract_address(recipient, &errmess, &start, &end,
@@ -2920,7 +2980,7 @@ while (done <= 0)
 
     if (recipient == NULL)
       {
 
     if (recipient == NULL)
       {
-      done = synprot_error(L_smtp_syntax_error, 501, smtp_data, errmess);
+      done = synprot_error(L_smtp_syntax_error, 501, smtp_command_argument, errmess);
       rcpt_fail_count++;
       break;
       }
       rcpt_fail_count++;
       break;
       }
@@ -2950,7 +3010,7 @@ while (done <= 0)
         {
         rcpt_fail_count++;
         smtp_printf("501 %s: recipient address must contain a domain\r\n",
         {
         rcpt_fail_count++;
         smtp_printf("501 %s: recipient address must contain a domain\r\n",
-          smtp_data);
+          smtp_command_argument);
         log_write(L_smtp_syntax_error,
           LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: "
           "<%s> %s%s", recipient, host_and_ident(TRUE),
         log_write(L_smtp_syntax_error,
           LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: "
           "<%s> %s%s", recipient, host_and_ident(TRUE),
@@ -3024,7 +3084,7 @@ while (done <= 0)
         "discarded by %s ACL%s%s", host_and_ident(TRUE),
         (sender_address_unrewritten != NULL)?
         sender_address_unrewritten : sender_address,
         "discarded by %s ACL%s%s", host_and_ident(TRUE),
         (sender_address_unrewritten != NULL)?
         sender_address_unrewritten : sender_address,
-        smtp_data, recipients_discarded? "MAIL" : "RCPT",
+        smtp_command_argument, recipients_discarded? "MAIL" : "RCPT",
         (log_msg == NULL)? US"" : US": ",
         (log_msg == NULL)? US"" : log_msg);
       }
         (log_msg == NULL)? US"" : US": ",
         (log_msg == NULL)? US"" : log_msg);
       }
@@ -3073,11 +3133,11 @@ while (done <= 0)
       smtp_printf("554 Too many recipients\r\n");
       break;
       }
       smtp_printf("554 Too many recipients\r\n");
       break;
       }
-      
+
     if (acl_smtp_predata == NULL) rc = OK; else
     if (acl_smtp_predata == NULL) rc = OK; else
-      { 
+      {
       enable_dollar_recipients = TRUE;
       enable_dollar_recipients = TRUE;
-      rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg, 
+      rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg,
         &log_msg);
       enable_dollar_recipients = FALSE;
       }
         &log_msg);
       enable_dollar_recipients = FALSE;
       }
@@ -3098,8 +3158,7 @@ while (done <= 0)
 
 
     case VRFY_CMD:
 
 
     case VRFY_CMD:
-    rc = acl_check(ACL_WHERE_VRFY, smtp_data, acl_smtp_vrfy, &user_msg,
-      &log_msg);
+    rc = acl_check(ACL_WHERE_VRFY, NULL, acl_smtp_vrfy, &user_msg, &log_msg);
     if (rc != OK)
       done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
     else
     if (rc != OK)
       done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
     else
@@ -3108,7 +3167,7 @@ while (done <= 0)
       uschar *s = NULL;
 
       /* rfc821_domains = TRUE; << no longer needed */
       uschar *s = NULL;
 
       /* rfc821_domains = TRUE; << no longer needed */
-      address = parse_extract_address(smtp_data, &errmess, &start, &end,
+      address = parse_extract_address(smtp_command_argument, &errmess, &start, &end,
         &recipient_domain, FALSE);
       /* rfc821_domains = FALSE; << no longer needed */
 
         &recipient_domain, FALSE);
       /* rfc821_domains = FALSE; << no longer needed */
 
@@ -3135,7 +3194,7 @@ while (done <= 0)
             string_sprintf("550 <%s> %s", address, addr->message) :
             string_sprintf("550 <%s> is not deliverable", address);
           log_write(0, LOG_MAIN, "VRFY failed for %s %s",
             string_sprintf("550 <%s> %s", address, addr->message) :
             string_sprintf("550 <%s> is not deliverable", address);
           log_write(0, LOG_MAIN, "VRFY failed for %s %s",
-            smtp_data, host_and_ident(TRUE));
+            smtp_command_argument, host_and_ident(TRUE));
           break;
           }
         }
           break;
           }
         }
@@ -3146,17 +3205,16 @@ while (done <= 0)
 
 
     case EXPN_CMD:
 
 
     case EXPN_CMD:
-    rc = acl_check(ACL_WHERE_EXPN, smtp_data, acl_smtp_expn, &user_msg,
-      &log_msg);
+    rc = acl_check(ACL_WHERE_EXPN, NULL, acl_smtp_expn, &user_msg, &log_msg);
     if (rc != OK)
       done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg);
     else
       {
       BOOL save_log_testing_mode = log_testing_mode;
       address_test_mode = log_testing_mode = TRUE;
     if (rc != OK)
       done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg);
     else
       {
       BOOL save_log_testing_mode = log_testing_mode;
       address_test_mode = log_testing_mode = TRUE;
-      (void) verify_address(deliver_make_addr(smtp_data, FALSE), smtp_out,
-        vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1, NULL, NULL, 
-        NULL);
+      (void) verify_address(deliver_make_addr(smtp_command_argument, FALSE),
+        smtp_out, vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1,
+        NULL, NULL, NULL);
       address_test_mode = FALSE;
       log_testing_mode = save_log_testing_mode;    /* true for -bh */
       }
       address_test_mode = FALSE;
       log_testing_mode = save_log_testing_mode;    /* true for -bh */
       }
@@ -3283,7 +3341,7 @@ while (done <= 0)
 
     if (acl_smtp_quit != NULL)
       {
 
     if (acl_smtp_quit != NULL)
       {
-      rc = acl_check(ACL_WHERE_QUIT, US"", acl_smtp_quit,&user_msg,&log_msg);
+      rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit,&user_msg,&log_msg);
       if (rc == ERROR)
         log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
           log_msg);
       if (rc == ERROR)
         log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
           log_msg);
@@ -3372,11 +3430,10 @@ while (done <= 0)
       break;
       }
 
       break;
       }
 
-    log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_data,
+    log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_command_argument,
       host_and_ident(FALSE));
 
       host_and_ident(FALSE));
 
-    rc = acl_check(ACL_WHERE_ETRN, smtp_data, acl_smtp_etrn, &user_msg,
-      &log_msg);
+    rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, &user_msg, &log_msg);
     if (rc != OK)
       {
       done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg);
     if (rc != OK)
       {
       done = smtp_handle_acl_fail(ACL_WHERE_ETRN, rc, user_msg, log_msg);
@@ -3385,7 +3442,7 @@ while (done <= 0)
 
     /* Compute the serialization key for this command. */
 
 
     /* Compute the serialization key for this command. */
 
-    etrn_serialize_key = string_sprintf("etrn-%s\n", smtp_data);
+    etrn_serialize_key = string_sprintf("etrn-%s\n", smtp_command_argument);
 
     /* If a command has been specified for running as a result of ETRN, we
     permit any argument to ETRN. If not, only the # standard form is permitted,
 
     /* If a command has been specified for running as a result of ETRN, we
     permit any argument to ETRN. If not, only the # standard form is permitted,
@@ -3397,7 +3454,7 @@ while (done <= 0)
       uschar *error;
       BOOL rc;
       etrn_command = smtp_etrn_command;
       uschar *error;
       BOOL rc;
       etrn_command = smtp_etrn_command;
-      deliver_domain = smtp_data;
+      deliver_domain = smtp_command_argument;
       rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL,
         US"ETRN processing", &error);
       deliver_domain = NULL;
       rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL,
         US"ETRN processing", &error);
       deliver_domain = NULL;
@@ -3414,7 +3471,7 @@ while (done <= 0)
 
     else
       {
 
     else
       {
-      if (*smtp_data++ != '#')
+      if (*smtp_command_argument++ != '#')
         {
         done = synprot_error(L_smtp_syntax_error, 501, NULL,
           US"argument must begin with #");
         {
         done = synprot_error(L_smtp_syntax_error, 501, NULL,
           US"argument must begin with #");
@@ -3422,7 +3479,7 @@ while (done <= 0)
         }
       etrn_command = US"exim -R";
       argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R",
         }
       etrn_command = US"exim -R";
       argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R",
-        smtp_data);
+        smtp_command_argument);
       }
 
     /* If we are host-testing, don't actually do anything. */
       }
 
     /* If we are host-testing, don't actually do anything. */
@@ -3444,7 +3501,7 @@ while (done <= 0)
 
     if (smtp_etrn_serialize && !enq_start(etrn_serialize_key))
       {
 
     if (smtp_etrn_serialize && !enq_start(etrn_serialize_key))
       {
-      smtp_printf("458 Already processing %s\r\n", smtp_data);
+      smtp_printf("458 Already processing %s\r\n", smtp_command_argument);
       break;
       }
 
       break;
       }
 
@@ -3459,9 +3516,9 @@ while (done <= 0)
 
     if ((pid = fork()) == 0)
       {
 
     if ((pid = fork()) == 0)
       {
-      smtp_input = FALSE;    /* This process is not associated with the */
-      fclose(smtp_in);       /* SMTP call any more. */
-      fclose(smtp_out);
+      smtp_input = FALSE;       /* This process is not associated with the */
+      (void)fclose(smtp_in);    /* SMTP call any more. */
+      (void)fclose(smtp_out);
 
       signal(SIGCHLD, SIG_DFL);      /* Want to catch child */
 
 
       signal(SIGCHLD, SIG_DFL);      /* Want to catch child */
 
@@ -3538,8 +3595,7 @@ while (done <= 0)
     if (c > 150) c = 150;
     smtp_inptr[c] = 0;
     incomplete_transaction_log(US"sync failure");
     if (c > 150) c = 150;
     smtp_inptr[c] = 0;
     incomplete_transaction_log(US"sync failure");
-    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol violation: "
-      "synchronization error "
+    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
       "(next input sent too soon: pipelining was%s advertised): "
       "rejected \"%s\" %s next input=\"%s\"",
       pipelining_advertised? "" : " not",
       "(next input sent too soon: pipelining was%s advertised): "
       "rejected \"%s\" %s next input=\"%s\"",
       pipelining_advertised? "" : " not",
@@ -3554,7 +3610,7 @@ while (done <= 0)
     incomplete_transaction_log(US"too many non-mail commands");
     log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
       "nonmail commands (last was \"%.*s\")",  host_and_ident(FALSE),
     incomplete_transaction_log(US"too many non-mail commands");
     log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
       "nonmail commands (last was \"%.*s\")",  host_and_ident(FALSE),
-      smtp_data - cmd_buffer, cmd_buffer);
+      smtp_command_argument - cmd_buffer, cmd_buffer);
     smtp_printf("554 Too many nonmail commands\r\n");
     done = 1;   /* Pretend eof - drops connection */
     break;
     smtp_printf("554 Too many nonmail commands\r\n");
     done = 1;   /* Pretend eof - drops connection */
     break;