Compiler quietening
[exim.git] / src / src / dns.c
index deafedc7b0ccf36d108c62ebc4508463b0442304..f492613c4785f0e8141718cd94198ef7469316cb 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/dns.c,v 1.18 2009/06/10 07:34:04 tom Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for interfacing with the DNS. */
@@ -48,7 +46,7 @@ Returns:      length of returned data, or -1 on error (h_errno set)
 */
 
 static int
-fakens_search(uschar *domain, int type, uschar *answerptr, int size)
+fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
 {
 int len = Ustrlen(domain);
 int asize = size;                  /* Locally modified */
@@ -161,25 +159,85 @@ the first time we have been here, and set the resolver options.
 Arguments:
   qualify_single    TRUE to set the RES_DEFNAMES option
   search_parents    TRUE to set the RES_DNSRCH option
+  use_dnssec        TRUE to set the RES_USE_DNSSEC option
 
 Returns:            nothing
 */
 
 void
-dns_init(BOOL qualify_single, BOOL search_parents)
+dns_init(BOOL qualify_single, BOOL search_parents, BOOL use_dnssec)
 {
-if ((_res.options & RES_INIT) == 0)
+res_state resp = os_get_dns_resolver_res();
+
+if ((resp->options & RES_INIT) == 0)
   {
-  DEBUG(D_resolver) _res.options |= RES_DEBUG;     /* For Cygwin */
+  DEBUG(D_resolver) resp->options |= RES_DEBUG;     /* For Cygwin */
+  os_put_dns_resolver_res(resp);
   res_init();
-  DEBUG(D_resolver) _res.options |= RES_DEBUG;
+  DEBUG(D_resolver) resp->options |= RES_DEBUG;
+  os_put_dns_resolver_res(resp);
   }
 
-_res.options &= ~(RES_DNSRCH | RES_DEFNAMES);
-_res.options |= (qualify_single? RES_DEFNAMES : 0) |
+resp->options &= ~(RES_DNSRCH | RES_DEFNAMES);
+resp->options |= (qualify_single? RES_DEFNAMES : 0) |
                 (search_parents? RES_DNSRCH : 0);
-if (dns_retrans > 0) _res.retrans = dns_retrans;
-if (dns_retry > 0) _res.retry = dns_retry;
+if (dns_retrans > 0) resp->retrans = dns_retrans;
+if (dns_retry > 0) resp->retry = dns_retry;
+
+#ifdef RES_USE_EDNS0
+if (dns_use_edns0 >= 0)
+  {
+  if (dns_use_edns0)
+    resp->options |= RES_USE_EDNS0;
+  else
+    resp->options &= ~RES_USE_EDNS0;
+  DEBUG(D_resolver)
+    debug_printf("Coerced resolver EDNS0 support %s.\n",
+        dns_use_edns0 ? "on" : "off");
+  }
+#else
+if (dns_use_edns0 >= 0)
+  DEBUG(D_resolver)
+    debug_printf("Unable to %sset EDNS0 without resolver support.\n",
+        dns_use_edns0 ? "" : "un");
+#endif
+
+#ifndef DISABLE_DNSSEC
+# ifdef RES_USE_DNSSEC
+#  ifndef RES_USE_EDNS0
+#   error Have RES_USE_DNSSEC but not RES_USE_EDNS0?  Something hinky ...
+#  endif
+if (use_dnssec)
+  resp->options |= RES_USE_DNSSEC;
+if (dns_dnssec_ok >= 0)
+  {
+  if (dns_use_edns0 == 0 && dns_dnssec_ok != 0)
+    {
+    DEBUG(D_resolver)
+      debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n");
+    }
+  else
+    {
+    if (dns_dnssec_ok)
+      resp->options |= RES_USE_DNSSEC;
+    else
+      resp->options &= ~RES_USE_DNSSEC;
+    DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n",
+        dns_dnssec_ok ? "on" : "off");
+    }
+  }
+# else
+if (dns_dnssec_ok >= 0)
+  DEBUG(D_resolver)
+    debug_printf("Unable to %sset DNSSEC without resolver support.\n",
+        dns_dnssec_ok ? "" : "un");
+if (use_dnssec)
+  DEBUG(D_resolver)
+    debug_printf("Unable to set DNSSEC without resolver support.\n");
+# endif
+#endif /* DISABLE_DNSSEC */
+
+os_put_dns_resolver_res(resp);
 }
 
 
@@ -372,6 +430,41 @@ return &(dnss->srr);
 
 
 
+/*************************************************
+*    Return whether AD bit set in DNS result     *
+*************************************************/
+
+/* 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)
+
+Argument:   pointer to dns answer block
+Returns:    bool indicating presence of AD bit
+*/
+
+BOOL
+dns_is_secure(const dns_answer * dnsa)
+{
+#ifdef DISABLE_DNSSEC
+DEBUG(D_dns)
+  debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n");
+return FALSE;
+#else
+HEADER *h = (HEADER *)dnsa->answer;
+return h->ad ? TRUE : FALSE;
+#endif
+}
+
+static void
+dns_set_insecure(dns_answer * dnsa)
+{
+HEADER * h = (HEADER *)dnsa->answer;
+h->ad = 0;
+}
+
+
+
+
 /*************************************************
 *            Turn DNS type into text             *
 *************************************************/
@@ -393,11 +486,13 @@ switch(t)
   case T_AAAA:  return US"AAAA";
   case T_A6:    return US"A6";
   case T_TXT:   return US"TXT";
+  case T_SPF:   return US"SPF";
   case T_PTR:   return US"PTR";
   case T_SOA:   return US"SOA";
   case T_SRV:   return US"SRV";
   case T_NS:    return US"NS";
   case T_CNAME: return US"CNAME";
+  case T_TLSA:  return US"TLSA";
   default:      return US"?";
   }
 }
@@ -422,11 +517,12 @@ Returns:     the return code
 */
 
 static int
-dns_return(uschar *name, int type, int rc)
+dns_return(const uschar * name, int type, int rc)
 {
+res_state resp = os_get_dns_resolver_res();
 tree_node *node = store_get_perm(sizeof(tree_node) + 290);
 sprintf(CS node->name, "%.255s-%s-%lx", name, dns_text_type(type),
-  _res.options);
+  resp->options);
 node->data.val = rc;
 (void)tree_insertnode(&tree_dns_fails, node);
 return rc;
@@ -460,12 +556,13 @@ Returns:    DNS_SUCCEED   successful lookup
 */
 
 int
-dns_basic_lookup(dns_answer *dnsa, uschar *name, int type)
+dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type)
 {
-int rc = -1;
 #ifndef STAND_ALONE
+int rc = -1;
 uschar *save;
 #endif
+res_state resp = os_get_dns_resolver_res();
 
 tree_node *previous;
 uschar node_name[290];
@@ -476,7 +573,7 @@ have many addresses in the same domain. We rely on the resolver and name server
 caching for successful lookups. */
 
 sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type),
-  _res.options);
+  resp->options);
 previous = tree_search(tree_dns_fails, node_name);
 if (previous != NULL)
   {
@@ -505,9 +602,9 @@ For SRV records, we omit the initial _smtp._tcp. components at the start. */
 
 #ifndef STAND_ALONE   /* Omit this for stand-alone tests */
 
-if (check_dns_names_pattern[0] != 0 && type != T_PTR)
+if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
   {
-  uschar *checkname = name;
+  const uschar *checkname = name;
   int ovector[3*(EXPAND_MAXN+1)];
 
   if (regex_check_dns_names == NULL)
@@ -517,13 +614,13 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR)
   /* 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)
+  if (type == T_SRV || type == T_TLSA)
     {
     while (*checkname++ != '.');
     while (*checkname++ != '.');
     }
 
-  if (pcre_exec(regex_check_dns_names, NULL, CS checkname, Ustrlen(checkname),
+  if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
       0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0)
     {
     DEBUG(D_dns)
@@ -560,7 +657,7 @@ domains, and interfaces to a fake nameserver for certain special zones. */
 if (running_in_test_harness)
   dnsa->answerlen = fakens_search(name, type, dnsa->answer, MAXPACKET);
 else
-  dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET);
+  dnsa->answerlen = res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET);
 
 if (dnsa->answerlen > MAXPACKET)
   {
@@ -581,9 +678,9 @@ if (dnsa->answerlen < 0) switch (h_errno)
     name, dns_text_type(type));
 
   /* Cut this out for various test programs */
-  #ifndef STAND_ALONE
+#ifndef STAND_ALONE
   save = deliver_domain;
-  deliver_domain = name;  /* set $domain */
+  deliver_domain = string_copy(name);  /* set $domain */
   rc = match_isinlist(name, &dns_again_means_nonexist, 0, NULL, NULL,
     MCL_DOMAIN, TRUE, NULL);
   deliver_domain = save;
@@ -596,9 +693,9 @@ if (dnsa->answerlen < 0) switch (h_errno)
     "DNS_NOMATCH\n", name);
   return dns_return(name, type, DNS_NOMATCH);
 
-  #else   /* For stand-alone tests */
+#else   /* For stand-alone tests */
   return dns_return(name, type, DNS_AGAIN);
-  #endif
+#endif
 
   case NO_RECOVERY:
   DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n"
@@ -659,10 +756,12 @@ Returns:                DNS_SUCCEED   successful lookup
 */
 
 int
-dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name)
+dns_lookup(dns_answer *dnsa, const uschar *name, int type,
+  uschar **fully_qualified_name)
 {
 int i;
-uschar *orig_name = name;
+const uschar *orig_name = name;
+BOOL secure_so_far = TRUE;
 
 /* Loop to follow CNAME chains so far, but no further... */
 
@@ -717,7 +816,12 @@ for (i = 0; i < 10; i++)
 
   /* If any data records of the correct type were found, we are done. */
 
-  if (type_rr.data != NULL) return DNS_SUCCEED;
+  if (type_rr.data != NULL)
+    {
+    if (!secure_so_far)        /* mark insecure if any element of CNAME chain was */
+      dns_set_insecure(dnsa);
+    return DNS_SUCCEED;
+    }
 
   /* If there are no data records, we need to re-scan the DNS using the
   domain given in the CNAME record, which should exist (otherwise we should
@@ -726,10 +830,13 @@ for (i = 0; i < 10; i++)
 
   if (cname_rr.data == NULL) return DNS_FAIL;
   datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
-    cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256);
+    cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data));
   if (datalen < 0) return DNS_FAIL;
   name = data;
 
+  if (!dns_is_secure(dnsa))
+    secure_so_far = FALSE;
+
   DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name);
   }       /* Loop back to do another lookup */
 
@@ -768,7 +875,7 @@ Returns:                DNS_SUCCEED   successful lookup
 */
 
 int
-dns_special_lookup(dns_answer *dnsa, uschar *name, int type,
+dns_special_lookup(dns_answer *dnsa, const uschar *name, int type,
   uschar **fully_qualified_name)
 {
 if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name);
@@ -782,7 +889,7 @@ root servers. */
 
 if (type == T_ZNS)
   {
-  uschar *d = name;
+  const uschar *d = name;
   while (d != 0)
     {
     int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name);
@@ -815,7 +922,7 @@ if (type == T_CSA)
   rc = dns_lookup(dnsa, srvname, T_SRV, NULL);
   if (rc == DNS_SUCCEED || rc == DNS_AGAIN)
     {
-    if (rc == DNS_SUCCEED) *fully_qualified_name = name;
+    if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name);
     return rc;
     }
 
@@ -923,7 +1030,7 @@ if (type == T_CSA)
       /* Extract the numerical SRV fields (p is incremented) */
       p = rr->data;
       GETSHORT(priority, p);
-      GETSHORT(weight, p);
+      GETSHORT(weight, p);     weight = weight; /* compiler quietening */
       GETSHORT(port, p);
 
       /* Check the CSA version number */
@@ -1165,4 +1272,6 @@ else
 return yield;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of dns.c */