{ "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) },
+ (void *)offsetof(smtp_transport_options_block, dnssec.request) },
{ "dnssec_require_domains", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dnssec_require_domains) },
+ (void *)offsetof(smtp_transport_options_block, dnssec.require) },
{ "dscp", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, dscp) },
{ "fallback_hosts", opt_stringptr,
#endif
NULL, /* hosts_require_tls */
NULL, /* hosts_avoid_tls */
- US"*", /* hosts_verify_avoid_tls */
+ NULL, /* hosts_verify_avoid_tls */
NULL, /* hosts_avoid_pipelining */
NULL, /* hosts_avoid_esmtp */
NULL, /* hosts_nopass_tls */
NULL, /* expand_retry_include_ip_address */
TRUE /* retry_include_ip_address */
#ifdef EXPERIMENTAL_SOCKS
-#endif
,NULL /* socks_proxy */
+#endif
#ifdef SUPPORT_TLS
,NULL, /* tls_certificate */
NULL, /* tls_crl */
return FALSE;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+/* Handle lack of advertised SMTPUTF8, for international message */
+if (*errno_value == ERRNO_UTF8_FWD)
+ {
+ *message = US string_sprintf("utf8 support required but not offered for forwarding");
+ DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message);
+ return TRUE;
+ }
+#endif
+
/* Handle error responses from the remote mailer. */
if (buffer[0] != 0)
BOOL prdr_offered = FALSE;
BOOL prdr_active;
#endif
+#ifdef EXPERIMENTAL_INTERNATIONAL
+BOOL utf8_needed = FALSE;
+BOOL utf8_offered = FALSE;
+#endif
BOOL dsn_all_lasthop = TRUE;
#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
BOOL dane = FALSE;
uschar buffer[4096];
uschar inbuffer[4096];
uschar outbuffer[4096];
-address_item * current_address;
suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */
if (host->dnssec == DS_YES)
{
- if( dane_required
- || verify_check_given_host(&ob->hosts_try_dane, host) == OK
+ if( ( dane_required
+ || verify_check_given_host(&ob->hosts_try_dane, host) == OK
+ )
+ && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK
)
- if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
- return rc;
+ {
+ set_errno(addrlist, ERRNO_DNSDEFER,
+ string_sprintf("DANE error: tlsa lookup %s",
+ rc == DEFER ? "DEFER" : "FAIL"),
+ rc, FALSE, NULL);
+ return rc;
+ }
}
else if (dane_required)
{
- log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
- return FAIL;
+ set_errno(addrlist, ERRNO_DNSDEFER,
+ string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
+ FAIL, FALSE, NULL);
+ return FAIL;
}
if (dane)
delayed till here so that $sending_interface and $sending_port are set. */
helo_data = expand_string(ob->helo_data);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ if (helo_data)
+ {
+ uschar * errstr = NULL;
+ if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr)
+ {
+ errstr = string_sprintf("failed to expand helo_data: %s", errstr);
+ set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
+ }
+#endif
/* The first thing is to wait for an initial OK response. The dreaded "goto"
is nevertheless a reasonably clean way of programming this kind of logic,
if (prdr_offered)
{DEBUG(D_transport) debug_printf("PRDR usable\n");}
#endif
+
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ if (addrlist->prop.utf8_msg)
+ {
+ utf8_needed = !addrlist->prop.utf8_downcvt
+ && !addrlist->prop.utf8_downcvt_maybe;
+ DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n",
+ addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
+
+ utf8_offered = esmtp
+ && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
+ PCRE_EOPT, NULL, 0) >= 0;
+ }
+#endif
}
/* For continuing deliveries down the same channel, the socket is the standard
#ifndef DISABLE_PRDR
prdr_offered = esmtp
&& pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 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
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ if (addrlist->prop.utf8_msg)
+ utf8_offered = esmtp
+ && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
+ PCRE_EOPT, NULL, 0) >= 0;
+#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;
+ smtp_use_dsn = esmtp
+ && pcre_exec(regex_DSN, NULL, CS buffer, 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.
setting_up = FALSE;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+/* If this is an international message we need the host to speak SMTPUTF8 */
+if (utf8_needed && !utf8_offered)
+ {
+ errno = ERRNO_UTF8_FWD;
+ goto RESPONSE_FAILED;
+ }
+#endif
+
/* If there is a filter command specified for this transport, we can now
set it up. This cannot be done until the identify of the host is known. */
}
#endif
+#ifdef EXPERIMENTAL_INTERNATIONAL
+if (addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt && utf8_offered)
+ sprintf(CS p, " SMTPUTF8"), p += 9;
+#endif
+
/* check if all addresses have lasthop flag */
/* do not send RET and ENVID if true */
-dsn_all_lasthop = TRUE;
-for (addr = first_addr;
+for (dsn_all_lasthop = TRUE, addr = first_addr;
address_count < max_rcpt && addr != NULL;
addr = addr->next)
if ((addr->dsn_flags & rf_dsnlasthop) != 1)
+ {
dsn_all_lasthop = FALSE;
+ break;
+ }
/* Add any DSN flags to the mail command */
-if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE))
+if (smtp_use_dsn && !dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
{
pending_MAIL = TRUE; /* The block starts with MAIL */
-rc = smtp_write_command(&outblock, smtp_use_pipelining,
- "MAIL FROM:<%s>%s\r\n", return_path, buffer);
+ {
+ uschar * s = return_path;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ uschar * errstr = NULL;
+
+ /* If we must downconvert, do the from-address here. Remember we had to
+ for the to-addresses (done below), and also (ugly) for re-doing when building
+ the delivery log line. */
+
+ if (addrlist->prop.utf8_msg && (addrlist->prop.utf8_downcvt || !utf8_offered))
+ {
+ if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
+ {
+ set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
+ yield = ERROR;
+ goto SEND_QUIT;
+ }
+ setflag(addrlist, af_utf8_downcvt);
+ }
+#endif
+
+ rc = smtp_write_command(&outblock, smtp_use_pipelining,
+ "MAIL FROM:<%s>%s\r\n", s, buffer);
+ }
+
mail_command = string_copy(big_buffer); /* Save for later error message */
switch(rc)
{
case -1: /* Transmission error */
- goto SEND_FAILED;
+ goto SEND_FAILED;
case +1: /* Block was sent */
- if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout))
- {
- if (errno == 0 && buffer[0] == '4')
{
- errno = ERRNO_MAIL4XX;
- addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ if (errno == 0 && buffer[0] == '4')
+ {
+ errno = ERRNO_MAIL4XX;
+ addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+ }
+ goto RESPONSE_FAILED;
}
- goto RESPONSE_FAILED;
- }
- pending_MAIL = FALSE;
- break;
+ pending_MAIL = FALSE;
+ break;
}
/* Pass over all the relevant recipient addresses for this host, which are the
{
int count;
BOOL no_flush;
+ uschar * rcpt_addr;
addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no;
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. */
+ rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes);
+
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ {
+ uschar * dummy_errstr;
+ if ( testflag(addrlist, af_utf8_downcvt)
+ && (rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, &dummy_errstr),
+ dummy_errstr
+ ) )
+ {
+ errno = ERRNO_EXPANDFAIL;
+ goto SEND_FAILED;
+ }
+ }
+#endif
+
count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
- transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer);
+ rcpt_addr, igquotstr, buffer);
if (count < 0) goto SEND_FAILED;
if (count > 0)
switch(save_errno)
{
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ case ERRNO_UTF8_FWD:
+ code = '5';
+ /*FALLTHROUGH*/
+#endif
case 0:
case ERRNO_MAIL4XX:
case ERRNO_DATA4XX:
- message_error = TRUE;
- break;
+ message_error = TRUE;
+ break;
case ETIMEDOUT:
- message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 ||
- Ustrncmp(smtp_command,"end ",4) == 0;
- break;
+ message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 ||
+ Ustrncmp(smtp_command,"end ",4) == 0;
+ break;
case ERRNO_SMTPCLOSED:
- message_error = Ustrncmp(smtp_command,"end ",4) == 0;
- break;
+ message_error = Ustrncmp(smtp_command,"end ",4) == 0;
+ break;
default:
- message_error = FALSE;
- break;
+ message_error = FALSE;
+ break;
}
/* Handle the cases that are treated as message errors. These are:
(a) negative response or timeout after MAIL
(b) negative response after DATA
(c) negative response or timeout or dropped connection after "."
+ (d) utf8 support required and not offered
It won't be a negative response or timeout after RCPT, as that is dealt
with separately above. The action in all cases is to set an appropriate
rc = host_find_byname(host, NULL, flags, NULL, TRUE);
else
rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
- ob->dnssec_request_domains, ob->dnssec_require_domains,
+ &ob->dnssec, /* domains for request/require */
NULL, NULL);
/* Update the host (and any additional blocks, resulting from
case, see if any of them are deferred. */
if (rc == OK)
- {
- for (addr = addrlist; addr != NULL; addr = addr->next)
- {
+ for (addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == DEFER)
{
some_deferred = TRUE;
break;
}
- }
- }
/* If no addresses deferred or the result was ERROR, return. We do this for
ERROR because a failing filter set-up or add_headers expansion is likely to