Debug: fix coding in dnssec reporting. Bug 2205
[exim.git] / src / src / host.c
index 25dab2bb8c3f569c13aa9e53a375c89e52c719f8..1f0d91959b9f1982e2cfedeba4328c96aadb338e 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 finding hosts, either by gethostbyname(), gethostbyaddr(), or
@@ -554,7 +554,7 @@ if (sender_helo_name == NULL) show_helo = FALSE;
 /* If HELO/EHLO was followed by an IP literal, it's messy because of two
 features of IPv6. Firstly, there's the "IPv6:" prefix (Exim is liberal and
 doesn't require this, for historical reasons). Secondly, IPv6 addresses may not
-be given in canonical form, so we have to canonicize them before comparing. As
+be given in canonical form, so we have to canonicalize them before comparing. As
 it happens, the code works for both IPv4 and IPv6. */
 
 else if (sender_helo_name[0] == '[' &&
@@ -586,46 +586,45 @@ else if (sender_helo_name[0] == '[' &&
 
 /* Host name is not verified */
 
-if (sender_host_name == NULL)
+if (!sender_host_name)
   {
   uschar *portptr = Ustrstr(address, "]:");
-  int size = 0;
-  int ptr = 0;
+  gstring * g;
   int adlen;    /* Sun compiler doesn't like ++ in initializers */
 
-  adlen = (portptr == NULL)? Ustrlen(address) : (++portptr - address);
-  sender_fullhost = (sender_helo_name == NULL)? address :
-    string_sprintf("(%s) %s", sender_helo_name, address);
+  adlen = portptr ? (++portptr - address) : Ustrlen(address);
+  sender_fullhost = sender_helo_name
+    ? string_sprintf("(%s) %s", sender_helo_name, address)
+    : address;
 
-  sender_rcvhost = string_catn(NULL, &size, &ptr, address, adlen);
+  g = string_catn(NULL, address, adlen);
 
   if (sender_ident != NULL || show_helo || portptr != NULL)
     {
     int firstptr;
-    sender_rcvhost = string_catn(sender_rcvhost, &size, &ptr, US" (", 2);
-    firstptr = ptr;
+    g = string_catn(g, US" (", 2);
+    firstptr = g->ptr;
 
-    if (portptr != NULL)
-      sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2, US"port=",
-        portptr + 1);
+    if (portptr)
+      g = string_append(g, 2, US"port=", portptr + 1);
 
     if (show_helo)
-      sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2,
-        (firstptr == ptr)? US"helo=" : US" helo=", sender_helo_name);
+      g = string_append(g, 2,
+        firstptr == g->ptr ? US"helo=" : US" helo=", sender_helo_name);
 
-    if (sender_ident != NULL)
-      sender_rcvhost = string_append(sender_rcvhost, &size, &ptr, 2,
-        (firstptr == ptr)? US"ident=" : US" ident=", sender_ident);
+    if (sender_ident)
+      g = string_append(g, 2,
+        firstptr == g->ptr ? US"ident=" : US" ident=", sender_ident);
 
-    sender_rcvhost = string_catn(sender_rcvhost, &size, &ptr, US")", 1);
+    g = string_catn(g, US")", 1);
     }
 
-  sender_rcvhost[ptr] = 0;   /* string_cat() always leaves room */
+  sender_rcvhost = string_from_gstring(g);
 
   /* Release store, because string_cat allocated a minimum of 100 bytes that
   are rarely completely used. */
 
-  store_reset(sender_rcvhost + ptr + 1);
+  store_reset(sender_rcvhost + g->ptr + 1);
   }
 
 /* Host name is known and verified. Unless we've already found that the HELO
@@ -737,16 +736,14 @@ host_build_ifacelist(const uschar *list, uschar *name)
 {
 int sep = 0;
 uschar *s;
-uschar buffer[64];
-ip_address_item *yield = NULL;
-ip_address_item *last = NULL;
-ip_address_item *next;
+ip_address_item * yield = NULL, * last = NULL, * next;
 
-while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+while ((s = string_nextinlist(&list, &sep, NULL, 0)))
   {
   int ipv;
   int port = host_address_extract_port(s);            /* Leaves just the IP address */
-  if ((ipv = string_is_ip_address(s, NULL)) == 0)
+
+  if (!(ipv = string_is_ip_address(s, NULL)))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Malformed IP address \"%s\" in %s",
       s, name);
 
@@ -764,7 +761,9 @@ while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
   next->port = port;
   next->v6_include_v4 = FALSE;
 
-  if (yield == NULL) yield = last = next; else
+  if (!yield)
+    yield = last = next;
+  else
     {
     last->next = next;
     last = next;
@@ -919,21 +918,21 @@ if (type < 0)
   if (family == AF_INET6)
     {
     struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
-    yield = (uschar *)inet_ntop(family, &(sk->sin6_addr), CS addr_buffer,
+    yield = US inet_ntop(family, &(sk->sin6_addr), CS addr_buffer,
       sizeof(addr_buffer));
     if (portptr != NULL) *portptr = ntohs(sk->sin6_port);
     }
   else
     {
     struct sockaddr_in *sk = (struct sockaddr_in *)arg;
-    yield = (uschar *)inet_ntop(family, &(sk->sin_addr), CS addr_buffer,
+    yield = US inet_ntop(family, &(sk->sin_addr), CS addr_buffer,
       sizeof(addr_buffer));
     if (portptr != NULL) *portptr = ntohs(sk->sin_port);
     }
   }
 else
   {
-  yield = (uschar *)inet_ntop(type, arg, CS addr_buffer, sizeof(addr_buffer));
+  yield = US inet_ntop(type, arg, CS addr_buffer, sizeof(addr_buffer));
   }
 
 /* If the result is a mapped IPv4 address, show it in V4 format. */
@@ -1160,10 +1159,7 @@ tt--;   /* lose final separator */
 if (mask < 0)
   *tt = 0;
 else
-  {
-  sprintf(CS tt, "/%d", mask);
-  while (*tt) tt++;
-  }
+  tt += sprintf(CS tt, "/%d", mask);
 
 return tt - buffer;
 }
@@ -1587,7 +1583,7 @@ if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
 /* Copy and lowercase the name, which is in static storage in many systems.
 Put it in permanent memory. */
 
-s = (uschar *)hosts->h_name;
+s = US hosts->h_name;
 len = Ustrlen(s) + 1;
 t = sender_host_name = store_get_perm(len);
 while (*s != 0) *t++ = tolower(*s++);
@@ -1646,7 +1642,7 @@ Returns:      OK on success, the answer being placed in the global variable
               FAIL if no host name can be found
               DEFER if a temporary error was encountered
 
-The variable host_lookup_msg is set to an empty string on sucess, or to a
+The variable host_lookup_msg is set to an empty string on success, or to a
 reason for the failure otherwise, in a form suitable for tagging onto an error
 message, and also host_lookup_failed is set TRUE if the lookup failed. If there
 was a defer, host_lookup_deferred is set TRUE.
@@ -1742,7 +1738,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
         truncated and dn_expand may fail. */
 
         if (dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen,
-             (uschar *)(rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
+             US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
           {
           log_write(0, LOG_MAIN, "host name alias list truncated for %s",
             sender_host_address);
@@ -1831,7 +1827,7 @@ the names, and accepts only those that have the correct IP address. */
 
 save_hostname = sender_host_name;   /* Save for error messages */
 aliases = sender_host_aliases;
-for (hname = sender_host_name; hname != NULL; hname = *aliases++)
+for (hname = sender_host_name; hname; hname = *aliases++)
   {
   int rc;
   BOOL ok = FALSE;
@@ -1859,7 +1855,7 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++)
          h.dnssec == DS_YES ? "DNSSEC verified (AD)" : "unverified");
     if (h.dnssec != DS_YES) sender_host_dnssec = FALSE;
 
-    for (hh = &h; hh != NULL; hh = hh->next)
+    for (hh = &h; hh; hh = hh->next)
       if (host_is_in_net(hh->address, sender_host_address, 0))
         {
         HDEBUG(D_host_lookup) debug_printf("  %s OK\n", hh->address);
@@ -2099,7 +2095,7 @@ for (i = 1; i <= times;
 
   if (hostdata->h_name[0] != 0 &&
       Ustrcmp(host->name, hostdata->h_name) != 0)
-    host->name = string_copy_dnsdomain((uschar *)hostdata->h_name);
+    host->name = string_copy_dnsdomain(US hostdata->h_name);
   if (fully_qualified_name != NULL) *fully_qualified_name = host->name;
 
   /* Get the list of addresses. IPv4 and IPv6 addresses can be distinguished
@@ -2273,6 +2269,7 @@ Arguments:
 
 Returns:       HOST_FIND_FAILED     couldn't find A record
                HOST_FIND_AGAIN      try again later
+              HOST_FIND_SECURITY   dnssec required but not acheived
                HOST_FOUND           found AAAA and/or A record(s)
                HOST_IGNORED         found, but all IPs ignored
 */
@@ -2286,6 +2283,7 @@ set_address_from_dns(host_item *host, host_item **lastptr,
 dns_record *rr;
 host_item *thishostlast = NULL;    /* Indicates not yet filled in anything */
 BOOL v6_find_again = FALSE;
+BOOL dnssec_fail = FALSE;
 int i;
 
 /* If allow_ip is set, a name which is an IP address returns that value
@@ -2381,8 +2379,8 @@ for (; i >= 0; i--)
       {
       if (dnssec_require)
        {
-       log_write(L_host_lookup_failed, LOG_MAIN,
-               "dnssec fail on %s for %.256s",
+       dnssec_fail = TRUE;
+       DEBUG(D_host_lookup) debug_printf("dnssec fail on %s for %.256s",
                i>0 ? "AAAA" : "A", host->name);
        continue;
        }
@@ -2504,10 +2502,14 @@ for (; i >= 0; i--)
     }
   }
 
-/* Control gets here only if the econdookup (the A record) succeeded.
+/* Control gets here only if the second lookup (the A record) succeeded.
 However, the address may not be filled in if it was ignored. */
 
-return host->address ? HOST_FOUND : HOST_IGNORED;
+return host->address
+  ? HOST_FOUND
+  : dnssec_fail
+  ? HOST_FIND_SECURITY
+  : HOST_IGNORED;
 }
 
 
@@ -2546,6 +2548,7 @@ Returns:                HOST_FIND_FAILED  Failed to find the host or domain;
                                           if there was a syntax error,
                                           host_find_failed_syntax is set.
                         HOST_FIND_AGAIN   Could not resolve at this time
+                       HOST_FIND_SECURITY dnsssec required but not acheived
                         HOST_FOUND        Host found
                         HOST_FOUND_LOCAL  The lowest MX record points to this
                                           machine, if MX records were found, or
@@ -2609,8 +2612,8 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0)
 
   DEBUG(D_dns)
     if ((dnssec_request || dnssec_require)
-       & !dns_is_secure(&dnsa)
-       & dns_is_aa(&dnsa))
+       && !dns_is_secure(&dnsa)
+       && dns_is_aa(&dnsa))
       debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name);
 
   if (dnssec_request)
@@ -2652,7 +2655,7 @@ same domain. The result will be DNS_NODATA if the domain exists but has no MX
 records. On DNS failures, we give the "try again" error unless the domain is
 listed as one for which we continue. */
 
-if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0)
+if (rc != DNS_SUCCEED  &&  whichrrs & HOST_FIND_BY_MX)
   {
   ind_type = T_MX;
   dnssec = DS_UNK;
@@ -2660,13 +2663,12 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0)
   rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
 
   DEBUG(D_dns)
-    if ((dnssec_request || dnssec_require)
-       & !dns_is_secure(&dnsa)
-       & dns_is_aa(&dnsa))
+    if (  (dnssec_request || dnssec_require)
+       && !dns_is_secure(&dnsa)
+       && dns_is_aa(&dnsa))
       debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name);
 
   if (dnssec_request)
-    {
     if (dns_is_secure(&dnsa))
       {
       DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
@@ -2676,7 +2678,6 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0)
       {
       dnssec = DS_NO; lookup_dnssec_authenticated = US"no";
       }
-    }
 
   switch (rc)
     {
@@ -2686,17 +2687,22 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0)
     case DNS_SUCCEED:
       if (!dnssec_require || dns_is_secure(&dnsa))
        break;
-      log_write(L_host_lookup_failed, LOG_MAIN,
-                 "dnssec fail on MX for %.256s", host->name);
+      DEBUG(D_host_lookup)
+       debug_printf("dnssec fail on MX for %.256s", host->name);
+#ifndef STAND_ALONE
+      if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL,
+         MCL_DOMAIN, TRUE, NULL) != OK)
+       { yield = HOST_FIND_SECURITY; goto out; }
+#endif
       rc = DNS_FAIL;
       /*FALLTHROUGH*/
 
     case DNS_FAIL:
     case DNS_AGAIN:
-      #ifndef STAND_ALONE
+#ifndef STAND_ALONE
       if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL,
          MCL_DOMAIN, TRUE, NULL) != OK)
-      #endif
+#endif
        { yield = HOST_FIND_AGAIN; goto out; }
       DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA "
        "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN");
@@ -3062,13 +3068,17 @@ for (h = host; h != last->next; h = h->next)
   if (rc != HOST_FOUND)
     {
     h->status = hstatus_unusable;
-    if (rc == HOST_FIND_AGAIN)
+    switch (rc)
       {
-      yield = rc;
-      h->why = hwhy_deferred;
+      case HOST_FIND_AGAIN:
+       yield = rc; h->why = hwhy_deferred; break;
+      case HOST_FIND_SECURITY:
+       yield = rc; h->why = hwhy_insecure; break;
+      case HOST_IGNORED:
+       h->why = hwhy_ignored; break;
+      default:
+       h->why = hwhy_failed; break;
       }
-    else
-      h->why = rc == HOST_IGNORED ? hwhy_ignored : hwhy_failed;
     }
   }
 
@@ -3077,7 +3087,7 @@ been explicitly ignored, and remove them from the list, as if they did not
 exist. If we end up with just a single, ignored host, flatten its fields as if
 nothing was found. */
 
-if (ignore_target_hosts != NULL)
+if (ignore_target_hosts)
   {
   host_item *prev = NULL;
   for (h = host; h != last->next; h = h->next)
@@ -3113,24 +3123,22 @@ single MX preference value, IPv6 addresses come first. This can separate the
 addresses of a multihomed host, but that should not matter. */
 
 #if HAVE_IPV6
-if (h != last && !disable_ipv6)
+if (h != last && !disable_ipv6) for (h = host; h != last; h = h->next)
   {
-  for (h = host; h != last; h = h->next)
-    {
-    host_item temp;
-    host_item *next = h->next;
-    if (h->mx != next->mx ||                   /* If next is different MX */
-        h->address == NULL ||                  /* OR this one is unset */
-        Ustrchr(h->address, ':') != NULL ||    /* OR this one is IPv6 */
-        (next->address != NULL &&
-         Ustrchr(next->address, ':') == NULL)) /* OR next is IPv4 */
-      continue;                                /* move on to next */
-    temp = *h;                                 /* otherwise, swap */
-    temp.next = next->next;
-    *h = *next;
-    h->next = next;
-    *next = temp;
-    }
+  host_item temp;
+  host_item *next = h->next;
+
+  if (h->mx != next->mx ||                   /* If next is different MX */
+      h->address == NULL ||                  /* OR this one is unset */
+      Ustrchr(h->address, ':') != NULL ||    /* OR this one is IPv6 */
+      (next->address != NULL &&
+       Ustrchr(next->address, ':') == NULL)) /* OR next is IPv4 */
+    continue;                                /* move on to next */
+  temp = *h;                                 /* otherwise, swap */
+  temp.next = next->next;
+  *h = *next;
+  h->next = next;
+  *next = temp;
   }
 #endif
 
@@ -3154,6 +3162,7 @@ DEBUG(D_host_lookup)
   debug_printf("host_find_bydns yield = %s (%d); returned hosts:\n",
     (yield == HOST_FOUND)? "HOST_FOUND" :
     (yield == HOST_FOUND_LOCAL)? "HOST_FOUND_LOCAL" :
+    (yield == HOST_FIND_SECURITY)? "HOST_FIND_SECURITY" :
     (yield == HOST_FIND_AGAIN)? "HOST_FIND_AGAIN" :
     (yield == HOST_FIND_FAILED)? "HOST_FIND_FAILED" : "?",
     yield);
@@ -3243,7 +3252,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
   else if (Ustrcmp(buffer, "request_dnssec")    == 0) request_dnssec = TRUE;
   else if (Ustrcmp(buffer, "no_request_dnssec") == 0) request_dnssec = FALSE;
   else if (Ustrcmp(buffer, "require_dnssec")    == 0) require_dnssec = TRUE;
-  else if (Ustrcmp(buffer, "no_reqiret_dnssec") == 0) require_dnssec = FALSE;
+  else if (Ustrcmp(buffer, "no_require_dnssec") == 0) require_dnssec = FALSE;
   else if (Ustrcmp(buffer, "test_harness") == 0)
     running_in_test_harness = !running_in_test_harness;
   else if (Ustrcmp(buffer, "ipv6") == 0) disable_ipv6 = !disable_ipv6;
@@ -3285,9 +3294,13 @@ while (Ufgets(buffer, 256, stdin) != NULL)
       : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL,
                        &d, &fully_qualified_name, NULL);
 
-    if (rc == HOST_FIND_FAILED) printf("Failed\n");
-      else if (rc == HOST_FIND_AGAIN) printf("Again\n");
-        else if (rc == HOST_FOUND_LOCAL) printf("Local\n");
+    switch (rc)
+      {
+      case HOST_FIND_FAILED:   printf("Failed\n");     break;
+      case HOST_FIND_AGAIN:    printf("Again\n");      break;
+      case HOST_FIND_SECURITY: printf("Security\n");   break;
+      case HOST_FOUND_LOCAL:   printf("Local\n");      break;
+      }
     }
 
   printf("\n> ");