Provide readn() as a wrapper around read()
[exim.git] / src / src / dns.c
index e02f494e82177ac1c0a143ed1d9d904f796dda09..4950079b4dc971114adad67c810222bbdc0d3671 100644 (file)
@@ -2,7 +2,7 @@
 *     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. */
@@ -271,10 +271,7 @@ else
     {
     int j;
     for (j = 0; j < 32; j += 4)
-      {
-      sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
-      pp += 2;
-      }
+      pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
     }
   Ustrcpy(pp, "ip6.arpa.");
 
@@ -326,7 +323,7 @@ The result is in static storage which must be copied if it is to be preserved.
 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
 */
@@ -349,8 +346,8 @@ trace = trace;
 
 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 */
@@ -369,6 +366,7 @@ if (reset != RESET_NEXT)
   /* 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
@@ -378,6 +376,7 @@ if (reset != RESET_NEXT)
     {
     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)
@@ -400,6 +399,8 @@ if (reset != RESET_NEXT)
       }
     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);
   }
@@ -443,23 +444,24 @@ for convenience so that the scans can use nice-looking for loops. */
 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 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?)
 
-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?)
+Scan the whole AUTHORITY section, since it may contain other records
+(e.g. NSEC3) too.
 
-We've cycle through the 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 */
@@ -468,11 +470,11 @@ dns_scan dnss;
 dns_record * rr;
 const HEADER * h = (const HEADER *) dnsa->answer;
 
-if (!h->nscount || !h->aa) return NULL;
-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 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;
 }
 
@@ -485,7 +487,7 @@ 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
@@ -505,7 +507,7 @@ 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.
@@ -534,14 +536,14 @@ dns_set_insecure(dns_answer * dnsa)
 {
 #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
@@ -625,7 +627,7 @@ return rc;
 
 /* 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.
@@ -693,7 +695,7 @@ if ((previous = tree_search(tree_dns_fails, node_name)))
   }
 #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;
@@ -726,7 +728,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
     }
 
   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,
@@ -755,14 +757,15 @@ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0)
 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)
@@ -885,8 +888,7 @@ for (i = 0; i < 10; i++)
 
   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;
@@ -1014,7 +1016,7 @@ switch (type)
   assertion field. */
   case T_CSA:
     {
-    uschar *srvname, *namesuff, *tld, *p;
+    uschar *srvname, *namesuff, *tld;
     int priority, weight, port;
     int limit, rc, i;
     BOOL ipv6;
@@ -1090,8 +1092,7 @@ switch (type)
            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 ||
@@ -1126,8 +1127,7 @@ switch (type)
       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)) if (rr->type == T_SRV)
+          rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_SRV)
        {
        const uschar * p = rr->data;