X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/b1770b6e4e865e3422e18e903b8e918df8802aa0..a39bd74d3e942db1b465cae643e1a4766ffb304e:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index e317c095c..446b3702b 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -19,6 +19,11 @@ before the lower case letters). Some live in the transport_instance block so as to be publicly visible; these are flagged with opt_public. */ optionlist smtp_transport_options[] = { + { "*expand_multi_domain", opt_stringptr | opt_hidden | opt_public, + (void *)offsetof(transport_instance, expand_multi_domain) }, + { "*expand_retry_include_ip_address", opt_stringptr | opt_hidden, + (void *)(offsetof(smtp_transport_options_block, expand_retry_include_ip_address)) }, + { "address_retry_include_sender", opt_bool, (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) }, { "allow_localhost", opt_bool, @@ -55,6 +60,12 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, { "dns_search_parents", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_search_parents) }, + { "dnssec_request_domains", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dnssec_request_domains) }, + { "dnssec_require_domains", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dnssec_require_domains) }, + { "dscp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dscp) }, { "fallback_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, fallback_hosts) }, { "final_timeout", opt_time, @@ -63,7 +74,7 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, gethostbyname) }, #ifdef SUPPORT_TLS /* These are no longer honoured, as of Exim 4.80; for now, we silently - ignore; a later release will warn, and a later-still release will remove + ignore; 4.83 will warn, and a later-still release will remove these options, so that using them becomes an error. */ { "gnutls_require_kx", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, @@ -96,14 +107,38 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, hosts_override) }, { "hosts_randomize", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_randomize) }, +#if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP) + { "hosts_request_ocsp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) }, +#endif { "hosts_require_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_auth) }, #ifdef SUPPORT_TLS +# ifdef EXPERIMENTAL_DANE + { "hosts_require_dane", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_require_dane) }, +# endif +# ifndef DISABLE_OCSP + { "hosts_require_ocsp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_require_ocsp) }, +# endif { "hosts_require_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_tls) }, #endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { "hosts_try_dane", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_try_dane) }, +#endif +#ifndef DISABLE_PRDR + { "hosts_try_prdr", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) }, +#endif +#ifdef SUPPORT_TLS + { "hosts_verify_avoid_tls", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) }, +#endif { "interface", opt_stringptr, (void *)offsetof(smtp_transport_options_block, interface) }, { "keepalive", opt_bool, @@ -112,23 +147,29 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) }, { "max_rcpt", opt_int | opt_public, (void *)offsetof(transport_instance, max_addresses) }, - { "multi_domain", opt_bool | opt_public, + { "multi_domain", opt_expand_bool | opt_public, (void *)offsetof(transport_instance, multi_domain) }, { "port", opt_stringptr, (void *)offsetof(smtp_transport_options_block, port) }, { "protocol", opt_stringptr, (void *)offsetof(smtp_transport_options_block, protocol) }, - { "retry_include_ip_address", opt_bool, + { "retry_include_ip_address", opt_expand_bool, (void *)offsetof(smtp_transport_options_block, retry_include_ip_address) }, { "serialize_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } +#ifdef EXPERIMENTAL_SOCKS + ,{ "socks_proxy", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, socks_proxy) } +#endif #ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, { "tls_crl", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_crl) }, + { "tls_dh_min_bits", opt_int, + (void *)offsetof(smtp_transport_options_block, tls_dh_min_bits) }, { "tls_privatekey", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_privatekey) }, { "tls_require_ciphers", opt_stringptr, @@ -137,8 +178,14 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_sni) }, { "tls_tempfail_tryclear", opt_bool, (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, + { "tls_try_verify_hosts", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) }, + { "tls_verify_cert_hostnames", opt_stringptr, + (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)}, { "tls_verify_certificates", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) } + (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }, + { "tls_verify_hosts", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) } #endif }; @@ -160,11 +207,24 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* interface */ NULL, /* port */ US"smtp", /* protocol */ + NULL, /* DSCP */ NULL, /* serialize_hosts */ NULL, /* hosts_try_auth */ NULL, /* hosts_require_auth */ +#ifdef EXPERIMENTAL_DANE + NULL, /* hosts_try_dane */ + NULL, /* hosts_require_dane */ +#endif +#ifndef DISABLE_PRDR + US"*", /* hosts_try_prdr */ +#endif +#ifndef DISABLE_OCSP + US"*", /* hosts_request_ocsp (except under DANE; tls_client_start()) */ + NULL, /* hosts_require_ocsp */ +#endif NULL, /* hosts_require_tls */ NULL, /* hosts_avoid_tls */ + US"*", /* hosts_verify_avoid_tls */ NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ NULL, /* hosts_nopass_tls */ @@ -181,12 +241,18 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* gethostbyname */ TRUE, /* dns_qualify_single */ FALSE, /* dns_search_parents */ + NULL, /* dnssec_request_domains */ + NULL, /* dnssec_require_domains */ TRUE, /* delay_after_cutoff */ FALSE, /* hosts_override */ FALSE, /* hosts_randomize */ TRUE, /* keepalive */ FALSE, /* lmtp_ignore_quota */ + NULL, /* expand_retry_include_ip_address */ TRUE /* retry_include_ip_address */ +#ifdef EXPERIMENTAL_SOCKS +#endif + ,NULL /* socks_proxy */ #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ @@ -195,9 +261,14 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* gnutls_require_kx */ NULL, /* gnutls_require_mac */ NULL, /* gnutls_require_proto */ - NULL, /* tls_verify_certificates */ + NULL, /* tls_sni */ + US"system", /* tls_verify_certificates */ + EXIM_CLIENT_DH_DEFAULT_MIN_BITS, + /* tls_dh_min_bits */ TRUE, /* tls_tempfail_tryclear */ - NULL /* tls_sni */ + NULL, /* tls_verify_hosts */ + US"*", /* tls_try_verify_hosts */ + US"*" /* tls_verify_cert_hostnames */ #endif #ifndef DISABLE_DKIM ,NULL, /* dkim_canon */ @@ -209,6 +280,14 @@ smtp_transport_options_block smtp_transport_option_defaults = { #endif }; +/* some DSN flags for use later */ + +static int rf_list[] = {rf_notify_never, rf_notify_success, + rf_notify_failure, rf_notify_delay }; + +static uschar *rf_names[] = { US"NEVER", US"SUCCESS", US"FAILURE", US"DELAY" }; + + /* Local statics */ @@ -333,6 +412,15 @@ if (ob->hosts_override && ob->hosts != NULL) tblock->overrides_hosts = TRUE; for them, but do not do any lookups at this time. */ host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE); + +#ifdef SUPPORT_TLS +if ( ob->gnutls_require_kx + || ob->gnutls_require_mac + || ob->gnutls_require_proto) + log_write(0, LOG_MAIN, "WARNING: smtp transport options" + " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols" + " are obsolete\n"); +#endif } @@ -352,6 +440,7 @@ Arguments: msg to put in each address's message field rc to put in each address's transport_return field pass_message if TRUE, set the "pass message" flag in the address + host if set, mark addrs as having used this host If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -362,7 +451,7 @@ Returns: nothing static void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, - BOOL pass_message) + BOOL pass_message, host_item * host) { address_item *addr; int orvalue = 0; @@ -372,17 +461,19 @@ if (errno_value == ERRNO_CONNECTTIMEOUT) orvalue = RTEF_CTOUT; } for (addr = addrlist; addr != NULL; addr = addr->next) - { - if (addr->transport_return < PENDING) continue; - addr->basic_errno = errno_value; - addr->more_errno |= orvalue; - if (msg != NULL) + if (addr->transport_return >= PENDING) { - addr->message = msg; - if (pass_message) setflag(addr, af_pass_message); + addr->basic_errno = errno_value; + addr->more_errno |= orvalue; + if (msg != NULL) + { + addr->message = msg; + if (pass_message) setflag(addr, af_pass_message); + } + addr->transport_return = rc; + if (host) + addr->host_used = host; } - addr->transport_return = rc; - } } @@ -409,7 +500,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""; @@ -426,8 +518,8 @@ if (smtp_use_pipelining && if (*errno_value == ETIMEDOUT) { - *message = US string_sprintf("SMTP timeout while connected to %s [%s] " - "after %s%s", host->name, host->address, pl, smtp_command); + *message = US string_sprintf("SMTP timeout after %s%s", + pl, smtp_command); if (transport_count > 0) *message = US string_sprintf("%s (%d bytes written)", *message, transport_count); @@ -438,15 +530,13 @@ if (*errno_value == ETIMEDOUT) if (*errno_value == ERRNO_SMTPFORMAT) { - uschar *malfresp = string_printing(buffer); + const uschar *malfresp = string_printing(buffer); while (isspace(*malfresp)) malfresp++; - if (*malfresp == 0) - *message = string_sprintf("Malformed SMTP reply (an empty line) from " - "%s [%s] in response to %s%s", host->name, host->address, pl, - smtp_command); - else - *message = string_sprintf("Malformed SMTP reply from %s [%s] in response " - "to %s%s: %s", host->name, host->address, pl, smtp_command, malfresp); + *message = *malfresp == 0 + ? string_sprintf("Malformed SMTP reply (an empty line) " + "in response to %s%s", pl, smtp_command) + : string_sprintf("Malformed SMTP reply in response to %s%s: %s", + pl, smtp_command, malfresp); return FALSE; } @@ -484,9 +574,9 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE) if (buffer[0] != 0) { - uschar *s = string_printing(buffer); + const uschar *s = string_printing(buffer); *message = US string_sprintf("SMTP error from remote mail server after %s%s: " - "host %s [%s]: %s", pl, smtp_command, host->name, host->address, s); + "%s", pl, smtp_command, s); *pass_message = TRUE; *yield = buffer[0]; return TRUE; @@ -501,8 +591,8 @@ assume the connection is now dead. */ if (*errno_value == 0 || *errno_value == ECONNRESET) { *errno_value = ERRNO_SMTPCLOSED; - *message = US string_sprintf("Remote host %s [%s] closed connection " - "in response to %s%s", host->name, host->address, pl, smtp_command); + *message = US string_sprintf("Remote host closed connection " + "in response to %s%s", pl, smtp_command); } else *message = US string_sprintf("%s [%s]", host->name, host->address); @@ -527,9 +617,11 @@ Returns: nothing static void write_logs(address_item *addr, host_item *host) { -if (addr->message != NULL) +uschar * message = string_sprintf("H=%s [%s]", host->name, host->address); + +if (addr->message) { - uschar *message = addr->message; + message = string_sprintf("%s: %s", message, addr->message); if (addr->basic_errno > 0) message = string_sprintf("%s: %s", message, strerror(addr->basic_errno)); log_write(0, LOG_MAIN, "%s", message); @@ -537,20 +629,77 @@ if (addr->message != NULL) } else { - uschar *msg = - ((log_extra_selector & LX_outgoing_port) != 0)? - string_sprintf("%s [%s]:%d", host->name, host->address, - (host->port == PORT_NONE)? 25 : host->port) - : - string_sprintf("%s [%s]", host->name, host->address); - log_write(0, LOG_MAIN, "%s %s", msg, strerror(addr->basic_errno)); - deliver_msglog("%s %s %s\n", tod_stamp(tod_log), msg, - strerror(addr->basic_errno)); + if (log_extra_selector & LX_outgoing_port) + message = string_sprintf("%s:%d", message, + host->port == PORT_NONE ? 25 : host->port); + log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno)); + deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, + strerror(addr->basic_errno)); } } +static void +msglog_line(host_item * host, uschar * message) +{ + deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log), + host->name, host->address, message); +} + +#ifdef EXPERIMENTAL_EVENT +/************************************************* +* Post-defer action * +*************************************************/ + +/* This expands an arbitrary per-transport string. + It might, for example, be used to write to the database log. + +Arguments: + addr the address item containing error information + host the current host + +Returns: nothing +*/ + +static void +deferred_event_raise(address_item *addr, host_item *host) +{ +uschar * action = addr->transport->event_action; +const uschar * save_domain; +uschar * save_local; + +if (!action) + return; + +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; +event_defer_errno = addr->basic_errno; + +router_name = addr->router->name; +transport_name = addr->transport->name; +deliver_domain = addr->domain; +deliver_localpart = addr->local_part; + +(void) event_raise(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 + /************************************************* * Synchronize SMTP responses * *************************************************/ @@ -640,6 +789,14 @@ if (pending_MAIL) } errno = save_errno; } + + if (pending_DATA) count--; /* Number of RCPT responses to come */ + while (count-- > 0) /* Mark any pending addrs with the host used */ + { + while (addr->transport_return != PENDING_DEFER) addr = addr->next; + addr->host_used = host; + addr = addr->next; + } return -3; } } @@ -655,6 +812,7 @@ while (count-- > 0) while (addr->transport_return != PENDING_DEFER) addr = addr->next; /* The address was accepted */ + addr->host_used = host; if (smtp_read_response(inblock, buffer, buffsize, '2', timeout)) { @@ -678,11 +836,9 @@ while (count-- > 0) else if (errno == ETIMEDOUT) { - int save_errno = errno; - uschar *message = string_sprintf("SMTP timeout while connected to %s [%s] " - "after RCPT TO:<%s>", host->name, host->address, - transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, save_errno, message, DEFER, FALSE); + uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", + transport_rcpt_address(addr, include_affixes)); + set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -706,10 +862,10 @@ while (count-- > 0) { addr->message = string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: " - "host %s [%s]: %s", transport_rcpt_address(addr, include_affixes), - host->name, host->address, string_printing(buffer)); + "%s", transport_rcpt_address(addr, include_affixes), + string_printing(buffer)); setflag(addr, af_pass_message); - deliver_msglog("%s %s\n", tod_stamp(tod_log), addr->message); + msglog_line(host, addr->message); /* The response was 5xx */ @@ -727,9 +883,11 @@ while (count-- > 0) addr->basic_errno = ERRNO_RCPT4XX; addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; - /* Log temporary errors if there are more hosts to be tried. */ + /* Log temporary errors if there are more hosts to be tried. + If not, log this last one in the == line. */ - if (host->next != NULL) log_write(0, LOG_MAIN, "%s", addr->message); + if (host->next) + log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message); /* Do not put this message on the list of those waiting for specific hosts, as otherwise it is likely to be tried too often. */ @@ -788,6 +946,342 @@ return yield; +/* Do the client side of smtp-level authentication */ +/* +Arguments: + buffer EHLO response from server (gets overwritten) + addrlist chain of potential addresses to deliver + host host to deliver to + ob transport options + ibp, obp comms channel control blocks + +Returns: + OK Success, or failed (but not required): global "smtp_authenticated" set + DEFER Failed authentication (and was required) + ERROR Internal problem + + FAIL_SEND Failed communications - transmit + FAIL - response +*/ + +int +smtp_auth(uschar *buffer, unsigned bufsize, address_item *addrlist, host_item *host, + 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"; + +smtp_authenticated = FALSE; +client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; +require_auth = verify_check_given_host(&ob->hosts_require_auth, host); + +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 */ + + /* Must not do this check until after we have saved the result of the + regex match above. */ + + if (require_auth == OK || + verify_check_given_host(&ob->hosts_try_auth, host) == OK) + { + auth_instance *au; + fail_reason = US"no common mechanisms were found"; + + 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. + */ + + 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 */ + + 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; + } + + /* 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); + + /* 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, ERRNO_AUTHPROB, string_copy(buffer), + DEFER, FALSE, NULL); + return ERROR; + } + + 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 (require_auth == OK && !smtp_authenticated) + { + set_errno(addrlist, ERRNO_AUTHFAIL, + string_sprintf("authentication required but %s", fail_reason), DEFER, + FALSE, NULL); + return DEFER; + } + +return OK; +} + + +/* Construct AUTH appendix string for MAIL TO */ +/* +Arguments + buffer to build string + addrlist chain of potential addresses to deliver + ob transport options + +Globals smtp_authenticated + client_authenticated_sender +Return True on error, otherwise buffer has (possibly empty) terminated string +*/ + +BOOL +smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist, + smtp_transport_options_block *ob) +{ +uschar *local_authenticated_sender = authenticated_sender; + +#ifdef notdef + debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, smtp_authenticated?"Y":"N"); +#endif + +if (ob->authenticated_sender != NULL) + { + uschar *new = expand_string(ob->authenticated_sender); + if (new == NULL) + { + if (!expand_string_forcedfail) + { + uschar *message = string_sprintf("failed to expand " + "authenticated_sender: %s", expand_string_message); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + return TRUE; + } + } + else if (new[0] != 0) local_authenticated_sender = new; + } + +/* Add the authenticated sender address if present */ + +if ((smtp_authenticated || ob->authenticated_sender_force) && + local_authenticated_sender != NULL) + { + string_format(buffer, bufsize, " AUTH=%s", + auth_xtextencode(local_authenticated_sender, + Ustrlen(local_authenticated_sender))); + client_authenticated_sender = string_copy(local_authenticated_sender); + } +else + *buffer= 0; + +return FALSE; +} + + + +#ifdef EXPERIMENTAL_DANE +int +tlsa_lookup(const host_item * host, dns_answer * dnsa, + BOOL dane_required, BOOL * dane) +{ +/* move this out to host.c given the similarity to dns_lookup() ? */ +uschar buffer[300]; +const uschar * fullname = buffer; + +/* TLSA lookup string */ +(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name); + +switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname)) + { + case DNS_AGAIN: + return DEFER; /* just defer this TLS'd conn */ + + default: + case DNS_FAIL: + if (dane_required) + { + log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed"); + return FAIL; + } + break; + + case DNS_SUCCEED: + if (!dns_is_secure(dnsa)) + { + log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); + return DEFER; + } + *dane = TRUE; + break; + } +return OK; +} +#endif + + + +typedef struct smtp_compare_s +{ + uschar *current_sender_address; + struct transport_instance *tblock; +} smtp_compare_t; + +/* +Create a unique string that identifies this message, it is based on +sender_address, helo_data and tls_certificate if enabled. */ + +static uschar * +smtp_local_identity(uschar * sender, struct transport_instance * tblock) +{ +address_item * addr1; +uschar * if1 = US""; +uschar * helo1 = US""; +#ifdef SUPPORT_TLS +uschar * tlsc1 = US""; +#endif +uschar * save_sender_address = sender_address; +uschar * local_identity = NULL; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tblock->options_block; + +sender_address = sender; + +addr1 = deliver_make_addr (sender, TRUE); +deliver_set_expansions(addr1); + +if (ob->interface) + if1 = expand_string(ob->interface); + +if (ob->helo_data) + helo1 = expand_string(ob->helo_data); + +#ifdef SUPPORT_TLS +if (ob->tls_certificate) + tlsc1 = expand_string(ob->tls_certificate); +local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1); +#else +local_identity = string_sprintf ("%s^%s", if1, helo1); +#endif + +deliver_set_expansions(NULL); +sender_address = save_sender_address; + +return local_identity; +} + + + +/* This routine is a callback that is called from transport_check_waiting. +This function will evaluate the incoming message versus the previous +message. If the incoming message is using a different local identity then +we will veto this new message. */ + +static BOOL +smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare) +{ +uschar * save_sender_address = sender_address; +uschar * current_local_identity = + smtp_local_identity(s_compare->current_sender_address, s_compare->tblock); +uschar * new_sender_address = deliver_get_sender_address(message_id); +uschar * message_local_identity = + smtp_local_identity(new_sender_address, s_compare->tblock); + +sender_address = save_sender_address; + +return Ustrcmp(current_local_identity, message_local_identity) == 0; +} + + + /************************************************* * Deliver address list to given host * *************************************************/ @@ -814,8 +1308,6 @@ Arguments: port default TCP/IP port to use, in host byte order interface interface to bind to, or NULL tblock transport instance block - copy_host TRUE if host set in addr->host_used must be copied, because - it is specific to this call of the transport message_defer set TRUE if yield is OK, but all addresses were deferred because of a non-recipient, non-host failure, that is, a 4xx response to MAIL FROM, DATA, or ".". This is a defer @@ -836,7 +1328,7 @@ Returns: OK - the connection was made and the delivery attempted; static int smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port, - uschar *interface, transport_instance *tblock, BOOL copy_host, + uschar *interface, transport_instance *tblock, BOOL *message_defer, BOOL suppress_tls) { address_item *addr; @@ -859,18 +1351,30 @@ BOOL completed_address = FALSE; BOOL esmtp = TRUE; BOOL pending_MAIL; BOOL pass_message = FALSE; +#ifndef DISABLE_PRDR +BOOL prdr_offered = FALSE; +BOOL prdr_active; +#endif +BOOL dsn_all_lasthop = TRUE; +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) +BOOL dane = FALSE; +BOOL dane_required; +dns_answer tlsa_dnsa; +#endif smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; -uschar *local_authenticated_sender = authenticated_sender; + uschar *helo_data = NULL; + uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; uschar *p; uschar buffer[4096]; uschar inbuffer[4096]; -uschar outbuffer[1024]; +uschar outbuffer[4096]; +address_item * current_address; suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ @@ -895,41 +1399,28 @@ outblock.authenticating = FALSE; /* Reset the parameters of a TLS session. */ -tls_bits = 0; -tls_cipher = NULL; -tls_peerdn = NULL; +tls_out.bits = 0; +tls_out.cipher = NULL; /* the one we may use for this transport */ +tls_out.ourcert = NULL; +tls_out.peercert = NULL; +tls_out.peerdn = NULL; #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) -tls_sni = NULL; +tls_out.sni = NULL; #endif +tls_out.ocsp = OCSP_NOT_REQ; -/* If an authenticated_sender override has been specified for this transport -instance, expand it. If the expansion is forced to fail, and there was already -an authenticated_sender for this message, the original value will be used. -Other expansion failures are serious. An empty result is ignored, but there is -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. */ +/* Flip the legacy TLS-related variables over to the outbound set in case +they're used in the context of the transport. Don't bother resetting +afterward as we're in a subprocess. */ -if (ob->authenticated_sender != NULL) - { - uschar *new = expand_string(ob->authenticated_sender); - if (new == NULL) - { - if (!expand_string_forcedfail) - { - uschar *message = string_sprintf("failed to expand " - "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); - return ERROR; - } - } - else if (new[0] != 0) local_authenticated_sender = new; - } +tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); - return ERROR; + set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", + DEFER, FALSE, NULL); + return ERROR; } #endif @@ -939,17 +1430,43 @@ 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); /* This puts port into host->port */ + smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock); if (inblock.sock < 0) { set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE); + NULL, DEFER, FALSE, NULL); return DEFER; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + tls_out.dane_verified = FALSE; + tls_out.tlsa_usage = 0; + + dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK; + + if (host->dnssec == DS_YES) + { + if( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK) + return rc; + } + else if (dane_required) + { + log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); + return FAIL; + } + + if (dane) + ob->tls_tempfail_tryclear = FALSE; + } +#endif /*DANE*/ + /* Expand the greeting message while waiting for the initial response. (Makes sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is delayed till here so that $sending_interface and $sending_port are set. */ @@ -965,6 +1482,23 @@ if (continue_hostname == NULL) if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_EVENT + { + uschar * s; + lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes" + : host->dnssec==DS_NO ? US"no" : NULL; + s = event_raise(tblock->event_action, US"smtp:connect", buffer); + if (s) + { + set_errno(addrlist, ERRNO_EXPANDFAIL, + string_sprintf("deferred by smtp:connect event expansion: %s", s), + DEFER, FALSE, NULL); + yield = DEFER; + goto SEND_QUIT; + } + } +#endif + /* Now check if the helo_data expansion went well, and sign off cleanly if it didn't. */ @@ -972,7 +1506,7 @@ if (continue_hostname == NULL) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1013,13 +1547,12 @@ goto SEND_QUIT; mailers use upper case for some reason (the RFC is quite clear about case independence) so, for peace of mind, I gave in. */ - esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, - host->name, host->address, NULL) != OK; + esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK; /* 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; @@ -1028,7 +1561,7 @@ goto SEND_QUIT; smtp_command = US"SSL-on-connect"; goto TLS_NEGOTIATE; } - #endif +#endif if (esmtp) { @@ -1065,11 +1598,21 @@ 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 + prdr_offered = esmtp + && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0 + && verify_check_given_host(&ob->hosts_try_prdr, host) == OK; + + if (prdr_offered) + {DEBUG(D_transport) debug_printf("PRDR usable\n");} +#endif } /* For continuing deliveries down the same channel, the socket is the standard @@ -1095,9 +1638,9 @@ the client not be required to use TLS. If the response is bad, copy the buffer for error analysis. */ #ifdef SUPPORT_TLS -if (tls_offered && !suppress_tls && - verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name, - host->address, NULL) != OK) +if ( tls_offered + && !suppress_tls + && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK) { uschar buffer2[4096]; if (smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") < 0) @@ -1126,17 +1669,11 @@ if (tls_offered && !suppress_tls && else TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, - host, - addrlist, - NULL, /* No DH param */ - ob->tls_certificate, - ob->tls_privatekey, - ob->tls_sni, - ob->tls_verify_certificates, - ob->tls_crl, - ob->tls_require_ciphers, - ob->command_timeout); + int rc = tls_client_start(inblock.sock, host, addrlist, tblock +# ifdef EXPERIMENTAL_DANE + , dane ? &tlsa_dnsa : NULL +# endif + ); /* 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 @@ -1144,6 +1681,17 @@ if (tls_offered && !suppress_tls && if (rc != OK) { +# ifdef EXPERIMENTAL_DANE + if (rc == DEFER && dane && !dane_required) + { + log_write(0, LOG_MAIN, "DANE attempt failed;" + " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + host->name, host->address); + dane = FALSE; + goto TLS_NEGOTIATE; + } +# endif + save_errno = ERRNO_TLSFAILURE; message = US"failure while setting up TLS session"; send_quit = FALSE; @@ -1153,13 +1701,14 @@ if (tls_offered && !suppress_tls && /* TLS session is set up */ for (addr = addrlist; addr != NULL; addr = addr->next) - { if (addr->transport_return == PENDING_DEFER) { - addr->cipher = tls_cipher; - addr->peerdn = tls_peerdn; + addr->cipher = tls_out.cipher; + addr->ourcert = tls_out.ourcert; + addr->peercert = tls_out.peercert; + addr->peerdn = tls_out.peerdn; + addr->ocsp = tls_out.ocsp; } - } } } @@ -1173,7 +1722,7 @@ another process, and so we won't have expanded helo_data above. We have to expand it here. $sending_ip_address and $sending_port are set up right at the start of the Exim process (in exim.c). */ -if (tls_active >= 0) +if (tls_out.active >= 0) { char *greeting_cmd; if (helo_data == NULL) @@ -1183,7 +1732,7 @@ if (tls_active >= 0) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1216,17 +1765,20 @@ if (tls_active >= 0) /* If the host is required to use a secure channel, ensure that we have one. */ -else if (verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) == OK) +else if ( +# ifdef EXPERIMENTAL_DANE + dane || +# endif + verify_check_given_host(&ob->hosts_require_tls, host) == OK + ) { save_errno = ERRNO_TLSREQUIRED; - message = string_sprintf("a TLS session is required for %s [%s], but %s", - host->name, host->address, + message = string_sprintf("a TLS session is required, but %s", tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support"); goto TLS_FAILED; } -#endif +#endif /*SUPPORT_TLS*/ /* If TLS is active, we have just started it up and re-done the EHLO command, so its response needs to be analyzed. If TLS is not active and this is a @@ -1234,14 +1786,11 @@ continued session down a previously-used socket, we haven't just done EHLO, so we skip this. */ if (continue_hostname == NULL - #ifdef SUPPORT_TLS - || tls_active >= 0 - #endif +#ifdef SUPPORT_TLS + || tls_out.active >= 0 +#endif ) { - int require_auth; - uschar *fail_reason = US"server did not advertise AUTH support"; - /* Set for IGNOREQUOTA if the response to LHLO specifies support and the lmtp_ignore_quota option was set. */ @@ -1260,149 +1809,41 @@ if (continue_hostname == NULL the current host, esmtp will be false, so PIPELINING can never be used. If the current host matches hosts_avoid_pipelining, don't do it. */ - smtp_use_pipelining = esmtp && - verify_check_this_host(&(ob->hosts_avoid_pipelining), NULL, host->name, - host->address, NULL) != OK && - pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0; + smtp_use_pipelining = esmtp + && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK + && pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; DEBUG(D_transport) debug_printf("%susing PIPELINING\n", smtp_use_pipelining? "" : "not "); +#ifndef DISABLE_PRDR + prdr_offered = esmtp + && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0 + && verify_check_given_host(&ob->hosts_try_prdr, host) == OK; + + if (prdr_offered) + {DEBUG(D_transport) debug_printf("PRDR usable\n");} +#endif + + /* Note if the server supports DSN */ + smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; + DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn); + /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do the business. The host name and address must be available when the authenticator's client driver is running. */ - smtp_authenticated = FALSE; - require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL, - host->name, host->address, NULL); - - if (esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) + switch (yield = smtp_auth(buffer, sizeof(buffer), addrlist, host, + ob, esmtp, &inblock, &outblock)) { - 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. */ - - 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"); - - /* 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; - } - - /* Loop to scan supported server mechanisms */ - - 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; - } - - /* 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"; - outblock.authenticating = TRUE; - rc = (au->info->clientcode)(au, &inblock, &outblock, - ob->command_timeout, buffer, sizeof(buffer)); - outblock.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. */ - - switch(rc) - { - case OK: - smtp_authenticated = TRUE; /* stops the outer loop */ - break; - - /* Failure after writing a command */ - - case FAIL_SEND: - goto SEND_FAILED; - - /* Failure after reading a response */ - - case FAIL: - if (errno != 0 || buffer[0] != '5') goto RESPONSE_FAILED; - 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: - yield = ERROR; - set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); - goto SEND_QUIT; - } - - 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 (require_auth == OK && !smtp_authenticated) - { - yield = DEFER; - set_errno(addrlist, ERRNO_AUTHFAIL, - string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE); - goto SEND_QUIT; + default: goto SEND_QUIT; + case OK: break; + case FAIL_SEND: goto SEND_FAILED; + case FAIL: goto RESPONSE_FAILED; } } @@ -1429,7 +1870,7 @@ if (tblock->filter_command != NULL) if (!rc) { set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, - FALSE); + FALSE, NULL); yield = ERROR; goto SEND_QUIT; } @@ -1468,14 +1909,66 @@ if (smtp_use_size) while (*p) p++; } -/* Add the authenticated sender address if present */ +#ifndef DISABLE_PRDR +prdr_active = FALSE; +if (prdr_offered) + { + for (addr = first_addr; addr; addr = addr->next) + if (addr->transport_return == PENDING_DEFER) + { + for (addr = addr->next; addr; addr = addr->next) + if (addr->transport_return == PENDING_DEFER) + { /* at least two recipients to send */ + prdr_active = TRUE; + sprintf(CS p, " PRDR"); p += 5; + break; + } + break; + } + } +#endif -if ((smtp_authenticated || ob->authenticated_sender_force) && - local_authenticated_sender != NULL) +/* check if all addresses have lasthop flag */ +/* do not send RET and ENVID if true */ +dsn_all_lasthop = TRUE; +for (addr = first_addr; + address_count < max_rcpt && addr != NULL; + addr = addr->next) + if ((addr->dsn_flags & rf_dsnlasthop) != 1) + dsn_all_lasthop = FALSE; + +/* Add any DSN flags to the mail command */ + +if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE)) { - string_format(p, sizeof(buffer) - (p-buffer), " AUTH=%s", - auth_xtextencode(local_authenticated_sender, - Ustrlen(local_authenticated_sender))); + if (dsn_ret == dsn_ret_hdrs) + { + Ustrcpy(p, " RET=HDRS"); + while (*p) p++; + } + else if (dsn_ret == dsn_ret_full) + { + Ustrcpy(p, " RET=FULL"); + while (*p) p++; + } + if (dsn_envid != NULL) + { + string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid); + while (*p) p++; + } + } + +/* If an authenticated_sender override has been specified for this transport +instance, expand it. If the expansion is forced to fail, and there was already +an authenticated_sender for this message, the original value will be used. +Other expansion failures are serious. An empty result is ignored, but there is +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)) + { + yield = ERROR; + goto SEND_QUIT; } /* From here until we send the DATA command, we can make use of PIPELINING @@ -1530,18 +2023,52 @@ for (addr = first_addr; int count; BOOL no_flush; + addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no; + if (addr->transport_return != PENDING_DEFER) continue; address_count++; no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); + /* 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 ((addr->dsn_flags & rf_dsnflags) != 0) + { + int i; + BOOL first = TRUE; + Ustrcpy(p, " NOTIFY="); + while (*p) p++; + for (i = 0; i < 4; i++) + if ((addr->dsn_flags & rf_list[i]) != 0) + { + if (!first) *p++ = ','; + first = FALSE; + Ustrcpy(p, rf_names[i]); + while (*p) p++; + } + } + + if (addr->dsn_orcpt != NULL) + { + string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", + addr->dsn_orcpt); + while (*p) p++; + } + } + /* Now send the RCPT command, and process outstanding responses when necessary. After a timeout on RCPT, we just end the function, leaving the 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. */ - count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", - transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", + transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); + if (count < 0) goto SEND_FAILED; if (count > 0) { @@ -1573,16 +2100,15 @@ RCPT. */ if (mua_wrapper) { address_item *badaddr; - for (badaddr = first_addr; badaddr != NULL; badaddr = badaddr->next) - { - if (badaddr->transport_return != PENDING_OK) break; - } - if (badaddr != NULL) - { - set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message)); - ok = FALSE; - } + for (badaddr = first_addr; badaddr; badaddr = badaddr->next) + if (badaddr->transport_return != PENDING_OK) + { + /*XXX could we find a better errno than 0 here? */ + set_errno(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message), NULL); + ok = FALSE; + break; + } } /* If ok is TRUE, we know we have got at least one good recipient, and must now @@ -1631,6 +2157,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 | @@ -1684,8 +2211,31 @@ if (!ok) ok = TRUE; else smtp_command = US"end of data"; - /* For SMTP, we now read a single response that applies to the whole message. - If it is OK, then all the addresses have been delivered. */ +#ifndef DISABLE_PRDR + /* For PRDR we optionally get a partial-responses warning + * followed by the individual responses, before going on with + * the overall response. If we don't get the warning then deal + * with per non-PRDR. */ + if(prdr_active) + { + ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '3', + ob->final_timeout); + if (!ok && errno == 0) + switch(buffer[0]) + { + case '2': prdr_active = FALSE; + ok = TRUE; + break; + case '4': errno = ERRNO_DATA4XX; + addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + break; + } + } + else +#endif + + /* For non-PRDR SMTP, we now read a single response that applies to the + whole message. If it is OK, then all the addresses have been delivered. */ if (!lmtp) { @@ -1715,31 +2265,24 @@ if (!ok) ok = TRUE; else int flag = '='; int delivery_time = (int)(time(NULL) - start_delivery_time); int len; - host_item *thost; uschar *conf = NULL; send_rset = FALSE; - /* Make a copy of the host if it is local to this invocation - of the transport. */ - - if (copy_host) - { - thost = store_get(sizeof(host_item)); - *thost = *host; - thost->name = string_copy(host->name); - thost->address = string_copy(host->address); - } - else thost = host; - /* Set up confirmation if needed - applies only to SMTP */ - if ((log_extra_selector & LX_smtp_confirmation) != 0 && !lmtp) + if ( +#ifndef EXPERIMENTAL_EVENT + (log_extra_selector & LX_smtp_confirmation) != 0 && +#endif + !lmtp + ) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } - /* Process all transported addresses - for LMTP, read a status for + /* Process all transported addresses - for LMTP or PRDR, read a status for each one. */ for (addr = addrlist; addr != first_addr; addr = addr->next) @@ -1751,13 +2294,22 @@ if (!ok) ok = TRUE; else address. For temporary errors, add a retry item for the address so that it doesn't get tried again too soon. */ +#ifndef DISABLE_PRDR + if (lmtp || prdr_active) +#else if (lmtp) +#endif { if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->final_timeout)) { if (errno != 0 || buffer[0] == 0) goto RESPONSE_FAILED; - addr->message = string_sprintf("LMTP error after %s: %s", + addr->message = string_sprintf( +#ifndef DISABLE_PRDR + "%s error after %s: %s", prdr_active ? "PRDR":"LMTP", +#else + "LMTP error after %s: %s", +#endif big_buffer, string_printing(buffer)); setflag(addr, af_pass_message); /* Allow message to go to user */ if (buffer[0] == '5') @@ -1767,15 +2319,19 @@ if (!ok) ok = TRUE; else errno = ERRNO_DATA4XX; addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; addr->transport_return = DEFER; - retry_add_item(addr, addr->address_retry_key, 0); +#ifndef DISABLE_PRDR + if (!prdr_active) +#endif + retry_add_item(addr, addr->address_retry_key, 0); } continue; } completed_address = TRUE; /* NOW we can set this flag */ if ((log_extra_selector & LX_smtp_confirmation) != 0) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } } @@ -1784,28 +2340,76 @@ if (!ok) ok = TRUE; else addr->transport_return = OK; addr->more_errno = delivery_time; - addr->host_used = thost; + addr->host_used = host; addr->special_action = flag; addr->message = conf; +#ifndef DISABLE_PRDR + if (prdr_active) addr->flags |= af_prdr_used; +#endif flag = '-'; - /* Update the journal. For homonymic addresses, use the base address plus - the transport name. See lots of comments in deliver.c about the reasons - for the complications when homonyms are involved. Just carry on after - write error, as it may prove possible to update the spool file later. */ +#ifndef DISABLE_PRDR + if (!prdr_active) +#endif + { + /* Update the journal. For homonymic addresses, use the base address plus + the transport name. See lots of comments in deliver.c about the reasons + for the complications when homonyms are involved. Just carry on after + write error, as it may prove possible to update the spool file later. */ - if (testflag(addr, af_homonym)) - sprintf(CS buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); - else - sprintf(CS buffer, "%.500s\n", addr->unique); + if (testflag(addr, af_homonym)) + sprintf(CS buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + else + sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling %s", buffer); - len = Ustrlen(CS buffer); - if (write(journal_fd, buffer, len) != len) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " - "%s: %s", buffer, strerror(errno)); + DEBUG(D_deliver) debug_printf("journalling %s", buffer); + len = Ustrlen(CS buffer); + if (write(journal_fd, buffer, len) != len) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " + "%s: %s", buffer, strerror(errno)); + } } +#ifndef DISABLE_PRDR + if (prdr_active) + { + /* PRDR - get the final, overall response. For any non-success + upgrade all the address statuses. */ + ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->final_timeout); + if (!ok) + { + if(errno == 0 && buffer[0] == '4') + { + errno = ERRNO_DATA4XX; + addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + } + for (addr = addrlist; addr != first_addr; addr = addr->next) + if (buffer[0] == '5' || addr->transport_return == OK) + addr->transport_return = PENDING_OK; /* allow set_errno action */ + goto RESPONSE_FAILED; + } + + /* Update the journal, or setup retry. */ + for (addr = addrlist; addr != first_addr; addr = addr->next) + if (addr->transport_return == OK) + { + if (testflag(addr, af_homonym)) + sprintf(CS buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + else + sprintf(CS buffer, "%.500s\n", addr->unique); + + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer); + len = Ustrlen(CS buffer); + if (write(journal_fd, buffer, len) != len) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " + "%s: %s", buffer, strerror(errno)); + } + else if (addr->transport_return == DEFER) + retry_add_item(addr, addr->address_retry_key, -2); + } +#endif + /* Ensure the journal file is pushed out to disk. */ if (EXIMfsync(journal_fd) < 0) @@ -1847,10 +2451,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 @@ -1865,12 +2469,10 @@ if (!ok) if (setting_up) { if (code == '5') - { - set_errno(addrlist, save_errno, message, FAIL, pass_message); - } + set_errno(addrlist, save_errno, message, FAIL, pass_message, host); else { - set_errno(addrlist, save_errno, message, DEFER, pass_message); + set_errno(addrlist, save_errno, message, DEFER, pass_message, host); yield = DEFER; } } @@ -1926,7 +2528,7 @@ if (!ok) { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, - pass_message); + pass_message, host); /* If there's an errno, the message contains just the identity of the host. */ @@ -1936,7 +2538,7 @@ if (!ok) if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); - deliver_msglog("%s %s\n", tod_stamp(tod_log), message); + msglog_line(host, message); *message_defer = TRUE; } } @@ -1951,7 +2553,7 @@ if (!ok) { yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER, pass_message); + set_errno(addrlist, save_errno, message, DEFER, pass_message, host); } } } @@ -1993,15 +2595,21 @@ DEBUG(D_transport) if (completed_address && ok && send_quit) { BOOL more; - if (first_addr != NULL || continue_more || - ( - (tls_active < 0 || - verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name, - host->address, NULL) != OK) + smtp_compare_t t_compare; + + t_compare.tblock = tblock; + t_compare.current_sender_address = sender_address; + + if ( first_addr != NULL + || continue_more + || ( ( tls_out.active < 0 + || verify_check_given_host(&ob->hosts_nopass_tls, host) != OK + ) && transport_check_waiting(tblock->name, host->name, - tblock->connection_max_messages, new_message_id, &more) - )) + tblock->connection_max_messages, new_message_id, &more, + (oicf)smtp_are_same_identities, (void*)&t_compare) + ) ) { uschar *msg; BOOL pass_message; @@ -2022,7 +2630,8 @@ if (completed_address && ok && send_quit) &pass_message); if (!send_quit) { - DEBUG(D_transport) debug_printf("%s\n", msg); + DEBUG(D_transport) debug_printf("H=%s [%s] %s\n", + host->name, host->address, msg); } } } @@ -2043,10 +2652,10 @@ 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 - if (tls_active >= 0) +#ifdef SUPPORT_TLS + if (tls_out.active >= 0) { - tls_close(TRUE); + tls_close(FALSE, TRUE); if (smtps) ok = FALSE; else @@ -2054,7 +2663,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. */ @@ -2068,7 +2677,7 @@ if (completed_address && ok && send_quit) /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE); + else set_errno(first_addr, errno, msg, DEFER, FALSE, host); } } @@ -2096,12 +2705,10 @@ if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); END_OFF: #ifdef SUPPORT_TLS -tls_close(TRUE); +tls_close(FALSE, TRUE); #endif /* Close the socket, and return the appropriate value, first setting -continue_transport and continue_hostname NULL to prevent any other addresses -that may include the host from trying to re-use a continuation socket. This works because the NULL setting is passed back to the calling process, and remote_max_parallel is forced to 1 when delivering over an existing connection, @@ -2112,6 +2719,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_EVENT +(void) event_raise(tblock->event_action, US"tcp:close", NULL); +#endif + continue_transport = NULL; continue_hostname = NULL; return yield; @@ -2193,18 +2805,21 @@ prepare_addresses(address_item *addrlist, host_item *host) address_item *first_addr = NULL; address_item *addr; for (addr = addrlist; addr != NULL; addr = addr->next) - { - if (addr->transport_return != DEFER) continue; - if (first_addr == NULL) first_addr = addr; - addr->transport_return = PENDING_DEFER; - addr->basic_errno = 0; - addr->more_errno = (host->mx >= 0)? 'M' : 'A'; - addr->message = NULL; - #ifdef SUPPORT_TLS - addr->cipher = NULL; - addr->peerdn = NULL; - #endif - } + if (addr->transport_return == DEFER) + { + if (first_addr == NULL) first_addr = addr; + addr->transport_return = PENDING_DEFER; + addr->basic_errno = 0; + addr->more_errno = (host->mx >= 0)? 'M' : 'A'; + addr->message = NULL; +#ifdef SUPPORT_TLS + addr->cipher = NULL; + addr->ourcert = NULL; + addr->peercert = NULL; + addr->peerdn = NULL; + addr->ocsp = OCSP_NOT_REQ; +#endif + } return first_addr; } @@ -2293,8 +2908,7 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL)) if (Ustrchr(s, '$') != NULL) { - expanded_hosts = expand_string(s); - if (expanded_hosts == NULL) + if (!(expanded_hosts = expand_string(s))) { addrlist->message = string_sprintf("failed to expand list of hosts " "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message); @@ -2322,13 +2936,14 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL)) /* If there was no expansion of hosts, save the host list for next time. */ - if (expanded_hosts == NULL) ob->hostlist = hostlist; + if (!expanded_hosts) ob->hostlist = hostlist; } /* This is not the first time this transport has been run in this delivery; the host list was built previously. */ - else hostlist = ob->hostlist; + else + hostlist = ob->hostlist; } /* The host list was supplied with the address. If hosts_randomize is set, we @@ -2372,12 +2987,10 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continuing) hostlist = addrlist->host_list = newlist; } - /* Sort out the default port. */ if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE; - /* For each host-plus-IP-address on the list: . If this is a continued delivery and the host isn't the one with the @@ -2474,7 +3087,6 @@ for (cutoff_retry = 0; expired && { int new_port, flags; host_item *hh; - uschar *canonical_name; if (host->status >= hstatus_unusable) { @@ -2502,10 +3114,11 @@ for (cutoff_retry = 0; expired && if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS; if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0) - rc = host_find_byname(host, NULL, flags, &canonical_name, TRUE); + rc = host_find_byname(host, NULL, flags, NULL, TRUE); else rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - &canonical_name, NULL); + ob->dnssec_request_domains, ob->dnssec_require_domains, + NULL, NULL); /* Update the host (and any additional blocks, resulting from multihoming) with a host-specific port, if any. */ @@ -2581,7 +3194,8 @@ for (cutoff_retry = 0; expired && doing a two-stage queue run, don't do this if forcing. */ if ((!deliver_force || queue_2stage) && (queue_smtp || - match_isinlist(addrlist->domain, &queue_smtp_domains, 0, + match_isinlist(addrlist->domain, + (const uschar **)&queue_smtp_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)) { expired = FALSE; @@ -2604,6 +3218,9 @@ for (cutoff_retry = 0; expired && deliver_host = host->name; deliver_host_address = host->address; + lookup_dnssec_authenticated = host->dnssec == DS_YES ? US"yes" + : host->dnssec == DS_NO ? US"no" + : US""; /* Set up a string for adding to the retry key if the port number is not the standard SMTP port. A host may have its own port setting that overrides @@ -2630,14 +3247,20 @@ for (cutoff_retry = 0; expired && if (cutoff_retry == 0) { + BOOL incl_ip; /* Ensure the status of the address is set by checking retry data if - necessary. There maybe host-specific retry data (applicable to all + necessary. There may be host-specific retry data (applicable to all messages) and also data for retries of a specific message at this host. If either of these retry records are actually read, the keys used are returned to save recomputing them later. */ + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + continue; /* with next host */ + host_is_expired = retry_check_address(addrlist->domain, host, pistring, - ob->retry_include_ip_address, &retry_host_key, &retry_message_key); + incl_ip, &retry_host_key, &retry_message_key); DEBUG(D_transport) debug_printf("%s [%s]%s status = %s\n", host->name, (host->address == NULL)? US"" : host->address, pistring, @@ -2700,8 +3323,7 @@ for (cutoff_retry = 0; expired && sending the message down a pre-existing connection. */ if (!continuing && - verify_check_this_host(&(ob->serialize_hosts), NULL, host->name, - host->address, NULL) == OK) + verify_check_given_host(&ob->serialize_hosts, host) == OK) { serialize_key = string_sprintf("host-serialize-%s", host->name); if (!enq_start(serialize_key)) @@ -2738,7 +3360,7 @@ for (cutoff_retry = 0; expired && if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK, FALSE); + set_errno(addrlist, 0, NULL, OK, FALSE, NULL); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host; @@ -2771,6 +3393,20 @@ for (cutoff_retry = 0; expired && else { + host_item * thost; + /* Make a copy of the host if it is local to this invocation + of the transport. */ + + if (expanded_hosts) + { + thost = store_get(sizeof(host_item)); + *thost = *host; + thost->name = string_copy(host->name); + thost->address = string_copy(host->address); + } + else + thost = host; + if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try) { host_item *h; @@ -2790,8 +3426,8 @@ for (cutoff_retry = 0; expired && /* Attempt the delivery. */ total_hosts_tried++; - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, FALSE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, FALSE); /* Yield is one of: OK => connection made, each address contains its result; @@ -2812,6 +3448,11 @@ for (cutoff_retry = 0; expired && first_addr->basic_errno != ERRNO_TLSFAILURE) write_logs(first_addr, host); +#ifdef EXPERIMENTAL_EVENT + if (rc == DEFER) + deferred_event_raise(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 hosts_require_tls, and tls_tempfail_tryclear is true, try again, with @@ -2821,21 +3462,26 @@ for (cutoff_retry = 0; expired && session, so the in-clear transmission after those errors, if permitted, happens inside smtp_deliver().] */ - #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 SUPPORT_TLS + if ( rc == DEFER + && first_addr->basic_errno == ERRNO_TLSFAILURE + && ob->tls_tempfail_tryclear + && verify_check_given_host(&ob->hosts_require_tls, host) != OK + ) { log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " "to %s [%s] (not in hosts_require_tls)", host->name, host->address); first_addr = prepare_addresses(addrlist, host); - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, TRUE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) write_logs(first_addr, host); +# ifdef EXPERIMENTAL_EVENT + if (rc == DEFER) + deferred_event_raise(first_addr, host); +# endif } - #endif +#endif /*SUPPORT_TLS*/ } /* Delivery attempt finished */ @@ -2864,7 +3510,13 @@ for (cutoff_retry = 0; expired && int delete_flag = (rc != DEFER)? rf_delete : 0; if (retry_host_key == NULL) { - retry_host_key = ob->retry_include_ip_address? + BOOL incl_ip; + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + incl_ip = TRUE; /* error; use most-specific retry record */ + + retry_host_key = incl_ip ? string_sprintf("T:%S:%s%s", host->name, host->address, pistring) : string_sprintf("T:%S%s", host->name, pistring); } @@ -2906,7 +3558,13 @@ for (cutoff_retry = 0; expired && int delete_flag = message_defer? 0 : rf_delete; if (retry_message_key == NULL) { - retry_message_key = ob->retry_include_ip_address? + BOOL incl_ip; + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + incl_ip = TRUE; /* error; use most-specific retry record */ + + retry_message_key = incl_ip ? string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring, message_id) : string_sprintf("T:%S%s:%s", host->name, pistring, message_id); @@ -3094,9 +3752,12 @@ for (addr = addrlist; addr != NULL; addr = addr->next) /* Update the database which keeps information about which messages are waiting for which hosts to become available. For some message-specific errors, the update_waiting flag is turned off because we don't want follow-on deliveries in -those cases. */ +those cases. If this transport instance is explicitly limited to one message +per connection then follow-on deliveries are not possible and there's no need +to create/update the per-transport wait- database. */ -if (update_waiting) transport_update_waiting(hostlist, tblock->name); +if (update_waiting && tblock->connection_max_messages != 1) + transport_update_waiting(hostlist, tblock->name); END_TRANSPORT: @@ -3105,4 +3766,6 @@ DEBUG(D_transport) debug_printf("Leaving %s transport\n", tblock->name); return TRUE; /* Each address has its status */ } +/* vi: aw ai sw=2 +*/ /* End of transport/smtp.c */