Spamd: add missing initialiser. Rspamd mode was incorrectly sometimes seen.
[users/jgh/exim.git] / src / src / smtp_in.c
index 8086e94563b9f0a75dd0ea9559ecaada20fe779b..2f0c25dcd26a8eae9713e709fffb9744ea4e5b7a 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -71,6 +71,7 @@ enum {
   VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */
   ETRN_CMD,                     /* This by analogy with TURN from the RFC */
   STARTTLS_CMD,                 /* Required by the STARTTLS RFC */
+  TLS_AUTH_CMD,                        /* auto-command at start of SSL */
 
   /* This is a dummy to identify the non-sync commands when pipelining */
 
@@ -169,6 +170,7 @@ static smtp_cmd_list cmd_list[] = {
   { "auth",       sizeof("auth")-1,       AUTH_CMD, TRUE,  TRUE  },
   #ifdef SUPPORT_TLS
   { "starttls",   sizeof("starttls")-1,   STARTTLS_CMD, FALSE, FALSE },
+  { "tls_auth",   0,                      TLS_AUTH_CMD, FALSE, TRUE },
   #endif
 
 /* If you change anything above here, also fix the definitions below. */
@@ -192,6 +194,7 @@ static smtp_cmd_list *cmd_list_end =
 #define CMD_LIST_EHLO      2
 #define CMD_LIST_AUTH      3
 #define CMD_LIST_STARTTLS  4
+#define CMD_LIST_TLS_AUTH  5
 
 /* This list of names is used for performing the smtp_no_mail logging action.
 It must be kept in step with the SCH_xxx enumerations. */
@@ -202,7 +205,7 @@ static uschar *smtp_names[] =
   US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS",
   US"VRFY" };
 
-static uschar *protocols[] = {
+static uschar *protocols_local[] = {
   US"local-smtp",        /* HELO */
   US"local-smtps",       /* The rare case EHLO->STARTTLS->HELO */
   US"local-esmtp",       /* EHLO */
@@ -210,12 +213,19 @@ static uschar *protocols[] = {
   US"local-esmtpa",      /* EHLO->AUTH */
   US"local-esmtpsa"      /* EHLO->STARTTLS->EHLO->AUTH */
   };
+static uschar *protocols[] = {
+  US"smtp",              /* HELO */
+  US"smtps",             /* The rare case EHLO->STARTTLS->HELO */
+  US"esmtp",             /* EHLO */
+  US"esmtps",            /* EHLO->STARTTLS->EHLO */
+  US"esmtpa",            /* EHLO->AUTH */
+  US"esmtpsa"            /* EHLO->STARTTLS->EHLO->AUTH */
+  };
 
 #define pnormal  0
 #define pextend  2
 #define pcrpted  1  /* added to pextend or pnormal */
 #define pauthed  2  /* added to pextend */
-#define pnlocal  6  /* offset to remove "local" */
 
 /* Sanity check and validate optional args to MAIL FROM: envelope */
 enum {
@@ -1638,6 +1648,7 @@ while (done <= 0)
     it is the canonical extracted address which is all that is kept. */
 
     case MAIL_CMD:
+    smtp_mailcmd_count++;              /* Count for no-mail log */
     if (sender_address != NULL)
       /* The function moan_smtp_batch() does not return. */
       moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
@@ -1894,7 +1905,7 @@ reset later if any of EHLO/AUTH/STARTTLS are received. */
 
 else
   received_protocol =
-    protocols[pnormal] + ((sender_host_address != NULL)? pnlocal : 0);
+    (sender_host_address ? protocols : protocols_local) [pnormal];
 
 /* Set up the buffer for inputting using direct read() calls, and arrange to
 call the local functions instead of the standard C ones. */
@@ -2457,7 +2468,7 @@ if (++synprot_error_count > smtp_max_synprot_errors)
   yield = 1;
   log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
     "syntax or protocol errors (last command was \"%s\")",
-    host_and_ident(FALSE), smtp_cmd_buffer);
+    host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
   }
 
 if (code > 0)
@@ -2996,29 +3007,25 @@ else
 
   /* 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)
+  if (sender_host_name)
+    if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0))
       {
+      sender_helo_dnssec = sender_host_dnssec;
       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)
+      while (*aliases)
+        if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0))
+         {
+         sender_helo_dnssec = sender_host_dnssec;
+         break;
+         }
+
+      HDEBUG(D_receive) if (helo_verified)
           debug_printf("matched alias %s\n", *(--aliases));
-        }
       }
-    }
 
   /* Final attempt: try a forward lookup of the helo name */
 
@@ -3026,29 +3033,34 @@ else
     {
     int rc;
     host_item h;
+    dnssec_domains d;
+    host_item *hh;
+
     h.name = sender_helo_name;
     h.address = NULL;
     h.mx = MX_NONE;
     h.next = NULL;
+    d.request = US"*";
+    d.require = US"";
+
     HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
       sender_helo_name);
-    rc = host_find_byname(&h, NULL, 0, NULL, TRUE);
+    rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A,
+                         NULL, NULL, NULL, &d, NULL, NULL);
     if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
-      {
-      host_item *hh = &h;
-      while (hh != NULL)
-        {
+      for (hh = &h; hh; hh = hh->next)
         if (Ustrcmp(hh->address, sender_host_address) == 0)
           {
           helo_verified = TRUE;
+         if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE;
           HDEBUG(D_receive)
-            debug_printf("IP address for %s matches calling address\n",
-              sender_helo_name);
+           {
+            debug_printf("IP address for %s matches calling address\n"
+             "Forward DNS security status: %sverified\n",
+              sender_helo_name, sender_helo_dnssec ? "" : "un");
+           }
           break;
           }
-        hh = hh->next;
-        }
-      }
     }
   }
 
@@ -3085,6 +3097,113 @@ smtp_respond(code, len, TRUE, user_msg);
 
 
 
+static int
+smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss)
+{
+const uschar *set_id = NULL;
+int rc, i;
+
+/* Run the checking code, passing the remainder of the command line as
+data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
+it as the only set numerical variable. The authenticator may set $auth<n>
+and also set other numeric variables. The $auth<n> variables are preferred
+nowadays; the numerical variables remain for backwards compatibility.
+
+Afterwards, have a go at expanding the set_id string, even if
+authentication failed - for bad passwords it can be useful to log the
+userid. On success, require set_id to expand and exist, and put it in
+authenticated_id. Save this in permanent store, as the working store gets
+reset at HELO, RSET, etc. */
+
+for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
+expand_nmax = 0;
+expand_nlength[0] = 0;   /* $0 contains nothing */
+
+rc = (au->info->servercode)(au, smtp_cmd_data);
+if (au->set_id) set_id = expand_string(au->set_id);
+expand_nmax = -1;        /* Reset numeric variables */
+for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;   /* Reset $auth<n> */
+
+/* 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) set_id = string_printing(set_id);
+
+/* For the non-OK cases, set up additional logging data if set_id
+is not empty. */
+
+if (rc != OK)
+  set_id = set_id && *set_id
+    ? string_sprintf(" (set_id=%s)", set_id) : US"";
+
+/* Switch on the result */
+
+switch(rc)
+  {
+  case OK:
+  if (!au->set_id || set_id)    /* Complete success */
+    {
+    if (set_id) authenticated_id = string_copy_malloc(set_id);
+    sender_host_authenticated = au->name;
+    authentication_failed = FALSE;
+    authenticated_fail_id = NULL;   /* Impossible to already be set? */
+
+    received_protocol =
+      (sender_host_address ? protocols : protocols_local)
+       [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)];
+    *s = *ss = US"235 Authentication succeeded";
+    authenticated_by = au;
+    break;
+    }
+
+  /* Authentication succeeded, but we failed to expand the set_id string.
+  Treat this as a temporary error. */
+
+  auth_defer_msg = expand_string_message;
+  /* Fall through */
+
+  case DEFER:
+  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+  *s = string_sprintf("435 Unable to authenticate at present%s",
+    auth_defer_user_msg);
+  *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+    set_id, auth_defer_msg);
+  break;
+
+  case BAD64:
+  *s = *ss = US"501 Invalid base64 data";
+  break;
+
+  case CANCELLED:
+  *s = *ss = US"501 Authentication cancelled";
+  break;
+
+  case UNEXPECTED:
+  *s = *ss = US"553 Initial data not expected";
+  break;
+
+  case FAIL:
+  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+  *s = US"535 Incorrect authentication data";
+  *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+  break;
+
+  default:
+  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+  *s = US"435 Internal error";
+  *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+    "check", set_id, rc);
+  break;
+  }
+
+return rc;
+}
+
+
+
 /*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
@@ -3136,6 +3255,7 @@ cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
 cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
 #ifdef SUPPORT_TLS
 cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
+cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
 #endif
 
 /* Set the local signal handler for SIGTERM - it tries to end off tidily */
@@ -3159,7 +3279,6 @@ while (done <= 0)
   uschar *user_msg = NULL;
   uschar *recipient = NULL;
   uschar *hello = NULL;
-  const uschar *set_id = NULL;
   uschar *s, *ss;
   BOOL was_rej_mail = FALSE;
   BOOL was_rcpt = FALSE;
@@ -3172,6 +3291,41 @@ while (done <= 0)
   uschar *orcpt = NULL;
   int flags;
 
+#if defined(SUPPORT_TLS) && defined(AUTH_TLS)
+  /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
+  if (  tls_in.active >= 0
+     && tls_in.peercert
+     && tls_in.certificate_verified
+     && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
+     )
+    {
+    cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
+    if (acl_smtp_auth)
+      {
+      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);
+        continue;
+        }
+      }
+
+    for (au = auths; au; au = au->next)
+      if (strcmpic(US"tls", au->driver_name) == 0)
+       {
+       smtp_cmd_data = NULL;
+
+       if ((c = smtp_in_auth(au, &s, &ss)) != OK)
+         log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
+           au->name, host_and_ident(FALSE), ss);
+       else
+         DEBUG(D_auth) debug_printf("tls auth succeeded\n");
+
+       break;
+       }
+    }
+#endif
+
   switch(smtp_read_command(TRUE))
     {
     /* The AUTH command is not permitted to occur inside a transaction, and may
@@ -3199,13 +3353,13 @@ while (done <= 0)
         US"AUTH command used when not advertised");
       break;
       }
-    if (sender_host_authenticated != NULL)
+    if (sender_host_authenticated)
       {
       done = synprot_error(L_smtp_protocol_error, 503, NULL,
         US"already authenticated");
       break;
       }
-    if (sender_address != NULL)
+    if (sender_address)
       {
       done = synprot_error(L_smtp_protocol_error, 503, NULL,
         US"not permitted in mail transaction");
@@ -3214,7 +3368,7 @@ while (done <= 0)
 
     /* Check the ACL */
 
-    if (acl_smtp_auth != NULL)
+    if (acl_smtp_auth)
       {
       rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg);
       if (rc != OK)
@@ -3251,121 +3405,23 @@ while (done <= 0)
     as a server and which has been advertised (unless, sigh, allow_auth_
     unadvertised is set). */
 
-    for (au = auths; au != NULL; au = au->next)
-      {
+    for (au = auths; au; au = au->next)
       if (strcmpic(s, au->public_name) == 0 && au->server &&
-          (au->advertised || allow_auth_unadvertised)) break;
-      }
-
-    if (au == NULL)
-      {
-      done = synprot_error(L_smtp_protocol_error, 504, NULL,
-        string_sprintf("%s authentication mechanism not supported", s));
-      break;
-      }
-
-    /* Run the checking code, passing the remainder of the command line as
-    data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
-    it as the only set numerical variable. The authenticator may set $auth<n>
-    and also set other numeric variables. The $auth<n> variables are preferred
-    nowadays; the numerical variables remain for backwards compatibility.
+          (au->advertised || allow_auth_unadvertised))
+       break;
 
-    Afterwards, have a go at expanding the set_id string, even if
-    authentication failed - for bad passwords it can be useful to log the
-    userid. On success, require set_id to expand and exist, and put it in
-    authenticated_id. Save this in permanent store, as the working store gets
-    reset at HELO, RSET, etc. */
-
-    for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
-    expand_nmax = 0;
-    expand_nlength[0] = 0;   /* $0 contains nothing */
-
-    c = (au->info->servercode)(au, smtp_cmd_data);
-    if (au->set_id != NULL) set_id = expand_string(au->set_id);
-    expand_nmax = -1;        /* Reset numeric variables */
-    for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;   /* Reset $auth<n> */
-
-    /* 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. */
-
-    if (c != OK)
-      {
-      if (set_id != NULL && *set_id != 0)
-        set_id = string_sprintf(" (set_id=%s)", set_id);
-      else set_id = US"";
-      }
-
-    /* Switch on the result */
-
-    switch(c)
+    if (au)
       {
-      case OK:
-      if (au->set_id == NULL || set_id != NULL)    /* Complete success */
-        {
-        if (set_id != NULL) authenticated_id = string_copy_malloc(set_id);
-        sender_host_authenticated = au->name;
-        authentication_failed = FALSE;
-        authenticated_fail_id = NULL;   /* Impossible to already be set? */
-        received_protocol =
-          protocols[pextend + pauthed + ((tls_in.active >= 0)? pcrpted:0)] +
-            ((sender_host_address != NULL)? pnlocal : 0);
-        s = ss = US"235 Authentication succeeded";
-        authenticated_by = au;
-        break;
-        }
-
-      /* Authentication succeeded, but we failed to expand the set_id string.
-      Treat this as a temporary error. */
+      c = smtp_in_auth(au, &s, &ss);
 
-      auth_defer_msg = expand_string_message;
-      /* Fall through */
-
-      case DEFER:
-      if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id);
-      s = string_sprintf("435 Unable to authenticate at present%s",
-        auth_defer_user_msg);
-      ss = string_sprintf("435 Unable to authenticate at present%s: %s",
-        set_id, auth_defer_msg);
-      break;
-
-      case BAD64:
-      s = ss = US"501 Invalid base64 data";
-      break;
-
-      case CANCELLED:
-      s = ss = US"501 Authentication cancelled";
-      break;
-
-      case UNEXPECTED:
-      s = ss = US"553 Initial data not expected";
-      break;
-
-      case FAIL:
-      if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id);
-      s = US"535 Incorrect authentication data";
-      ss = string_sprintf("535 Incorrect authentication data%s", set_id);
-      break;
-
-      default:
-      if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id);
-      s = US"435 Internal error";
-      ss = string_sprintf("435 Internal error%s: return %d from authentication "
-        "check", set_id, c);
-      break;
+      smtp_printf("%s\r\n", s);
+      if (c != OK)
+       log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
+         au->name, host_and_ident(FALSE), ss);
       }
-
-    smtp_printf("%s\r\n", s);
-    if (c != OK)
-      log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
-        au->name, host_and_ident(FALSE), ss);
+    else
+      done = synprot_error(L_smtp_protocol_error, 504, NULL,
+        string_sprintf("%s authentication mechanism not supported", s));
 
     break;  /* AUTH_CMD */
 
@@ -3416,7 +3472,7 @@ while (done <= 0)
         {
         log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
           "syntax or protocol errors (last command was \"%s\")",
-          host_and_ident(FALSE), smtp_cmd_buffer);
+          host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
         done = 1;
         }
 
@@ -3461,7 +3517,7 @@ while (done <= 0)
       now obsolescent, since the verification can now be requested selectively
       at ACL time. */
 
-      helo_verified = helo_verify_failed = FALSE;
+      helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE;
       if (helo_required || helo_verify)
         {
         BOOL tempfail = !smtp_verify_helo();
@@ -3651,38 +3707,40 @@ while (done <= 0)
       letters, so output the names in upper case, though we actually recognize
       them in either case in the AUTH command. */
 
-      if (auths != NULL)
-        {
-        if (verify_check_host(&auth_advertise_hosts) == OK)
-          {
-          auth_instance *au;
-          BOOL first = TRUE;
-          for (au = auths; au != NULL; au = au->next)
-            {
-            if (au->server && (au->advertise_condition == NULL ||
-                expand_check_condition(au->advertise_condition, au->name,
-                US"authenticator")))
-              {
-              int saveptr;
-              if (first)
-                {
-                s = string_cat(s, &size, &ptr, smtp_code, 3);
-                s = string_cat(s, &size, &ptr, US"-AUTH", 5);
-                first = FALSE;
-                auth_advertised = TRUE;
-                }
-              saveptr = ptr;
-              s = string_cat(s, &size, &ptr, US" ", 1);
-              s = string_cat(s, &size, &ptr, au->public_name,
-                Ustrlen(au->public_name));
-              while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]);
-              au->advertised = TRUE;
-              }
-            else au->advertised = FALSE;
-            }
-          if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2);
-          }
-        }
+      if (  auths
+#if defined(SUPPORT_TLS) && defined(AUTH_TLS)
+        && !sender_host_authenticated
+#endif
+         && verify_check_host(&auth_advertise_hosts) == OK
+        )
+       {
+       auth_instance *au;
+       BOOL first = TRUE;
+       for (au = auths; au; au = au->next)
+         if (au->server && (au->advertise_condition == NULL ||
+             expand_check_condition(au->advertise_condition, au->name,
+             US"authenticator")))
+           {
+           int saveptr;
+           if (first)
+             {
+             s = string_cat(s, &size, &ptr, smtp_code, 3);
+             s = string_cat(s, &size, &ptr, US"-AUTH", 5);
+             first = FALSE;
+             auth_advertised = TRUE;
+             }
+           saveptr = ptr;
+           s = string_cat(s, &size, &ptr, US" ", 1);
+           s = string_cat(s, &size, &ptr, au->public_name,
+             Ustrlen(au->public_name));
+           while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]);
+           au->advertised = TRUE;
+           }
+         else
+           au->advertised = FALSE;
+
+       if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2);
+       }
 
       /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer)
       if it has been included in the binary, and the host matches
@@ -3746,16 +3804,13 @@ while (done <= 0)
     helo_seen = TRUE;
 
     /* Reset the protocol and the state, abandoning any previous message. */
-
-    received_protocol = (esmtp?
-      protocols[pextend +
-        ((sender_host_authenticated != NULL)? pauthed : 0) +
-        ((tls_in.active >= 0)? pcrpted : 0)]
-      :
-      protocols[pnormal + ((tls_in.active >= 0)? pcrpted : 0)])
-      +
-      ((sender_host_address != NULL)? pnlocal : 0);
-
+    received_protocol =
+      (sender_host_address ? protocols : protocols_local)
+       [ (esmtp
+         ? pextend + (sender_host_authenticated ? pauthed : 0)
+         : pnormal)
+       + (tls_in.active >= 0 ? pcrpted : 0)
+       ];
     smtp_reset(reset_point);
     toomany = FALSE;
     break;   /* HELO/EHLO */
@@ -3828,10 +3883,8 @@ while (done <= 0)
            (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list);
            mail_args++
           )
-        {
         if (strcmpic(name, mail_args->name) == 0)
           break;
-        }
       if (mail_args->need_value && strcmpic(value, US"") == 0)
         break;
 
@@ -3859,16 +3912,17 @@ while (done <= 0)
         and "7BIT" as body types, but take no action. */
         case ENV_MAIL_OPT_BODY:
           if (accept_8bitmime) {
-            if (strcmpic(value, US"8BITMIME") == 0) {
+            if (strcmpic(value, US"8BITMIME") == 0)
               body_8bitmime = 8;
-            } else if (strcmpic(value, US"7BIT") == 0) {
+            else if (strcmpic(value, US"7BIT") == 0)
               body_8bitmime = 7;
-            } else {
+            else
+             {
               body_8bitmime = 0;
               done = synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"invalid data for BODY");
               goto COMMAND_LOOP;
-            }
+              }
             DEBUG(D_receive) debug_printf("8BITMIME: %d\n", body_8bitmime);
            break;
           }
@@ -3880,35 +3934,43 @@ while (done <= 0)
         is included only if configured in at build time. */
 
         case ENV_MAIL_OPT_RET:
-          if (dsn_advertised) {
+          if (dsn_advertised)
+           {
             /* Check if RET has already been set */
-            if (dsn_ret > 0) {
+            if (dsn_ret > 0)
+             {
               synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"RET can be specified once only");
               goto COMMAND_LOOP;
-            }
-            dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs :
-                    (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0;
+             }
+            dsn_ret = strcmpic(value, US"HDRS") == 0
+             ? dsn_ret_hdrs
+             : strcmpic(value, US"FULL") == 0
+             ? dsn_ret_full
+             : 0;
             DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret);
             /* Check for invalid invalid value, and exit with error */
-            if (dsn_ret == 0) {
+            if (dsn_ret == 0)
+             {
               synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"Value for RET is invalid");
               goto COMMAND_LOOP;
-            }
-          }
+             }
+           }
           break;
         case ENV_MAIL_OPT_ENVID:
-          if (dsn_advertised) {
+          if (dsn_advertised)
+           {
             /* Check if the dsn envid has been already set */
-            if (dsn_envid != NULL) {
+            if (dsn_envid != NULL)
+             {
               synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"ENVID can be specified once only");
               goto COMMAND_LOOP;
-            }
+             }
             dsn_envid = string_copy(value);
             DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid);
-          }
+           }
           break;
 
         /* Handle the AUTH extension. If the value given is not "<>" and either
@@ -3948,34 +4010,34 @@ while (done <= 0)
             switch (rc)
               {
               case OK:
-              if (authenticated_by == NULL ||
-                  authenticated_by->mail_auth_condition == NULL ||
-                  expand_check_condition(authenticated_by->mail_auth_condition,
-                      authenticated_by->name, US"authenticator"))
-                break;     /* Accept the AUTH */
-  
-              ignore_msg = US"server_mail_auth_condition failed";
-              if (authenticated_id != NULL)
-                ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"",
-                  ignore_msg, authenticated_id);
+               if (authenticated_by == NULL ||
+                   authenticated_by->mail_auth_condition == NULL ||
+                   expand_check_condition(authenticated_by->mail_auth_condition,
+                       authenticated_by->name, US"authenticator"))
+                 break;     /* Accept the AUTH */
+    
+               ignore_msg = US"server_mail_auth_condition failed";
+               if (authenticated_id != NULL)
+                 ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"",
+                   ignore_msg, authenticated_id);
   
               /* Fall through */
   
               case FAIL:
-              authenticated_sender = NULL;
-              log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)",
-                value, host_and_ident(TRUE), ignore_msg);
-              break;
+               authenticated_sender = NULL;
+               log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)",
+                 value, host_and_ident(TRUE), ignore_msg);
+               break;
   
               /* Should only get DEFER or ERROR here. Put back terminator
               overrides for error message */
   
               default:
-              value[-1] = '=';
-              name[-1] = ' ';
-              (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg,
-                log_msg);
-              goto COMMAND_LOOP;
+               value[-1] = '=';
+               name[-1] = ' ';
+               (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg,
+                 log_msg);
+               goto COMMAND_LOOP;
               }
             }
             break;
@@ -3990,7 +4052,11 @@ while (done <= 0)
 #ifdef EXPERIMENTAL_INTERNATIONAL
         case ENV_MAIL_OPT_UTF8:
          if (smtputf8_advertised)
-           message_smtputf8 = TRUE;
+           {
+           DEBUG(D_receive) debug_printf("smtputf8 requested\n");
+           message_smtputf8 = allow_utf8_domains = TRUE;
+           received_protocol = string_sprintf("utf8%s", received_protocol);
+           }
          break;
 #endif
         /* Unknown option. Stick back the terminator characters and break
@@ -4025,9 +4091,10 @@ while (done <= 0)
     /* Now extract the address, first applying any SMTP-time rewriting. The
     TRUE flag allows "<>" as a sender address. */
 
-    raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
-      rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
-        global_rewrite_rules) : smtp_cmd_data;
+    raw_sender = rewrite_existflags & rewrite_smtp
+      ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+                   global_rewrite_rules)
+      : smtp_cmd_data;
 
     /* rfc821_domains = TRUE; << no longer needed */
     raw_sender =
@@ -4392,16 +4459,12 @@ while (done <= 0)
       receive_add_recipient(recipient, -1);
  
       /* Set the dsn flags in the recipients_list */
-      if (orcpt != NULL)
-        recipients_list[recipients_count-1].orcpt = orcpt;
-      else
-        recipients_list[recipients_count-1].orcpt = NULL;
+      recipients_list[recipients_count-1].orcpt = orcpt;
+      recipients_list[recipients_count-1].dsn_flags = flags;
 
-      if (flags != 0)
-        recipients_list[recipients_count-1].dsn_flags = flags;
-      else
-        recipients_list[recipients_count-1].dsn_flags = 0;
-      DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags);
+      DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n",
+       recipients_list[recipients_count-1].orcpt,
+       recipients_list[recipients_count-1].dsn_flags);
       }
 
     /* The recipient was discarded */
@@ -4412,13 +4475,11 @@ while (done <= 0)
         else smtp_user_msg(US"250", user_msg);
       rcpt_fail_count++;
       discarded = TRUE;
-      log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: "
+      log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
         "discarded by %s ACL%s%s", host_and_ident(TRUE),
-        (sender_address_unrewritten != NULL)?
-        sender_address_unrewritten : sender_address,
+        sender_address_unrewritten? sender_address_unrewritten : sender_address,
         smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT",
-        (log_msg == NULL)? US"" : US": ",
-        (log_msg == NULL)? US"" : log_msg);
+        log_msg ? US": " : US"", log_msg ? log_msg : US"");
       }
 
     /* Either the ACL failed the address, or it was deferred. */
@@ -4654,6 +4715,7 @@ while (done <= 0)
         helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE;
       cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
       cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
+      cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
       if (sender_helo_name != NULL)
         {
         store_free(sender_helo_name);
@@ -4662,13 +4724,13 @@ while (done <= 0)
         set_process_info("handling incoming TLS connection from %s",
           host_and_ident(FALSE));
         }
-      received_protocol = (esmtp?
-        protocols[pextend + pcrpted +
-          ((sender_host_authenticated != NULL)? pauthed : 0)]
-        :
-        protocols[pnormal + pcrpted])
-        +
-        ((sender_host_address != NULL)? pnlocal : 0);
+      received_protocol =
+       (sender_host_address ? protocols : protocols_local)
+         [ (esmtp
+           ? pextend + (sender_host_authenticated ? pauthed : 0)
+           : pnormal)
+         + (tls_in.active >= 0 ? pcrpted : 0)
+         ];
 
       sender_host_authenticated = NULL;
       authenticated_id = NULL;
@@ -5053,7 +5115,7 @@ while (done <= 0)
       done = 2;
       log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
         "unrecognized commands (last was \"%s\")", host_and_ident(FALSE),
-        smtp_cmd_buffer);
+        string_printing(smtp_cmd_buffer));
       }
     else
       done = synprot_error(L_smtp_syntax_error, 500, NULL,