pl, smtp_command, s);
return FALSE;
+ case ERRNO_TLSFAILURE: /* Handle bad first read; can happen with
+ GnuTLS and TLS1.3 */
+ *message = US"bad first read from TLS conn";
+ return TRUE;
+
case ERRNO_FILTER_FAIL: /* Handle a failed filter process error;
can't send QUIT as we mustn't end the DATA. */
*message = string_sprintf("transport filter process failed (%d)%s",
static void
write_logs(const host_item *host, const uschar *suffix, int basic_errno)
{
-uschar *message = LOGGING(outgoing_port)
- ? string_sprintf("H=%s [%s]:%d", host->name, host->address,
+gstring * message = LOGGING(outgoing_port)
+ ? string_fmt_append(NULL, "H=%s [%s]:%d", host->name, host->address,
host->port == PORT_NONE ? 25 : host->port)
- : string_sprintf("H=%s [%s]", host->name, host->address);
+ : string_fmt_append(NULL, "H=%s [%s]", host->name, host->address);
if (suffix)
{
- message = string_sprintf("%s: %s", message, suffix);
+ message = string_fmt_append(message, ": %s", suffix);
if (basic_errno > 0)
- message = string_sprintf("%s: %s", message, strerror(basic_errno));
+ message = string_fmt_append(message, ": %s", strerror(basic_errno));
}
else
- message = string_sprintf("%s %s", message, exim_errstr(basic_errno));
+ message = string_fmt_append(message, " %s", exim_errstr(basic_errno));
-log_write(0, LOG_MAIN, "%s", message);
-deliver_msglog("%s %s\n", tod_stamp(tod_log), message);
+log_write(0, LOG_MAIN, "%s", string_from_gstring(message));
+deliver_msglog("%s %s\n", tod_stamp(tod_log), message->s);
}
static void
uschar * ehlo_resp_key = ehlo_cache_key(sx);
dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
+ HDEBUG(D_transport) debug_printf("writing clr %04x/%04x cry %04x/%04x\n",
+ sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
+ sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths);
+
dbfn_write(dbm_file, ehlo_resp_key, &er, (int)sizeof(er));
dbfn_close(dbm_file);
}
Return:
OK all well
+ DEFER error on first read of TLS'd conn
FAIL SMTP error in response
*/
int
{
BOOL pending_BANNER = sx->pending_BANNER;
BOOL pending_EHLO = sx->pending_EHLO;
+int rc = FAIL;
sx->pending_BANNER = FALSE; /* clear early to avoid recursion */
sx->pending_EHLO = FALSE;
if (!smtp_reap_banner(sx))
{
DEBUG(D_transport) debug_printf("bad banner\n");
+ if (tls_out.active.sock >= 0) rc = DEFER;
goto fail;
}
}
if (!smtp_reap_ehlo(sx))
{
DEBUG(D_transport) debug_printf("bad response for EHLO\n");
+ if (tls_out.active.sock >= 0) rc = DEFER;
goto fail;
}
|| (authbits = study_ehlo_auths(sx)) != *ap)
{
HDEBUG(D_transport)
- debug_printf("EHLO extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
+ debug_printf("EHLO %s extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
+ tls_out.active.sock < 0 ? "cleartext" : "crypted",
sx->peer_offered, *ap, peer_offered, authbits);
+ *(tls_out.active.sock < 0
+ ? &sx->ehlo_resp.cleartext_features : &sx->ehlo_resp.crypted_features) = peer_offered;
*ap = authbits;
if (peer_offered & OPTION_EARLY_PIPE)
write_ehlo_cache_entry(sx);
fail:
invalidate_ehlo_cache_entry(sx);
(void) smtp_discard_responses(sx, sx->conn_args.ob, *countp);
- return FAIL;
+ return rc;
}
#endif
-2 I/O or other non-response error for RCPT
-3 DATA or MAIL failed - errno and buffer set
-4 banner or EHLO failed (early-pipelining)
+ -5 banner or EHLO failed (early-pipelining, TLS)
*/
static int
address_item * addr = sx->sync_addr;
smtp_transport_options_block * ob = sx->conn_args.ob;
int yield = 0;
+int rc;
#ifdef EXPERIMENTAL_PIPE_CONNECT
-if (smtp_reap_early_pipe(sx, &count) != OK)
- return -4;
+if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
+ return rc == FAIL ? -4 : -5;
#endif
/* Handle the response for a MAIL command. On error, reinstate the original
{
DEBUG(D_transport) debug_printf("bad response for MAIL\n");
Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */
+ if (errno == ERRNO_TLSFAILURE)
+ return -5;
if (errno == 0 && sx->buffer[0] != 0)
{
int save_errno = 0;
}
}
+ /* Error on first TLS read */
+
+ else if (errno == ERRNO_TLSFAILURE)
+ return -5;
+
/* Timeout while reading the response */
else if (errno == ETIMEDOUT)
If not, log this last one in the == line. */
if (sx->conn_args.host->next)
- log_write(0, LOG_MAIN, "H=%s [%s]: %s",
- sx->conn_args.host->name, sx->conn_args.host->address, addr->message);
+ if (LOGGING(outgoing_port))
+ log_write(0, LOG_MAIN, "H=%s [%s]:%d %s", sx->conn_args.host->name,
+ sx->conn_args.host->address,
+ sx->port == PORT_NONE ? 25 : sx->port, addr->message);
+ else
+ log_write(0, LOG_MAIN, "H=%s [%s]: %s", sx->conn_args.host->name,
+ sx->conn_args.host->address, addr->message);
#ifndef DISABLE_EVENT
else
int code;
uschar *msg;
BOOL pass_message;
+
+ if (errno == ERRNO_TLSFAILURE) /* Error on first TLS read */
+ return -5;
+
if (pending_DATA > 0 || (yield & 1) != 0)
{
if (errno == 0 && sx->buffer[0] == '4')
tc_chunk_last add LAST option to SMTP BDAT command
tc_reap_prev reap response to previous SMTP commands
-Returns: OK or ERROR
+Returns:
+ OK or ERROR
+ DEFER TLS error on first read (EHLO-resp); errno set
*/
static int
case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
case 0: break; /* No 2xx or 5xx, but no probs */
- case -1: /* Timeout on RCPT */
+ case -5: errno = ERRNO_TLSFAILURE;
+ return DEFER;
#ifdef EXPERIMENTAL_PIPE_CONNECT
case -4: /* non-2xx for pipelined banner or EHLO */
#endif
+ case -1: /* Timeout on RCPT */
default: return ERROR; /* I/O error, or any MAIL/DATA error */
}
cmd_count = 1;
uschar * message = NULL;
int yield = OK;
int rc;
+#ifdef SUPPORT_TLS
+uschar * tls_errstr;
+#endif
sx->conn_args.ob = ob;
TLS_NEGOTIATE:
{
address_item * addr;
- uschar * errstr;
sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
sx->addrlist, sx->conn_args.tblock,
# ifdef SUPPORT_DANE
sx->dane ? &tlsa_dnsa : NULL,
# endif
- &tls_out, &errstr);
+ &tls_out, &tls_errstr);
if (!sx->cctx.tls_ctx)
{
/* TLS negotiation failed; give an error. From outside, this function may
be called again to try in clear on a new connection, if the options permit
it for this host. */
- DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr);
+GNUTLS_CONN_FAILED:
+ DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr);
# ifdef SUPPORT_DANE
if (sx->dane)
{
log_write(0, LOG_MAIN,
"DANE attempt failed; TLS connection to %s [%s]: %s",
- sx->conn_args.host->name, sx->conn_args.host->address, errstr);
+ sx->conn_args.host->name, sx->conn_args.host->address, tls_errstr);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", US"validation-failure"); /* could do with better detail */
# endif
errno = ERRNO_TLSFAILURE;
- message = string_sprintf("TLS session: %s", errstr);
+ message = string_sprintf("TLS session: %s", tls_errstr);
sx->send_quit = FALSE;
goto TLS_FAILED;
}
#endif
{
if (!smtp_reap_ehlo(sx))
+#ifdef USE_GNUTLS
+ {
+ /* The GnuTLS layer in Exim only spots a server-rejection of a client
+ cert late, under TLS1.3 - which means here; the first time we try to
+ receive crypted data. Treat it as if it was a connect-time failure.
+ See also the early-pipe equivalent... which will be hard; every call
+ to sync_responses will need to check the result.
+ It would be nicer to have GnuTLS check the cert during the handshake.
+ Can it do that, with all the flexibility we need? */
+
+ tls_errstr = US"error on first read";
+ goto GNUTLS_CONN_FAILED;
+ }
+#else
goto RESPONSE_FAILED;
+#endif
smtp_peer_options = 0;
}
}
}
#ifndef DISABLE_PRDR
-/* If it supports Per-Recipient Data Reponses, and we have omre than one recipient,
+/* If it supports Per-Recipient Data Responses, and we have more than one recipient,
request that */
sx->prdr_active = FALSE;
#ifdef EXPERIMENTAL_PIPE_CONNECT
case -4: return -1; /* non-2xx for pipelined banner or EHLO */
+ case -5: return -1; /* TLS first-read error */
#endif
}
sx->pending_MAIL = FALSE; /* Dealt with MAIL */
case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
- case 0: break; /* No 2xx or 5xx, but no probs */
+ case 0: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case -1: goto END_OFF; /* Timeout on RCPT */
#ifdef EXPERIMENTAL_PIPE_CONNECT
+ case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
#endif
{
case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */
case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */
- break;
+ break;
- case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
- case 0: break; /* No 2xx or 5xx, but no probs */
+ case 0: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case -1: goto END_OFF; /* Timeout on RCPT */
#ifdef EXPERIMENTAL_PIPE_CONNECT
+ case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
#endif
- default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */
+ default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */
}
}
many as to hit the configured limit. The function transport_check_waiting looks
for a waiting message and returns its id. Then transport_pass_socket tries to
set up a continued delivery by passing the socket on to another process. The
-variable send_rset is FALSE if a message has just been successfully transfered.
+variable send_rset is FALSE if a message has just been successfully transferred.
If we are already sending down a continued channel, there may be further
addresses not yet delivered that are aimed at the same host, but which have not
incl_ip, &retry_host_key, &retry_message_key);
DEBUG(D_transport) debug_printf("%s [%s]%s retry-status = %s\n", host->name,
- (host->address == NULL)? US"" : host->address, pistring,
- (host->status == hstatus_usable)? "usable" :
- (host->status == hstatus_unusable)? "unusable" :
- (host->status == hstatus_unusable_expired)? "unusable (expired)" : "?");
+ host->address ? host->address : US"", pistring,
+ host->status == hstatus_usable ? "usable"
+ : host->status == hstatus_unusable ? "unusable"
+ : host->status == hstatus_unusable_expired ? "unusable (expired)" : "?");
/* Skip this address if not usable at this time, noting if it wasn't
actually expired, both locally and in the address. */
message_id, host->name, host->address, addrlist->address,
addrlist->next ? ", ..." : "");
- set_process_info("delivering %s to %s [%s] (%s%s)",
- message_id, host->name, host->address, addrlist->address,
+ set_process_info("delivering %s to %s [%s]%s (%s%s)",
+ message_id, host->name, host->address, pistring, addrlist->address,
addrlist->next ? ", ..." : "");
/* This is not for real; don't do the delivery. If there are
: rc == ERROR ? US"ERROR"
: US"?";
- set_process_info("delivering %s: just tried %s [%s] for %s%s: result %s",
- message_id, host->name, host->address, addrlist->address,
+ set_process_info("delivering %s: just tried %s [%s]%s for %s%s: result %s",
+ message_id, host->name, host->address, pistring, addrlist->address,
addrlist->next ? " (& others)" : "", rs);
/* Release serialization if set up */
"hosts_max_try (message older than host's retry time)\n");
}
}
+
+ DEBUG(D_transport)
+ {
+ if (unexpired_hosts_tried >= ob->hosts_max_try)
+ debug_printf("reached transport hosts_max_try limit %d\n",
+ ob->hosts_max_try);
+ if (total_hosts_tried >= ob->hosts_max_try_hardlimit)
+ debug_printf("reached transport hosts_max_try_hardlimit limit %d\n",
+ ob->hosts_max_try_hardlimit);
+ }
+
if (f.running_in_test_harness) millisleep(500); /* let server debug out */
} /* End of loop for trying multiple hosts. */