*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
{ "hosts_request_ocsp", opt_stringptr, LOFF(hosts_request_ocsp) },
#endif
+ { "hosts_require_alpn", opt_stringptr, LOFF(hosts_require_alpn) },
{ "hosts_require_auth", opt_stringptr, LOFF(hosts_require_auth) },
#ifndef DISABLE_TLS
# ifdef SUPPORT_DANE
{ "socks_proxy", opt_stringptr, LOFF(socks_proxy) },
#endif
#ifndef DISABLE_TLS
+ { "tls_alpn", opt_stringptr, LOFF(tls_alpn) },
{ "tls_certificate", opt_stringptr, LOFF(tls_certificate) },
{ "tls_crl", opt_stringptr, LOFF(tls_crl) },
{ "tls_dh_min_bits", opt_int, LOFF(tls_dh_min_bits) },
.keepalive = TRUE,
.retry_include_ip_address = TRUE,
#ifndef DISABLE_TLS
-# 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,
.tls_try_verify_hosts = US"*",
void
smtp_deliver_init(void)
{
-if (!regex_PIPELINING) regex_PIPELINING =
- regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_SIZE) regex_SIZE =
- regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_AUTH) regex_AUTH =
- regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+struct list
+ {
+ const pcre2_code ** re;
+ const uschar * string;
+ } list[] =
+ {
+ { ®ex_AUTH, AUTHS_REGEX },
+ { ®ex_CHUNKING, US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)" },
+ { ®ex_DSN, US"\\n250[\\s\\-]DSN(\\s|\\n|$)" },
+ { ®ex_IGNOREQUOTA, US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)" },
+ { ®ex_PIPELINING, US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)" },
+ { ®ex_SIZE, US"\\n250[\\s\\-]SIZE(\\s|\\n|$)" },
#ifndef DISABLE_TLS
-if (!regex_STARTTLS) regex_STARTTLS =
- regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_STARTTLS, US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)" },
#endif
-
-if (!regex_CHUNKING) regex_CHUNKING =
- regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
-
#ifndef DISABLE_PRDR
-if (!regex_PRDR) regex_PRDR =
- regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_PRDR, US"\\n250[\\s\\-]PRDR(\\s|\\n|$)" },
#endif
-
#ifdef SUPPORT_I18N
-if (!regex_UTF8) regex_UTF8 =
- regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_UTF8, US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)" },
#endif
-
-if (!regex_DSN) regex_DSN =
- regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
- regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
-
#ifndef DISABLE_PIPE_CONNECT
-if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
- regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+ { ®ex_EARLY_PIPE, US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)" },
#endif
-
#ifdef EXPERIMENTAL_ESMTP_LIMITS
-if (!regex_LIMITS) regex_LIMITS =
- regex_must_compile(US"\\n250[\\s\\-]LIMITS\\s", FALSE, TRUE);
+ { ®ex_LIMITS, US"\\n250[\\s\\-]LIMITS\\s" },
#endif
+ };
+
+for (struct list * l = list; l < list + nelem(list); l++)
+ if (!*l->re)
+ *l->re = regex_must_compile(l->string, FALSE, TRUE);
}
: string_copy(addr->message)
: addr->basic_errno > 0
? string_copy(US strerror(addr->basic_errno))
- : NULL);
+ : NULL,
+ NULL);
deliver_localpart = save_local;
deliver_domain = save_domain;
{ /* 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));
+ if (poll(&p, 1, 1000) >= 0) /* retval test solely for compiler quitening */
+ {
+ (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),
#endif
#ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
- US"smtp:ehlo", sx->buffer);
+ US"smtp:ehlo", sx->buffer, NULL);
#endif
return TRUE;
}
static void
ehlo_response_limits_read(smtp_context * sx)
{
-int ovec[3]; /* results vector for a main-match only */
+uschar * match;
/* matches up to just after the first space after the keyword */
-if (pcre_exec(regex_LIMITS, NULL, CS sx->buffer, Ustrlen(sx->buffer),
- 0, PCRE_EOPT, ovec, nelem(ovec)) >= 0)
- for (const uschar * s = sx->buffer + ovec[1]; *s; )
+if (regex_match(regex_LIMITS, sx->buffer, -1, &match))
+ for (const uschar * s = sx->buffer + Ustrlen(match); *s; )
{
while (isspace(*s)) s++;
if (*s == '\n') break;
/* Cache EHLO-response info for use by early-pipe.
Called
- During a normal flow on EHLO response (either cleartext or under TLS),
- when we are willing to do PIPE_CONNECT and it is offered
+ when we are willing to do PIPECONNECT and it is offered
- During an early-pipe flow on receiving the actual EHLO response and noting
- disparity versus the cached info used, when PIPE_CONNECT is still being offered
+ disparity versus the cached info used, when PIPECONNECT is still being offered
We assume that suitable values have been set in the sx.ehlo_resp structure for
features and auths; we handle the copy of limits. */
host_item * host = sx->conn_args.host; /* host to deliver to */
int rc;
+/* Set up globals for error messages */
+
+authenticator_name = au->name;
+driver_srcfile = au->srcfile;
+driver_srcline = au->srcline;
+
sx->outblock.authenticating = TRUE;
rc = (au->info->clientcode)(au, sx, ob->command_timeout,
sx->buffer, sizeof(sx->buffer));
sx->outblock.authenticating = FALSE;
+driver_srcfile = authenticator_name = NULL; driver_srcline = 0;
DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc);
/* A temporary authentication failure must hold up delivery to
static unsigned
ehlo_response(uschar * buf, unsigned checks)
{
-size_t bsize = Ustrlen(buf);
+PCRE2_SIZE bsize = Ustrlen(buf);
+pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx);
/* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
#ifndef DISABLE_TLS
if ( checks & OPTION_TLS
- && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_STARTTLS,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_TLS;
if ( checks & OPTION_IGNQ
- && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_IGNOREQUOTA,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_IGNQ;
if ( checks & OPTION_CHUNKING
- && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_CHUNKING,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_CHUNKING;
#ifndef DISABLE_PRDR
if ( checks & OPTION_PRDR
- && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_PRDR,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_PRDR;
#ifdef SUPPORT_I18N
if ( checks & OPTION_UTF8
- && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_UTF8,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_UTF8;
if ( checks & OPTION_DSN
- && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_DSN,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_DSN;
if ( checks & OPTION_PIPE
- && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_PIPELINING,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_PIPE;
if ( checks & OPTION_SIZE
- && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_SIZE,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
checks &= ~OPTION_SIZE;
#ifndef DISABLE_PIPE_CONNECT
if ( checks & OPTION_EARLY_PIPE
- && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
- PCRE_EOPT, NULL, 0) < 0)
+ && pcre2_match(regex_EARLY_PIPE,
+ (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0)
#endif
checks &= ~OPTION_EARLY_PIPE;
+pcre2_match_data_free(md);
/* debug_printf("%s: found 0x%04x\n", __FUNCTION__, checks); */
return checks;
}
uschar * tls_errstr;
#endif
+/* Many lines of clearing individual elements of *sx that used to
+be here have been replaced by a full memset to zero (de41aff051).
+There are two callers, this file and verify.c . Now we only set
+up nonzero elements. */
+
sx->conn_args.ob = ob;
sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
sx->smtps = strcmpic(ob->protocol, US"smtps") == 0;
-/* sx->ok = FALSE; */
sx->send_rset = TRUE;
sx->send_quit = TRUE;
sx->setting_up = TRUE;
sx->esmtp = TRUE;
-/* sx->esmtp_sent = FALSE; */
-#ifdef SUPPORT_I18N
-/* sx->utf8_needed = FALSE; */
-#endif
sx->dsn_all_lasthop = TRUE;
#ifdef SUPPORT_DANE
-/* sx->conn_args.dane = FALSE; */
sx->dane_required =
verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
#endif
-#ifndef DISABLE_PIPE_CONNECT
-/* sx->early_pipe_active = sx->early_pipe_ok = FALSE; */
-/* sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; */
-/* sx->pending_BANNER = sx->pending_EHLO = sx->pending_MAIL = FALSE; */
-#endif
if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) sx->max_mail = 999999;
if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
-/* sx->peer_offered = 0; */
-/* sx->avoid_option = 0; */
sx->igquotstr = US"";
if (!sx->helo_data) sx->helo_data = ob->helo_data;
-#ifdef EXPERIMENTAL_DSN_INFO
-/* sx->smtp_greeting = NULL; */
-/* sx->helo_response = NULL; */
-#endif
smtp_command = US"initial connection";
-/* sx->buffer[0] = '\0'; */
/* Set up the buffer for reading SMTP response packets. */
sx->outblock.buffer = sx->outbuffer;
sx->outblock.buffersize = sizeof(sx->outbuffer);
sx->outblock.ptr = sx->outbuffer;
-/* sx->outblock.cmd_count = 0; */
-/* sx->outblock.authenticating = FALSE; */
-/* sx->outblock.conn_args = NULL; */
/* Reset the parameters of a TLS session. */
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", sx->dane_required
- ? US"dane-required" : US"dnssec-invalid");
+ ? US"dane-required" : US"dnssec-invalid",
+ NULL);
# endif
return rc;
}
DEBUG(D_transport)
debug_printf("Closing proxied-TLS connection due to SNI mismatch\n");
- HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> QUIT\n");
+ smtp_debug_cmd(US"QUIT", 0);
write(0, "QUIT\r\n", 6);
close(0);
continue_hostname = continue_proxy_cipher = NULL;
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", sx->dane_required
- ? US"dane-required" : US"dnssec-invalid");
+ ? US"dane-required" : US"dnssec-invalid",
+ NULL);
# endif
return rc;
}
FAIL, FALSE, &sx->delivery_start);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
- US"dane:fail", US"dane-required");
+ US"dane:fail", US"dane-required", NULL);
# endif
return FAIL;
}
sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom =
#endif
sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
+#ifndef DISABLE_CLIENT_CMD_LOG
+ client_cmd_log = NULL;
+#endif
#ifndef DISABLE_PIPE_CONNECT
if ( verify_check_given_host(CUSS &ob->hosts_pipe_connect,
&& sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
{
DEBUG(D_transport)
- debug_printf("Using cached cleartext PIPE_CONNECT\n");
+ debug_printf("Using cached cleartext PIPECONNECT\n");
sx->early_pipe_active = TRUE;
sx->peer_offered = sx->ehlo_resp.cleartext_features;
}
uschar * s;
lookup_dnssec_authenticated = sx->conn_args.host->dnssec==DS_YES ? US"yes"
: sx->conn_args.host->dnssec==DS_NO ? US"no" : NULL;
- s = event_raise(sx->conn_args.tblock->event_action, US"smtp:connect", sx->buffer);
+ s = event_raise(sx->conn_args.tblock->event_action, US"smtp:connect", sx->buffer, NULL);
if (s)
{
set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL,
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 */
+ US"dane:fail", US"validation-failure", NULL); /* could do with better detail */
# endif
}
# endif
sx->peer_offered = sx->ehlo_resp.crypted_features;
if ((sx->early_pipe_active =
!!(sx->ehlo_resp.crypted_features & OPTION_EARLY_PIPE)))
- DEBUG(D_transport) debug_printf("Using cached crypted PIPE_CONNECT\n");
+ DEBUG(D_transport) debug_printf("Using cached crypted PIPECONNECT\n");
}
#endif
#ifdef EXPERIMMENTAL_ESMTP_LIMITS
(void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
smtp_peer_options & OPTION_TLS
? US"validation-failure" /* could do with better detail */
- : US"starttls-not-supported");
+ : US"starttls-not-supported",
+ NULL);
# endif
goto TLS_FAILED;
}
smtp_peer_options & OPTION_PIPE ? "" : "not ");
if ( sx->peer_offered & OPTION_CHUNKING
- && verify_check_given_host(CUSS &ob->hosts_try_chunking, sx->conn_args.host) != OK)
- sx->peer_offered &= ~OPTION_CHUNKING;
+ && verify_check_given_host(CUSS &ob->hosts_try_chunking, sx->conn_args.host) == OK)
+ smtp_peer_options |= OPTION_CHUNKING;
- if (sx->peer_offered & OPTION_CHUNKING)
+ if (smtp_peer_options & OPTION_CHUNKING)
DEBUG(D_transport) debug_printf("CHUNKING usable\n");
#ifndef DISABLE_PRDR
if ( sx->peer_offered & OPTION_PRDR
- && verify_check_given_host(CUSS &ob->hosts_try_prdr, sx->conn_args.host) != OK)
- sx->peer_offered &= ~OPTION_PRDR;
+ && verify_check_given_host(CUSS &ob->hosts_try_prdr, sx->conn_args.host) == OK)
+ smtp_peer_options |= OPTION_PRDR;
- if (sx->peer_offered & OPTION_PRDR)
+ if (smtp_peer_options & OPTION_PRDR)
DEBUG(D_transport) debug_printf("PRDR usable\n");
#endif
&& ( sx->ehlo_resp.cleartext_features | sx->ehlo_resp.crypted_features)
& OPTION_EARLY_PIPE)
{
- DEBUG(D_transport) debug_printf("PIPE_CONNECT usable in future for this IP\n");
+ DEBUG(D_transport) debug_printf("PIPECONNECT usable in future for this IP\n");
sx->ehlo_resp.crypted_auths = study_ehlo_auths(sx);
write_ehlo_cache_entry(sx);
}
SEND_FAILED:
code = '4';
- message = US string_sprintf("send() to %s [%s] failed: %s",
+ message = US string_sprintf("smtp send to %s [%s] failed: %s",
sx->conn_args.host->name, sx->conn_args.host->address, strerror(errno));
sx->send_quit = FALSE;
yield = DEFER;
sx->cctx.sock = -1;
#ifndef DISABLE_EVENT
-(void) event_raise(sx->conn_args.tblock->event_action, US"tcp:close", NULL);
+(void) event_raise(sx->conn_args.tblock->event_action, US"tcp:close", NULL, NULL);
#endif
+smtp_debug_cmd_report();
continue_transport = NULL;
continue_hostname = NULL;
return yield;
request that */
sx->prdr_active = FALSE;
-if (sx->peer_offered & OPTION_PRDR)
+if (smtp_peer_options & OPTION_PRDR)
for (address_item * addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == PENDING_DEFER)
{
smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
int timeout)
{
-fd_set rfds, efds;
-int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
+struct pollfd p[2] = {{.fd = tls_out.active.sock, .events = POLLIN},
+ {.fd = pfd[0], .events = POLLIN}};
int rc, i;
BOOL send_tls_shutdown = TRUE;
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
set_process_info("proxying TLS connection for continued transport");
-FD_ZERO(&rfds);
-FD_SET(tls_out.active.sock, &rfds);
-FD_SET(pfd[0], &rfds);
-for (int fd_bits = 3; fd_bits; )
+do
{
time_t time_left = timeout;
time_t time_start = time(NULL);
/* wait for data */
- efds = rfds;
do
{
- struct timeval tv = { time_left, 0 };
-
- rc = select(max_fd,
- (SELECT_ARG2_TYPE *)&rfds, NULL, (SELECT_ARG2_TYPE *)&efds, &tv);
+ rc = poll(p, 2, time_left * 1000);
if (rc < 0 && errno == EINTR)
if ((time_left -= time(NULL) - time_start) > 0) continue;
/* For errors where not readable, bomb out */
- if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds))
+ if (p[0].revents & POLLERR || p[1].revents & POLLERR)
{
DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
- FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
- if (!(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)))
+ p[0].revents & POLLERR ? "tls" : "proxy");
+ if (!(p[0].revents & POLLIN || p[1].events & POLLIN))
goto done;
DEBUG(D_transport) debug_printf("- but also readable; no exit yet\n");
}
}
- while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)));
+ while (rc < 0 || !(p[0].revents & POLLIN || p[1].revents & POLLIN));
/* handle inbound data */
- if (FD_ISSET(tls_out.active.sock, &rfds))
+ if (p[0].revents & POLLIN)
if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0) /* Expect -1 for EOF; */
{ /* that reaps the TLS Close Notify record */
- fd_bits &= ~1;
- FD_CLR(tls_out.active.sock, &rfds);
+ p[0].fd = -1;
shutdown(pfd[0], SHUT_WR);
timeout = 5;
}
/* Handle outbound data. We cannot combine payload and the TLS-close
due to the limitations of the (pipe) channel feeding us. Maybe use a unix-domain
socket? */
- if (FD_ISSET(pfd[0], &rfds))
+ if (p[1].revents & POLLIN)
if ((rc = read(pfd[0], buf, bsize)) <= 0)
{
- fd_bits &= ~2;
- FD_CLR(pfd[0], &rfds);
+ p[1].fd = -1;
# ifdef EXIM_TCP_CORK /* Use _CORK to get TLS Close Notify in FIN segment */
(void) setsockopt(tls_out.active.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
for (int nbytes = 0; rc - nbytes > 0; nbytes += i)
if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
goto done;
-
- if (fd_bits & 1) FD_SET(tls_out.active.sock, &rfds);
- if (fd_bits & 2) FD_SET(pfd[0], &rfds);
}
+while (p[0].fd >= 0 || p[1].fd >= 0);
done:
if (send_tls_shutdown) tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
uschar *message = NULL;
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
-smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
+smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted, for the data buffers */
BOOL pass_message = FALSE;
#ifdef EXPERIMENTAL_ESMTP_LIMITS
BOOL mail_limit = FALSE;
memset(sx, 0, sizeof(*sx));
sx->addrlist = addrlist;
sx->conn_args.host = host;
-sx->conn_args.host_af = host_af,
+sx->conn_args.host_af = host_af;
sx->port = defport;
sx->conn_args.interface = interface;
sx->helo_data = NULL;
sx->conn_args.tblock = tblock;
-/* sx->verify = FALSE; */
gettimeofday(&sx->delivery_start, NULL);
sx->sync_addr = sx->first_addr = addrlist;
yield ERROR. */
if (!transport_set_up_command(&transport_filter_argv,
- tblock->filter_command, TRUE, DEFER, addrlist,
- string_sprintf("%.50s transport", tblock->name), NULL))
+ tblock->filter_command, TRUE, DEFER, addrlist, FALSE,
+ string_sprintf("%.50s transport filter", tblock->name), NULL))
{
set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
FALSE, &sx->delivery_start);
if ( transport_filter_argv
&& *transport_filter_argv
&& **transport_filter_argv
- && sx->peer_offered & OPTION_CHUNKING
+ && smtp_peer_options & OPTION_CHUNKING
#ifndef DISABLE_DKIM
/* When dkim signing, chunking is handled even with a transport-filter */
&& !(ob->dkim.dkim_private_key && ob->dkim.dkim_domain && ob->dkim.dkim_selector)
#endif
)
{
- sx->peer_offered &= ~OPTION_CHUNKING;
+ smtp_peer_options &= ~OPTION_CHUNKING;
DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
}
}
If using CHUNKING, do not send a BDAT until we know how big a chunk we want
to send is. */
-if ( !(sx->peer_offered & OPTION_CHUNKING)
+if ( !(smtp_peer_options & OPTION_CHUNKING)
&& (sx->ok || (pipelining_active && !mua_wrapper)))
{
int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n");
(Haven't been able to make it work using select() for writing yet.) */
if ( !sx->ok
- && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active))
+ && (!(smtp_peer_options & OPTION_CHUNKING) || !pipelining_active))
{
/* Save the first address of the next batch. */
sx->first_addr = sx->next_addr;
of responses. The callback needs a whole bunch of state so set up
a transport-context structure to be passed around. */
- if (sx->peer_offered & OPTION_CHUNKING)
+ if (smtp_peer_options & OPTION_CHUNKING)
{
tctx.check_string = tctx.escape_string = NULL;
tctx.options |= topt_use_bdat;
transport_write_timeout = ob->data_timeout;
smtp_command = US"sending data block"; /* For error messages */
DEBUG(D_transport|D_v)
- if (sx->peer_offered & OPTION_CHUNKING)
+ if (smtp_peer_options & OPTION_CHUNKING)
debug_printf(" will write message using CHUNKING\n");
else
debug_printf(" SMTP>> writing message and terminating \".\"\n");
If we can, we want the message-write to not flush (the tail end of) its data out. */
if ( sx->pipelining_used
- && (sx->ok && sx->completed_addr || sx->peer_offered & OPTION_CHUNKING)
+ && (sx->ok && sx->completed_addr || smtp_peer_options & OPTION_CHUNKING)
&& sx->send_quit
&& !(sx->first_addr || f.continue_more)
&& f.deliver_firsttime
sx->send_quit = FALSE; /* avoid sending it later */
#ifndef DISABLE_TLS
- if (sx->cctx.tls_ctx) /* need to send TLS Cloe Notify */
+ if (sx->cctx.tls_ctx && sx->send_tlsclose) /* need to send TLS Close Notify */
{
# ifdef EXIM_TCP_CORK /* Use _CORK to get Close Notify in FIN segment */
(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
}
}
- if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1)
+ if (smtp_peer_options & OPTION_CHUNKING && sx->cmd_count > 1)
{
/* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
switch(sync_responses(sx, sx->cmd_count-1, 0))
#ifndef DISABLE_PRDR
if (sx->prdr_active) setflag(addr, af_prdr_used);
#endif
- if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
+ if (smtp_peer_options & OPTION_CHUNKING) setflag(addr, af_chunking_used);
flag = '-';
#ifndef DISABLE_PRDR
"%s: %s", sx->buffer, strerror(errno));
}
else if (addr->transport_return == DEFER)
+ /*XXX magic value -2 ? maybe host+message ? */
retry_add_item(addr, addr->address_retry_key, -2);
}
#endif
{
save_errno = errno;
code = '4';
- message = string_sprintf("send() to %s [%s] failed: %s",
+ message = string_sprintf("smtp send to %s [%s] failed: %s",
host->name, host->address, message ? message : US strerror(save_errno));
sx->send_quit = FALSE;
goto FAILED;
message_error = Ustrncmp(smtp_command,"end ",4) == 0;
break;
+#ifndef DISABLE_DKIM
+ case EACCES:
+ /* DKIM signing failure: avoid thinking we pipelined quit,
+ just abandon the message and close the socket. */
+
+ message_error = FALSE;
+# ifndef DISABLE_TLS
+ if (sx->cctx.tls_ctx)
+ {
+ tls_close(sx->cctx.tls_ctx,
+ sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY);
+ sx->cctx.tls_ctx = NULL;
+ }
+# endif
+ break;
+#endif
default:
message_error = FALSE;
break;
if (sx->send_rset)
if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0))
{
- msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
+ msg = US string_sprintf("smtp send to %s [%s] failed: %s", host->name,
host->address, strerror(errno));
sx->send_quit = FALSE;
}
a new EHLO. If we don't get a good response, we don't attempt to pass
the socket on. */
- tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ tls_close(sx->cctx.tls_ctx,
+ sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY);
sx->send_tlsclose = FALSE;
sx->cctx.tls_ctx = NULL;
tls_out.active.sock = -1;
{ /* Use _MORE to get QUIT in FIN segment */
(void)smtp_write_command(sx, SCMD_MORE, "QUIT\r\n");
#ifndef DISABLE_TLS
- if (sx->cctx.tls_ctx)
+ if (sx->cctx.tls_ctx && sx->send_tlsclose)
{
# ifdef EXIM_TCP_CORK /* Use _CORK to get TLS Close Notify in FIN segment */
(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
while (!sigalrm_seen && n > 0);
ALARM_CLR(0);
+ if (sx->send_tlsclose)
+ {
# ifdef EXIM_TCP_CORK
- (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
+ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
# endif
- tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ }
+ else
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WONLY);
sx->cctx.tls_ctx = NULL;
}
#endif
- millisleep(20);
- if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+
+ /* Drain any trailing data from the socket before close, to avoid sending a RST */
+
+ if ( poll_one_fd(sx->cctx.sock, POLLIN, 20) != 0 /* 20ms */
+ && fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
for (int i = 16, n; /* drain socket */
(n = read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer))) > 0 && i > 0;
i--) HDEBUG(D_transport|D_acl|D_v)
sx->cctx.sock = -1;
continue_transport = NULL;
continue_hostname = NULL;
+smtp_debug_cmd_report();
#ifndef DISABLE_EVENT
-(void) event_raise(tblock->event_action, US"tcp:close", NULL);
+(void) event_raise(tblock->event_action, US"tcp:close", NULL, NULL);
#endif
#ifdef SUPPORT_DANE
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
- if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted host list '%s' from '%s' in transport %s", s, ob->hosts, tblock->name))
+ if (is_tainted(s))
{
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted host list '%s' from '%s' in transport %s",
+ s, ob->hosts, tblock->name);
/* Avoid leaking info to an attacker */
addrlist->message = US"internal configuration error";
addrlist->transport_return = PANIC;
if (expanded_hosts)
{
- thost = store_get(sizeof(host_item), FALSE);
+ thost = store_get(sizeof(host_item), GET_UNTAINTED);
*thost = *host;
thost->name = string_copy(host->name);
thost->address = string_copy(host->address);