+if (dbm_file) dbfn_close(dbm_file);
+}
+
+
+/* Cutthrough-multi. If the existing cached cutthrough connection matches
+the one we would make for a subsequent recipient, use it. Send the RCPT TO
+and check the result, nonpipelined as it may be wanted immediately for
+recipient-verification.
+
+It seems simpler to deal with this case separately from the main callout loop.
+We will need to remember it has sent, or not, so that rcpt-acl tail code
+can do it there for the non-rcpt-verify case. For this we keep an addresscount.
+
+Return: TRUE for a definitive result for the recipient
+*/
+static int
+cutthrough_multi(address_item * addr, host_item * host_list,
+ transport_feedback * tf, int * yield)
+{
+BOOL done = FALSE;
+host_item * host;
+
+if (addr->transport == cutthrough.addr.transport)
+ for (host = host_list; host; host = host->next)
+ if (Ustrcmp(host->address, cutthrough.host.address) == 0)
+ {
+ int host_af;
+ uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
+ int port = 25;
+
+ deliver_host = host->name;
+ deliver_host_address = host->address;
+ deliver_host_port = host->port;
+ deliver_domain = addr->domain;
+ transport_name = addr->transport->name;
+
+ host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+
+ if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
+ US"callout") ||
+ !smtp_get_port(tf->port, addr, &port, US"callout"))
+ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
+ addr->message);
+
+ if ( ( interface == cutthrough.interface
+ || ( interface
+ && cutthrough.interface
+ && Ustrcmp(interface, cutthrough.interface) == 0
+ ) )
+ && port == cutthrough.host.port
+ )
+ {
+ uschar * resp = NULL;
+
+ /* Match! Send the RCPT TO, set done from the response */
+ done =
+ smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
+ transport_rcpt_address(addr,
+ addr->transport->rcpt_include_affixes)) >= 0 &&
+ cutthrough_response('2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
+
+ /* This would go horribly wrong if a callout fail was ignored by ACL.
+ We punt by abandoning cutthrough on a reject, like the
+ first-rcpt does. */
+
+ if (done)
+ {
+ address_item * na = store_get(sizeof(address_item));
+ *na = cutthrough.addr;
+ cutthrough.addr = *addr;
+ cutthrough.addr.host_used = &cutthrough.host;
+ cutthrough.addr.next = na;
+
+ cutthrough.nrcpt++;
+ }
+ else
+ {
+ cancel_cutthrough_connection("recipient rejected");
+ if (!resp || errno == ETIMEDOUT)
+ {
+ HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+ }
+ else if (errno == 0)
+ {
+ if (*resp == 0)
+ Ustrcpy(resp, US"connection dropped");
+
+ addr->message =
+ string_sprintf("response to \"%s\" was: %s",
+ big_buffer, string_printing(resp));
+
+ addr->user_message =
+ string_sprintf("Callout verification failed:\n%s", resp);
+
+ /* Hard rejection ends the process */
+
+ if (resp[0] == '5') /* Address rejected */
+ {
+ *yield = FAIL;
+ done = TRUE;
+ }
+ }
+ }
+ }
+ break; /* host_list */
+ }
+if (!done)
+ cancel_cutthrough_connection("incompatible connection");
+return done;
+}
+
+
+/*************************************************
+* Do callout verification for an address *
+*************************************************/
+
+/* This function is called from verify_address() when the address has routed to
+a host list, and a callout has been requested. Callouts are expensive; that is
+why a cache is used to improve the efficiency.
+
+Arguments:
+ addr the address that's been routed
+ host_list the list of hosts to try
+ tf the transport feedback block
+
+ ifstring "interface" option from transport, or NULL
+ portstring "port" option from transport, or NULL
+ protocolstring "protocol" option from transport, or NULL
+ callout the per-command callout timeout
+ callout_overall the overall callout timeout (if < 0 use 4*callout)
+ callout_connect the callout connection timeout (if < 0 use callout)
+ options the verification options - these bits are used:
+ vopt_is_recipient => this is a recipient address
+ vopt_callout_no_cache => don't use callout cache
+ vopt_callout_fullpm => if postmaster check, do full one
+ vopt_callout_random => do the "random" thing
+ vopt_callout_recipsender => use real sender for recipient
+ vopt_callout_recippmaster => use postmaster for recipient
+ se_mailfrom MAIL FROM address for sender verify; NULL => ""
+ pm_mailfrom if non-NULL, do the postmaster check with this sender
+
+Returns: OK/FAIL/DEFER
+*/
+
+static int
+do_callout(address_item *addr, host_item *host_list, transport_feedback *tf,
+ int callout, int callout_overall, int callout_connect, int options,
+ uschar *se_mailfrom, uschar *pm_mailfrom)
+{
+int yield = OK;
+int old_domain_cache_result = ccache_accept;
+BOOL done = FALSE;
+uschar *address_key;
+uschar *from_address;
+uschar *random_local_part = NULL;
+const uschar *save_deliver_domain = deliver_domain;
+uschar **failure_ptr = options & vopt_is_recipient
+ ? &recipient_verify_failure : &sender_verify_failure;
+dbdata_callout_cache new_domain_record;
+dbdata_callout_cache_address new_address_record;
+time_t callout_start_time;
+
+new_domain_record.result = ccache_unknown;
+new_domain_record.postmaster_result = ccache_unknown;
+new_domain_record.random_result = ccache_unknown;
+
+memset(&new_address_record, 0, sizeof(new_address_record));
+
+/* For a recipient callout, the key used for the address cache record must
+include the sender address if we are using the real sender in the callout,
+because that may influence the result of the callout. */
+
+if (options & vopt_is_recipient)
+ if (options & vopt_callout_recipsender)
+ {
+ from_address = sender_address;
+ address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
+ if (cutthrough.delivery) options |= vopt_callout_no_cache;
+ }
+ else if (options & vopt_callout_recippmaster)
+ {
+ from_address = string_sprintf("postmaster@%s", qualify_domain_sender);
+ address_key = string_sprintf("%s/<postmaster@%s>", addr->address,
+ qualify_domain_sender);
+ }
+ else
+ {
+ from_address = US"";
+ address_key = addr->address;
+ }
+
+/* For a sender callout, we must adjust the key if the mailfrom address is not
+empty. */
+
+else
+ {
+ from_address = se_mailfrom ? se_mailfrom : US"";
+ address_key = *from_address
+ ? string_sprintf("%s/<%s>", addr->address, from_address) : addr->address;
+ }
+
+if (cached_callout_lookup(addr, address_key, from_address,
+ &options, &pm_mailfrom, &yield, failure_ptr,
+ &new_domain_record, &old_domain_cache_result))
+ goto END_CALLOUT;
+