* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for interfacing with the DNS. */
+/* Increment the aptr in dnss, checking against dnsa length.
+Return: TRUE for a bad result
+*/
+static BOOL
+dnss_inc(dns_answer * dnsa, dns_scan * dnss, unsigned delta)
+{
+return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen;
+}
+
/*************************************************
* Get next DNS record from answer block *
*************************************************/
HEADER *h = (HEADER *)dnsa->answer;
int namelen;
+char * trace = NULL;
+#ifdef rr_trace
+# define TRACE DEBUG(D_dns)
+#else
+trace = trace;
+# define TRACE if (FALSE)
+#endif
+
/* Reset the saved data when requested to, and skip to the first required RR */
if (reset != RESET_NEXT)
{
+ TRACE debug_printf("%s: reset\n", __FUNCTION__);
dnss->rrcount = ntohs(h->qdcount);
dnss->aptr = dnsa->answer + sizeof(HEADER);
while (dnss->rrcount-- > 0)
{
+ TRACE trace = "Q-namelen";
namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- dnss->aptr, (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME);
- if (namelen < 0) { dnss->rrcount = 0; return NULL; }
- dnss->aptr += namelen + 4; /* skip name & type & class */
+ dnss->aptr, (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME);
+ if (namelen < 0) goto null_return;
+ /* skip name & type & class */
+ TRACE trace = "Q-skip";
+ if (dnss_inc(dnsa, dnss, namelen+4)) goto null_return;
}
/* Get the number of answer records. */
the NS records (i.e. authority section) if wanting to look at the additional
records. */
- if (reset == RESET_ADDITIONAL) dnss->rrcount += ntohs(h->nscount);
+ if (reset == RESET_ADDITIONAL)
+ {
+ TRACE debug_printf("%s: additional\n", __FUNCTION__);
+ dnss->rrcount += ntohs(h->nscount);
+ }
if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL)
{
+ TRACE if (reset == RESET_AUTHORITY)
+ debug_printf("%s: authority\n", __FUNCTION__);
while (dnss->rrcount-- > 0)
{
+ TRACE trace = "A-namelen";
namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- dnss->aptr, (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME);
- if (namelen < 0) { dnss->rrcount = 0; return NULL; }
- dnss->aptr += namelen + 8; /* skip name, type, class & TTL */
+ dnss->aptr, (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME);
+ if (namelen < 0) goto null_return;
+ /* skip name, type, class & TTL */
+ TRACE trace = "A-hdr";
+ if (dnss_inc(dnsa, dnss, namelen+8)) goto null_return;
GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */
- dnss->aptr += dnss->srr.size; /* skip over it */
+ /* skip over it */
+ TRACE trace = "A-skip";
+ if (dnss_inc(dnsa, dnss, dnss->srr.size)) goto null_return;
}
- dnss->rrcount = (reset == RESET_AUTHORITY)
+ dnss->rrcount = reset == RESET_AUTHORITY
? ntohs(h->nscount) : ntohs(h->arcount);
}
+ TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount);
}
+else
+ TRACE debug_printf("%s: next (%d left)\n", __FUNCTION__, dnss->rrcount);
/* The variable dnss->aptr is now pointing at the next RR, and dnss->rrcount
contains the number of RR records left. */
/* If expanding the RR domain name fails, behave as if no more records
(something safe). */
+TRACE trace = "R-namelen";
namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, dnss->aptr,
- (DN_EXPAND_ARG4_TYPE) &(dnss->srr.name), DNS_MAXNAME);
-if (namelen < 0) { dnss->rrcount = 0; return NULL; }
+ (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME);
+if (namelen < 0) goto null_return;
/* Move the pointer past the name and fill in the rest of the data structure
from the following bytes. */
-dnss->aptr += namelen;
-GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
-dnss->aptr += 2; /* Don't want class */
-GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */
-GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */
-dnss->srr.data = dnss->aptr; /* The record's data follows */
-dnss->aptr += dnss->srr.size; /* Advance to next RR */
+TRACE trace = "R-name";
+if (dnss_inc(dnsa, dnss, namelen)) goto null_return;
+
+GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
+TRACE trace = "R-class";
+if (dnss_inc(dnsa, dnss, 2)) goto null_return; /* Don't want class */
+GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */
+GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */
+dnss->srr.data = dnss->aptr; /* The record's data follows */
+
+/* Unchecked increment ok here since no further access on this iteration;
+will be checked on next at "R-name". */
+
+dnss->aptr += dnss->srr.size; /* Advance to next RR */
/* Return a pointer to the dns_record structure within the dns_answer. This is
for convenience so that the scans can use nice-looking for loops. */
-return &(dnss->srr);
+return &dnss->srr;
+
+null_return:
+ TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s\n",
+ __FUNCTION__, dnss->rrcount, trace);
+ dnss->rrcount = 0;
+ return NULL;
}
static void
dns_set_insecure(dns_answer * dnsa)
{
+#ifndef DISABLE_DNSSEC
HEADER * h = (HEADER *)dnsa->answer;
h->ad = 0;
+#endif
}
/************************************************
BOOL
dns_is_aa(const dns_answer *dnsa)
{
+#ifdef DISABLE_DNSSEC
+return FALSE;
+#else
return ((HEADER*)dnsa->answer)->aa;
+#endif
}
res_state resp = os_get_dns_resolver_res();
tree_node *node = store_get_perm(sizeof(tree_node) + 290);
sprintf(CS node->name, "%.255s-%s-%lx", name, dns_text_type(type),
- resp->options);
+ (unsigned long) resp->options);
node->data.val = rc;
(void)tree_insertnode(&tree_dns_fails, node);
return rc;
caching for successful lookups. */
sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type),
- resp->options);
-previous = tree_search(tree_dns_fails, node_name);
-if (previous != NULL)
+ (unsigned long) resp->options);
+if ((previous = tree_search(tree_dns_fails, node_name)))
{
DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n",
name, dns_text_type(type),
return previous->data.val;
}
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
/* Convert all names to a-label form before doing lookup */
{
uschar * alabel;
if (dnsa->answerlen < 0) switch (h_errno)
{
case HOST_NOT_FOUND:
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n"
- "returning DNS_NOMATCH\n", name, dns_text_type(type));
- return dns_return(name, type, DNS_NOMATCH);
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n"
+ "returning DNS_NOMATCH\n", name, dns_text_type(type));
+ return dns_return(name, type, DNS_NOMATCH);
case TRY_AGAIN:
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
- name, dns_text_type(type));
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
+ name, dns_text_type(type));
- /* Cut this out for various test programs */
+ /* Cut this out for various test programs */
#ifndef STAND_ALONE
- save_domain = deliver_domain;
- deliver_domain = string_copy(name); /* set $domain */
- rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL,
- MCL_DOMAIN, TRUE, NULL);
- deliver_domain = save_domain;
- if (rc != OK)
- {
- DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");
- return dns_return(name, type, DNS_AGAIN);
- }
- DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning "
- "DNS_NOMATCH\n", name);
- return dns_return(name, type, DNS_NOMATCH);
+ save_domain = deliver_domain;
+ deliver_domain = string_copy(name); /* set $domain */
+ rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL,
+ MCL_DOMAIN, TRUE, NULL);
+ deliver_domain = save_domain;
+ if (rc != OK)
+ {
+ DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n");
+ return dns_return(name, type, DNS_AGAIN);
+ }
+ DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning "
+ "DNS_NOMATCH\n", name);
+ return dns_return(name, type, DNS_NOMATCH);
#else /* For stand-alone tests */
- return dns_return(name, type, DNS_AGAIN);
+ return dns_return(name, type, DNS_AGAIN);
#endif
case NO_RECOVERY:
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n"
- "returning DNS_FAIL\n", name, dns_text_type(type));
- return dns_return(name, type, DNS_FAIL);
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n"
+ "returning DNS_FAIL\n", name, dns_text_type(type));
+ return dns_return(name, type, DNS_FAIL);
case NO_DATA:
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n"
- "returning DNS_NODATA\n", name, dns_text_type(type));
- return dns_return(name, type, DNS_NODATA);
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n"
+ "returning DNS_NODATA\n", name, dns_text_type(type));
+ return dns_return(name, type, DNS_NODATA);
default:
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
- "returning DNS_FAIL\n", name, dns_text_type(type), h_errno);
- return dns_return(name, type, DNS_FAIL);
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
+ "returning DNS_FAIL\n", name, dns_text_type(type), h_errno);
+ return dns_return(name, type, DNS_FAIL);
}
DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n",
for (i = 0; i < 10; i++)
{
- uschar data[256];
+ uschar * data;
dns_record *rr, cname_rr, type_rr;
dns_scan dnss;
int datalen, rc;
if ( rr_name
&& Ustrcmp(rr_name, *fully_qualified_name) != 0
&& rr_name[0] != '*'
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
&& ( !string_is_utf8(*fully_qualified_name)
|| Ustrcmp(rr_name,
string_domain_utf8_to_alabel(*fully_qualified_name, NULL)) != 0
/* If any data records of the correct type were found, we are done. */
- if (type_rr.data != NULL)
+ if (type_rr.data)
{
if (!secure_so_far) /* mark insecure if any element of CNAME chain was */
dns_set_insecure(dnsa);
have had a failure from dns_lookup). However code against the possibility of
its not existing. */
- if (cname_rr.data == NULL) return DNS_FAIL;
+ if (!cname_rr.data)
+ return DNS_FAIL;
+
+ data = store_get(256);
datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data));
- if (datalen < 0) return DNS_FAIL;
+ cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256);
+ if (datalen < 0)
+ return DNS_FAIL;
name = data;
if (!dns_is_secure(dnsa))
* Get address(es) from DNS record *
*************************************************/
-/* The record type is either T_A for an IPv4 address or T_AAAA (or T_A6 when
-supported) for an IPv6 address.
+/* The record type is either T_A for an IPv4 address or T_AAAA for an IPv6 address.
Argument:
dnsa the DNS answer block
{
if (rr->data + 16 <= dnsa_lim)
{
+ struct in6_addr in6;
+ int i;
+ for (i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i];
yield = store_get(sizeof(dns_address) + 50);
- inet_ntop(AF_INET6, US rr->data, CS yield->address, 50);
+ inet_ntop(AF_INET6, &in6, CS yield->address, 50);
yield->next = NULL;
}
}