struct dnssec_domains
[exim.git] / src / src / host.c
index d40925ecdd9745ac28ecd4a5d2d7ff91d467bd7a..b3d38c57824840699e254f8feb7d1e61674dd228 100644 (file)
@@ -94,6 +94,53 @@ random_seed = 1103515245 * random_seed + 12345;
 return (unsigned int)(random_seed >> 16) % limit;
 }
 
+/*************************************************
+*      Wrappers for logging lookup times         *
+*************************************************/
+
+/* When the 'slow_lookup_log' variable is enabled, these wrappers will
+write to the log file all (potential) dns lookups that take more than
+slow_lookup_log milliseconds
+*/
+
+static void
+log_long_lookup(const uschar * type, const uschar * data, unsigned long msec)
+{
+log_write(0, LOG_MAIN, "Long %s lookup for '%s': %lu msec",
+  type, data, msec);
+}
+
+
+/* returns the current system epoch time in milliseconds. */
+static unsigned long
+get_time_in_ms()
+{
+struct timeval tmp_time;
+unsigned long seconds, microseconds;
+
+gettimeofday(&tmp_time, NULL);
+seconds = (unsigned long) tmp_time.tv_sec;
+microseconds = (unsigned long) tmp_time.tv_usec;
+return seconds*1000 + microseconds/1000;
+}
+
+
+static int
+dns_lookup_timerwrap(dns_answer *dnsa, const uschar *name, int type,
+  const uschar **fully_qualified_name)
+{
+int retval;
+unsigned long time_msec;
+
+if (!slow_lookup_log)
+  return dns_lookup(dnsa, name, type, fully_qualified_name);
+
+time_msec = get_time_in_ms();
+retval = dns_lookup(dnsa, name, type, fully_qualified_name);
+if ((time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
+  log_long_lookup(US"name", name, time_msec);
+return retval;
+}
 
 
 /*************************************************
@@ -101,8 +148,7 @@ return (unsigned int)(random_seed >> 16) % limit;
 *************************************************/
 
 /* This function is called instead of gethostbyname(), gethostbyname2(), or
-getipnodebyname() when running in the test harness. It recognizes the name
-"manyhome.test.ex" and generates a humungous number of IP addresses. It also
+getipnodebyname() when running in the test harness. . It also
 recognizes an unqualified "localhost" and forces it to the appropriate loopback
 address. IP addresses are treated as literals. For other names, it uses the DNS
 to find the host name. In the test harness, this means it will access only the
@@ -139,34 +185,6 @@ DEBUG(D_host_lookup)
   debug_printf("using host_fake_gethostbyname for %s (%s)\n", name,
     (af == AF_INET)? "IPv4" : "IPv6");
 
-/* Handle the name that needs a vast number of IP addresses */
-
-if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET)
-  {
-  int i, j;
-  yield = store_get(sizeof(struct hostent));
-  alist = store_get(2049 * sizeof(char *));
-  adds  = store_get(2048 * alen);
-  yield->h_name = CS name;
-  yield->h_aliases = NULL;
-  yield->h_addrtype = af;
-  yield->h_length = alen;
-  yield->h_addr_list = CSS alist;
-  for (i = 104; i <= 111; i++)
-    {
-    for (j = 0; j <= 255; j++)
-      {
-      *alist++ = adds;
-      *adds++ = 10;
-      *adds++ = 250;
-      *adds++ = i;
-      *adds++ = j;
-      }
-    }
-  *alist = NULL;
-  return yield;
-  }
-
 /* Handle unqualified "localhost" */
 
 if (Ustrcmp(name, "localhost") == 0)
@@ -217,7 +235,7 @@ if (ipa != 0)
 else
   {
   int type = (af == AF_INET)? T_A:T_AAAA;
-  int rc = dns_lookup(&dnsa, lname, type, NULL);
+  int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL);
   int count = 0;
 
   lookup_dnssec_authenticated = NULL;
@@ -233,11 +251,10 @@ else
     }
 
   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-       rr != NULL;
+       rr;
        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
-    {
-    if (rr->type == type) count++;
-    }
+    if (rr->type == type)
+      count++;
 
   yield = store_get(sizeof(struct hostent));
   alist = store_get((count + 1) * sizeof(char **));
@@ -250,14 +267,14 @@ else
   yield->h_addr_list = CSS alist;
 
   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-       rr != NULL;
+       rr;
        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
     {
     int i, n;
     int x[4];
     dns_address *da;
     if (rr->type != type) continue;
-    da = dns_address_from_rr(&dnsa, rr);
+    if (!(da = dns_address_from_rr(&dnsa, rr))) break;
     *alist++ = adds;
     n = host_aton(da->address, x);
     for (i = 0; i < n; i++)
@@ -1454,6 +1471,9 @@ int len;
 uschar *s, *t;
 struct hostent *hosts;
 struct in_addr addr;
+unsigned long time_msec;
+
+if (slow_lookup_log) time_msec = get_time_in_ms();
 
 /* Lookup on IPv6 system */
 
@@ -1489,6 +1509,11 @@ addr.s_addr = (S_ADDR_TYPE)inet_addr(CS sender_host_address);
 hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET);
 #endif
 
+if (  slow_lookup_log
+   && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log
+   )
+  log_long_lookup(US"name", sender_host_address, time_msec);
+
 /* Failed to look up the host. */
 
 if (hosts == NULL)
@@ -1621,7 +1646,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
     {
     dns_init(FALSE, FALSE, FALSE);    /* dnssec ctrl by dns_dnssec_ok glbl */
     dns_build_reverse(sender_host_address, buffer);
-    rc = dns_lookup(&dnsa, buffer, T_PTR, NULL);
+    rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL);
 
     /* The first record we come across is used for the name; others are
     considered to be aliases. We have to scan twice, in order to find out the
@@ -1917,7 +1942,7 @@ some circumstances when the get..byname() function actually calls the DNS. */
 
 dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0,
          (flags & HOST_FIND_SEARCH_PARENTS) != 0,
-        FALSE);        /*XXX dnssec? */
+        FALSE);                /* Cannot retrieve dnssec status so do not request */
 
 /* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both
 kinds of address, so go round the loop twice. Note that we have ensured that
@@ -1961,11 +1986,14 @@ for (i = 1; i <= times;
   BOOL ipv4_addr;
   int error_num = 0;
   struct hostent *hostdata;
+  unsigned long time_msec;
 
   #ifdef STAND_ALONE
   printf("Looking up: %s\n", host->name);
   #endif
 
+  if (slow_lookup_log) time_msec = get_time_in_ms();
+
   #if HAVE_IPV6
   if (running_in_test_harness)
     hostdata = host_fake_gethostbyname(host->name, af, &error_num);
@@ -1989,17 +2017,21 @@ for (i = 1; i <= times;
     }
   #endif   /* HAVE_IPV6 */
 
+  if (slow_lookup_log
+      && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
+    log_long_lookup(US"name", host->name, time_msec);
+
   if (hostdata == NULL)
     {
     uschar *error;
     switch (error_num)
       {
       case HOST_NOT_FOUND: error = US"HOST_NOT_FOUND"; break;
-      case TRY_AGAIN: error = US"TRY_AGAIN"; break;
-      case NO_RECOVERY: error = US"NO_RECOVERY"; break;
-      case NO_DATA: error = US"NO_DATA"; break;
+      case TRY_AGAIN:      error = US"TRY_AGAIN"; break;
+      case NO_RECOVERY:    error = US"NO_RECOVERY"; break;
+      case NO_DATA:        error = US"NO_DATA"; break;
       #if NO_DATA != NO_ADDRESS
-      case NO_ADDRESS: error = US"NO_ADDRESS"; break;
+      case NO_ADDRESS:     error = US"NO_ADDRESS"; break;
       #endif
       default: error = US"?"; break;
       }
@@ -2247,11 +2279,7 @@ loop once only, looking only for A records. */
     else
   #endif        /* STAND_ALONE */
 
-  #ifdef SUPPORT_A6
-  i = 2;        /* look up A6 and AAAA and A records */
-  #else
   i = 1;        /* look up AAAA and A records */
-  #endif        /* SUPPORT_A6 */
 
 /* The IPv4 world */
 
@@ -2267,7 +2295,7 @@ for (; i >= 0; i--)
   dns_answer dnsa;
   dns_scan dnss;
 
-  int rc = dns_lookup(&dnsa, host->name, type, fully_qualified_name);
+  int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name);
   lookup_dnssec_authenticated = !dnssec_request ? NULL
     : dns_is_secure(&dnsa) ? US"yes" : US"no";
 
@@ -2321,10 +2349,13 @@ for (; i >= 0; i--)
 
   /* Lookup succeeded: fill in the given host item with the first non-ignored
   address found; create additional items for any others. A single A6 record
-  may generate more than one address. */
+  may generate more than one address.  The lookup had a chance to update the
+  fqdn; we do not want any later times round the loop to do so. */
+
+  fully_qualified_name = NULL;
 
   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-       rr != NULL;
+       rr;
        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
     {
     if (rr->type == type)
@@ -2336,15 +2367,14 @@ for (; i >= 0; i--)
 
       DEBUG(D_host_lookup)
         {
-        if (da == NULL)
-          debug_printf("no addresses extracted from A6 RR for %s\n",
+        if (!da) debug_printf("no addresses extracted from A6 RR for %s\n",
             host->name);
         }
 
       /* This loop runs only once for A and AAAA records, but may run
       several times for an A6 record that generated multiple addresses. */
 
-      for (; da != NULL; da = da->next)
+      for (; da; da = da->next)
         {
         #ifndef STAND_ALONE
         if (ignore_target_hosts != NULL &&
@@ -2464,8 +2494,8 @@ Arguments:
   srv_service           when SRV used, the service name
   srv_fail_domains      DNS errors for these domains => assume nonexist
   mx_fail_domains       DNS errors for these domains => assume nonexist
-  dnssec_request_domains => make dnssec request
-  dnssec_require_domains => ditto and nonexist failures
+  dnssec_d.request =>   make dnssec request: domainlist
+  dnssec_d.require =>   ditto and nonexist failures
   fully_qualified_name  if not NULL, return fully-qualified name
   removed               set TRUE if local host was removed from the list
 
@@ -2483,7 +2513,7 @@ Returns:                HOST_FIND_FAILED  Failed to find the host or domain;
 int
 host_find_bydns(host_item *host, const uschar *ignore_target_hosts, int whichrrs,
   uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains,
-  uschar *dnssec_request_domains, uschar *dnssec_require_domains,
+  const dnssec_domains *dnssec_d,
   const uschar **fully_qualified_name, BOOL *removed)
 {
 host_item *h, *last;
@@ -2493,11 +2523,13 @@ int ind_type = 0;
 int yield;
 dns_answer dnsa;
 dns_scan dnss;
-BOOL dnssec_require = match_isinlist(host->name, CUSS &dnssec_require_domains,
+BOOL dnssec_require = dnssec_d
+                   && match_isinlist(host->name, CUSS &dnssec_d->require,
                                    0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
 BOOL dnssec_request = dnssec_require
-                   || match_isinlist(host->name, CUSS &dnssec_request_domains,
-                                   0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK;
+                   || (  dnssec_d
+                      && match_isinlist(host->name, CUSS &dnssec_d->request,
+                                   0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK);
 dnssec_status_t dnssec;
 
 /* Set the default fully qualified name to the incoming name, initialize the
@@ -2531,7 +2563,7 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0)
 
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name);
+  rc = dns_lookup_timerwrap(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name);
 
   if (dnssec_request)
     {
@@ -2577,7 +2609,7 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0)
   ind_type = T_MX;
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name);
+  rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
 
   if (dnssec_request)
     {
@@ -3083,9 +3115,6 @@ dns_init(FALSE, FALSE, FALSE);    /* clear the dnssec bit for getaddrbyname */
 return yield;
 }
 
-
-
-
 /*************************************************
 **************************************************
 *             Stand-alone test program           *
@@ -3176,6 +3205,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
   else
     {
     int flags = whichrrs;
+    dnssec d;
 
     h.name = buffer;
     h.next = NULL;
@@ -3188,12 +3218,13 @@ while (Ufgets(buffer, 256, stdin) != NULL)
     if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
     if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
+    d.request = request_dnssec ? &h.name : NULL;
+    d.require = require_dnssec ? &h.name : NULL;
+
     rc = byname
       ? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE)
       : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL,
-                       request_dnssec ? &h.name : NULL,
-                       require_dnssec ? &h.name : NULL,
-                       &fully_qualified_name, NULL);
+                       &d, &fully_qualified_name, NULL);
 
     if (rc == HOST_FIND_FAILED) printf("Failed\n");
       else if (rc == HOST_FIND_AGAIN) printf("Again\n");