* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for interfacing with the DNS. */
Return: TRUE for a bad result
*/
static BOOL
-dnss_inc(dns_answer * dnsa, dns_scan * dnss, unsigned delta)
+dnss_inc_aptr(const dns_answer * dnsa, dns_scan * dnss, unsigned delta)
{
return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen;
}
Arguments:
dnsa pointer to dns answer block
dnss pointer to dns scan block
- reset option specifing what portion to scan, as described above
+ reset option specifying what portion to scan, as described above
Returns: next dns record, or NULL when no more
*/
dns_record *
-dns_next_rr(dns_answer *dnsa, dns_scan *dnss, int reset)
+dns_next_rr(const dns_answer *dnsa, dns_scan *dnss, int reset)
{
-HEADER *h = (HEADER *)dnsa->answer;
+const HEADER * h = (const HEADER *)dnsa->answer;
int namelen;
char * trace = NULL;
if (reset != RESET_NEXT)
{
- TRACE debug_printf("%s: reset\n", __FUNCTION__);
dnss->rrcount = ntohs(h->qdcount);
+ TRACE debug_printf("%s: reset (Q rrcount %d)\n", __FUNCTION__, dnss->rrcount);
dnss->aptr = dnsa->answer + sizeof(HEADER);
/* Skip over questions; failure to expand the name just gives up */
if (namelen < 0) goto null_return;
/* skip name & type & class */
TRACE trace = "Q-skip";
- if (dnss_inc(dnsa, dnss, namelen+4)) goto null_return;
+ if (dnss_inc_aptr(dnsa, dnss, namelen+4)) goto null_return;
}
/* Get the number of answer records. */
dnss->rrcount = ntohs(h->ancount);
+ TRACE debug_printf("%s: reset (A rrcount %d)\n", __FUNCTION__, dnss->rrcount);
/* Skip over answers if we want to look at the authority section. Also skip
the NS records (i.e. authority section) if wanting to look at the additional
{
TRACE debug_printf("%s: additional\n", __FUNCTION__);
dnss->rrcount += ntohs(h->nscount);
+ TRACE debug_printf("%s: reset (NS rrcount %d)\n", __FUNCTION__, dnss->rrcount);
}
if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL)
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;
+ if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return;
GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */
/* skip over it */
TRACE trace = "A-skip";
- if (dnss_inc(dnsa, dnss, dnss->srr.size)) goto null_return;
+ if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) goto null_return;
}
dnss->rrcount = reset == RESET_AUTHORITY
? ntohs(h->nscount) : ntohs(h->arcount);
+ TRACE debug_printf("%s: reset (%s rrcount %d)\n", __FUNCTION__,
+ reset == RESET_AUTHORITY ? "NS" : "AR", dnss->rrcount);
}
TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount);
}
from the following bytes. */
TRACE trace = "R-name";
-if (dnss_inc(dnsa, dnss, namelen)) goto null_return;
+if (dnss_inc_aptr(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 */
+if (dnss_inc_aptr(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 */
return &dnss->srr;
null_return:
- TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s\n",
- __FUNCTION__, dnss->rrcount, trace);
+ TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s; errno %d %s\n",
+ __FUNCTION__, dnss->rrcount, trace, errno, strerror(errno));
dnss->rrcount = 0;
return NULL;
}
-/* Extract the AUTHORITY information from the answer. If the
-answer isn't authoritive (AA not set), we do not extract anything.
+/* Extract the AUTHORITY information from the answer. If the answer isn't
+authoritative (AA not set), we do not extract anything.
-The AUTHORITIVE section contains NS records if
-the name in question was found, it contains a SOA record
-otherwise. (This is just from experience and some tests, is there
-some spec?)
+The AUTHORITY section contains NS records if the name in question was found,
+it contains a SOA record otherwise. (This is just from experience and some
+tests, is there some spec?)
-We've cycle through the AUTHORITY section, since it may contain
-other records (e.g. NSEC3) too. */
+Scan the whole AUTHORITY section, since it may contain other records
+(e.g. NSEC3) too.
+
+Return: name for the authority, in an allocated string, or NULL if none found */
static const uschar *
dns_extract_auth_name(const dns_answer * dnsa) /* FIXME: const dns_answer */
{
dns_scan dnss;
dns_record * rr;
-HEADER * h = (HEADER *) dnsa->answer;
+const HEADER * h = (const HEADER *) dnsa->answer;
-if (!h->nscount || !h->aa) return NULL;
-for (rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_AUTHORITY);
- rr;
- rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_NEXT))
- if (rr->type == (h->ancount ? T_NS : T_SOA)) return rr->name;
+if (h->nscount && h->aa)
+ for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
+ rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+ if (rr->type == (h->ancount ? T_NS : T_SOA))
+ return string_copy(rr->name);
return NULL;
}
/* We do not perform DNSSEC work ourselves; if the administrator has installed
a verifying resolver which sets AD as appropriate, though, we'll use that.
-(AD = Authentic Data, AA = Authoritive Answer)
+(AD = Authentic Data, AA = Authoritative Answer)
Argument: pointer to dns answer block
Returns: bool indicating presence of AD bit
debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n");
return FALSE;
#else
-HEADER * h = (HEADER *) dnsa->answer;
+const HEADER * h = (const HEADER *) dnsa->answer;
const uschar * auth_name;
const uschar * trusted;
if (h->ad) return TRUE;
-/* If the resolver we ask is authoritive for the domain in question, it
+/* If the resolver we ask is authoritative for the domain in question, it
* may not set the AD but the AA bit. If we explicitly trust
* the resolver for that domain (via a domainlist in dns_trust_aa),
* we return TRUE to indicate a secure answer.
{
#ifndef DISABLE_DNSSEC
HEADER * h = (HEADER *)dnsa->answer;
-h->ad = 0;
+h->aa = h->ad = 0;
#endif
}
/************************************************
* Check whether the AA bit is set *
* We need this to warn if we requested AD *
- * from an authoritive server *
+ * from an authoritative server *
************************************************/
BOOL
#ifdef DISABLE_DNSSEC
return FALSE;
#else
-return ((HEADER*)dnsa->answer)->aa;
+return ((const HEADER*)dnsa->answer)->aa;
#endif
}
/* Call the resolver to look up the given domain name, using the given type,
and check the result. The error code TRY_AGAIN is documented as meaning "non-
-Authoritive Host not found, or SERVERFAIL". Sometimes there are badly set
+Authoritative Host not found, or SERVERFAIL". Sometimes there are badly set
up nameservers that produce this error continually, so there is the option of
providing a list of domains for which this is treated as a non-existent
host.
}
#endif
-/* If configured, check the hygene of the name passed to lookup. Otherwise,
+/* If configured, check the hygiene of the name passed to lookup. Otherwise,
although DNS lookups may give REFUSED at the lower level, some resolvers
turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such
domains cannot be in the DNS. The check is now done by a regular expression;
}
if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
- 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0)
+ 0, PCRE_EOPT, ovector, nelem(ovector)) < 0)
{
DEBUG(D_dns)
debug_printf("DNS name syntax check failed: %s (%s)\n", name,
domains, and interfaces to a fake nameserver for certain special zones. */
dnsa->answerlen = running_in_test_harness
- ? fakens_search(name, type, dnsa->answer, MAXPACKET)
- : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET);
+ ? fakens_search(name, type, dnsa->answer, sizeof(dnsa->answer))
+ : res_search(CCS name, C_IN, type, dnsa->answer, sizeof(dnsa->answer));
-if (dnsa->answerlen > MAXPACKET)
+if (dnsa->answerlen > (int) sizeof(dnsa->answer))
{
- DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n",
- name, dns_text_type(type), dnsa->answerlen, MAXPACKET);
- dnsa->answerlen = MAXPACKET;
+ DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet"
+ " (size %d), truncating to %u.\n",
+ name, dns_text_type(type), dnsa->answerlen, (unsigned int) sizeof(dnsa->answer));
+ dnsa->answerlen = sizeof(dnsa->answer);
}
if (dnsa->answerlen < 0) switch (h_errno)
/* DNS lookup failures get passed straight back. */
- if ((rc = dns_basic_lookup(dnsa, name, type)) != DNS_SUCCEED) return rc;
+ if ((rc = dns_basic_lookup(dnsa, name, type)) != DNS_SUCCEED)
+ return rc;
/* We should have either records of the required type, or a CNAME record,
or both. We need to know whether both exist for getting the fully qualified
cname_rr.data = type_rr.data = NULL;
for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
- rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- {
+ rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
if (rr->type == type)
{
if (type_rr.data == NULL) type_rr = *rr;
if (cname_rr.data != NULL) break;
}
- else if (rr->type == T_CNAME) cname_rr = *rr;
- }
+ else if (rr->type == T_CNAME)
+ cname_rr = *rr;
/* For the first time round this loop, if a CNAME was found, take the fully
qualified name from it; otherwise from the first data record, if present. */
- if (i == 0 && fully_qualified_name != NULL)
+ if (i == 0 && fully_qualified_name)
{
- uschar * rr_name = cname_rr.data ? cname_rr.name
- : type_rr.data ? type_rr.name : NULL;
+ uschar * rr_name = cname_rr.data
+ ? cname_rr.name : type_rr.data ? type_rr.name : NULL;
if ( rr_name
&& Ustrcmp(rr_name, *fully_qualified_name) != 0
&& rr_name[0] != '*'
return DNS_FAIL;
data = store_get(256);
- datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
- cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256);
- if (datalen < 0)
+ if ((datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+ cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256)) < 0)
return DNS_FAIL;
name = data;
success and packet length return values.) For added safety we only reset
the packet length if the packet header looks plausible. */
- HEADER *h = (HEADER *)dnsa->answer;
+ const HEADER * h = (const HEADER *)dnsa->answer;
if (h->qr == 1 && h->opcode == QUERY && h->tc == 0
&& (h->rcode == NOERROR || h->rcode == NXDOMAIN)
&& ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0
dnsa->answerlen = MAXPACKET;
for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
- rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
+ rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
)
if (rr->type != T_SOA) continue;
else if (strcmpic(rr->name, US"") == 0 ||
might make stricter assertions than its parent domain. */
for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
- rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+ rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_SRV)
{
- if (rr->type != T_SRV) continue;
+ const uschar * p = rr->data;
/* Extract the numerical SRV fields (p is incremented) */
- p = rr->data;
GETSHORT(priority, p);
GETSHORT(weight, p); weight = weight; /* compiler quietening */
GETSHORT(port, p);