{ "dane_require_tls_ciphers", opt_stringptr, LOFF(dane_require_tls_ciphers) },
# endif
{ "data_timeout", opt_time, LOFF(data_timeout) },
- { "delay_after_cutoff", opt_bool, LOFF(delay_after_cutoff) },
+ { "delay_after_cutoff", opt_bool, LOFF(delay_after_cutoff) },
#ifndef DISABLE_DKIM
{ "dkim_canon", opt_stringptr, LOFF(dkim.dkim_canon) },
{ "dkim_domain", opt_stringptr, LOFF(dkim.dkim_domain) },
/* Default private options block for the smtp transport. */
smtp_transport_options_block smtp_transport_option_defaults = {
- .hosts = NULL,
- .fallback_hosts = NULL,
- .hostlist = NULL,
- .fallback_hostlist = NULL,
+ /* All non-mentioned elements 0/NULL/FALSE */
.helo_data = US"$primary_hostname",
- .interface = NULL,
- .port = NULL,
.protocol = US"smtp",
- .dscp = NULL,
- .serialize_hosts = NULL,
- .hosts_try_auth = NULL,
- .hosts_require_auth = NULL,
.hosts_try_chunking = US"*",
#ifdef SUPPORT_DANE
.hosts_try_dane = US"*",
- .hosts_require_dane = NULL,
- .dane_require_tls_ciphers = NULL,
#endif
.hosts_try_fastopen = US"*",
#ifndef DISABLE_PRDR
#endif
#ifndef DISABLE_OCSP
.hosts_request_ocsp = US"*", /* hosts_request_ocsp (except under DANE; tls_client_start()) */
- .hosts_require_ocsp = NULL,
-#endif
- .hosts_require_tls = NULL,
- .hosts_avoid_tls = NULL,
- .hosts_verify_avoid_tls = NULL,
- .hosts_avoid_pipelining = NULL,
-#ifndef DISABLE_PIPE_CONNECT
- .hosts_pipe_connect = NULL,
-#endif
- .hosts_avoid_esmtp = NULL,
-#ifndef DISABLE_TLS
- .hosts_nopass_tls = NULL,
- .hosts_noproxy_tls = NULL,
#endif
.command_timeout = 5*60,
.connect_timeout = 5*60,
.hosts_max_try_hardlimit = 50,
.message_linelength_limit = 998,
.address_retry_include_sender = TRUE,
- .allow_localhost = FALSE,
- .authenticated_sender_force = FALSE,
- .gethostbyname = FALSE,
.dns_qualify_single = TRUE,
- .dns_search_parents = FALSE,
.dnssec = { .request= US"*", .require=NULL },
.delay_after_cutoff = TRUE,
- .hosts_override = FALSE,
- .hosts_randomize = FALSE,
.keepalive = TRUE,
- .lmtp_ignore_quota = FALSE,
- .expand_retry_include_ip_address = NULL,
.retry_include_ip_address = TRUE,
-#ifdef SUPPORT_SOCKS
- .socks_proxy = NULL,
-#endif
#ifndef DISABLE_TLS
- .tls_certificate = NULL,
- .tls_crl = NULL,
- .tls_privatekey = NULL,
- .tls_require_ciphers = NULL,
- .tls_sni = NULL,
+# if defined(SUPPORT_SYSDEFAULT_CABUNDLE) || !defined(USE_GNUTLS)
.tls_verify_certificates = US"system",
+# endif
.tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
.tls_tempfail_tryclear = TRUE,
-# ifndef DISABLE_TLS_RESUME
- .tls_resumption_hosts = NULL,
-# endif
- .tls_verify_hosts = NULL,
.tls_try_verify_hosts = US"*",
.tls_verify_cert_hostnames = US"*",
#endif
#endif
#ifndef DISABLE_DKIM
.dkim =
- {.dkim_domain = NULL,
- .dkim_identity = NULL,
- .dkim_private_key = NULL,
- .dkim_selector = NULL,
- .dkim_canon = NULL,
- .dkim_sign_headers = NULL,
- .dkim_strict = NULL,
- .dkim_hash = US"sha256",
- .dkim_timestamps = NULL,
- .dot_stuffed = FALSE,
- .force_bodyhash = FALSE,
-# ifdef EXPERIMENTAL_ARC
- .arc_signspec = NULL,
-# endif
- },
-# ifdef EXPERIMENTAL_ARC
- .arc_sign = NULL,
-# endif
+ { .dkim_hash = US"sha256", },
#endif
};
static BOOL
smtp_reap_banner(smtp_context * sx)
{
-BOOL good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
+BOOL good_response;
+#if defined(__linux__) && defined(TCP_QUICKACK)
+ { /* Hack to get QUICKACK disabled; has to be right after 3whs, and has to on->off */
+ int sock = sx->cctx.sock;
+ struct pollfd p = {.fd = sock, .events = POLLOUT};
+ int rc = poll(&p, 1, 1000);
+ (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &on, sizeof(on));
+ (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+ }
+#endif
+good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', (SOB sx->conn_args.ob)->command_timeout);
#ifdef EXPERIMENTAL_DSN_INFO
sx->smtp_greeting = string_copy(sx->buffer);
uschar * ehlo_resp_key = ehlo_cache_key(sx);
dbdata_ehlo_resp * er;
- if (!(er = dbfn_read(dbm_file, ehlo_resp_key)))
+ if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp))))
{ DEBUG(D_transport) debug_printf("no ehlo-resp record\n"); }
else if (time(NULL) - er->time_stamp > retry_data_expire)
{
{
DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
count--;
- sx->pending_MAIL = FALSE;
+ sx->pending_MAIL = sx->RCPT_452 = FALSE;
if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
/* The address was accepted */
addr->host_used = sx->conn_args.host;
- DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
+ DEBUG(D_transport) debug_printf("%s expect rcpt for %s\n", __FUNCTION__, addr->address);
if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
if (addr->more_errno >> 8 == 52 && yield & 3)
{
- if (!sx->RCPT_452)
+ if (!sx->RCPT_452) /* initialised at MAIL-ack above */
{
DEBUG(D_transport)
debug_printf("%s: seen first 452 too-many-rcpts\n", __FUNCTION__);
}
}
}
+ if (count && !(addr = addr->next))
+ return -2;
} /* Loop for next RCPT response */
/* Update where to start at for the next block of responses, unless we
else
#endif
{
- if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0)
+ blob lazy_conn = {.data = NULL};
+ /* For TLS-connect, a TFO lazy-connect is useful since the Client Hello
+ can go on the TCP SYN. */
+
+ if ((sx->cctx.sock = smtp_connect(&sx->conn_args,
+ sx->smtps ? &lazy_conn : NULL)) < 0)
{
set_errno_nohost(sx->addrlist,
errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
sx->send_quit = FALSE;
return DEFER;
}
+#ifdef TCP_QUICKACK
+ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off,
+ sizeof(off));
+#endif
}
/* Expand the greeting message while waiting for the initial response. (Makes
sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
else
#endif
{
-#ifdef TCP_QUICKACK
- (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off,
- sizeof(off));
-#endif
if (!smtp_reap_banner(sx))
goto RESPONSE_FAILED;
}
!sx->lmtp
)
{
- const uschar *s = string_printing(sx->buffer);
+ const uschar * s = string_printing(sx->buffer);
/* deconst cast ok here as string_printing was checked to have alloc'n'copied */
- conf = (s == sx->buffer)? US string_copy(s) : US s;
+ conf = s == sx->buffer ? US string_copy(s) : US s;
}
/* Process all transported addresses - for LMTP or PRDR, read a status for
- each one. */
+ each one. We used to drop out at first_addr, until someone returned a 452
+ followed by a 250... and we screwed up the accepted addresses. */
- for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ for (address_item * addr = addrlist; addr; addr = addr->next)
{
if (addr->transport_return != PENDING_OK) continue;
*message_defer = TRUE;
}
+#ifdef TIOCOUTQ
+ DEBUG(D_transport) if (sx->cctx.sock >= 0)
+ {
+ int n;
+ if (ioctl(sx->cctx.sock, TIOCOUTQ, &n) == 0)
+ debug_printf("%d bytes remain in socket output buffer\n", n);
+ }
+#endif
}
-
/* Otherwise, we have an I/O error or a timeout other than after MAIL or
".", or some other transportation error. We defer all addresses and yield
DEFER, except for the case of failed add_headers expansion, or a transport
}
/* End off tidily with QUIT unless the connection has died or the socket has
-been passed to another process. There has been discussion on the net about what
-to do after sending QUIT. The wording of the RFC suggests that it is necessary
-to wait for a response, but on the other hand, there isn't anything one can do
-with an error response, other than log it. Exim used to do that. However,
-further discussion suggested that it is positively advantageous not to wait for
-the response, but to close the session immediately. This is supposed to move
-the TCP/IP TIME_WAIT state from the server to the client, thereby removing some
-load from the server. (Hosts that are both servers and clients may not see much
-difference, of course.) Further discussion indicated that this was safe to do
-on Unix systems which have decent implementations of TCP/IP that leave the
-connection around for a while (TIME_WAIT) after the application has gone away.
-This enables the response sent by the server to be properly ACKed rather than
-timed out, as can happen on broken TCP/IP implementations on other OS.
-
-This change is being made on 31-Jul-98. After over a year of trouble-free
-operation, the old commented-out code was removed on 17-Sep-99. */
+been passed to another process. */
SEND_QUIT:
-#ifdef TCP_CORK
-(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
-#endif
-if (sx->send_quit) (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
+if (sx->send_quit)
+ /* Use _MORE to get QUIT in FIN segment */
+ (void)smtp_write_command(sx, SCMD_MORE, "QUIT\r\n");
END_OFF:
#ifndef DISABLE_TLS
+# ifdef EXIM_TCP_CORK
+if (sx->cctx.tls_ctx) /* Use _CORK to get TLS Close Notify in FIN segment */
+ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
+# endif
+
tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
sx->cctx.tls_ctx = NULL;
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx->send_quit)
{
+ /* This flushes data queued in the socket, being the QUIT and any TLS Close,
+ sending them along with the client FIN flag. Us (we hope) sending FIN first
+ means we (client) take the TIME_WAIT state, so the server (which likely has a
+ higher connection rate) does no have to. */
+
shutdown(sx->cctx.sock, SHUT_WR);
+
+ /* Wait for (we hope) ack of our QUIT, and a server FIN. Discard any data
+ received, then discard the socket. Any packet received after then, or receive
+ data still in the socket, will get a RST - hence the pause/drain. */
+
millisleep(20);
testharness_pause_ms(200);
if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)