X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/a1bccd48f3956b50a13a34f5aed4b72c658c61af..0e0f3f562bf23cf035baf85cdd071d392751b676:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 7b2a7d559..a455ba553 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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, @@ -142,13 +147,13 @@ 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) }, @@ -171,10 +176,8 @@ optionlist smtp_transport_options[] = { (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) }, -#ifdef EXPERIMENTAL_CERTNAMES { "tls_verify_cert_hostnames", opt_stringptr, (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)}, -#endif { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }, { "tls_verify_hosts", opt_stringptr, @@ -209,10 +212,10 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* hosts_require_dane */ #endif #ifndef DISABLE_PRDR - NULL, /* hosts_try_prdr */ + US"*", /* hosts_try_prdr */ #endif #ifndef DISABLE_OCSP - US"*", /* hosts_request_ocsp (except under DANE) */ + US"*", /* hosts_request_ocsp (except under DANE; tls_client_start()) */ NULL, /* hosts_require_ocsp */ #endif NULL, /* hosts_require_tls */ @@ -241,6 +244,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* hosts_randomize */ TRUE, /* keepalive */ FALSE, /* lmtp_ignore_quota */ + NULL, /* expand_retry_include_ip_address */ TRUE /* retry_include_ip_address */ #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ @@ -251,15 +255,13 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* gnutls_require_mac */ NULL, /* gnutls_require_proto */ NULL, /* tls_sni */ - NULL, /* tls_verify_certificates */ + US"system", /* tls_verify_certificates */ EXIM_CLIENT_DH_DEFAULT_MIN_BITS, /* tls_dh_min_bits */ TRUE, /* tls_tempfail_tryclear */ NULL, /* tls_verify_hosts */ - NULL /* tls_try_verify_hosts */ -# ifdef EXPERIMENTAL_CERTNAMES - ,NULL /* tls_verify_cert_hostnames */ -# endif + NULL, /* tls_try_verify_hosts */ + US"*" /* tls_verify_cert_hostnames */ #endif #ifndef DISABLE_DKIM ,NULL, /* dkim_canon */ @@ -277,7 +279,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { static int rf_list[] = {rf_notify_never, rf_notify_success, rf_notify_failure, rf_notify_delay }; -static uschar *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" }; +static uschar *rf_names[] = { US"NEVER", US"SUCCESS", US"FAILURE", US"DELAY" }; #endif @@ -433,6 +435,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 @@ -443,7 +446,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; @@ -463,6 +466,8 @@ for (addr = addrlist; addr != NULL; addr = addr->next) if (pass_message) setflag(addr, af_pass_message); } addr->transport_return = rc; + if (host) + addr->host_used = host; } } @@ -508,8 +513,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); @@ -522,13 +527,11 @@ if (*errno_value == ERRNO_SMTPFORMAT) { 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; } @@ -568,7 +571,7 @@ if (buffer[0] != 0) { 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; @@ -583,8 +586,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); @@ -609,9 +612,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); @@ -619,21 +624,25 @@ 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_TPDA + +#ifdef EXPERIMENTAL_EVENT /************************************************* * Post-defer action * *************************************************/ @@ -649,9 +658,9 @@ Returns: nothing */ static void -tpda_deferred(address_item *addr, host_item *host) +deferred_event_raise(address_item *addr, host_item *host) { -uschar * action = addr->transport->tpda_event_action; +uschar * action = addr->transport->event_action; uschar * save_domain; uschar * save_local; @@ -663,15 +672,15 @@ 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; +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) tpda_raise_event(action, US"msg:host:defer", +(void) event_raise(action, US"msg:host:defer", addr->message ? addr->basic_errno > 0 ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) @@ -777,6 +786,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; } } @@ -792,6 +809,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)) { @@ -816,10 +834,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, save_errno, message, DEFER, FALSE, NULL); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -843,10 +860,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 */ @@ -864,9 +881,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. */ @@ -953,8 +972,7 @@ 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); +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|$)", @@ -969,8 +987,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) regex match above. */ if (require_auth == OK || - verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name, - host->address, NULL) == OK) + verify_check_given_host(&ob->hosts_try_auth, host) == OK) { auth_instance *au; fail_reason = US"no common mechanisms were found"; @@ -1068,7 +1085,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) /* Internal problem, message in buffer. */ case ERROR: - set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); + set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE, NULL); return ERROR; } @@ -1084,7 +1101,7 @@ if (require_auth == OK && !smtp_authenticated) { set_errno(addrlist, ERRNO_AUTHFAIL, string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE); + FALSE, NULL); return DEFER; } @@ -1123,7 +1140,7 @@ if (ob->authenticated_sender != NULL) { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, 0, message, DEFER, FALSE, NULL); return TRUE; } } @@ -1148,6 +1165,46 @@ 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]; +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 + + /************************************************* * Deliver address list to given host * *************************************************/ @@ -1174,8 +1231,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 @@ -1196,7 +1251,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; @@ -1226,6 +1281,10 @@ BOOL prdr_active; #ifdef EXPERIMENTAL_DSN BOOL dsn_all_lasthop = TRUE; #endif +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) +BOOL dane = FALSE; +dns_answer tlsa_dnsa; +#endif smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; @@ -1280,7 +1339,7 @@ tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); + set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE, NULL); return ERROR; } #endif @@ -1295,18 +1354,46 @@ if (continue_hostname == NULL) inblock.sock = outblock.sock = smtp_connect(host, host_af, port, interface, ob->connect_timeout, ob->keepalive, ob->dscp -#ifdef EXPERIMENTAL_TPDA - , tblock->tpda_event_action +#ifdef EXPERIMENTAL_EVENT + , tblock->event_action #endif ); 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) + { + BOOL dane_required; + + 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. */ @@ -1322,15 +1409,21 @@ 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) +#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) { - uschar *message = US"deferred by smtp:connect event expansion"; - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, 0, + 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 @@ -1340,7 +1433,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, 0, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1381,8 +1474,7 @@ 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 @@ -1440,11 +1532,10 @@ goto SEND_QUIT; #endif #ifndef DISABLE_PRDR - prdr_offered = esmtp && - (pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, - PCRE_EOPT, NULL, 0) >= 0) && - (verify_check_this_host(&(ob->hosts_try_prdr), NULL, host->name, - host->address, NULL) == OK); + 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");} @@ -1474,9 +1565,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) @@ -1505,7 +1596,11 @@ if (tls_offered && !suppress_tls && else TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, host, addrlist, tblock); + 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 @@ -1522,7 +1617,6 @@ 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_out.cipher; @@ -1531,7 +1625,6 @@ if (tls_offered && !suppress_tls && addr->peerdn = tls_out.peerdn; addr->ocsp = tls_out.ocsp; } - } } } @@ -1555,7 +1648,7 @@ if (tls_out.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, 0, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1588,22 +1681,20 @@ if (tls_out.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 -#ifdef EXPERIMENTAL_DANE - || verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name, - host->address, NULL) == OK -#endif +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 @@ -1634,21 +1725,19 @@ 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_this_host(&(ob->hosts_try_prdr), NULL, host->name, - host->address, NULL) == OK; + 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");} @@ -1699,7 +1788,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; } @@ -1773,12 +1862,12 @@ if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE)) { if (dsn_ret == dsn_ret_hdrs) { - strcpy(p, " RET=HDRS"); + Ustrcpy(p, " RET=HDRS"); while (*p) p++; } else if (dsn_ret == dsn_ret_full) { - strcpy(p, " RET=FULL"); + Ustrcpy(p, " RET=FULL"); while (*p) p++; } if (dsn_envid != NULL) @@ -1875,14 +1964,14 @@ for (addr = first_addr; { int i; BOOL first = TRUE; - strcpy(p, " NOTIFY="); + 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; - strcpy(p, rf_names[i]); + Ustrcpy(p, rf_names[i]); while (*p) p++; } } @@ -1948,7 +2037,7 @@ if (mua_wrapper) if (badaddr != NULL) { set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message)); + testflag(badaddr, af_pass_message), NULL); ok = FALSE; } } @@ -2107,26 +2196,13 @@ 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 ( -#ifndef EXPERIMENTAL_TPDA +#ifndef EXPERIMENTAL_EVENT (log_extra_selector & LX_smtp_confirmation) != 0 && #endif !lmtp @@ -2193,7 +2269,7 @@ 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 @@ -2322,12 +2398,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; } } @@ -2383,7 +2457,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. */ @@ -2393,7 +2467,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; } } @@ -2408,7 +2482,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); } } } @@ -2450,15 +2524,15 @@ DEBUG(D_transport) if (completed_address && ok && send_quit) { BOOL more; - if (first_addr != NULL || continue_more || - ( - (tls_out.active < 0 || - verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name, - host->address, NULL) != OK) + 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) - )) + ) ) { uschar *msg; BOOL pass_message; @@ -2479,7 +2553,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); } } } @@ -2525,7 +2600,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); } } @@ -2568,8 +2643,8 @@ 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); +#ifdef EXPERIMENTAL_EVENT +(void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif continue_transport = NULL; @@ -2756,8 +2831,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); @@ -2785,7 +2859,7 @@ 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; @@ -3097,14 +3171,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, @@ -3167,8 +3247,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)) @@ -3205,7 +3284,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; @@ -3238,6 +3317,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; @@ -3257,8 +3350,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; @@ -3279,9 +3372,9 @@ for (cutoff_retry = 0; expired && first_addr->basic_errno != ERRNO_TLSFAILURE) write_logs(first_addr, host); -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT if (rc == DEFER) - tpda_deferred(first_addr, host); + deferred_event_raise(first_addr, host); #endif /* If STARTTLS was accepted, but there was a failure in setting up the @@ -3297,24 +3390,19 @@ for (cutoff_retry = 0; expired && 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 - && verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name, - host->address, NULL) != OK -# endif + && 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_TPDA +# ifdef EXPERIMENTAL_EVENT if (rc == DEFER) - tpda_deferred(first_addr, host); + deferred_event_raise(first_addr, host); # endif } #endif /*SUPPORT_TLS*/ @@ -3346,7 +3434,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); } @@ -3388,7 +3482,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);