* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
/* Structure for caching DNSBL lookups */
typedef struct dnsbl_cache_block {
+ time_t expiry;
dns_address *rhs;
uschar *text;
int rc;
if (cache_record == NULL)
{
- HDEBUG(D_verify) debug_printf("callout cache: no %s record found\n", type);
+ HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key);
return NULL;
}
if (now - cache_record->time_stamp > expire)
{
- HDEBUG(D_verify) debug_printf("callout cache: %s record expired\n", type);
+ HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key);
return NULL;
}
cache_record->random_result = ccache_unknown;
}
-HDEBUG(D_verify) debug_printf("callout cache: found %s record\n", type);
+HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key);
return cache_record;
}
dbdata_callout_cache_address new_address_record;
host_item *host;
time_t callout_start_time;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+BOOL utf8_offered = FALSE;
+#endif
new_domain_record.result = ccache_unknown;
new_domain_record.postmaster_result = ccache_unknown;
host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
- if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
+ if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
US"callout") ||
!smtp_get_port(tf->port, addr, &port, US"callout"))
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
deliver_domain = addr->domain;
transport_name = addr->transport->name;
- if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
- US"callout") ||
- !smtp_get_port(tf->port, addr, &port, US"callout"))
+ if ( !smtp_get_interface(tf->interface, host_af, addr, &interface,
+ US"callout")
+ || !smtp_get_port(tf->port, addr, &port, US"callout")
+ )
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port);
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
- {
- int rc;
-
- 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*/
-
/* Set up the buffer for reading SMTP response packets. */
inblock.buffer = inbuffer;
continue;
}
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ {
+ int rc;
+
+ 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
+ )
+ && (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 helo_data string to find the host name to use. */
if (tf->helo_data != NULL)
}
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ else if ( addr->prop.utf8_msg
+ && !addr->prop.utf8_downcvt
+ && !( esmtp
+ && ( regex_UTF8
+ || ( (regex_UTF8 = regex_must_compile(
+ US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE)),
+ TRUE
+ ) )
+ && ( (utf8_offered = pcre_exec(regex_UTF8, NULL,
+ CS responsebuffer, Ustrlen(responsebuffer),
+ 0, PCRE_EOPT, NULL, 0) >= 0)
+ || addr->prop.utf8_downcvt_maybe
+ ) ) )
+ {
+ HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n");
+ errno = ERRNO_UTF8_FWD;
+ setflag(addr, af_verify_nsfail);
+ done = FALSE;
+ }
+ else if ( addr->prop.utf8_msg
+ && (addr->prop.utf8_downcvt || !utf8_offered)
+ && (setflag(addr, af_utf8_downcvt),
+ from_address = string_address_utf8_to_alabel(from_address,
+ &addr->message),
+ addr->message
+ ) )
+ {
+ errno = ERRNO_EXPANDFAIL;
+ setflag(addr, af_verify_nsfail);
+ done = FALSE;
+ }
+#endif
+
/* If we haven't authenticated, but are required to, give up. */
/* Try to AUTH */
( (addr->auth_sndr = client_authenticated_sender),
/* Send the MAIL command */
- (smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>%s\r\n",
+ (smtp_write_command(&outblock, FALSE,
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ addr->prop.utf8_msg && !addr->prop.utf8_downcvt
+ ? "MAIL FROM:<%s>%s SMTPUTF8\r\n"
+ :
+#endif
+ "MAIL FROM:<%s>%s\r\n",
from_address, responsebuffer) >= 0)
) &&
else
{
+ const uschar * rcpt_domain = addr->domain;
+
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ uschar * errstr = NULL;
+ if ( testflag(addr, af_utf8_downcvt)
+ && (rcpt_domain = string_domain_utf8_to_alabel(rcpt_domain,
+ &errstr), errstr)
+ )
+ {
+ addr->message = errstr;
+ errno = ERRNO_EXPANDFAIL;
+ setflag(addr, af_verify_nsfail);
+ done = FALSE;
+ rcpt_domain = US""; /*XXX errorhandling! */
+ }
+#endif
+
new_domain_record.result =
(old_domain_cache_result == ccache_reject_mfnull)?
ccache_reject_mfnull: ccache_accept;
BOOL random_ok =
smtp_write_command(&outblock, FALSE,
"RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part,
- addr->domain) >= 0 &&
+ rcpt_domain) >= 0 &&
smtp_read_response(&inblock, randombuffer,
sizeof(randombuffer), '2', callout);
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout) &&
- smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n",
+ smtp_write_command(&outblock, FALSE,
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ addr->prop.utf8_msg && !addr->prop.utf8_downcvt
+ ? "MAIL FROM:<%s> SMTPUTF8\r\n"
+ :
+#endif
+ "MAIL FROM:<%s>\r\n",
from_address) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
/* Get the rcpt_include_affixes flag from the transport if there is one,
but assume FALSE if there is not. */
+ uschar * rcpt = transport_rcpt_address(addr,
+ addr->transport ? addr->transport->rcpt_include_affixes : FALSE);
+
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ /*XXX should the conversion be moved into transport_rcpt_address() ? */
+ uschar * dummy_errstr = NULL;
+ if ( testflag(addr, af_utf8_downcvt)
+ && (rcpt = string_address_utf8_to_alabel(rcpt, &dummy_errstr),
+ dummy_errstr
+ ) )
+ {
+ errno = ERRNO_EXPANDFAIL;
+ *failure_ptr = US"recipient";
+ done = FALSE;
+ }
+ else
+#endif
+
done =
smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n",
- transport_rcpt_address(addr,
- (addr->transport == NULL)? FALSE :
- addr->transport->rcpt_include_affixes)) >= 0 &&
+ rcpt) >= 0 &&
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
((
smtp_write_command(&outblock, FALSE,
- "RCPT TO:<postmaster@%.1000s>\r\n", addr->domain) >= 0 &&
+ "RCPT TO:<postmaster@%.1000s>\r\n", rcpt_domain) >= 0 &&
smtp_read_response(&inblock, responsebuffer,
sizeof(responsebuffer), '2', callout)
)
HDEBUG(D_verify) debug_printf("SMTP timeout\n");
send_quit = FALSE;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ else if (errno == ERRNO_UTF8_FWD)
+ {
+ extern int acl_where; /* src/acl.c */
+ errno = 0;
+ addr->message = string_sprintf(
+ "response to \"%s\" from %s [%s] did not include SMTPUTF8",
+ big_buffer, host->name, host->address);
+ addr->user_message = acl_where == ACL_WHERE_RCPT
+ ? US"533 mailbox name not allowed"
+ : US"550 mailbox unavailable";
+ yield = FAIL;
+ done = TRUE;
+ }
+#endif
else if (errno == 0)
{
if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped");
vaddr->user_message = addr->user_message;
vaddr->basic_errno = addr->basic_errno;
vaddr->more_errno = addr->more_errno;
- vaddr->p.address_data = addr->p.address_data;
+ vaddr->prop.address_data = addr->prop.address_data;
copyflag(vaddr, addr, af_pass_message);
}
return yield;
/* Just in case some router parameter refers to it. */
- return_path = (addr->p.errors_address != NULL)?
- addr->p.errors_address : sender_address;
+ return_path = (addr->prop.errors_address != NULL)?
+ addr->prop.errors_address : sender_address;
/* Split the address into domain and local part, handling the %-hack if
necessary, and then route it. While routing a sender address, set
(void)host_find_byname(host, NULL, flags, NULL, TRUE);
else
{
- uschar * d_request = NULL, * d_require = NULL;
+ dnssec_domains * dnssec_domains = NULL;
if (Ustrcmp(addr->transport->driver_name, "smtp") == 0)
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *)
addr->transport->options_block;
- d_request = ob->dnssec_request_domains;
- d_require = ob->dnssec_require_domains;
+ dnssec_domains = &ob->dnssec;
}
(void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
- d_request, d_require, NULL, NULL);
+ dnssec_domains, NULL, NULL);
}
}
}
/* If we have carried on to verify a child address, we want the value
of $address_data to be that of the child */
- vaddr->p.address_data = addr->p.address_data;
+ vaddr->prop.address_data = addr->prop.address_data;
yield = OK;
goto out;
}
fprintf(f, "%s", CS addr->address);
#ifdef EXPERIMENTAL_SRS
- if(addr->p.srs_sender)
- fprintf(f, " [srs = %s]", addr->p.srs_sender);
+ if(addr->prop.srs_sender)
+ fprintf(f, " [srs = %s]", addr->prop.srs_sender);
#endif
/* If the address is a duplicate, show something about it. */
while (len++ < maxaddlen) fprintf(f," ");
if (h->mx >= 0) fprintf(f, "MX=%d", h->mx);
if (h->port != PORT_NONE) fprintf(f, " port=%d", h->port);
+ if (running_in_test_harness)
+#ifndef DISABLE_DNSSEC
+ fprintf(f, " ad=%s", h->dnssec==DS_YES ? "yes" : "no");
+#else
+ fprintf(f, " ad=no");
+#endif
if (h->status == hstatus_unusable) fprintf(f, " ** unusable **");
fprintf(f, "\n");
}
if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout)
< 0)
{
- if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0)
+ if (errno == ETIMEDOUT && LOGGING(ident_timeout))
{
log_write(0, LOG_MAIN, "ident connection to %s timed out",
sender_host_address);
h.address = NULL;
h.mx = MX_NONE;
+ /* Using byname rather than bydns here means we cannot determine dnssec
+ status. On the other hand it is unclear how that could be either
+ propagated up or enforced. */
+
rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE);
if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
{
/* Look for this query in the cache. */
-t = tree_search(dnsbl_cache, query);
+if ( (t = tree_search(dnsbl_cache, query))
+ && (cb = t->data.ptr)->expiry > time(NULL)
+ )
+
+/* Previous lookup was cached */
+
+ {
+ HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
+ }
/* If not cached from a previous lookup, we must do a DNS lookup, and
cache the result in permanent memory. */
-if (t == NULL)
+else
{
+ uint ttl = UINT_MAX;
+
store_pool = POOL_PERM;
- /* Set up a tree entry to cache the lookup */
+ if (t)
+ {
+ HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; ");
+ }
- t = store_get(sizeof(tree_node) + Ustrlen(query));
- Ustrcpy(t->name, query);
- t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
- (void)tree_insertnode(&dnsbl_cache, t);
+ else
+ { /* Set up a tree entry to cache the lookup */
+ t = store_get(sizeof(tree_node) + Ustrlen(query));
+ Ustrcpy(t->name, query);
+ t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+ (void)tree_insertnode(&dnsbl_cache, t);
+ }
/* Do the DNS loopup . */
dns_record *rr;
dns_address **addrp = &(cb->rhs);
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
- rr != NULL;
+ rr;
rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
{
if (rr->type == T_A)
{
dns_address *da = dns_address_from_rr(&dnsa, rr);
- if (da != NULL)
+ if (da)
{
*addrp = da;
while (da->next != NULL) da = da->next;
addrp = &(da->next);
+ if (ttl > rr->ttl) ttl = rr->ttl;
}
}
}
if (cb->rhs == NULL) cb->rc = DNS_NODATA;
}
+ cb->expiry = time(NULL)+ttl;
store_pool = old_pool;
}
-/* Previous lookup was cached */
-
-else
- {
- HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
- cb = t->data.ptr;
- }
-
/* We now have the result of the DNS lookup, either newly done, or cached
from a previous call. If the lookup succeeded, check against the address
list if there is one. This may be a positive equality list (introduced by