Routing: fix $verify_mode to be usable in routers
[users/jgh/exim.git] / src / src / verify.c
index 2634e755275b92b28aeb49193391c8dad42e4155..69ed33e23c73a6ffade874c416ab677925d8f5d5 100644 (file)
@@ -174,9 +174,7 @@ dbdata_callout_cache new_domain_record;
 dbdata_callout_cache_address new_address_record;
 host_item *host;
 time_t callout_start_time;
-#ifdef SUPPORT_I18N
-BOOL utf8_offered = FALSE;
-#endif
+uschar peer_offered = 0;
 
 new_domain_record.result = ccache_unknown;
 new_domain_record.postmaster_result = ccache_unknown;
@@ -542,6 +540,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
     uschar inbuffer[4096];
     uschar outbuffer[1024];
     uschar responsebuffer[4096];
+    uschar * size_str;
 
     clearflag(addr, af_verify_pmfail);  /* postmaster callout flag */
     clearflag(addr, af_verify_nsfail);  /* null sender callout flag */
@@ -641,12 +640,14 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
 
       if (host->dnssec == DS_YES)
        {
-       if(  (  dane_required
-            || verify_check_given_host(&ob->hosts_try_dane, host) == OK
-            )
-         && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK
+       if(  dane_required
+         || verify_check_given_host(&ob->hosts_try_dane, host) == OK
          )
-         return rc;
+         {
+         if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required)) != OK)
+           return rc;
+         dane = TRUE;
+         }
        }
       else if (dane_required)
        {
@@ -711,7 +712,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
 #ifdef SUPPORT_TLS
     if (smtps  &&  tls_out.active < 0) /* ssl-on-connect, first pass */
       {
-      tls_offered = TRUE;
+      peer_offered &= ~PEER_OFFERED_TLS;
       ob->tls_tempfail_tryclear = FALSE;
       }
     else                               /* all other cases */
@@ -730,27 +731,40 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
          goto RESPONSE_FAILED;
          }
 #ifdef SUPPORT_TLS
-        tls_offered = FALSE;
+       peer_offered &= ~PEER_OFFERED_TLS;
 #endif
         esmtp = FALSE;
         goto esmtp_retry;                      /* fallback to HELO */
         }
 
       /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
-#ifdef SUPPORT_TLS
-      if (esmtp && !suppress_tls &&  tls_out.active < 0)
-       {
-       if (regex_STARTTLS == NULL) regex_STARTTLS =
-         regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
 
-       tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer,
-                     Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0;
-       }
-      else
-        tls_offered = FALSE;
+      peer_offered = esmtp
+       ? ehlo_response(responsebuffer, sizeof(responsebuffer),
+               (!suppress_tls && tls_out.active < 0 ? PEER_OFFERED_TLS : 0)
+             | 0       /* no IGNQ */
+             | 0       /* no PRDR */
+#ifdef SUPPORT_I18N
+             | (addr->prop.utf8_msg && !addr->prop.utf8_downcvt
+               ? PEER_OFFERED_UTF8 : 0)
 #endif
+             | 0       /* no DSN */
+             | 0       /* no PIPE */
+
+             /* only care about SIZE if we have size from inbound */
+             | (message_size > 0 && ob->size_addition >= 0
+               ? PEER_OFFERED_SIZE : 0)
+           )
+       : 0;
       }
 
+    size_str = peer_offered & PEER_OFFERED_SIZE
+      ? string_sprintf(" SIZE=%d", message_size + ob->size_addition) : US"";
+
+#ifdef SUPPORT_TLS
+    tls_offered = !!(peer_offered & PEER_OFFERED_TLS);
+#endif
+
     /* If TLS is available on this connection attempt to
     start up a TLS session, unless the host is in hosts_avoid_tls. If successful,
     send another EHLO - the server may give a different answer in secure mode. We
@@ -760,7 +774,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
     for error analysis. */
 
 #ifdef SUPPORT_TLS
-    if (  tls_offered
+    if (  peer_offered & PEER_OFFERED_TLS
        && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK
        && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK
        )
@@ -780,8 +794,10 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
       if (!smtps && !smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
                        ob->command_timeout))
         {
-        if (errno != 0 || buffer2[0] == 0 ||
-               (buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
+        if (  errno != 0
+          || buffer2[0] == 0
+          || buffer2[0] == '4' && !ob->tls_tempfail_tryclear
+          )
          {
          Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
          done= FALSE;
@@ -815,24 +831,10 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
            (void) event_raise(addr->transport->event_action,
                                    US"tcp:close", NULL);
 # endif
-# ifdef EXPERIMENTAL_DANE
-           if (dane)
-             {
-             if (!dane_required)
-               {
-               log_write(0, LOG_MAIN, "DANE attempt failed;"
-                 " trying CA-root TLS to %s [%s] (not in hosts_require_dane)",
-                 host->name, host->address);
-               dane = FALSE;
-               goto tls_negotiate;
-               }
-             }
-           else
-# endif
-             if (  ob->tls_tempfail_tryclear
-                && !smtps
-                && verify_check_given_host(&ob->hosts_require_tls, host) != OK
-                )
+           if (  ob->tls_tempfail_tryclear
+              && !smtps
+              && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+              )
              {
              log_write(0, LOG_MAIN, "TLS session failure:"
                " delivering unencrypted to %s [%s] (not in hosts_require_tls)",
@@ -875,8 +877,9 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
         log_write(0, LOG_MAIN,
          "H=%s [%s]: a TLS session is required for this host, but %s",
           host->name, host->address,
-         tls_offered ? "an attempt to start TLS failed"
-                     : "the server did not offer TLS support");
+         peer_offered & PEER_OFFERED_TLS
+         ? "an attempt to start TLS failed"
+         : "the server did not offer TLS support");
         done= FALSE;
         goto TLS_FAILED;
         }
@@ -885,8 +888,6 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
 
     done = TRUE; /* so far so good; have response to HELO */
 
-    /*XXX the EHLO response would be analyzed here for IGNOREQUOTA, SIZE, PIPELINING */
-
     /* For now, transport_filter by cutthrough-delivery is not supported */
     /* Need proper integration with the proper transport mechanism. */
     if (cutthrough.delivery)
@@ -927,17 +928,8 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
 #ifdef SUPPORT_I18N
     else if (  addr->prop.utf8_msg
            && !addr->prop.utf8_downcvt
-           && !(  esmtp
-               && (  regex_UTF8
-                  || ( (regex_UTF8 = regex_must_compile(
-                         US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE)),
-                     TRUE
-                  )  )
-               && (  (utf8_offered = pcre_exec(regex_UTF8, NULL,
-                           CS responsebuffer, Ustrlen(responsebuffer),
-                           0, PCRE_EOPT, NULL, 0) >= 0)
-                  || addr->prop.utf8_downcvt_maybe
-           )   )  )
+           && !(peer_offered & PEER_OFFERED_UTF8)
+           )
       {
       HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n");
       errno = ERRNO_UTF8_FWD;
@@ -945,7 +937,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
       done = FALSE;
       }
     else if (  addr->prop.utf8_msg
-           && (addr->prop.utf8_downcvt || !utf8_offered)
+           && (addr->prop.utf8_downcvt || !(peer_offered & PEER_OFFERED_UTF8))
            && (setflag(addr, af_utf8_downcvt),
                from_address = string_address_utf8_to_alabel(from_address,
                                      &addr->message),
@@ -978,11 +970,11 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
         (smtp_write_command(&outblock, FALSE,
 #ifdef SUPPORT_I18N
          addr->prop.utf8_msg && !addr->prop.utf8_downcvt
-         ? "MAIL FROM:<%s>%s SMTPUTF8\r\n"
+         ? "MAIL FROM:<%s>%s%s SMTPUTF8\r\n"
          :
 #endif
-           "MAIL FROM:<%s>%s\r\n",
-          from_address, responsebuffer) >= 0)
+           "MAIL FROM:<%s>%s%s\r\n",
+          from_address, responsebuffer, size_str) >= 0)
       )  &&
 
       smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
@@ -1902,7 +1894,7 @@ if (address[0] == 0) return OK;
 
 /* Flip the legacy TLS-related variables over to the outbound set in case
 they're used in the context of a transport used by verification. Reset them
-at exit from this routine. */
+at exit from this routine (so no returns allowed from here on). */
 
 tls_modify_variables(&tls_out);
 
@@ -1911,6 +1903,10 @@ while verifying a sender address (a nice bit of self-reference there). */
 
 save_sender = sender_address;
 
+/* Observability variable for router/transport use */
+
+verify_mode = is_recipient ? US"R" : US"S";
+
 /* Update the address structure with the possibly qualified and rewritten
 address. Set it up as the starting address on the chain of new addresses. */
 
@@ -1926,7 +1922,7 @@ If an address generates more than one child, the loop is used only when
 full_info is set, and this can only be set locally. Remote enquiries just get
 information about the top level address, not anything that it generated. */
 
-while (addr_new != NULL)
+while (addr_new)
   {
   int rc;
   address_item *addr = addr_new;
@@ -2111,10 +2107,8 @@ while (addr_new != NULL)
 #ifdef SUPPORT_TLS
          deliver_set_expansions(addr);
 #endif
-         verify_mode = is_recipient ? US"R" : US"S";
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
-         verify_mode = NULL;
           }
         }
       else
@@ -2365,6 +2359,7 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
 the -bv or -bt case). */
 
 out:
+verify_mode = NULL;
 tls_modify_variables(&tls_in);
 
 return yield;