summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2014-08-20 20:22:21 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2014-08-20 20:22:21 +0100
commita1bccd48f3956b50a13a34f5aed4b72c658c61af (patch)
treec2f216cea6f67db514741d89631ef11aa25abc09 /src
parentd8129d876786c938f06dfbe91e51ebe36f09ae43 (diff)
parenta7538db17824b7fd70c12ef7561a67b85d6f247e (diff)
Merge branch 'master' into dane
Conflicts: doc/doc-txt/ChangeLog src/src/tls-openssl.c src/src/transports/smtp.c src/src/verify.c
Diffstat (limited to 'src')
-rw-r--r--src/OS/os.h-OpenBSD7
-rw-r--r--src/src/acl.c31
-rw-r--r--src/src/auths/dovecot.c14
-rw-r--r--src/src/deliver.c187
-rw-r--r--src/src/exim.c7
-rw-r--r--src/src/exipick.src6
-rw-r--r--src/src/expand.c15
-rw-r--r--src/src/functions.h8
-rw-r--r--src/src/globals.c10
-rw-r--r--src/src/globals.h10
-rw-r--r--src/src/mime.c26
-rw-r--r--src/src/smtp_out.c14
-rw-r--r--src/src/structs.h2
-rw-r--r--src/src/tls-gnu.c82
-rw-r--r--src/src/tls-openssl.c69
-rw-r--r--src/src/transport.c4
-rw-r--r--src/src/transports/smtp.c416
-rw-r--r--src/src/transports/smtp.h3
-rw-r--r--src/src/verify.c119
19 files changed, 641 insertions, 389 deletions
diff --git a/src/OS/os.h-OpenBSD b/src/OS/os.h-OpenBSD
index 55bade674..9578047af 100644
--- a/src/OS/os.h-OpenBSD
+++ b/src/OS/os.h-OpenBSD
@@ -5,6 +5,13 @@
#define HAVE_SYS_MOUNT_H
#define SIOCGIFCONF_GIVES_ADDR
#define HAVE_ARC4RANDOM
+/* In May 2014, OpenBSD 5.5 was released which cleaned up the arc4random_* API
+ which removed the arc4random_stir() function. Set NOT_HAVE_ARC4RANDOM_STIR
+ if the version released is past that point. */
+#include <sys/param.h>
+#if OpenBSD >= 201405
+#define NOT_HAVE_ARC4RANDOM_STIR
+#endif
typedef struct flock flock_t;
diff --git a/src/src/acl.c b/src/src/acl.c
index 6e635fbf1..fe1e254bd 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -4129,7 +4129,11 @@ while (acl != NULL)
switch(acl->verb)
{
case ACL_ACCEPT:
- if (cond == OK || cond == DISCARD) return cond;
+ if (cond == OK || cond == DISCARD)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name);
+ return cond;
+ }
if (endpass_seen)
{
HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n");
@@ -4140,17 +4144,26 @@ while (acl != NULL)
case ACL_DEFER:
if (cond == OK)
{
+ HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name);
acl_temp_details = TRUE;
return DEFER;
}
break;
case ACL_DENY:
- if (cond == OK) return FAIL;
+ if (cond == OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name);
+ return FAIL;
+ }
break;
case ACL_DISCARD:
- if (cond == OK || cond == DISCARD) return DISCARD;
+ if (cond == OK || cond == DISCARD)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name);
+ return DISCARD;
+ }
if (endpass_seen)
{
HDEBUG(D_acl) debug_printf("discard: endpass encountered - denying access\n");
@@ -4159,11 +4172,19 @@ while (acl != NULL)
break;
case ACL_DROP:
- if (cond == OK) return FAIL_DROP;
+ if (cond == OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name);
+ return FAIL_DROP;
+ }
break;
case ACL_REQUIRE:
- if (cond != OK) return cond;
+ if (cond != OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name);
+ return cond;
+ }
break;
case ACL_WARN:
diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c
index 1874f3238..c89411af8 100644
--- a/src/src/auths/dovecot.c
+++ b/src/src/auths/dovecot.c
@@ -240,7 +240,7 @@ int auth_dovecot_server(auth_instance *ablock, uschar *data)
uschar *p;
int nargs, tmp;
int crequid = 1, cont = 1, fd, ret = DEFER;
- BOOL found = FALSE;
+ BOOL found = FALSE, have_mech_line = FALSE;
HDEBUG(D_auth) debug_printf("dovecot authentication\n");
@@ -300,8 +300,20 @@ int auth_dovecot_server(auth_instance *ablock, uschar *data)
OUT("authentication socket protocol version mismatch");
} else if (Ustrcmp(args[0], US"MECH") == 0) {
CHECK_COMMAND("MECH", 1, INT_MAX);
+ have_mech_line = TRUE;
if (strcmpic(US args[1], ablock->public_name) == 0)
found = TRUE;
+ } else if (Ustrcmp(args[0], US"SPID") == 0) {
+ /* Unfortunately the auth protocol handshake wasn't designed well
+ to differentiate between auth-client/userdb/master. auth-userdb
+ and auth-master send VERSION + SPID lines only and nothing
+ afterwards, while auth-client sends VERSION + MECH + SPID +
+ CUID + more. The simplest way that we can determine if we've
+ connected to the correct socket is to see if MECH line exists or
+ not (alternatively we'd have to have a small timeout after SPID
+ to see if CUID is sent or not). */
+ if (!have_mech_line)
+ OUT("authentication socket type mismatch (connected to auth-master instead of auth-client)");
} else if (Ustrcmp(args[0], US"DONE") == 0) {
CHECK_COMMAND("DONE", 0, 0);
cont = 0;
diff --git a/src/src/deliver.c b/src/src/deliver.c
index ab0815ed4..d00af9c11 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -141,11 +141,13 @@ the first address. */
if (addr->host_list == NULL)
{
deliver_host = deliver_host_address = US"";
+ deliver_host_port = 0;
}
else
{
deliver_host = addr->host_list->name;
deliver_host_address = addr->host_list->address;
+ deliver_host_port = addr->host_list->port;
}
deliver_recipients = addr;
@@ -713,6 +715,43 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
}
#endif
+
+#ifdef EXPERIMENTAL_TPDA
+int
+tpda_raise_event(uschar * action, uschar * event, uschar * ev_data)
+{
+uschar * s;
+if (action)
+ {
+ DEBUG(D_deliver)
+ debug_printf("TPDA(%s): tpda_event_action=|%s| tpda_delivery_IP=%s\n",
+ event,
+ action, deliver_host_address);
+
+ tpda_event = event;
+ tpda_data = ev_data;
+
+ if (!(s = expand_string(action)) && *expand_string_message)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "failed to expand tpda_event_action %s in %s: %s\n",
+ event, transport_name, expand_string_message);
+
+ tpda_event = tpda_data = NULL;
+
+ /* If the expansion returns anything but an empty string, flag for
+ the caller to modify his normal processing
+ */
+ if (s && *s)
+ {
+ DEBUG(D_deliver)
+ debug_printf("TPDA(%s): event_action returned \"%s\"\n", s);
+ return DEFER;
+ }
+ }
+return OK;
+}
+#endif
+
/* If msg is NULL this is a delivery log and logchar is used. Otherwise
this is a nonstandard call; no two-character delivery flag is written
but sender-host and sender are prefixed and "msg" is inserted in the log line.
@@ -736,12 +775,7 @@ have a pointer to the host item that succeeded; local deliveries can have a
pointer to a single host item in their host list, for use by the transport. */
#ifdef EXPERIMENTAL_TPDA
- tpda_delivery_ip = NULL; /* presume no successful remote delivery */
- tpda_delivery_port = 0;
- tpda_delivery_fqdn = NULL;
- tpda_delivery_local_part = NULL;
- tpda_delivery_domain = NULL;
- tpda_delivery_confirmation = NULL;
+ /* presume no successful remote delivery */
lookup_dnssec_authenticated = NULL;
#endif
@@ -790,13 +824,8 @@ if ((log_extra_selector & LX_delivery_size) != 0)
if (addr->transport->info->local)
{
- if (addr->host_list != NULL)
- {
+ if (addr->host_list)
s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
- #ifdef EXPERIMENTAL_TPDA
- tpda_delivery_fqdn = addr->host_list->name;
- #endif
- }
if (addr->shadow_message != NULL)
s = string_cat(s, &size, &ptr, addr->shadow_message,
Ustrlen(addr->shadow_message));
@@ -812,24 +841,20 @@ else
if (continue_sequence > 1)
s = string_cat(s, &size, &ptr, US"*", 1);
- #ifdef EXPERIMENTAL_TPDA
- tpda_delivery_ip = addr->host_used->address;
- tpda_delivery_port = addr->host_used->port;
- tpda_delivery_fqdn = addr->host_used->name;
- tpda_delivery_local_part = addr->local_part;
- tpda_delivery_domain = addr->domain;
- tpda_delivery_confirmation = addr->message;
+#ifdef EXPERIMENTAL_TPDA
+ deliver_host_address = addr->host_used->address;
+ deliver_host_port = addr->host_used->port;
/* DNS lookup status */
lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes"
: addr->host_used->dnssec==DS_NO ? US"no"
: NULL;
- #endif
+#endif
}
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
s = d_tlslog(s, &size, &ptr, addr);
- #endif
+#endif
if (addr->authenticator)
{
@@ -842,10 +867,10 @@ else
}
}
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
if (addr->flags & af_prdr_used)
s = string_append(s, &size, &ptr, 1, US" PRDR");
- #endif
+#endif
}
/* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
@@ -885,19 +910,22 @@ s[ptr] = 0;
log_write(0, flags, "%s", s);
#ifdef EXPERIMENTAL_TPDA
-if (addr->transport->tpda_delivery_action)
{
- DEBUG(D_deliver)
- debug_printf(" TPDA(Delivery): tpda_deliver_action=|%s| tpda_delivery_IP=%s\n",
- addr->transport->tpda_delivery_action, tpda_delivery_ip);
-
- router_name = addr->router->name;
- transport_name = addr->transport->name;
- if (!expand_string(addr->transport->tpda_delivery_action) && *expand_string_message)
- log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_deliver_action in %s: %s\n",
- transport_name, expand_string_message);
- router_name = NULL;
- transport_name = NULL;
+ uschar * save_domain = deliver_domain;
+ uschar * save_local = deliver_localpart;
+
+ router_name = addr->router ? addr->router->name : NULL;
+ transport_name = addr->transport ? addr->transport->name : NULL;
+ deliver_domain = addr->domain;
+ deliver_localpart = addr->local_part;
+
+ (void) tpda_raise_event(addr->transport->tpda_event_action, US"msg:delivery",
+ addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
+ ? addr->message : NULL);
+
+ deliver_localpart = save_local;
+ deliver_domain = save_domain;
+ router_name = transport_name = NULL;
}
#endif
store_reset(reset_point);
@@ -1097,7 +1125,7 @@ if (result == OK)
}
/* Certificates for logging (via TPDA) */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_out.ourcert = addr->ourcert;
addr->ourcert = NULL;
tls_out.peercert = addr->peercert;
@@ -1106,11 +1134,11 @@ if (result == OK)
tls_out.cipher = addr->cipher;
tls_out.peerdn = addr->peerdn;
tls_out.ocsp = addr->ocsp;
- #endif
+#endif
delivery_log(LOG_MAIN, addr, logchar, NULL);
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (tls_out.ourcert)
{
tls_free_cert(tls_out.ourcert);
@@ -1124,7 +1152,7 @@ if (result == OK)
tls_out.cipher = NULL;
tls_out.peerdn = NULL;
tls_out.ocsp = OCSP_NOT_REQ;
- #endif
+#endif
}
@@ -1305,9 +1333,9 @@ else
if (addr->host_used != NULL)
s = d_hostlog(s, &size, &ptr, addr);
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
s = d_tlslog(s, &size, &ptr, addr);
- #endif
+#endif
if (addr->basic_errno > 0)
s = string_append(s, &size, &ptr, 2, US": ",
@@ -1896,19 +1924,19 @@ if ((pid = fork()) == 0)
diagnosis that it's reasonable to make them something that has to be explicitly requested.
*/
- #ifdef RLIMIT_CORE
+#ifdef RLIMIT_CORE
struct rlimit rl;
rl.rlim_cur = 0;
rl.rlim_max = 0;
if (setrlimit(RLIMIT_CORE, &rl) < 0)
{
- #ifdef SETRLIMIT_NOT_SUPPORTED
+# ifdef SETRLIMIT_NOT_SUPPORTED
if (errno != ENOSYS && errno != ENOTSUP)
- #endif
+# endif
log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_CORE) failed: %s",
strerror(errno));
}
- #endif
+#endif
/* Reset the random number generator, so different processes don't all
have the same sequence. */
@@ -2995,7 +3023,7 @@ while (!done)
it in with the other info, in order to keep each message short enough to
guarantee it won't be split in the pipe. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
case 'X':
if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */
switch (*ptr++)
@@ -3023,17 +3051,17 @@ while (!done)
(void) tls_import_cert(ptr, &addr->ourcert);
break;
- #ifndef DISABLE_OCSP
+# ifndef DISABLE_OCSP
case '4':
addr->ocsp = OCSP_NOT_REQ;
if (*ptr)
addr->ocsp = *ptr - '0';
break;
- #endif
+# endif
}
while (*ptr++);
break;
- #endif /*SUPPORT_TLS*/
+#endif /*SUPPORT_TLS*/
case 'C': /* client authenticator information */
switch (*ptr++)
@@ -3057,14 +3085,14 @@ while (!done)
break;
#endif
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
case 'D':
if (addr == NULL) break;
memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
ptr += sizeof(addr->dsn_aware);
DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
break;
- #endif
+#endif
case 'A':
if (addr == NULL)
@@ -3962,11 +3990,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
that it can use either of them, though it prefers O_NONBLOCK, which
distinguishes between EOF and no-more-data. */
- #ifdef O_NONBLOCK
+#ifdef O_NONBLOCK
(void)fcntl(pfd[pipe_read], F_SETFL, O_NONBLOCK);
- #else
+#else
(void)fcntl(pfd[pipe_read], F_SETFL, O_NDELAY);
- #endif
+#endif
/* If the maximum number of subprocesses already exist, wait for a process
to finish. If we ran out of file descriptors, parmax will have been reduced
@@ -4138,7 +4166,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
#endif
/* Use an X item only if there's something to send */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (addr->cipher)
{
ptr = big_buffer;
@@ -4174,7 +4202,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
*ptr++ = 0;
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
- #ifndef DISABLE_OCSP
+# ifndef DISABLE_OCSP
if (addr->ocsp > OCSP_NOT_REQ)
{
ptr = big_buffer;
@@ -4182,8 +4210,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
while(*ptr++);
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
- # endif
- #endif /*SUPPORT_TLS*/
+# endif
+#endif /*SUPPORT_TLS*/
if (client_authenticator)
{
@@ -4207,17 +4235,17 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
}
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
if (addr->flags & af_prdr_used)
rmt_dlv_checked_write(fd, "P", 1);
- #endif
+#endif
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
big_buffer[0] = 'D';
memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware));
rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1);
DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
- #endif
+#endif
/* Retry information: for most success cases this will be null. */
@@ -4932,7 +4960,7 @@ attempted. */
if (deliver_freeze)
{
- #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
+#ifdef SUPPORT_MOVE_FROZEN_MESSAGES
/* Moving to another directory removes the message from Exim's view. Other
tools must be used to deal with it. Logging of this action happens in
spool_move_message() and its subfunctions. */
@@ -4940,7 +4968,7 @@ if (deliver_freeze)
if (move_frozen_messages &&
spool_move_message(id, message_subdir, US"", US"F"))
return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */
- #endif
+#endif
/* For all frozen messages (bounces or not), timeout_frozen_after sets the
maximum time to keep messages that are frozen. Thaw if we reach it, with a
@@ -5369,13 +5397,13 @@ if (process_recipients != RECIP_IGNORE)
if (r->pno >= 0)
new->onetime_parent = recipients_list[r->pno].address;
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
/* If DSN support is enabled, set the dsn flags and the original receipt
to be passed on to other DSN enabled MTAs */
new->dsn_flags = r->dsn_flags & rf_dsnflags;
new->dsn_orcpt = r->orcpt;
DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags);
- #endif
+#endif
switch (process_recipients)
{
@@ -6311,21 +6339,21 @@ if (addr_remote != NULL)
regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
FALSE, TRUE);
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (regex_STARTTLS == NULL) regex_STARTTLS =
regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
- #endif
+#endif
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
if (regex_PRDR == NULL) regex_PRDR =
regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
- #endif
+#endif
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
/* Set the regex to check for DSN support on remote MTA */
if (regex_DSN == NULL) regex_DSN =
regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
- #endif
+#endif
/* Now sort the addresses if required, and do the deliveries. The yield of
do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
@@ -6522,8 +6550,8 @@ if (addr_senddsn != NULL)
"Content-type: text/plain; charset=us-ascii\n\n"
"This message was created automatically by mail delivery software.\n"
- " ----- The following addresses had successful delivery notifications -----\n"
- qualify_domain_sender, sender_addres, boundaryStrs, boundarySt);
+ " ----- The following addresses had successful delivery notifications -----\n",
+ qualify_domain_sender, sender_address, boundaryStr, boundaryStr);
addr_dsntmp = addr_senddsn;
while(addr_dsntmp)
@@ -6955,8 +6983,9 @@ wording. */
for (addr = handled_addr; addr; addr = addr->next)
{
fprintf(f, "Action: failed\n"
- "Final-Recipient: rfc822;%s\n", addr->address
- "Status: 5.0.0\n");
+ "Final-Recipient: rfc822;%s\n"
+ "Status: 5.0.0\n",
+ addr->address);
if (addr->host_used && addr->host_used->name)
fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n",
addr->host_used->name, addr->basic_errno);
@@ -7665,10 +7694,10 @@ if (remove_journal)
/* Move the message off the spool if reqested */
- #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
+#ifdef SUPPORT_MOVE_FROZEN_MESSAGES
if (deliver_freeze && move_frozen_messages)
(void)spool_move_message(id, message_subdir, US"", US"F");
- #endif
+#endif
}
/* Closing the data file frees the lock; if the file has been unlinked it
diff --git a/src/src/exim.c b/src/src/exim.c
index 8a9de72ac..85a7c812c 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -267,6 +267,10 @@ will wait for ever, so we panic in this instance. (There was a case of this
when a bug in a function that calls milliwait() caused it to pass invalid data.
That's when I added the check. :-)
+We assume it to be not worth sleeping for under 100us; this value will
+require revisiting as hardware advances. This avoids the issue of
+a zero-valued timer setting meaning "never fire".
+
Argument: an itimerval structure containing the interval
Returns: nothing
*/
@@ -276,6 +280,9 @@ milliwait(struct itimerval *itval)
{
sigset_t sigmask;
sigset_t old_sigmask;
+
+if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
+ return;
(void)sigemptyset(&sigmask); /* Empty mask */
(void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */
(void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */
diff --git a/src/src/exipick.src b/src/src/exipick.src
index ed3b66154..4708ebb4a 100644
--- a/src/src/exipick.src
+++ b/src/src/exipick.src
@@ -1020,6 +1020,12 @@ sub _parse_header {
return($self->_error("incorrect format: $_")) if (length($2) != $3);
$self->{_recips}{$1} = { pno => $4, errors_to => $2 };
$addr = $1;
+ } elsif (/^(\S*)\s(\S*)\s(\d+),(\d+)\s(\S*)\s(\d+),(-?\d+)#3$/) {
+ #print STDERR "exim4 new type #3 DSN (untested): $_\n";
+ return($self->_error("incorrect format: $_"))
+ if ((length($2) != $3) || (length($5) != $6));
+ $self->{_recips}{$1} = { pno => $7, errors_to => $5 };
+ $addr = $1;
} elsif (/^.*#(\d+)$/) {
#print STDERR "exim4 #$1 style (unimplemented): $_\n";
$self->_error("exim4 #$1 style (unimplemented): $_");
diff --git a/src/src/expand.c b/src/src/expand.c
index b0e76ba27..8111c4212 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -501,6 +501,7 @@ static var_entry var_table[] = {
{ "host_data", vtype_stringptr, &host_data },
{ "host_lookup_deferred",vtype_int, &host_lookup_deferred },
{ "host_lookup_failed", vtype_int, &host_lookup_failed },
+ { "host_port", vtype_int, &deliver_host_port },
{ "inode", vtype_ino, &deliver_inode },
{ "interface_address", vtype_stringptr, &interface_address },
{ "interface_port", vtype_int, &interface_port },
@@ -712,14 +713,12 @@ static var_entry var_table[] = {
{ "tod_zone", vtype_todzone, NULL },
{ "tod_zulu", vtype_todzulu, NULL },
#ifdef EXPERIMENTAL_TPDA
+ { "tpda_data", vtype_stringptr, &tpda_data },
+
+ /*XXX want to use generic vars for as many of these as possible*/
{ "tpda_defer_errno", vtype_int, &tpda_defer_errno },
- { "tpda_defer_errstr", vtype_stringptr, &tpda_defer_errstr },
- { "tpda_delivery_confirmation", vtype_stringptr, &tpda_delivery_confirmation },
- { "tpda_delivery_domain", vtype_stringptr, &tpda_delivery_domain },
- { "tpda_delivery_fqdn", vtype_stringptr, &tpda_delivery_fqdn },
- { "tpda_delivery_ip", vtype_stringptr, &tpda_delivery_ip },
- { "tpda_delivery_local_part",vtype_stringptr,&tpda_delivery_local_part },
- { "tpda_delivery_port", vtype_int, &tpda_delivery_port },
+
+ { "tpda_event", vtype_stringptr, &tpda_event },
#endif
{ "transport_name", vtype_stringptr, &transport_name },
{ "value", vtype_stringptr, &lookup_value },
@@ -919,7 +918,9 @@ vaguely_random_number(int max)
#ifdef HAVE_ARC4RANDOM
/* cryptographically strong randomness, common on *BSD platforms, not
so much elsewhere. Alas. */
+#ifndef NOT_HAVE_ARC4RANDOM_STIR
arc4random_stir();
+#endif
#elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV)
#ifdef HAVE_SRANDOMDEV
/* uses random(4) for seeding */
diff --git a/src/src/functions.h b/src/src/functions.h
index a6257a913..fee4429a5 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -44,7 +44,7 @@ extern uschar * tls_cert_fprt_sha1(void *);
extern uschar * tls_cert_fprt_sha256(void *);
extern int tls_client_start(int, host_item *, address_item *,
- void *);
+ transport_instance *);
extern void tls_close(BOOL, BOOL);
extern int tls_export_cert(uschar *, size_t, void *);
extern int tls_feof(void);
@@ -337,7 +337,11 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *,
extern void sigalrm_handler(int);
extern BOOL smtp_buffered(void);
extern void smtp_closedown(uschar *);
-extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar *);
+extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar *
+#ifdef EXPERIMENTAL_TPDA
+ , uschar *
+#endif
+ );
extern int smtp_feof(void);
extern int smtp_ferror(void);
extern uschar *smtp_get_connection_info(void);
diff --git a/src/src/globals.c b/src/src/globals.c
index 409c324e9..f5ed80fa2 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -585,6 +585,7 @@ time_t deliver_frozen_at = 0;
uschar *deliver_home = NULL;
uschar *deliver_host = NULL;
uschar *deliver_host_address = NULL;
+int deliver_host_port = 0;
uschar *deliver_in_buffer = NULL;
ino_t deliver_inode = 0;
uschar *deliver_localpart = NULL;
@@ -1337,13 +1338,8 @@ BOOL timestamps_utc = FALSE;
#ifdef EXPERIMENTAL_TPDA
int tpda_defer_errno = 0;
-uschar *tpda_defer_errstr = NULL;
-uschar *tpda_delivery_ip = NULL;
-int tpda_delivery_port = 0;
-uschar *tpda_delivery_fqdn = NULL;
-uschar *tpda_delivery_local_part= NULL;
-uschar *tpda_delivery_domain = NULL;
-uschar *tpda_delivery_confirmation = NULL;
+uschar *tpda_event = NULL;
+uschar *tpda_data = NULL;
#endif
transport_instance *transports = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index 1adda6411..0b5335e6f 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -332,6 +332,7 @@ extern uschar *deliver_home; /* Home directory for pipes */
extern uschar *deliver_host; /* (First) host for routed local deliveries */
/* Remote host for filter */
extern uschar *deliver_host_address; /* Address for remote delivery filter */
+extern int deliver_host_port; /* Address for remote delivery filter */
extern uschar *deliver_in_buffer; /* Buffer for copying file */
extern ino_t deliver_inode; /* Inode for appendfile */
extern uschar *deliver_localpart; /* The local part for delivery */
@@ -877,13 +878,8 @@ extern BOOL timestamps_utc; /* Use UTC for all times */
#ifdef EXPERIMENTAL_TPDA
extern int tpda_defer_errno; /* error number set when a remote delivery is deferred with a host error */
-extern uschar *tpda_defer_errstr; /* error string set when a remote delivery is deferred with a host error */
-extern uschar *tpda_delivery_ip; /* IP of host, which has accepted delivery */
-extern int tpda_delivery_port; /* port of host, which has accepted delivery */
-extern uschar *tpda_delivery_fqdn; /* FQDN of host, which has accepted delivery */
-extern uschar *tpda_delivery_local_part;/* local part of address being delivered */
-extern uschar *tpda_delivery_domain; /* domain part of address being delivered */
-extern uschar *tpda_delivery_confirmation; /* SMTP confirmation message */
+extern uschar *tpda_event; /* event classification */
+extern uschar *tpda_data;; /* event data */
#endif
extern uschar *transport_name; /* Name of transport last started */
diff --git a/src/src/mime.c b/src/src/mime.c
index 6a9e31a0a..95d3da472 100644
--- a/src/src/mime.c
+++ b/src/src/mime.c
@@ -601,16 +601,28 @@ NEXT_PARAM_SEARCH:
int param_value_len = 0;
/* found an interesting parameter? */
- if (strncmpic(mp->name, p,mp->namelen) == 0)
+ if (strncmpic(mp->name, p, mp->namelen) == 0)
{
uschar *q = p + mp->namelen;
+ int size = 0;
+ int ptr = 0;
+
/* yes, grab the value and copy to its corresponding expansion variable */
- while(*q != ';') q++;
- param_value_len = (q - (p + mp->namelen));
- param_value = (uschar *)malloc(param_value_len+1);
- memset(param_value,0,param_value_len+1);
- q = p + mp->namelen;
- Ustrncpy(param_value, q, param_value_len);
+ while(*q && *q != ';') /* ; terminates */
+ {
+ if (*q == '"')
+ {
+ q++; /* skip leading " */
+ while(*q && *q != '"') /* which protects ; */
+ param_value = string_cat(param_value, &size, &ptr, q++, 1);
+ if (*q) q++; /* skip trailing " */
+ }
+ else
+ param_value = string_cat(param_value, &size, &ptr, q++, 1);
+ }
+ param_value[ptr++] = '\0';
+ param_value_len = ptr;
+
param_value = rfc2047_decode(param_value, check_rfc2047_length, NULL, 32, &param_value_len, &q);
debug_printf("Found %s MIME parameter in %s header, value is '%s'\n", mp->name, mime_header_list[i].name, param_value);
*((uschar **)(mp->value)) = param_value;
diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c
index b6ff51108..4920b7371 100644
--- a/src/src/smtp_out.c
+++ b/src/src/smtp_out.c
@@ -165,13 +165,18 @@ Arguments:
timeout timeout value or 0
keepalive TRUE to use keepalive
dscp DSCP value to assign to socket
+ tpda_event event expansion
Returns: connected socket number, or -1 with errno set
*/
int
smtp_connect(host_item *host, int host_af, int port, uschar *interface,
- int timeout, BOOL keepalive, const uschar *dscp)
+ int timeout, BOOL keepalive, const uschar *dscp
+#ifdef EXPERIMENTAL_TPDA
+ , uschar * tpda_event
+#endif
+ )
{
int on = 1;
int save_errno = 0;
@@ -198,6 +203,13 @@ HDEBUG(D_transport|D_acl|D_v)
host->address, port, interface);
}
+#ifdef EXPERIMENTAL_TPDA
+ /*XXX Called from both delivery and verify. Is that status observable? */
+ deliver_host_address = host->address;
+ deliver_host_port = port;
+ if (tpda_raise_event(tpda_event, US"tcp:connect", NULL) == DEFER) return -1;
+#endif
+
/* Create the socket */
if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
diff --git a/src/src/structs.h b/src/src/structs.h
index 27b73e903..4f7862dc5 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -188,7 +188,7 @@ typedef struct transport_instance {
BOOL log_defer_output;
BOOL retry_use_local_part; /* Defaults true for local, false for remote */
#ifdef EXPERIMENTAL_TPDA
- uschar *tpda_delivery_action; /* String to expand on success */
+ uschar *tpda_event_action; /* String to expand on notable events */
#endif
} transport_instance;
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 266ab8909..b7eae1793 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -47,6 +47,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
# warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
#endif
+#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_TPDA)
+# warning "GnuTLS library version too old; TPDA tls:cert event unsupported"
+# undef EXPERIMENTAL_TPDA
+#endif
#ifndef DISABLE_OCSP
# include <gnutls/ocsp.h>
@@ -115,6 +119,9 @@ typedef struct exim_gnutls_state {
#ifdef EXPERIMENTAL_CERTNAMES
uschar *exp_tls_verify_cert_hostnames;
#endif
+#ifdef EXPERIMENTAL_TPDA
+ uschar *event_action;
+#endif
tls_support *tlsp; /* set in tls_init() */
@@ -133,6 +140,9 @@ static const exim_gnutls_state_st exim_gnutls_state_init = {
#ifdef EXPERIMENTAL_CERTNAMES
NULL,
#endif
+#ifdef EXPERIMENTAL_TPDA
+ NULL,
+#endif
NULL,
NULL, 0, 0, 0, 0,
};
@@ -144,7 +154,9 @@ context we're currently dealing with" pointer and rely upon being
single-threaded to keep from processing data on an inbound TLS connection while
talking to another TLS connection for an outbound check. This does mean that
there's no way for heart-beats to be responded to, for the duration of the
-second connection. */
+second connection.
+XXX But see gnutls_session_get_ptr()
+*/
static exim_gnutls_state_st state_server, state_client;
@@ -174,18 +186,18 @@ static BOOL exim_gnutls_base_init_done = FALSE;
the library logging; a value less than 0 disables the calls to set up logging
callbacks. */
#ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL
-#define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1
+# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1
#endif
#ifndef EXIM_CLIENT_DH_MIN_BITS
-#define EXIM_CLIENT_DH_MIN_BITS 1024
+# define EXIM_CLIENT_DH_MIN_BITS 1024
#endif
/* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we
can ask for a bit-strength. Without that, we stick to the constant we had
before, for now. */
#ifndef EXIM_SERVER_DH_BITS_PRE2_12
-#define EXIM_SERVER_DH_BITS_PRE2_12 1024
+# define EXIM_SERVER_DH_BITS_PRE2_12 1024
#endif
#define exim_gnutls_err_check(Label) do { \
@@ -1512,6 +1524,52 @@ return 0;
#endif
+#ifdef EXPERIMENTAL_TPDA
+/*
+We use this callback to get observability and detail-level control
+for an exim client TLS connection, raising a TPDA tls:cert event
+for each cert in the chain presented by the server. Any event
+can deny verification.
+
+Return 0 for the handshake to continue or non-zero to terminate.
+*/
+
+static int
+client_verify_cb(gnutls_session_t session)
+{
+const gnutls_datum * cert_list;
+unsigned int cert_list_size = 0;
+gnutls_x509_crt_t crt;
+int rc;
+exim_gnutls_state_st * state = gnutls_session_get_ptr(session);
+
+cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
+if (cert_list)
+ while (cert_list_size--)
+ {
+ rc = import_cert(&cert_list[cert_list_size], &crt);
+ if (rc != GNUTLS_E_SUCCESS)
+ {
+ DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n",
+ cert_list_size, gnutls_strerror(rc));
+ break;
+ }
+
+ state->tlsp->peercert = crt;
+ if (tpda_raise_event(state->event_action,
+ US"tls:cert", string_sprintf("%d", cert_list_size)) == DEFER)
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify denied by event-action: depth=%d", cert_list_size);
+ return 1; /* reject */
+ }
+ state->tlsp->peercert = NULL;
+ }
+
+return 0;
+}
+
+#endif
@@ -1694,7 +1752,7 @@ Arguments:
fd the fd of the connection
host connected host (for messages)
addr the first address (not used)
- ob smtp transport options
+ tb transport (always smtp)
Returns: OK/DEFER/FAIL (because using common functions),
but for a client, DEFER and FAIL have the same meaning
@@ -1703,9 +1761,10 @@ Returns: OK/DEFER/FAIL (because using common functions),
int
tls_client_start(int fd, host_item *host,
address_item *addr ARG_UNUSED,
- void *v_ob)
+ transport_instance *tb)
{
-smtp_transport_options_block *ob = v_ob;
+smtp_transport_options_block *ob =
+ (smtp_transport_options_block *)tb->options_block;
int rc;
const char *error;
exim_gnutls_state_st *state = NULL;
@@ -1804,6 +1863,15 @@ if (request_ocsp)
}
#endif
+#ifdef EXPERIMENTAL_TPDA
+if (tb->tpda_event_action)
+ {
+ state->event_action = tb->tpda_event_action;
+ gnutls_session_set_ptr(state->session, state);
+ gnutls_certificate_set_verify_function(state->x509_cred, client_verify_cb);
+ }
+#endif
+
gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)(long) fd);
state->fd_in = fd;
state->fd_out = fd;
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 343122615..735ebff06 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -120,6 +120,9 @@ typedef struct tls_ext_ctx_cb {
#ifdef EXPERIMENTAL_CERTNAMES
uschar * verify_cert_hostnames;
#endif
+#ifdef EXPERIMENTAL_TPDA
+ uschar * event_action;
+#endif
} tls_ext_ctx_cb;
/* should figure out a cleanup of API to handle state preserved per
@@ -266,6 +269,9 @@ when asked. We get here only if a certificate has been received. Handling of
optional verification for this case is done when requesting SSL to verify, by
setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
Arguments:
state current yes/no state as 1/0
x509ctx certificate information.
@@ -279,6 +285,7 @@ verify_callback(int state, X509_STORE_CTX *x509ctx,
tls_support *tlsp, BOOL *calledp, BOOL *optionalp)
{
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
static uschar txt[256];
X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
@@ -286,7 +293,7 @@ X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
if (state == 0)
{
log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s",
- X509_STORE_CTX_get_error_depth(x509ctx),
+ depth,
X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)),
txt);
tlsp->certificate_verified = FALSE;
@@ -300,10 +307,9 @@ if (state == 0)
"tls_try_verify_hosts)\n");
}
-else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0)
+else if (depth != 0)
{
- DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n",
- X509_STORE_CTX_get_error_depth(x509ctx), txt);
+ DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt);
#ifndef DISABLE_OCSP
if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store)
{ /* client, wanting stapling */
@@ -315,6 +321,23 @@ else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0)
ERR_clear_error();
}
#endif
+#ifdef EXPERIMENTAL_TPDA
+ if (tlsp == &tls_out && client_static_cbinfo->event_action)
+ {
+ tlsp->peercert = X509_dup(cert);
+ if (tpda_raise_event(client_static_cbinfo->event_action,
+ US"tls:cert", string_sprintf("%d", depth)) == DEFER)
+ {
+ log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+ "depth=%d cert=%s", depth, txt);
+ tlsp->certificate_verified = FALSE;
+ *calledp = TRUE;
+ return 0; /* reject */
+ }
+ X509_free(tlsp->peercert);
+ tlsp->peercert = NULL;
+ }
+#endif
}
else
{
@@ -367,13 +390,28 @@ else
# endif
#endif /*EXPERIMENTAL_CERTNAMES*/
+#ifdef EXPERIMENTAL_TPDA
+ if (tlsp == &tls_out)
+ {
+ if (tpda_raise_event(client_static_cbinfo->event_action,
+ US"tls:cert", US"0") == DEFER)
+ {
+ log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+ "depth=0 cert=%s", txt);
+ tlsp->certificate_verified = FALSE;
+ *calledp = TRUE;
+ return 0; /* reject */
+ }
+ }
+#endif
+
DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
*calledp ? "" : " authenticated", txt);
if (!*calledp) tlsp->certificate_verified = TRUE;
*calledp = TRUE;
}
-return 1; /* accept */
+return 1; /* accept, at least for this level */
}
static int
@@ -917,7 +955,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
{
tls_out.ocsp = OCSP_FAILED;
if (log_extra_selector & LX_tls_cipher)
- log_write(0, LOG_MAIN, "Received TLS status response, parse error");
+ log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
else
DEBUG(D_tls) debug_printf(" parse error\n");
return 0;
@@ -927,7 +965,7 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
{
tls_out.ocsp = OCSP_FAILED;
if (log_extra_selector & LX_tls_cipher)
- log_write(0, LOG_MAIN, "Received TLS status response, error parsing response");
+ log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
else
DEBUG(D_tls) debug_printf(" error parsing response\n");
OCSP_RESPONSE_free(rsp);
@@ -957,6 +995,8 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
{
tls_out.ocsp = OCSP_FAILED;
+ if (log_extra_selector & LX_tls_cipher)
+ log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
@@ -1059,7 +1099,7 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
long init_options;
int rc;
BOOL okay;
-tls_ext_ctx_cb *cbinfo;
+tls_ext_ctx_cb * cbinfo;
cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
cbinfo->certificate = certificate;
@@ -1077,6 +1117,9 @@ else
cbinfo->dhparam = dhparam;
cbinfo->server_cipher_list = NULL;
cbinfo->host = host;
+#ifdef EXPERIMENTAL_TPDA
+cbinfo->event_action = NULL;
+#endif
SSL_load_error_strings(); /* basic set up */
OpenSSL_add_ssl_algorithms();
@@ -1717,7 +1760,7 @@ Argument:
fd the fd of the connection
host connected host (for messages)
addr the first address
- ob smtp transport options
+ tb transport (always smtp)
Returns: OK on success
FAIL otherwise - note that tls_error() will not give DEFER
@@ -1726,9 +1769,10 @@ Returns: OK on success
int
tls_client_start(int fd, host_item *host, address_item *addr,
- void *v_ob)
+ transport_instance *tb)
{
-smtp_transport_options_block * ob = v_ob;
+smtp_transport_options_block * ob =
+ (smtp_transport_options_block *)tb->options_block;
static uschar txt[256];
uschar * expciphers;
X509 * server_cert;
@@ -1910,6 +1954,9 @@ if (request_ocsp)
}
#endif
+#ifdef EXPERIMENTAL_TPDA
+client_static_cbinfo->event_action = tb->tpda_event_action;
+#endif
/* There doesn't seem to be a built-in timeout on connection. */
diff --git a/src/src/transport.c b/src/src/transport.c
index 3648bfc82..31437b146 100644
--- a/src/src/transport.c
+++ b/src/src/transport.c
@@ -95,8 +95,8 @@ optionlist optionlist_transports[] = {
{ "shadow_transport", opt_stringptr|opt_public,
(void *)offsetof(transport_instance, shadow) },
#ifdef EXPERIMENTAL_TPDA
- { "tpda_delivery_action",opt_stringptr | opt_public,
- (void *)offsetof(transport_instance, tpda_delivery_action) },
+ { "tpda_event_action",opt_stringptr | opt_public,
+ (void *)offsetof(transport_instance, tpda_event_action) },
#endif
{ "transport_filter", opt_stringptr|opt_public,
(void *)offsetof(transport_instance, filter_command) },
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 1865adee8..7b2a7d559 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -180,10 +180,6 @@ optionlist smtp_transport_options[] = {
{ "tls_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
#endif
-#ifdef EXPERIMENTAL_TPDA
- ,{ "tpda_host_defer_action", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tpda_host_defer_action) },
-#endif
};
/* Size of the options list. An extern variable has to be used so that its
@@ -273,9 +269,6 @@ smtp_transport_options_block smtp_transport_option_defaults = {
NULL, /* dkim_sign_headers */
NULL /* dkim_strict */
#endif
-#ifdef EXPERIMENTAL_TPDA
- ,NULL /* tpda_host_defer_action */
-#endif
};
#ifdef EXPERIMENTAL_DSN
@@ -497,7 +490,8 @@ Arguments:
Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE
*/
-static BOOL check_response(host_item *host, int *errno_value, int more_errno,
+static BOOL
+check_response(host_item *host, int *errno_value, int more_errno,
uschar *buffer, int *yield, uschar **message, BOOL *pass_message)
{
uschar *pl = US"";
@@ -648,7 +642,6 @@ else
It might, for example, be used to write to the database log.
Arguments:
- ob transport options block
addr the address item containing error information
host the current host
@@ -656,36 +649,39 @@ Returns: nothing
*/
static void
-tpda_deferred(smtp_transport_options_block *ob, address_item *addr, host_item *host)
+tpda_deferred(address_item *addr, host_item *host)
{
-uschar *action = ob->tpda_host_defer_action;
+uschar * action = addr->transport->tpda_event_action;
+uschar * save_domain;
+uschar * save_local;
+
if (!action)
- return;
-
-tpda_delivery_ip = string_copy(host->address);
-tpda_delivery_port = (host->port == PORT_NONE)? 25 : host->port;
-tpda_delivery_fqdn = string_copy(host->name);
-tpda_delivery_local_part = string_copy(addr->local_part);
-tpda_delivery_domain = string_copy(addr->domain);
-tpda_defer_errno = addr->basic_errno;
-
-tpda_defer_errstr = addr->message
- ? addr->basic_errno > 0
- ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno))
- : string_copy(addr->message)
- : addr->basic_errno > 0
- ? string_copy(US strerror(addr->basic_errno))
- : NULL;
+ return;
-DEBUG(D_transport)
- debug_printf(" TPDA(host defer): tpda_host_defer_action=|%s| tpda_delivery_IP=%s\n",
- action, tpda_delivery_ip);
+save_domain = deliver_domain;
+save_local = deliver_localpart;
+
+/*XXX would ip & port already be set up? */
+deliver_host_address = string_copy(host->address);
+deliver_host_port = (host->port == PORT_NONE)? 25 : host->port;
+tpda_defer_errno = addr->basic_errno;
router_name = addr->router->name;
transport_name = addr->transport->name;
-if (!expand_string(action) && *expand_string_message)
- log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_defer_action in %s: %s\n",
- transport_name, expand_string_message);
+deliver_domain = addr->domain;
+deliver_localpart = addr->local_part;
+
+(void) tpda_raise_event(action, US"msg:host:defer",
+ addr->message
+ ? addr->basic_errno > 0
+ ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno))
+ : string_copy(addr->message)
+ : addr->basic_errno > 0
+ ? string_copy(US strerror(addr->basic_errno))
+ : NULL);
+
+deliver_localpart = save_local;
+deliver_domain = save_domain;
router_name = transport_name = NULL;
}
#endif
@@ -952,147 +948,147 @@ smtp_auth(uschar *buffer, unsigned bufsize, address_item *addrlist, host_item *h
smtp_transport_options_block *ob, BOOL is_esmtp,
smtp_inblock *ibp, smtp_outblock *obp)
{
- int require_auth;
- uschar *fail_reason = US"server did not advertise AUTH support";
+int require_auth;
+uschar *fail_reason = US"server did not advertise AUTH support";
- smtp_authenticated = FALSE;
- client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
- require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL,
- host->name, host->address, NULL);
+smtp_authenticated = FALSE;
+client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
+require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL,
+ host->name, host->address, NULL);
- if (is_esmtp && !regex_AUTH) regex_AUTH =
- regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
- FALSE, TRUE);
+if (is_esmtp && !regex_AUTH) regex_AUTH =
+ regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
+ FALSE, TRUE);
- if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
- {
- uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
- expand_nmax = -1; /* reset */
+if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+ {
+ uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
+ expand_nmax = -1; /* reset */
- /* Must not do this check until after we have saved the result of the
- regex match above. */
+ /* Must not do this check until after we have saved the result of the
+ regex match above. */
- if (require_auth == OK ||
- verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name,
- host->address, NULL) == OK)
- {
- auth_instance *au;
- fail_reason = US"no common mechanisms were found";
+ if (require_auth == OK ||
+ verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name,
+ host->address, NULL) == OK)
+ {
+ auth_instance *au;
+ fail_reason = US"no common mechanisms were found";
- DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
+ DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
- /* Scan the configured authenticators looking for one which is configured
- for use as a client, which is not suppressed by client_condition, and
- whose name matches an authentication mechanism supported by the server.
- If one is found, attempt to authenticate by calling its client function.
- */
+ /* Scan the configured authenticators looking for one which is configured
+ for use as a client, which is not suppressed by client_condition, and
+ whose name matches an authentication mechanism supported by the server.
+ If one is found, attempt to authenticate by calling its client function.
+ */
- for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
- {
- uschar *p = names;
- if (!au->client ||
- (au->client_condition != NULL &&
- !expand_check_condition(au->client_condition, au->name,
- US"client authenticator")))
- {
- DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
- au->name,
- (au->client)? "client_condition is false" :
- "not configured as a client");
- continue;
- }
+ for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
+ {
+ uschar *p = names;
+ if (!au->client ||
+ (au->client_condition != NULL &&
+ !expand_check_condition(au->client_condition, au->name,
+ US"client authenticator")))
+ {
+ DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
+ au->name,
+ (au->client)? "client_condition is false" :
+ "not configured as a client");
+ continue;
+ }
- /* Loop to scan supported server mechanisms */
+ /* Loop to scan supported server mechanisms */
- while (*p != 0)
- {
- int rc;
- int len = Ustrlen(au->public_name);
- while (isspace(*p)) p++;
+ while (*p != 0)
+ {
+ int rc;
+ int len = Ustrlen(au->public_name);
+ while (isspace(*p)) p++;
- if (strncmpic(au->public_name, p, len) != 0 ||
- (p[len] != 0 && !isspace(p[len])))
- {
- while (*p != 0 && !isspace(*p)) p++;
- continue;
- }
+ if (strncmpic(au->public_name, p, len) != 0 ||
+ (p[len] != 0 && !isspace(p[len])))
+ {
+ while (*p != 0 && !isspace(*p)) p++;
+ continue;
+ }
- /* Found data for a listed mechanism. Call its client entry. Set
- a flag in the outblock so that data is overwritten after sending so
- that reflections don't show it. */
+ /* Found data for a listed mechanism. Call its client entry. Set
+ a flag in the outblock so that data is overwritten after sending so
+ that reflections don't show it. */
- fail_reason = US"authentication attempt(s) failed";
- obp->authenticating = TRUE;
- rc = (au->info->clientcode)(au, ibp, obp,
- ob->command_timeout, buffer, bufsize);
- obp->authenticating = FALSE;
- DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
- au->name, rc);
+ fail_reason = US"authentication attempt(s) failed";
+ obp->authenticating = TRUE;
+ rc = (au->info->clientcode)(au, ibp, obp,
+ ob->command_timeout, buffer, bufsize);
+ obp->authenticating = FALSE;
+ DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
+ au->name, rc);
- /* A temporary authentication failure must hold up delivery to
- this host. After a permanent authentication failure, we carry on
- to try other authentication methods. If all fail hard, try to
- deliver the message unauthenticated unless require_auth was set. */
+ /* A temporary authentication failure must hold up delivery to
+ this host. After a permanent authentication failure, we carry on
+ to try other authentication methods. If all fail hard, try to
+ deliver the message unauthenticated unless require_auth was set. */
- switch(rc)
- {
- case OK:
- smtp_authenticated = TRUE; /* stops the outer loop */
- client_authenticator = au->name;
- if (au->set_client_id != NULL)
- client_authenticated_id = expand_string(au->set_client_id);
- break;
-
- /* Failure after writing a command */
-
- case FAIL_SEND:
- return FAIL_SEND;
-
- /* Failure after reading a response */
-
- case FAIL:
- if (errno != 0 || buffer[0] != '5') return FAIL;
- log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
- au->name, host->name, host->address, buffer);
- break;
-
- /* Failure by some other means. In effect, the authenticator
- decided it wasn't prepared to handle this case. Typically this
- is the result of "fail" in an expansion string. Do we need to
- log anything here? Feb 2006: a message is now put in the buffer
- if logging is required. */
-
- case CANCELLED:
- if (*buffer != 0)
- log_write(0, LOG_MAIN, "%s authenticator cancelled "
- "authentication H=%s [%s] %s", au->name, host->name,
- host->address, buffer);
- break;
-
- /* Internal problem, message in buffer. */
-
- case ERROR:
- set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE);
- return ERROR;
- }
+ switch(rc)
+ {
+ case OK:
+ smtp_authenticated = TRUE; /* stops the outer loop */
+ client_authenticator = au->name;
+ if (au->set_client_id != NULL)
+ client_authenticated_id = expand_string(au->set_client_id);
+ break;
+
+ /* Failure after writing a command */
+
+ case FAIL_SEND:
+ return FAIL_SEND;
+
+ /* Failure after reading a response */
+
+ case FAIL:
+ if (errno != 0 || buffer[0] != '5') return FAIL;
+ log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
+ au->name, host->name, host->address, buffer);
+ break;
+
+ /* Failure by some other means. In effect, the authenticator
+ decided it wasn't prepared to handle this case. Typically this
+ is the result of "fail" in an expansion string. Do we need to
+ log anything here? Feb 2006: a message is now put in the buffer
+ if logging is required. */
+
+ case CANCELLED:
+ if (*buffer != 0)
+ log_write(0, LOG_MAIN, "%s authenticator cancelled "
+ "authentication H=%s [%s] %s", au->name, host->name,
+ host->address, buffer);
+ break;
+
+ /* Internal problem, message in buffer. */
+
+ case ERROR:
+ set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE);
+ return ERROR;
+ }
- break; /* If not authenticated, try next authenticator */
- } /* Loop for scanning supported server mechanisms */
- } /* Loop for further authenticators */
- }
+ break; /* If not authenticated, try next authenticator */
+ } /* Loop for scanning supported server mechanisms */
+ } /* Loop for further authenticators */
}
+ }
- /* If we haven't authenticated, but are required to, give up. */
+/* If we haven't authenticated, but are required to, give up. */
- if (require_auth == OK && !smtp_authenticated)
- {
- set_errno(addrlist, ERRNO_AUTHFAIL,
- string_sprintf("authentication required but %s", fail_reason), DEFER,
- FALSE);
- return DEFER;
- }
+if (require_auth == OK && !smtp_authenticated)
+ {
+ set_errno(addrlist, ERRNO_AUTHFAIL,
+ string_sprintf("authentication required but %s", fail_reason), DEFER,
+ FALSE);
+ return DEFER;
+ }
- return OK;
+return OK;
}
@@ -1295,9 +1291,14 @@ specially so they can be identified for retries. */
if (continue_hostname == NULL)
{
+ /* This puts port into host->port */
inblock.sock = outblock.sock =
smtp_connect(host, host_af, port, interface, ob->connect_timeout,
- ob->keepalive, ob->dscp); /* This puts port into host->port */
+ ob->keepalive, ob->dscp
+#ifdef EXPERIMENTAL_TPDA
+ , tblock->tpda_event_action
+#endif
+ );
if (inblock.sock < 0)
{
@@ -1321,6 +1322,17 @@ if (continue_hostname == NULL)
if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout)) goto RESPONSE_FAILED;
+#ifdef EXPERIMENTAL_TPDA
+ if (tpda_raise_event(tblock->tpda_event_action, US"smtp:connect", buffer)
+ == DEFER)
+ {
+ uschar *message = US"deferred by smtp:connect event expansion";
+ set_errno(addrlist, 0, message, DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
+#endif
+
/* Now check if the helo_data expansion went well, and sign off cleanly if
it didn't. */
@@ -1375,7 +1387,7 @@ goto SEND_QUIT;
/* Alas; be careful, since this goto is not an error-out, so conceivably
we might set data between here and the target which we assume to exist
and be usable. I can see this coming back to bite us. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (smtps)
{
tls_offered = TRUE;
@@ -1384,7 +1396,7 @@ goto SEND_QUIT;
smtp_command = US"SSL-on-connect";
goto TLS_NEGOTIATE;
}
- #endif
+#endif
if (esmtp)
{
@@ -1421,13 +1433,13 @@ goto SEND_QUIT;
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_offered = esmtp &&
pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0;
- #endif
+#endif
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
prdr_offered = esmtp &&
(pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0,
PCRE_EOPT, NULL, 0) >= 0) &&
@@ -1436,7 +1448,7 @@ goto SEND_QUIT;
if (prdr_offered)
{DEBUG(D_transport) debug_printf("PRDR usable\n");}
- #endif
+#endif
}
/* For continuing deliveries down the same channel, the socket is the standard
@@ -1493,7 +1505,7 @@ if (tls_offered && !suppress_tls &&
else
TLS_NEGOTIATE:
{
- int rc = tls_client_start(inblock.sock, host, addrlist, ob);
+ int rc = tls_client_start(inblock.sock, host, addrlist, tblock);
/* 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
@@ -1599,9 +1611,9 @@ continued session down a previously-used socket, we haven't just done EHLO, so
we skip this. */
if (continue_hostname == NULL
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
|| tls_out.active >= 0
- #endif
+#endif
)
{
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
@@ -1738,12 +1750,11 @@ if (prdr_offered)
{ /* at least two recipients to send */
prdr_active = TRUE;
sprintf(CS p, " PRDR"); p += 5;
- goto prdr_is_active;
+ break;
}
break;
}
}
-prdr_is_active:
#endif
#ifdef EXPERIMENTAL_DSN
@@ -1786,7 +1797,10 @@ otherwise no check - this feature is expected to be used with LMTP and other
cases where non-standard addresses (e.g. without domains) might be required. */
if (smtp_mail_auth_str(p, sizeof(buffer) - (p-buffer), addrlist, ob))
- return ERROR;
+ {
+ yield = ERROR;
+ goto SEND_QUIT;
+ }
/* From here until we send the DATA command, we can make use of PIPELINING
if the server host supports it. The code has to be able to check the responses
@@ -1840,25 +1854,22 @@ for (addr = first_addr;
int count;
BOOL no_flush;
- #ifdef EXPERIMENTAL_DSN
- if(smtp_use_dsn)
- addr->dsn_aware = dsn_support_yes;
- else
- addr->dsn_aware = dsn_support_no;
- #endif
+#ifdef EXPERIMENTAL_DSN
+ addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no;
+#endif
if (addr->transport_return != PENDING_DEFER) continue;
address_count++;
no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL);
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
/* Add any DSN flags to the rcpt command and add to the sent string */
p = buffer;
*p = 0;
- if ((smtp_use_dsn) && ((addr->dsn_flags & rf_dsnlasthop) != 1))
+ if (smtp_use_dsn && (addr->dsn_flags & rf_dsnlasthop) != 1)
{
if ((addr->dsn_flags & rf_dsnflags) != 0)
{
@@ -1867,7 +1878,6 @@ for (addr = first_addr;
strcpy(p, " NOTIFY=");
while (*p) p++;
for (i = 0; i < 4; i++)
- {
if ((addr->dsn_flags & rf_list[i]) != 0)
{
if (!first) *p++ = ',';
@@ -1875,16 +1885,16 @@ for (addr = first_addr;
strcpy(p, rf_names[i]);
while (*p) p++;
}
- }
}
- if (addr->dsn_orcpt != NULL) {
+ if (addr->dsn_orcpt != NULL)
+ {
string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s",
addr->dsn_orcpt);
while (*p) p++;
}
}
- #endif
+#endif
/* Now send the RCPT command, and process outstanding responses when
@@ -1892,13 +1902,13 @@ for (addr = first_addr;
yield as OK, because this error can often mean that there is a problem with
just one address, so we don't want to delay the host. */
- #ifdef EXPERIMENTAL_DSN
+#ifdef EXPERIMENTAL_DSN
count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer);
- #else
+#else
count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n",
transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr);
- #endif
+#endif
if (count < 0) goto SEND_FAILED;
if (count > 0)
@@ -1989,6 +1999,7 @@ if (!ok) ok = TRUE; else
DEBUG(D_transport|D_v)
debug_printf(" SMTP>> writing message and terminating \".\"\n");
transport_count = 0;
+
#ifndef DISABLE_DKIM
ok = dkim_transport_write_message(addrlist, inblock.sock,
topt_use_crlf | topt_end_dot | topt_escape_headers |
@@ -2115,9 +2126,9 @@ if (!ok) ok = TRUE; else
/* Set up confirmation if needed - applies only to SMTP */
if (
- #ifndef EXPERIMENTAL_TPDA
+#ifndef EXPERIMENTAL_TPDA
(log_extra_selector & LX_smtp_confirmation) != 0 &&
- #endif
+#endif
!lmtp
)
{
@@ -2293,10 +2304,10 @@ if (!ok)
in message and save_errno, and setting_up will always be true. Treat as
a temporary error. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
TLS_FAILED:
code = '4';
- #endif
+#endif
/* If the failure happened while setting up the call, see if the failure was
a 5xx response (this will either be on connection, or following HELO - a 5xx
@@ -2489,7 +2500,7 @@ if (completed_address && ok && send_quit)
when TLS is shut down. We test for this by sending a new EHLO. If we
don't get a good response, we don't attempt to pass the socket on. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (tls_out.active >= 0)
{
tls_close(FALSE, TRUE);
@@ -2500,7 +2511,7 @@ if (completed_address && ok && send_quit)
smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout);
}
- #endif
+#endif
/* If the socket is successfully passed, we musn't send QUIT (or
indeed anything!) from here. */
@@ -2556,6 +2567,11 @@ specified in the transports, and therefore not visible at top level, in which
case continue_more won't get set. */
(void)close(inblock.sock);
+
+#ifdef EXPERIMENTAL_TPDA
+(void) tpda_raise_event(tblock->tpda_event_action, US"tcp:close", NULL);
+#endif
+
continue_transport = NULL;
continue_hostname = NULL;
return yield;
@@ -2644,13 +2660,13 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
addr->basic_errno = 0;
addr->more_errno = (host->mx >= 0)? 'M' : 'A';
addr->message = NULL;
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
addr->cipher = NULL;
addr->ourcert = NULL;
addr->peercert = NULL;
addr->peerdn = NULL;
addr->ocsp = OCSP_NOT_REQ;
- #endif
+#endif
}
return first_addr;
}
@@ -3263,10 +3279,10 @@ for (cutoff_retry = 0; expired &&
first_addr->basic_errno != ERRNO_TLSFAILURE)
write_logs(first_addr, host);
- #ifdef EXPERIMENTAL_TPDA
+#ifdef EXPERIMENTAL_TPDA
if (rc == DEFER)
- tpda_deferred(ob, first_addr, host);
- #endif
+ tpda_deferred(first_addr, host);
+#endif
/* If STARTTLS was accepted, but there was a failure in setting up the
TLS session (usually a certificate screwup), and the host is not in
@@ -3277,16 +3293,16 @@ for (cutoff_retry = 0; expired &&
session, so the in-clear transmission after those errors, if permitted,
happens inside smtp_deliver().] */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if ( rc == DEFER
&& first_addr->basic_errno == ERRNO_TLSFAILURE
&& ob->tls_tempfail_tryclear
&& verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
host->address, NULL) != OK
-#ifdef EXPERIMENTAL_DANE
+# ifdef EXPERIMENTAL_DANE
&& verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name,
host->address, NULL) != OK
-#endif
+# endif
)
{
log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
@@ -3296,12 +3312,12 @@ for (cutoff_retry = 0; expired &&
expanded_hosts != NULL, &message_defer, TRUE);
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(first_addr, host);
- #ifdef EXPERIMENTAL_TPDA
+# ifdef EXPERIMENTAL_TPDA
if (rc == DEFER)
- tpda_deferred(ob, first_addr, host);
- #endif
+ tpda_deferred(first_addr, host);
+# endif
}
- #endif
+#endif /*SUPPORT_TLS*/
}
/* Delivery attempt finished */
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index d968a4d54..4494adfb2 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -85,9 +85,6 @@ typedef struct {
uschar *dkim_sign_headers;
uschar *dkim_strict;
#endif
-#ifdef EXPERIMENTAL_TPDA
- uschar *tpda_host_defer_action;
-#endif
} smtp_transport_options_block;
/* Data for reading the private options. */
diff --git a/src/src/verify.c b/src/src/verify.c
index c2ee47892..edd9ad17d 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -462,6 +462,7 @@ else
deliver_host = host->name;
deliver_host_address = host->address;
+ deliver_host_port = host->port;
deliver_domain = addr->domain;
if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
@@ -501,7 +502,12 @@ else
tls_retry_connection:
inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL);
+ smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL
+#ifdef EXPERIMENTAL_TPDA
+ /*XXX tpda action? NULL for now. */
+ , NULL
+#endif
+ );
/* reconsider DSCP here */
if (inblock.sock < 0)
{
@@ -533,12 +539,23 @@ else
/* Unless ssl-on-connect, wait for the initial greeting */
smtps_redo_greeting:
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (!smtps || (smtps && tls_out.active >= 0))
- #endif
+#endif
+ {
if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
goto RESPONSE_FAILED;
+#ifdef EXPERIMENTAL_TPDA
+ if (tpda_raise_event(addr->transport->tpda_event_action,
+ US"smtp:connect", responsebuffer) == DEFER)
+ {
+ /* Logging? Debug? */
+ goto RESPONSE_FAILED;
+ }
+#endif
+ }
+
/* Not worth checking greeting line for ESMTP support */
if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
host->name, host->address, NULL) != OK))
@@ -547,14 +564,14 @@ else
tls_redo_helo:
- #ifdef SUPPORT_TLS
+#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
+ else /* all other cases */
+#endif
{ esmtp_retry:
@@ -568,26 +585,26 @@ else
done= FALSE;
goto RESPONSE_FAILED;
}
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_offered = FALSE;
- #endif
+#endif
esmtp = FALSE;
goto esmtp_retry; /* fallback to HELO */
}
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
- #ifdef SUPPORT_TLS
+#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);
+ {
+ 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;
+ tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer,
+ Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0;
}
else
tls_offered = FALSE;
- #endif
+#endif
}
/* If TLS is available on this connection attempt to
@@ -598,7 +615,7 @@ else
the client not be required to use TLS. If the response is bad, copy the buffer
for error analysis. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (tls_offered &&
verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
host->address, NULL) != OK &&
@@ -623,11 +640,11 @@ else
{
if (errno != 0 || buffer2[0] == 0 ||
(buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
- {
- Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
- done= FALSE;
- goto RESPONSE_FAILED;
- }
+ {
+ Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
+ done= FALSE;
+ goto RESPONSE_FAILED;
+ }
}
/* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */
@@ -637,36 +654,36 @@ else
int rc;
ob->command_timeout = callout;
- rc = tls_client_start(inblock.sock, host, addr, ob);
+ rc = tls_client_start(inblock.sock, host, addr, addr->transport);
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
- && ob->tls_tempfail_tryclear
- && !smtps
- && verify_check_this_host(&(ob->hosts_require_tls), NULL,
- host->name, host->address, NULL) != OK
+ {
+ if ( rc == DEFER
+ && ob->tls_tempfail_tryclear
+ && !smtps
+ && verify_check_this_host(&(ob->hosts_require_tls), NULL,
+ host->name, host->address, NULL) != OK
#ifdef EXPERIMENTAL_DANE
- && verify_check_this_host(&(ob->hosts_require_dane), NULL,
- host->name, host->address, NULL) != OK
+ && verify_check_this_host(&(ob->hosts_require_dane), NULL,
+ host->name, host->address, NULL) != OK
#endif
- )
- {
- (void)close(inblock.sock);
- 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;
- }
+ )
+ {
+ (void)close(inblock.sock);
+ 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;
@@ -674,7 +691,7 @@ else
/* For SMTPS we need to wait for the initial OK response, then do HELO. */
if (smtps)
- goto smtps_redo_greeting;
+ goto smtps_redo_greeting;
/* For STARTTLS we need to redo EHLO */
goto tls_redo_helo;
@@ -714,13 +731,13 @@ else
cutthrough_delivery= FALSE;
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
}
- #ifndef DISABLE_DKIM
+#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
+#endif
}
SEND_FAILED:
@@ -1006,10 +1023,14 @@ else
cancel_cutthrough_connection("multiple verify calls");
if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
- #endif
+#endif
(void)close(inblock.sock);
+#ifdef EXPERIMENTAL_TPDA
+ (void) tpda_raise_event(addr->transport->tpda_event_action,
+ US"tcp:close", NULL);
+#endif
}
} /* Loop through all hosts, while !done */