- continue;
- }
-
- /* Check the overall callout timeout */
-
- if (time(NULL) - callout_start_time >= callout_overall)
- {
- HDEBUG(D_verify) debug_printf("overall timeout for callout exceeded\n");
- break;
- }
-
- /* Set IPv4 or IPv6 */
-
- host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
-
- /* Expand and interpret the interface and port strings. The latter will not
- be used if there is a host-specific port (e.g. from a manualroute router).
- This has to be delayed till now, because they may expand differently for
- different hosts. If there's a failure, log it, but carry on with the
- defaults. */
-
- deliver_host = host->name;
- deliver_host_address = host->address;
- deliver_host_port = host->port;
- deliver_domain = addr->domain;
- transport_name = addr->transport->name;
-
- if ( !smtp_get_interface(tf->interface, host_af, addr, NULL, &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);
-
- /* Set HELO string according to the protocol */
- lmtp= Ustrcmp(tf->protocol, "lmtp") == 0;
- smtps= Ustrcmp(tf->protocol, "smtps") == 0;
-
-
- HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port);
-
- /* Set up the buffer for reading SMTP response packets. */
-
- inblock.buffer = inbuffer;
- inblock.buffersize = sizeof(inbuffer);
- inblock.ptr = inbuffer;
- inblock.ptrend = inbuffer;
-
- /* Set up the buffer for holding SMTP commands while pipelining */
-
- outblock.buffer = outbuffer;
- outblock.buffersize = sizeof(outbuffer);
- outblock.ptr = outbuffer;
- outblock.cmd_count = 0;
- outblock.authenticating = FALSE;
-
- /* Connect to the host; on failure, just loop for the next one, but we
- set the error for the last one. Use the callout_connect timeout. */
-
- tls_retry_connection:
-
- /* Reset the parameters of a TLS session */
- tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL;
-
- inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, callout_connect,
- addr->transport);
- if (inblock.sock < 0)
- {
- addr->message = string_sprintf("could not connect to %s [%s]: %s",
- host->name, host->address, strerror(errno));
- transport_name = NULL;
- deliver_host = deliver_host_address = NULL;
- deliver_domain = save_deliver_domain;
- continue;
- }
-
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
- {
- int rc;
-
- tls_out.dane_verified = FALSE;
- tls_out.tlsa_usage = 0;
-
- dane_required =
- verify_check_given_host(&ob->hosts_require_dane, host) == OK;
-
- 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
- )
- return rc;
- }
- else if (dane_required)
- {
- log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
- return FAIL;
- }
-
- if (dane)
- ob->tls_tempfail_tryclear = FALSE;
- }
-#endif /*DANE*/
-
- /* Expand the helo_data string to find the host name to use. */
-
- if (tf->helo_data != NULL)
- {
- uschar *s = expand_string(tf->helo_data);
- if (s == NULL)
- log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
- "helo_data value for callout: %s", addr->address,
- expand_string_message);
- else active_hostname = s;
- }
-
- /* Wait for initial response, and send HELO. The smtp_write_command()
- function leaves its command in big_buffer. This is used in error responses.
- Initialize it in case the connection is rejected. */
-
- Ustrcpy(big_buffer, "initial connection");
-
- /* Unless ssl-on-connect, wait for the initial greeting */
- smtps_redo_greeting:
-
-#ifdef SUPPORT_TLS
- if (!smtps || (smtps && tls_out.active >= 0))
-#endif
- {
- if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
- goto RESPONSE_FAILED;
-
-#ifdef EXPERIMENTAL_EVENT
- lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
- : host->dnssec==DS_NO ? US"no" : NULL;
- if (event_raise(addr->transport->event_action,
- US"smtp:connect", responsebuffer))
- {
- lookup_dnssec_authenticated = NULL;
- /* Logging? Debug? */
- goto RESPONSE_FAILED;
- }
- lookup_dnssec_authenticated = NULL;
-#endif
- }
-
- /* Not worth checking greeting line for ESMTP support */
- if (!(esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK))
- DEBUG(D_transport)
- debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
-
- tls_redo_helo:
-
-#ifdef SUPPORT_TLS
- if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */
- {
- tls_offered = TRUE;
- ob->tls_tempfail_tryclear = FALSE;
- }
- else /* all other cases */
-#endif
-
- { esmtp_retry:
-
- if (!(done= smtp_write_command(&outblock, FALSE, "%s %s\r\n",
- !esmtp? "HELO" : lmtp? "LHLO" : "EHLO", active_hostname) >= 0))
- goto SEND_FAILED;
- if (!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))
- {
- if (errno != 0 || responsebuffer[0] == 0 || lmtp || !esmtp || tls_out.active >= 0)
- {
- done= FALSE;
- goto RESPONSE_FAILED;
- }
-#ifdef SUPPORT_TLS
- tls_offered = FALSE;
-#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;
-#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
- use a separate buffer for reading the response to STARTTLS so that if it is
- negative, the original EHLO data is available for subsequent analysis, should
- the client not be required to use TLS. If the response is bad, copy the buffer
- for error analysis. */
-
-#ifdef SUPPORT_TLS
- if ( tls_offered
- && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK
- && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK
- )
- {
- uschar buffer2[4096];
- if ( !smtps
- && !(done= smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") >= 0))
- goto SEND_FAILED;
-
- /* If there is an I/O error, transmission of this message is deferred. If
- there is a temporary rejection of STARRTLS and tls_tempfail_tryclear is
- false, we also defer. However, if there is a temporary rejection of STARTTLS
- and tls_tempfail_tryclear is true, or if there is an outright rejection of
- STARTTLS, we carry on. This means we will try to send the message in clear,
- unless the host is in hosts_require_tls (tested below). */
-
- 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))
- {
- Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
- done= FALSE;
- goto RESPONSE_FAILED;
- }
- }
-
- /* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */
- else
- {
- int oldtimeout = ob->command_timeout;
- int rc;
-
- tls_negotiate:
- ob->command_timeout = callout;
- rc = tls_client_start(inblock.sock, host, addr, addr->transport
-# ifdef EXPERIMENTAL_DANE
- , dane ? &tlsa_dnsa : NULL
-# endif
- );
- ob->command_timeout = oldtimeout;
-
- /* TLS negotiation failed; give an error. Try in clear on a new
- connection, if the options permit it for this host. */
- if (rc != OK)
- {
- if (rc == DEFER)
- {
- (void)close(inblock.sock);
-# ifdef EXPERIMENTAL_EVENT
- (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
- )
- {
- log_write(0, LOG_MAIN, "TLS session failure:"
- " delivering unencrypted to %s [%s] (not in hosts_require_tls)",
- host->name, host->address);
- suppress_tls = TRUE;
- goto tls_retry_connection;
- }
- }
-
- /*save_errno = ERRNO_TLSFAILURE;*/
- /*message = US"failure while setting up TLS session";*/
- send_quit = FALSE;
- done= FALSE;
- goto TLS_FAILED;
- }
-
- /* TLS session is set up. Copy info for logging. */
- addr->cipher = tls_out.cipher;
- addr->peerdn = tls_out.peerdn;
-
- /* For SMTPS we need to wait for the initial OK response, then do HELO. */
- if (smtps)
- goto smtps_redo_greeting;
-
- /* For STARTTLS we need to redo EHLO */
- goto tls_redo_helo;
- }
- }
-
- /* If the host is required to use a secure channel, ensure that we have one. */
- if (tls_out.active < 0)
- if (
-# ifdef EXPERIMENTAL_DANE
- dane ||
-# endif
- verify_check_given_host(&ob->hosts_require_tls, host) == OK
- )
- {
- /*save_errno = ERRNO_TLSREQUIRED;*/
- 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");
- done= FALSE;
- goto TLS_FAILED;
- }
-
-#endif /*SUPPORT_TLS*/
-
- 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)
- {
- if (addr->transport->filter_command)
- {
- cutthrough.delivery = FALSE;
- HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
- }
-#ifndef DISABLE_DKIM
- if (ob->dkim_domain)
- {
- cutthrough.delivery = FALSE;
- HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
- }
-#endif
- }
-
- SEND_FAILED:
- RESPONSE_FAILED:
- TLS_FAILED:
- ;
- /* Clear down of the TLS, SMTP and TCP layers on error is handled below. */
-
- /* Failure to accept HELO is cached; this blocks the whole domain for all
- senders. I/O errors and defer responses are not cached. */
-
- if (!done)
- {
- *failure_ptr = US"mail"; /* At or before MAIL */
- if (errno == 0 && responsebuffer[0] == '5')
- {
- setflag(addr, af_verify_nsfail);
- new_domain_record.result = ccache_reject;
- }
- }
-
-#ifdef EXPERIMENTAL_INTERNATIONAL
- 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
- ) ) )
- {
- HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n");
- errno = ERRNO_UTF8_FWD;
- setflag(addr, af_verify_nsfail);
- done = FALSE;