TLS: track changing fd of file-watcher when creds are releaded.
[exim.git] / src / src / dns.c
index 28bc5958d62ea162a7871941007a9b1074553fb3..490eb883d3d53eaf85f33063fd2e7bf148e37c43 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for interfacing with the DNS. */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for interfacing with the DNS. */
@@ -60,7 +61,8 @@ if (stat(CS utilname, &statbuf) >= 0)
   int infd, outfd, rc;
   uschar *argv[5];
 
   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;
 
   argv[0] = utilname;
   argv[1] = config_main_directory;
@@ -68,7 +70,7 @@ if (stat(CS utilname, &statbuf) >= 0)
   argv[3] = dns_text_type(type);
   argv[4] = NULL;
 
   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));
   if (pid < 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s",
       strerror(errno));
@@ -221,16 +223,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 */
 
@@ -240,14 +241,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
@@ -257,6 +257,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
@@ -265,8 +267,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
@@ -290,6 +292,7 @@ else
 
   }
 #endif
 
   }
 #endif
+return string_from_gstring(g);
 }
 
 
 }
 
 
@@ -331,7 +334,6 @@ char * trace = NULL;
 #ifdef rr_trace
 # define TRACE DEBUG(D_dns)
 #else
 #ifdef rr_trace
 # define TRACE DEBUG(D_dns)
 #else
-trace = trace;
 # define TRACE if (FALSE)
 #endif
 
 # define TRACE if (FALSE)
 #endif
 
@@ -499,6 +501,8 @@ const uschar * auth_name;
 const uschar * trusted;
 
 if (dnsa->answerlen < 0) return FALSE;
 const uschar * trusted;
 
 if (dnsa->answerlen < 0) return FALSE;
+/* Beware that newer versions of glibc on Linux will filter out the ad bit
+unless their shiny new RES_TRUSTAD bit is set for the resolver.  */
 if (h->ad) return TRUE;
 
 /* If the resolver we ask is authoritative for the domain in question, it may
 if (h->ad) return TRUE;
 
 /* If the resolver we ask is authoritative for the domain in question, it may
@@ -511,7 +515,7 @@ if (  !h->aa
    || !(trusted = expand_string(dns_trust_aa))
    || !*trusted
    || !(auth_name = dns_extract_auth_name(dnsa))
    || !(trusted = expand_string(dns_trust_aa))
    || !*trusted
    || !(auth_name = dns_extract_auth_name(dnsa))
-   || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL,
+   || OK != match_isinlist(auth_name, &trusted, 0, &domainlist_anchor, NULL,
                            MCL_DOMAIN, TRUE, NULL)
    )
   return FALSE;
                            MCL_DOMAIN, TRUE, NULL)
    )
   return FALSE;
@@ -667,13 +671,10 @@ e = previous->data.ptr;
 val = e->data.val;
 rc = e->expiry && e->expiry <= time(NULL) ? -1 : val;
 
 val = e->data.val;
 rc = e->expiry && e->expiry <= time(NULL) ? -1 : val;
 
-DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: %scached value %s%s\n",
+DEBUG(D_dns) debug_printf("DNS lookup of %.255s (%s): %scached value %s%s\n",
   name, dns_text_type(type),
   rc == -1 ? "" : "using ",
   name, dns_text_type(type),
   rc == -1 ? "" : "using ",
-    val == DNS_NOMATCH ? "DNS_NOMATCH" :
-    val == DNS_NODATA ? "DNS_NODATA" :
-    val == DNS_AGAIN ? "DNS_AGAIN" :
-    val == DNS_FAIL ? "DNS_FAIL" : "??",
+  dns_rc_names[val],
   rc == -1 ? " past valid time" : "");
 
 return rc;
   rc == -1 ? " past valid time" : "");
 
 return rc;
@@ -690,10 +691,12 @@ 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
 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. */
+the packet length if the packet header looks plausible.
 
 
-static void
-fake_dnsa_len_for_fail(dns_answer * dnsa)
+Return TRUE iff it seemed ok */
+
+static BOOL
+fake_dnsa_len_for_fail(dns_answer * dnsa, int type)
 {
 const HEADER * h = (const HEADER *)dnsa->answer;
 
 {
 const HEADER * h = (const HEADER *)dnsa->answer;
 
@@ -706,10 +709,14 @@ if (  h->qr == 1                          /* a response */
    && ntohs(h->ancount) == 0                   /* no answer records */
    && ntohs(h->nscount) >= 1)                  /* authority records */
   {
    && ntohs(h->ancount) == 0                   /* no answer records */
    && ntohs(h->nscount) >= 1)                  /* authority records */
   {
-  DEBUG(D_dns) debug_printf("faking res_search() response length as %d\n",
-    (int)sizeof(dnsa->answer));
+  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);
   dnsa->answerlen = sizeof(dnsa->answer);
+  return TRUE;
   }
   }
+DEBUG(D_dns) debug_printf("DNS: couldn't fake dnsa len\n");
+/* Maybe we should just do a second lookup for an SOA? */
+return FALSE;
 }
 
 
 }
 
 
@@ -719,41 +726,41 @@ bother doing a separate lookup; if not found return a forever TTL.
 */
 
 time_t
 */
 
 time_t
-dns_expire_from_soa(dns_answer * dnsa)
+dns_expire_from_soa(dns_answer * dnsa, int type)
 {
 dns_scan dnss;
 
 {
 dns_scan dnss;
 
-fake_dnsa_len_for_fail(dnsa);
+if (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)
+      ) if (rr->type == T_SOA)
+    {
+    const uschar * p = rr->data;
+    uschar discard_buf[256];
+    int len;
+    unsigned long ttl;
+
+    /* Skip the mname & rname strings */
+
+    if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+       p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
+      break;
+    p += len;
+    if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+       p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
+      break;
+    p += len;
+
+    /* Skip the SOA serial, refresh, retry & expire.  Grab the TTL */
+
+    if (p > dnsa->answer + dnsa->answerlen - 5 * INT32SZ)
+      break;
+    p += 4 * INT32SZ;
+    GETLONG(ttl, p);
+
+    return time(NULL) + ttl;
+    }
 
 
-for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
-     rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
-    ) if (rr->type == T_SOA)
-  {
-  const uschar * p = rr->data;
-  uschar discard_buf[256];
-  int len;
-  unsigned long ttl;
-
-  /* Skip the mname & rname strings */
-
-  if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
-      p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
-    break;
-  p += len;
-  if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
-      p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0)
-    break;
-  p += len;
-
-  /* Skip the SOA serial, refresh, retry & expire.  Grab the TTL */
-
-  if (p > dnsa->answer + dnsa->answerlen - 5 * INT32SZ)
-    break;
-  p += 4 * INT32SZ;
-  GETLONG(ttl, p);
-
-  return time(NULL) + ttl;
-  }
 DEBUG(D_dns) debug_printf("DNS: no SOA record found for neg-TTL\n");
 return 0;
 }
 DEBUG(D_dns) debug_printf("DNS: no SOA record found for neg-TTL\n");
 return 0;
 }
@@ -893,7 +900,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",
@@ -903,8 +910,8 @@ if (dnsa->answerlen < 0) switch (h_errno)
 #ifndef STAND_ALONE
     save_domain = deliver_domain;
     deliver_domain = string_copy(name);  /* set $domain */
 #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);
+    rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0,
+      &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL);
     deliver_domain = save_domain;
     if (rc != OK)
       {
     deliver_domain = save_domain;
     if (rc != OK)
       {
@@ -913,7 +920,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);
@@ -927,7 +934,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"
@@ -1198,18 +1205,7 @@ switch (type)
     If the TLD and the 2LD exist but the explicit CSA record lookup failed, then
     the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */
 
     If the TLD and the 2LD exist but the explicit CSA record lookup failed, then
     the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */
 
-    if (rc == DNS_NOMATCH)
-      {
-      fake_dnsa_len_for_fail(dnsa);
-
-      for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
-          rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
-         )
-       if (rr->type != T_SOA) continue;
-       else if (strcmpic(rr->name, US"") == 0 ||
-                strcmpic(rr->name, tld) == 0) return DNS_NOMATCH;
-       else break;
-      }
+    if (rc == DNS_NOMATCH) return DNS_NOMATCH;
 
     for (i = 0; i < limit; i++)
       {
 
     for (i = 0; i < limit; i++)
       {
@@ -1244,7 +1240,7 @@ switch (type)
 
        /* Extract the numerical SRV fields (p is incremented) */
        GETSHORT(priority, p);
 
        /* Extract the numerical SRV fields (p is incremented) */
        GETSHORT(priority, p);
-       GETSHORT(weight, p);    weight = weight; /* compiler quietening */
+       GETSHORT(weight, p);
        GETSHORT(port, p);
 
        /* Check the CSA version number */
        GETSHORT(port, p);
 
        /* Check the CSA version number */