Events: Fix msg:defer event for the hosts_max_try_hardlimit case. Bug 2554
[exim.git] / src / src / dns.c
index 4750f1b5210ef536dc8e9711b0c8960cfae5a3ec..d036162cb873b44a10f7a9927174411fd90a88da 100644 (file)
@@ -60,7 +60,8 @@ if (stat(CS utilname, &statbuf) >= 0)
   int infd, outfd, rc;
   uschar *argv[5];
 
-  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type));
+  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n",
+               name, dns_text_type(type));
 
   argv[0] = utilname;
   argv[1] = config_main_directory;
@@ -68,7 +69,7 @@ if (stat(CS utilname, &statbuf) >= 0)
   argv[3] = dns_text_type(type);
   argv[4] = NULL;
 
-  pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE);
+  pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE, US"fakens-search");
   if (pid < 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s",
       strerror(errno));
@@ -221,16 +222,15 @@ a name that can be used to look up PTR records.
 
 Arguments:
   string     the IP address as a string
-  buffer     a suitable buffer, long enough to hold the result
 
-Returns:     nothing
+Returns:     an allocated string
 */
 
-void
-dns_build_reverse(const uschar *string, uschar *buffer)
+uschar *
+dns_build_reverse(const uschar * string)
 {
-const uschar *p = string + Ustrlen(string);
-uschar *pp = buffer;
+const uschar * p = string + Ustrlen(string);
+gstring * g = NULL;
 
 /* Handle IPv4 address */
 
@@ -240,14 +240,13 @@ if (Ustrchr(string, ':') == NULL)
   {
   for (int i = 0; i < 4; i++)
     {
-    const uschar *ppp = p;
+    const uschar * ppp = p;
     while (ppp > string && ppp[-1] != '.') ppp--;
-    Ustrncpy(pp, ppp, p - ppp);
-    pp += p - ppp;
-    *pp++ = '.';
+    g = string_catn(g, ppp, p - ppp);
+    g = string_catn(g, US".", 1);
     p = ppp - 1;
     }
-  Ustrcpy(pp, US"in-addr.arpa");
+  g = string_catn(g, US"in-addr.arpa", 12);
   }
 
 /* Handle IPv6 address; convert to binary so as to fill out any
@@ -257,6 +256,8 @@ abbreviation in the textual form. */
 else
   {
   int v6[4];
+
+  g = string_get_tainted(32, is_tainted(string));
   (void)host_aton(string, v6);
 
   /* The original specification for IPv6 reverse lookup was to invert each
@@ -265,8 +266,8 @@ else
 
   for (int i = 3; i >= 0; i--)
     for (int j = 0; j < 32; j += 4)
-      pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
-  Ustrcpy(pp, US"ip6.arpa.");
+      g = string_fmt_append(g, "%x.", (v6[i] >> j) & 15);
+  g = string_catn(g, US"ip6.arpa.", 9);
 
   /* Another way of doing IPv6 reverse lookups was proposed in conjunction
   with A6 records. However, it fell out of favour when they did. The
@@ -290,6 +291,7 @@ else
 
   }
 #endif
+return string_from_gstring(g);
 }
 
 
@@ -498,13 +500,13 @@ const HEADER * h = (const HEADER *) dnsa->answer;
 const uschar * auth_name;
 const uschar * trusted;
 
+if (dnsa->answerlen < 0) return FALSE;
 if (h->ad) return TRUE;
 
-/* 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.
-*/
+/* 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.  */
 
 if (  !h->aa
    || !dns_trust_aa
@@ -540,12 +542,12 @@ h->aa = h->ad = 0;
  ************************************************/
 
 BOOL
-dns_is_aa(const dns_answer *dnsa)
+dns_is_aa(const dns_answer * dnsa)
 {
 #ifdef DISABLE_DNSSEC
 return FALSE;
 #else
-return ((const HEADER*)dnsa->answer)->aa;
+return dnsa->answerlen >= 0 && ((const HEADER *)dnsa->answer)->aa;
 #endif
 }
 
@@ -681,17 +683,6 @@ return rc;
 
 
 
-/* Return the TTL suitable for an NXDOMAIN result, which is given
-in the SOA.  We hope that one was returned in the lookup, and do not
-bother doing a separate lookup; if not found return a forever TTL.
-*/
-
-time_t
-dns_expire_from_soa(dns_answer * dnsa)
-{
-const HEADER * h = (const HEADER *)dnsa->answer;
-dns_scan dnss;
-
 /* This is really gross. The successful return value from res_search() is
 the packet length, which is stored in dnsa->answerlen. If we get a
 negative DNS reply then res_search() returns -1, which causes the bounds
@@ -703,12 +694,38 @@ re-implement res_search() and res_query() so that they don't muddle their
 success and packet length return values.) For added safety we only reset
 the packet length if the packet header looks plausible. */
 
-if (  h->qr == 1 && h->opcode == QUERY && h->tc == 0
+static void
+fake_dnsa_len_for_fail(dns_answer * dnsa, int type)
+{
+const HEADER * h = (const HEADER *)dnsa->answer;
+
+if (  h->qr == 1                               /* a response */
+   && h->opcode == QUERY
+   && h->tc == 0                               /* nmessage not truncated */
    && (h->rcode == NOERROR || h->rcode == NXDOMAIN)
-   && (ntohs(h->qdcount) == 1 || f.running_in_test_harness)
-   && ntohs(h->ancount) == 0
-   && ntohs(h->nscount) >= 1)
-      dnsa->answerlen = sizeof(dnsa->answer);
+   && (  ntohs(h->qdcount) == 1                        /* one question record */
+      || f.running_in_test_harness)
+   && ntohs(h->ancount) == 0                   /* no answer records */
+   && ntohs(h->nscount) >= 1)                  /* authority records */
+  {
+  DEBUG(D_dns) debug_printf("faking res_search(%s) response length as %d\n",
+    dns_text_type(type), (int)sizeof(dnsa->answer));
+  dnsa->answerlen = sizeof(dnsa->answer);
+  }
+}
+
+
+/* Return the TTL suitable for an NXDOMAIN result, which is given
+in the SOA.  We hope that one was returned in the lookup, and do not
+bother doing a separate lookup; if not found return a forever TTL.
+*/
+
+time_t
+dns_expire_from_soa(dns_answer * dnsa, int type)
+{
+dns_scan dnss;
+
+fake_dnsa_len_for_fail(dnsa, type);
 
 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
      rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
@@ -788,7 +805,10 @@ caching for successful lookups.
 */
 
 if ((rc = dns_fail_cache_hit(name, type)) > 0)
+  {
+  dnsa->answerlen = -1;
   return rc;
+  }
 
 #ifdef SUPPORT_I18N
 /* Convert all names to a-label form before doing lookup */
@@ -857,6 +877,7 @@ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0)
 (res_search), we call fakens_search(), which recognizes certain special
 domains, and interfaces to a fake nameserver for certain special zones. */
 
+h_errno = 0;
 dnsa->answerlen = f.running_in_test_harness
   ? fakens_search(name, type, dnsa->answer, sizeof(dnsa->answer))
   : res_search(CCS name, C_IN, type, dnsa->answer, sizeof(dnsa->answer));
@@ -874,7 +895,7 @@ 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_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH);
 
   case TRY_AGAIN:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
@@ -894,7 +915,7 @@ if (dnsa->answerlen < 0) switch (h_errno)
       }
     DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning "
       "DNS_NOMATCH\n", name);
-    return dns_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NOMATCH);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH);
 
 #else   /* For stand-alone tests */
     return dns_fail_return(name, type, 0, DNS_AGAIN);
@@ -908,7 +929,7 @@ if (dnsa->answerlen < 0) switch (h_errno)
   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_fail_return(name, type, dns_expire_from_soa(dnsa), DNS_NODATA);
+    return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NODATA);
 
   default:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
@@ -1181,23 +1202,7 @@ switch (type)
 
     if (rc == DNS_NOMATCH)
       {
-      /* This is really gross. The successful return value from res_search() is
-      the packet length, which is stored in dnsa->answerlen. If we get a
-      negative DNS reply then res_search() returns -1, which causes the bounds
-      checks for name decompression to fail when it is treated as a packet
-      length, which in turn causes the authority search to fail. The correct
-      packet length has been lost inside libresolv, so we have to guess a
-      replacement value. (The only way to fix this properly would be to
-      re-implement res_search() and res_query() so that they don't muddle their
-      success and packet length return values.) For added safety we only reset
-      the packet length if the packet header looks plausible. */
-
-      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
-         && ntohs(h->nscount) >= 1)
-           dnsa->answerlen = sizeof(dnsa->answer);
+      fake_dnsa_len_for_fail(dnsa, T_CSA);
 
       for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
           rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)