Hintsdb transactions
[exim.git] / src / src / transports / smtp.c
index ed5994241c0530e0763f049afb718abd9666cadf..172ee3445cde87762e194c161632bb9bffa8f7b4 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2023 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* SPDX-License-Identifier: GPL-2.0-or-later */
@@ -668,12 +668,15 @@ deferred_event_raise(address_item * addr, host_item * host, uschar * evstr)
 {
 uschar * action = addr->transport->event_action;
 const uschar * save_domain, * save_local;
+uschar * save_rn, * save_tn;
 
 if (!action)
   return;
 
 save_domain = deliver_domain;
 save_local = deliver_localpart;
+save_rn = router_name;
+save_tn = transport_name;
 
 /*XXX would ip & port already be set up? */
 deliver_host_address = string_copy(host->address);
@@ -697,7 +700,8 @@ deliver_localpart = addr->local_part;
 
 deliver_localpart = save_local;
 deliver_domain =    save_domain;
-router_name = transport_name = NULL;
+router_name = save_rn;
+router_name = save_tn;
 }
 #endif
 
@@ -945,7 +949,8 @@ if (  sx->early_pipe_active
     if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp))))
       debug_printf("no ehlo-resp record!\n");
     else
-      debug_printf("ehlo-resp record is %d seconds old\n", time(NULL) - er->time_stamp);
+      debug_printf("ehlo-resp record is %.0f seconds old\n",
+                   difftime(time(NULL), er->time_stamp));
     }
 
   dbfn_delete(dbm_file, ehlo_resp_key);
@@ -1086,7 +1091,7 @@ if (pending_BANNER)
     }
   /*XXX EXPERIMENTAL_ESMTP_LIMITS ? */
 
-# ifndef DISABLE_TLS_RESUME
+# if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME)
   GET_OPTION("host_name_extract");
   s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract;
   if (!s) s = HNE_DEFAULT;
@@ -1306,7 +1311,7 @@ while (count-- > 0)
 
     if (testflag(addr, af_dr_retry_exists))
       {
-      uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
+      uschar * altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
         sender_address);
       retry_add_item(addr, altkey, rf_delete);
       retry_add_item(addr, addr->address_retry_key, rf_delete);
@@ -1322,7 +1327,7 @@ while (count-- > 0)
 
   else if (errno == ETIMEDOUT)
     {
-    uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
+    uschar * message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
                transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
     set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE, &sx->delivery_start);
     retry_add_item(addr, addr->address_retry_key, 0);
@@ -2630,7 +2635,7 @@ goto SEND_QUIT;
          }
        }
 #endif
-#ifndef DISABLE_TLS_RESUME
+#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME)
       GET_OPTION("host_name_extract");
       if (!(s = ob->host_name_extract)) s = HNE_DEFAULT;
       ehlo_response_lbserver(sx, s);
@@ -3425,7 +3430,7 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop))
 
 /* Send MAIL FROM and RCPT TO commands.
 See sw_mrc_t definition for return codes.
- */
+*/
 
 sw_mrc_t
 smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield)
@@ -3795,7 +3800,6 @@ int save_errno;
 int rc;
 
 uschar *message = NULL;
-uschar new_message_id[MESSAGE_ID_LENGTH + 1];
 smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED);       /* tainted, for the data buffers */
 BOOL pass_message = FALSE;
 #ifndef DISABLE_ESMTP_LIMITS
@@ -3804,9 +3808,10 @@ BOOL mail_limit = FALSE;
 #ifdef SUPPORT_DANE
 BOOL dane_held;
 #endif
-BOOL tcw_done = FALSE, tcw = FALSE;
+BOOL tcw_done = FALSE, tcw = FALSE, passback_tcw = FALSE;
 
 *message_defer = FALSE;
+continue_next_id[0] = '\0';
 
 memset(sx, 0, sizeof(*sx));
 sx->addrlist = addrlist;
@@ -4127,7 +4132,7 @@ else
         &&
 #endif
            transport_check_waiting(tblock->name, host->name,
-             tblock->connection_max_messages, new_message_id,
+             tblock->connection_max_messages, continue_next_id,
             (oicf)smtp_are_same_identities, (void*)&t_compare);
     if (!tcw)
       {
@@ -4687,6 +4692,11 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
     {
     DEBUG(D_transport)
       debug_printf("reached limit %u for MAILs per conn\n", sx->max_mail);
+    /* We will close the smtp session and connection, and clear
+    continue_hostname.  Then if there are further addrs for the message we will
+    loop to the top of this function and make a fresh connection.  Any further
+    message found in the wait-tpt hintsdb would then do a transport_pass_socket
+    to get the connection fd back to the delivery process. */
     }
   else
 #endif
@@ -4694,9 +4704,9 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
     smtp_compare_t t_compare =
       {.tblock = tblock, .current_sender_address = sender_address};
 
-    if (  sx->first_addr                       /* more addrs for this message */
-       || f.continue_more                      /* more addrs for continued-host */
-       || tcw_done && tcw                      /* more messages for host */
+    if (  sx->first_addr               /* more addrs for this message */
+       || f.continue_more              /* more addrs for continued-host */
+       || tcw_done && tcw              /* more messages for host */
        || (
 #ifndef DISABLE_TLS
             (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
@@ -4705,7 +4715,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
          &&
 #endif
             transport_check_waiting(tblock->name, host->name,
-              sx->max_mail, new_message_id,
+              sx->max_mail, continue_next_id,
               (oicf)smtp_are_same_identities, (void*)&t_compare)
        )  )
       {
@@ -4756,6 +4766,20 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
          goto SEND_MESSAGE;
          }
 
+       /* If there is a next-message-id from the wait-transport hintsdb,
+       pretend caller said it has further message for us.  Note that we lose
+       the TLS session (below), and that our caller will pass back the id to
+       the delivery process.  If not, remember to later cancel the
+       next-message-id so that the transport-caller code (in deliver.c) does
+       not report it back up the pipe to the delivery process.
+       XXX It would be feasible to also report the other continue_* with the
+       _id - taking out the exec for the first continued-transport. But the
+       actual conn, and it's fd, is a problem. Maybe replace the transport
+       pipe with a unix-domain socket? */
+
+       if (!f.continue_more && continue_hostname && *continue_next_id)
+         f.continue_more = passback_tcw = TRUE;
+
        /* Unless caller said it already has more messages listed for this host,
        pass the connection on to a new Exim process (below, the call to
        transport_pass_socket).  If the caller has more ready, just return with
@@ -4817,7 +4841,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
   propagate it from the initial
   */
        if (sx->ok && transport_pass_socket(tblock->name, host->name,
-             host->address, new_message_id, socket_fd
+             host->address, continue_next_id, socket_fd
 #ifndef DISABLE_ESMTP_LIMITS
              , sx->peer_limit_mail, sx->peer_limit_rcpt, sx->peer_limit_rcptdom
 #endif
@@ -4850,8 +4874,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
              sx->cctx.tls_ctx = NULL;
              (void)close(sx->cctx.sock);
              sx->cctx.sock = -1;
-             continue_transport = NULL;
-             continue_hostname = NULL;
+             continue_transport = continue_hostname = NULL;
              goto TIDYUP;
              }
            log_write(0, LOG_PANIC_DIE, "fork failed");
@@ -4963,7 +4986,6 @@ if (sx->send_quit || tcw_done && !tcw)
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 (void)close(sx->cctx.sock);
 sx->cctx.sock = -1;
-continue_transport = NULL;
 continue_hostname = NULL;
 smtp_debug_cmd_report();
 
@@ -5005,19 +5027,21 @@ if (mail_limit && sx->first_addr)
   continue_sequence = 1;                       /* for consistency */
   clearflag(sx->first_addr, af_cont_conn);
   setflag(sx->first_addr, af_new_conn);                /* clear  * from logging */
-  goto REPEAT_CONN;
+  goto REPEAT_CONN;                            /* open a fresh connection */
   }
 #endif
 
-return yield;
+OUT:
+  if (!passback_tcw) continue_next_id[0] = '\0';
+  return yield;
 
 TIDYUP:
 #ifdef SUPPORT_DANE
-if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next)
-  if (a->transport_return == DANE)
-    a->transport_return = PENDING_DEFER;
+  if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next)
+    if (a->transport_return == DANE)
+      a->transport_return = PENDING_DEFER;
 #endif
-return yield;
+  goto OUT;
 }
 
 
@@ -5376,7 +5400,7 @@ retry_non_continued:
     BOOL host_is_expired = FALSE, message_defer = FALSE, some_deferred = FALSE;
     address_item * first_addr = NULL;
     uschar * interface = NULL;
-    uschar * retry_host_key = NULL, * retry_message_key = NULL;
+    const uschar * retry_host_key = NULL, * retry_message_key = NULL;
     uschar * serialize_key = NULL;
 
     /* Deal slightly better with a possible Linux kernel bug that results
@@ -5873,9 +5897,7 @@ retry_non_continued:
                  ob->expand_retry_include_ip_address, &incl_ip) != OK)
          incl_ip = TRUE;       /* error; use most-specific retry record */
 
-        retry_host_key = incl_ip
-         ? string_sprintf("T:%S:%s%s", host->name, host->address, pistring)
-         : string_sprintf("T:%S%s", host->name, pistring);
+        retry_host_key = retry_host_key_build(host, incl_ip, pistring);
         }
 
       /* If a delivery of another message over an existing SMTP connection
@@ -5921,10 +5943,8 @@ retry_non_continued:
                  ob->expand_retry_include_ip_address, &incl_ip) != OK)
          incl_ip = TRUE;       /* error; use most-specific retry record */
 
-        retry_message_key = incl_ip
-         ? string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring,
-             message_id)
-         : string_sprintf("T:%S%s:%s", host->name, pistring, message_id);
+        retry_message_key = string_sprintf("%s:%s",
+         retry_host_key_build(host, incl_ip, pistring), message_id);
         }
       retry_add_item(addrlist, retry_message_key,
         rf_message | rf_host | delete_flag);