Local_scan: bump the ABI major version number
[exim.git] / src / src / dns.c
index b309207cfd43dd6cf90781a71eb2ccaf6ba4b425..6333d3cff7018e0512ea9cf27d81b6d5ee1677ed 100644 (file)
@@ -10,7 +10,6 @@
 #include "exim.h"
 
 
 #include "exim.h"
 
 
-
 /*************************************************
 *               Fake DNS resolver                *
 *************************************************/
 /*************************************************
 *               Fake DNS resolver                *
 *************************************************/
@@ -222,16 +221,15 @@ a name that can be used to look up PTR records.
 
 Arguments:
   string     the IP address as a string
 
 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 */
 
 
 /* Handle IPv4 address */
 
@@ -241,14 +239,13 @@ if (Ustrchr(string, ':') == NULL)
   {
   for (int i = 0; i < 4; i++)
     {
   {
   for (int i = 0; i < 4; i++)
     {
-    const uschar *ppp = p;
+    const uschar * ppp = p;
     while (ppp > string && ppp[-1] != '.') ppp--;
     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;
     }
     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
   }
 
 /* Handle IPv6 address; convert to binary so as to fill out any
@@ -258,6 +255,8 @@ abbreviation in the textual form. */
 else
   {
   int v6[4];
 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
   (void)host_aton(string, v6);
 
   /* The original specification for IPv6 reverse lookup was to invert each
@@ -266,8 +265,8 @@ else
 
   for (int i = 3; i >= 0; i--)
     for (int j = 0; j < 32; j += 4)
 
   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
 
   /* Another way of doing IPv6 reverse lookups was proposed in conjunction
   with A6 records. However, it fell out of favour when they did. The
@@ -291,6 +290,7 @@ else
 
   }
 #endif
 
   }
 #endif
+return string_from_gstring(g);
 }
 
 
 }
 
 
@@ -499,13 +499,13 @@ const HEADER * h = (const HEADER *) dnsa->answer;
 const uschar * auth_name;
 const uschar * trusted;
 
 const uschar * auth_name;
 const uschar * trusted;
 
+if (dnsa->answerlen < 0) return FALSE;
 if (h->ad) return TRUE;
 
 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
 
 if (  !h->aa
    || !dns_trust_aa
@@ -541,12 +541,12 @@ h->aa = h->ad = 0;
  ************************************************/
 
 BOOL
  ************************************************/
 
 BOOL
-dns_is_aa(const dns_answer *dnsa)
+dns_is_aa(const dns_answer * dnsa)
 {
 #ifdef DISABLE_DNSSEC
 return FALSE;
 #else
 {
 #ifdef DISABLE_DNSSEC
 return FALSE;
 #else
-return ((const HEADER*)dnsa->answer)->aa;
+return dnsa->answerlen >= 0 && ((const HEADER *)dnsa->answer)->aa;
 #endif
 }
 
 #endif
 }
 
@@ -617,8 +617,10 @@ Arguments:
 Returns:     the return code
 */
 
 Returns:     the return code
 */
 
-/*XXX the derivation of this value needs explaining */
+/* we need:  255 +1 + (max(typetext) == 5) +1 + max(chars_for_long-max) +1 */
 #define DNS_FAILTAG_MAX 290
 #define DNS_FAILTAG_MAX 290
+#define DNS_FAILNODE_SIZE \
+  (sizeof(expiring_data) + sizeof(tree_node) + DNS_FAILTAG_MAX)
 
 static int
 dns_fail_return(const uschar * name, int type, time_t expiry, int rc)
 
 static int
 dns_fail_return(const uschar * name, int type, time_t expiry, int rc)
@@ -632,11 +634,9 @@ if ((previous = tree_search(tree_dns_fails, node_name)))
   e = previous->data.ptr;
 else
   {
   e = previous->data.ptr;
 else
   {
-  new = store_get_perm(
-    sizeof(tree_node) + DNS_FAILTAG_MAX + sizeof(expiring_data), is_tainted(name));
-
+  e = store_get_perm(DNS_FAILNODE_SIZE, is_tainted(name));
+  new = (void *)(e+1);
   dns_fail_tag(new->name, name, type);
   dns_fail_tag(new->name, name, type);
-  e = (expiring_data *)((char *)new + sizeof(tree_node) + DNS_FAILTAG_MAX);
   new->data.ptr = e;
   (void)tree_insertnode(&tree_dns_fails, new);
   }
   new->data.ptr = e;
   (void)tree_insertnode(&tree_dns_fails, new);
   }
@@ -682,17 +682,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.
-*/
-
-static 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
 /* 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
@@ -704,12 +693,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. */
 
 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)
    && (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)
 
 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
      rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
@@ -733,9 +748,9 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
 
   /* Skip the SOA serial, refresh, retry & expire.  Grab the TTL */
 
 
   /* Skip the SOA serial, refresh, retry & expire.  Grab the TTL */
 
-  if (p > dnsa->answer + dnsa->answerlen - 5 * NS_INT32SZ)
+  if (p > dnsa->answer + dnsa->answerlen - 5 * INT32SZ)
     break;
     break;
-  p += 4 * NS_INT32SZ;
+  p += 4 * INT32SZ;
   GETLONG(ttl, p);
 
   return time(NULL) + ttl;
   GETLONG(ttl, p);
 
   return time(NULL) + ttl;
@@ -789,7 +804,10 @@ caching for successful lookups.
 */
 
 if ((rc = dns_fail_cache_hit(name, type)) > 0)
 */
 
 if ((rc = dns_fail_cache_hit(name, type)) > 0)
+  {
+  dnsa->answerlen = -1;
   return rc;
   return rc;
+  }
 
 #ifdef SUPPORT_I18N
 /* Convert all names to a-label form before doing lookup */
 
 #ifdef SUPPORT_I18N
 /* Convert all names to a-label form before doing lookup */
@@ -819,34 +837,17 @@ regex has substrings that are used - the default uses a conditional.
 
 This test is omitted for PTR records. These occur only in calls from the dnsdb
 lookup, which constructs the names itself, so they should be OK. Besides,
 
 This test is omitted for PTR records. These occur only in calls from the dnsdb
 lookup, which constructs the names itself, so they should be OK. Besides,
-bitstring labels don't conform to normal name syntax. (But the aren't used any
-more.)
-
-For SRV records, we omit the initial _smtp._tcp. components at the start.
-The check has been seen to bite on the destination of a SRV lookup that
-initiall hit a CNAME, for which the next name had only two components.
-RFC2782 makes no mention of the possibiility of CNAMES, but the Wikipedia
-article on SRV says they are not a valid configuration. */
+bitstring labels don't conform to normal name syntax. (But they aren't used any
+more.) */
 
 #ifndef STAND_ALONE   /* Omit this for stand-alone tests */
 
 if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
   {
 
 #ifndef STAND_ALONE   /* Omit this for stand-alone tests */
 
 if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
   {
-  const uschar *checkname = name;
   int ovector[3*(EXPAND_MAXN+1)];
 
   dns_pattern_init();
   int ovector[3*(EXPAND_MAXN+1)];
 
   dns_pattern_init();
-
-  /* For an SRV lookup, skip over the first two components (the service and
-  protocol names, which both start with an underscore). */
-
-  if (type == T_SRV || type == T_TLSA)
-    {
-    while (*checkname && *checkname++ != '.') ;
-    while (*checkname && *checkname++ != '.') ;
-    }
-
-  if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
+  if (pcre_exec(regex_check_dns_names, NULL, CCS name, Ustrlen(name),
       0, PCRE_EOPT, ovector, nelem(ovector)) < 0)
     {
     DEBUG(D_dns)
       0, PCRE_EOPT, ovector, nelem(ovector)) < 0)
     {
     DEBUG(D_dns)
@@ -875,6 +876,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. */
 
 (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));
 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));
@@ -892,7 +894,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));
   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",
 
   case TRY_AGAIN:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n",
@@ -912,7 +914,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);
       }
     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);
 
 #else   /* For stand-alone tests */
     return dns_fail_return(name, type, 0, DNS_AGAIN);
@@ -926,7 +928,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));
   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"
 
   default:
     DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n"
@@ -1199,23 +1201,7 @@ switch (type)
 
     if (rc == DNS_NOMATCH)
       {
 
     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)
 
       for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
           rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)