Fix message-reception clock usage. Bug 2615
[users/heiko/exim.git] / src / src / host.c
index eda56d76377e8de28be22986fe8064a26088e8d5..99bbba7a3e0f7ee205ba94251563c0faf68d31ed 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* 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 finding hosts, either by gethostbyname(), gethostbyaddr(), or
@@ -138,7 +139,7 @@ if (!slow_lookup_log)
 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);
+  log_long_lookup(dns_text_type(type), name, time_msec);
 return retval;
 }
 
@@ -177,29 +178,28 @@ const uschar *lname = name;
 uschar *adds;
 uschar **alist;
 struct hostent *yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 
 DEBUG(D_host_lookup)
   debug_printf("using host_fake_gethostbyname for %s (%s)\n", name,
-    (af == AF_INET)? "IPv4" : "IPv6");
+    af == AF_INET ? "IPv4" : "IPv6");
 
 /* Handle unqualified "localhost" */
 
 if (Ustrcmp(name, "localhost") == 0)
-  lname = (af == AF_INET)? US"127.0.0.1" : US"::1";
+  lname = af == AF_INET ? US"127.0.0.1" : US"::1";
 
 /* Handle a literal IP address */
 
 if ((ipa = string_is_ip_address(lname, NULL)) != 0)
-  {
-  if ((ipa == 4 && af == AF_INET) ||
-      (ipa == 6 && af == AF_INET6))
+  if (   ipa == 4 && af == AF_INET
+     ||  ipa == 6 && af == AF_INET6)
     {
     int x[4];
-    yield = store_get(sizeof(struct hostent));
-    alist = store_get(2 * sizeof(char *));
-    adds  = store_get(alen);
+    yield = store_get(sizeof(struct hostent), FALSE);
+    alist = store_get(2 * sizeof(char *), FALSE);
+    adds  = store_get(alen, FALSE);
     yield->h_name = CS name;
     yield->h_aliases = NULL;
     yield->h_addrtype = af;
@@ -224,14 +224,13 @@ if ((ipa = string_is_ip_address(lname, NULL)) != 0)
     *error_num = HOST_NOT_FOUND;
     return NULL;
     }
-  }
 
 /* Handle a host name */
 
 else
   {
-  int type = (af == AF_INET)? T_A:T_AAAA;
-  int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL);
+  int type = af == AF_INET ? T_A:T_AAAA;
+  int rc = dns_lookup_timerwrap(dnsa, lname, type, NULL);
   int count = 0;
 
   lookup_dnssec_authenticated = NULL;
@@ -246,14 +245,14 @@ else
     case DNS_FAIL:    *error_num = NO_RECOVERY; return NULL;
     }
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     count++;
 
-  yield = store_get(sizeof(struct hostent));
-  alist = store_get((count + 1) * sizeof(char *));
-  adds  = store_get(count *alen);
+  yield = store_get(sizeof(struct hostent), FALSE);
+  alist = store_get((count + 1) * sizeof(char *), FALSE);
+  adds  = store_get(count *alen, FALSE);
 
   yield->h_name = CS name;
   yield->h_aliases = NULL;
@@ -261,13 +260,13 @@ else
   yield->h_length = alen;
   yield->h_addr_list = CSS alist;
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     {
     int x[4];
     dns_address *da;
-    if (!(da = dns_address_from_rr(&dnsa, rr))) break;
+    if (!(da = dns_address_from_rr(dnsa, rr))) break;
     *alist++ = adds;
     for (int n = host_aton(da->address, x), i = 0; i < n; i++)
       {
@@ -325,7 +324,7 @@ while ((name = string_nextinlist(&list, &sep, NULL, 0)))
     continue;
     }
 
-  h = store_get(sizeof(host_item));
+  h = store_get(sizeof(host_item), FALSE);
   h->name = name;
   h->address = NULL;
   h->port = PORT_NONE;
@@ -524,12 +523,13 @@ void
 host_build_sender_fullhost(void)
 {
 BOOL show_helo = TRUE;
-uschar * address, * fullhost, * rcvhost, * reset_point;
+uschar * address, * fullhost, * rcvhost;
+rmark reset_point;
 int len;
 
 if (!sender_host_address) return;
 
-reset_point = store_get(0);
+reset_point = store_mark();
 
 /* Set up address, with or without the port. After discussion, it seems that
 the only format that doesn't cause trouble is [aaaa]:pppp. However, we can't
@@ -643,10 +643,8 @@ else
     }
   }
 
-if (sender_fullhost) store_free(sender_fullhost);
-sender_fullhost = string_copy_malloc(fullhost);
-if (sender_rcvhost) store_free(sender_rcvhost);
-sender_rcvhost = string_copy_malloc(rcvhost);
+sender_fullhost = string_copy_perm(fullhost, TRUE);
+sender_rcvhost = string_copy_perm(rcvhost, TRUE);
 
 store_reset(reset_point);
 
@@ -668,6 +666,8 @@ return depends on whether sender_fullhost and sender_ident are set or not:
   ident set, no host  => U=ident
   ident set, host set => H=sender_fullhost U=ident
 
+Use taint-unchecked routines on the assumption we'll never expand the results.
+
 Arguments:
   useflag   TRUE if first item to be flagged (H= or U=); if there are two
               items, the second is always flagged
@@ -679,7 +679,7 @@ uschar *
 host_and_ident(BOOL useflag)
 {
 if (!sender_fullhost)
-  (void)string_format(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
+  string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "",
      sender_ident ? sender_ident : US"unknown");
 else
   {
@@ -688,10 +688,10 @@ else
   if (LOGGING(incoming_interface) && interface_address)
     iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
   if (sender_ident)
-    (void)string_format(big_buffer, big_buffer_size, "%s%s%s U=%s",
+    string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s",
       flag, sender_fullhost, iface, sender_ident);
   else
-    (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
+    string_format_nt(big_buffer, big_buffer_size, "%s%s%s",
       flag, sender_fullhost, iface);
   }
 return big_buffer;
@@ -728,6 +728,7 @@ host_build_ifacelist(const uschar *list, uschar *name)
 int sep = 0;
 uschar *s;
 ip_address_item * yield = NULL, * last = NULL, * next;
+BOOL taint = is_tainted(list);
 
 while ((s = string_nextinlist(&list, &sep, NULL, 0)))
   {
@@ -746,11 +747,12 @@ while ((s = string_nextinlist(&list, &sep, NULL, 0)))
   address above. The field in the ip_address_item is large enough to hold an
   IPv6 address. */
 
-  next = store_get(sizeof(ip_address_item));
+  next = store_get(sizeof(ip_address_item), taint);
   next->next = NULL;
   Ustrcpy(next->address, s);
   next->port = port;
   next->v6_include_v4 = FALSE;
+  next->log = NULL;
 
   if (!yield)
     yield = last = next;
@@ -799,7 +801,7 @@ add_unique_interface(ip_address_item *list, ip_address_item *ipa)
 ip_address_item *ipa2;
 for (ipa2 = list; ipa2; ipa2 = ipa2->next)
   if (Ustrcmp(ipa2->address, ipa->address) == 0) return list;
-ipa2 = store_get_perm(sizeof(ip_address_item));
+ipa2 = store_get_perm(sizeof(ip_address_item), FALSE);
 *ipa2 = *ipa;
 ipa2->next = list;
 return ipa2;
@@ -815,7 +817,7 @@ ip_address_item *running_interfaces = NULL;
 
 if (local_interface_data == NULL)
   {
-  void *reset_item = store_get(0);
+  void *reset_item = store_mark();
   ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces,
     US"local_interfaces");
   ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces,
@@ -943,13 +945,15 @@ else
 
 /* If there is no buffer, put the string into some new store. */
 
-if (buffer == NULL) return string_copy(yield);
+if (!buffer) buffer = store_get(46, FALSE);
 
 /* Callers of this function with a non-NULL buffer must ensure that it is
 large enough to hold an IPv6 address, namely, at least 46 bytes. That's what
-makes this use of strcpy() OK. */
+makes this use of strcpy() OK.
+If the library returned apparently an apparently tainted string, clean it;
+we trust IP addresses. */
 
-Ustrcpy(buffer, yield);
+string_format_nt(buffer, 46, "%s", yield);
 return buffer;
 }
 
@@ -1499,9 +1503,7 @@ Returns:     OK, DEFER, FAIL
 static int
 host_name_lookup_byaddr(void)
 {
-int len;
-uschar *s, *t;
-struct hostent *hosts;
+struct hostent * hosts;
 struct in_addr addr;
 unsigned long time_msec = 0;   /* init to quieten dumb static analysis */
 
@@ -1544,11 +1546,11 @@ hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET);
 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);
+  log_long_lookup(US"gethostbyaddr", sender_host_address, time_msec);
 
 /* Failed to look up the host. */
 
-if (hosts == NULL)
+if (!hosts)
   {
   HDEBUG(D_host_lookup) debug_printf("IP address lookup failed: h_errno=%d\n",
     h_errno);
@@ -1559,7 +1561,7 @@ if (hosts == NULL)
 treat this as non-existent. In some operating systems, this is returned as an
 empty string; in others as a single dot. */
 
-if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.')
+if (!hosts->h_name || !hosts->h_name[0] || hosts->h_name[0] == '.')
   {
   HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an empty name: "
     "treated as non-existent host name\n");
@@ -1569,29 +1571,29 @@ 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 = US hosts->h_name;
-len = Ustrlen(s) + 1;
-t = sender_host_name = store_get_perm(len);
-while (*s != 0) *t++ = tolower(*s++);
-*t = 0;
+  {
+  int old_pool = store_pool;
+  store_pool = POOL_TAINT_PERM;                /* names are tainted */
 
-/* If the host has aliases, build a copy of the alias list */
+  sender_host_name = string_copylc(US hosts->h_name);
 
-if (hosts->h_aliases)
-  {
-  int count = 1;
-  uschar **ptr;
-  for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
-  ptr = sender_host_aliases = store_get_perm(count * sizeof(uschar *));
-  for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
-    {
-    uschar *s = *aliases;
-    int len = Ustrlen(s) + 1;
-    uschar *t = *ptr++ = store_get_perm(len);
-    while (*s != 0) *t++ = tolower(*s++);
-    *t = 0;
-    }
-  *ptr = NULL;
+  /* If the host has aliases, build a copy of the alias list */
+
+  if (hosts->h_aliases)
+    {
+    int count = 1;
+    uschar **ptr;
+
+    for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++;
+    store_pool = POOL_PERM;
+    ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
+    store_pool = POOL_TAINT_PERM;
+
+    for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++)
+      *ptr++ = string_copylc(*aliases);
+    *ptr = NULL;
+    }
+  store_pool = old_pool;
   }
 
 return OK;
@@ -1644,10 +1646,9 @@ int old_pool, rc;
 int sep = 0;
 uschar *save_hostname;
 uschar **aliases;
-uschar buffer[256];
 uschar *ordername;
 const uschar *list = host_lookup_order;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 
 sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE;
@@ -1670,13 +1671,14 @@ if (f.running_in_test_harness &&
 /* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in
 the order specified by the host_lookup_order option. */
 
-while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((ordername = string_nextinlist(&list, &sep, NULL, 0)))
   {
   if (strcmpic(ordername, US"bydns") == 0)
     {
+    uschar * name = dns_build_reverse(sender_host_address);
+
     dns_init(FALSE, FALSE, FALSE);    /* dnssec ctrl by dns_dnssec_ok glbl */
-    dns_build_reverse(sender_host_address, buffer);
-    rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL);
+    rc = dns_lookup_timerwrap(dnsa, name, 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
@@ -1691,35 +1693,35 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
       int count = 0;
       int old_pool = store_pool;
 
-      sender_host_dnssec = dns_is_secure(&dnsa);
+      sender_host_dnssec = dns_is_secure(dnsa);
       DEBUG(D_dns)
         debug_printf("Reverse DNS security status: %s\n",
             sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified");
 
       store_pool = POOL_PERM;        /* Save names in permanent storage */
 
-      for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+      for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
            rr;
-           rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+           rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
        count++;
 
       /* Get store for the list of aliases. For compatibility with
       gethostbyaddr, we make an empty list if there are none. */
 
-      aptr = sender_host_aliases = store_get(count * sizeof(uschar *));
+      aptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE);
 
       /* Re-scan and extract the names */
 
-      for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+      for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
            rr;
-           rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
+           rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR)
         {
-        uschar * s = store_get(ssize);
+        uschar * s = store_get(ssize, TRUE);   /* names are tainted */
 
         /* If an overlong response was received, the data will have been
         truncated and dn_expand may fail. */
 
-        if (dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen,
+        if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
              US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0)
           {
           log_write(0, LOG_MAIN, "host name alias list truncated for %s",
@@ -1727,8 +1729,8 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
           break;
           }
 
-        store_reset(s + Ustrlen(s) + 1);
-        if (s[0] == 0)
+        store_release_above(s + Ustrlen(s) + 1);
+        if (!s[0])
           {
           HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
             "empty name: treated as non-existent host name\n");
@@ -1736,15 +1738,15 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
           }
         if (!sender_host_name) sender_host_name = s;
        else *aptr++ = s;
-        while (*s != 0) { *s = tolower(*s); s++; }
+        while (*s) { *s = tolower(*s); s++; }
         }
 
       *aptr = NULL;            /* End of alias list */
       store_pool = old_pool;   /* Reset store pool */
 
-      /* If we've found a names, break out of the "order" loop */
+      /* If we've found a name, break out of the "order" loop */
 
-      if (sender_host_name != NULL) break;
+      if (sender_host_name) break;
       }
 
     /* If the DNS lookup deferred, we must also defer. */
@@ -1948,6 +1950,13 @@ BOOL temp_error = FALSE;
 int af;
 #endif
 
+#ifndef DISABLE_TLS
+/* Copy the host name at this point to the value which is used for
+TLS certificate name checking, before anything modifies it.  */
+
+host->certname = host->name;
+#endif
+
 /* Make sure DNS options are set as required. This appears to be necessary in
 some circumstances when the get..byname() function actually calls the DNS. */
 
@@ -2030,9 +2039,9 @@ for (int i = 1; i <= times;
 
   if (   slow_lookup_log
       && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log)
-    log_long_lookup(US"name", host->name, time_msec);
+    log_long_lookup(US"gethostbyname", host->name, time_msec);
 
-  if (hostdata == NULL)
+  if (!hostdata)
     {
     uschar *error;
     switch (error_num)
@@ -2041,18 +2050,19 @@ for (int i = 1; i <= times;
       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
+    #if NO_DATA != NO_ADDRESS
       case NO_ADDRESS:     error = US"NO_ADDRESS"; break;
-      #endif
+    #endif
       default: error = US"?"; break;
       }
 
     DEBUG(D_host_lookup) debug_printf("%s returned %d (%s)\n",
+      f.running_in_test_harness ? "host_fake_gethostbyname" :
       #if HAVE_IPV6
         #if HAVE_GETIPNODEBYNAME
-        (af == AF_INET6)? "getipnodebyname(af=inet6)" : "getipnodebyname(af=inet)",
+        af == AF_INET6 ? "getipnodebyname(af=inet6)" : "getipnodebyname(af=inet)",
         #else
-        (af == AF_INET6)? "gethostbyname2(af=inet6)" : "gethostbyname2(af=inet)",
+        af == AF_INET6 ? "gethostbyname2(af=inet6)" : "gethostbyname2(af=inet)",
         #endif
       #else
       "gethostbyname",
@@ -2112,8 +2122,11 @@ for (int i = 1; i <= times;
 
     else
       {
-      host_item *next = store_get(sizeof(host_item));
+      host_item *next = store_get(sizeof(host_item), FALSE);
       next->name = host->name;
+#ifndef DISABLE_TLS
+      next->certname = host->certname;
+#endif
       next->mx = host->mx;
       next->address = text_address;
       next->port = PORT_NONE;
@@ -2132,12 +2145,12 @@ for (int i = 1; i <= times;
 NULL. If temp_error is set, at least one of the lookups gave a temporary error,
 so we pass that back. */
 
-if (host->address == NULL)
+if (!host->address)
   {
   uschar *msg =
     #ifndef STAND_ALONE
-    (message_id[0] == 0 && smtp_in != NULL)?
-      string_sprintf("no IP address found for host %s (during %s)", host->name,
+    message_id[0] == 0 && smtp_in
+      string_sprintf("no IP address found for host %s (during %s)", host->name,
           smtp_get_connection_info()) :
     #endif
     string_sprintf("no IP address found for host %s", host->name);
@@ -2257,6 +2270,13 @@ BOOL v6_find_again = FALSE;
 BOOL dnssec_fail = FALSE;
 int i;
 
+#ifndef DISABLE_TLS
+/* Copy the host name at this point to the value which is used for
+TLS certificate name checking, before any CNAME-following modifies it.  */
+
+host->certname = host->name;
+#endif
+
 /* If allow_ip is set, a name which is an IP address returns that value
 as its address. This is used for MX records when allow_mx_to_ip is set, for
 those sites that feel they have to flaunt the RFC rules. */
@@ -2305,17 +2325,17 @@ for (; i >= 0; i--)
   int type = types[i];
   int randoffset = i == (whichrrs & HOST_FIND_IPV4_FIRST ? 1 : 0)
     ? 500 : 0;  /* Ensures v6/4 sort order */
-  dns_answer dnsa;
+  dns_answer * dnsa = store_get_dns_answer();
   dns_scan dnss;
 
-  int rc = dns_lookup_timerwrap(&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";
+    : dns_is_secure(dnsa) ? US"yes" : US"no";
 
   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 (A/AAAA) requested AD, but got AA\n", host->name);
 
@@ -2343,7 +2363,7 @@ for (; i >= 0; i--)
 
   if (dnssec_request)
     {
-    if (dns_is_secure(&dnsa))
+    if (dns_is_secure(dnsa))
       {
       DEBUG(D_host_lookup) debug_printf("%s A DNSSEC\n", host->name);
       if (host->dnssec == DS_UNK) /* set in host_find_bydns() */
@@ -2374,11 +2394,11 @@ for (; i >= 0; i--)
 
   fully_qualified_name = NULL;
 
-  for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+  for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
        rr;
-       rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
+       rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == type)
     {
-    dns_address * da = dns_address_from_rr(&dnsa, rr);
+    dns_address * da = dns_address_from_rr(dnsa, rr);
 
     DEBUG(D_host_lookup)
       if (!da) debug_printf("no addresses extracted from A6 RR for %s\n",
@@ -2434,7 +2454,7 @@ for (; i >= 0; i--)
        /* Not a duplicate */
 
        new_sort_key = host->mx * 1000 + random_number(500) + randoffset;
-       next = store_get(sizeof(host_item));
+       next = store_get(sizeof(host_item), FALSE);
 
        /* New address goes first: insert the new block after the first one
        (so as not to disturb the original pointer) but put the new address
@@ -2540,7 +2560,7 @@ host_item *h, *last;
 int rc = DNS_FAIL;
 int ind_type = 0;
 int yield;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 BOOL dnssec_require = dnssec_d
                    && match_isinlist(host->name, CUSS &dnssec_d->require,
@@ -2582,18 +2602,18 @@ if (whichrrs & HOST_FIND_BY_SRV)
 
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup_timerwrap(&dnsa, temp_fully_qualified_name, ind_type,
+  rc = dns_lookup_timerwrap(dnsa, temp_fully_qualified_name, ind_type,
        CUSS &temp_fully_qualified_name);
 
   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)
     {
-    if (dns_is_secure(&dnsa))
+    if (dns_is_secure(dnsa))
       { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; }
     else
       { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; }
@@ -2605,7 +2625,7 @@ if (whichrrs & HOST_FIND_BY_SRV)
   /* On DNS failures, we give the "try again" error unless the domain is
   listed as one for which we continue. */
 
-  if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(&dnsa))
+  if (rc == DNS_SUCCEED && dnssec_require && !dns_is_secure(dnsa))
     {
     log_write(L_host_lookup_failed, LOG_MAIN,
                "dnssec fail on SRV for %.256s", host->name);
@@ -2635,18 +2655,18 @@ if (rc != DNS_SUCCEED  &&  whichrrs & HOST_FIND_BY_MX)
   ind_type = T_MX;
   dnssec = DS_UNK;
   lookup_dnssec_authenticated = NULL;
-  rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name);
+  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))
+       && !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))
+    if (dns_is_secure(dnsa))
       {
-      DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name);
+      DEBUG(D_host_lookup) debug_printf("%s (MX resp) DNSSEC\n", host->name);
       dnssec = DS_YES; lookup_dnssec_authenticated = US"yes";
       }
     else
@@ -2660,7 +2680,7 @@ if (rc != DNS_SUCCEED  &&  whichrrs & HOST_FIND_BY_MX)
       yield = HOST_FIND_FAILED; goto out;
 
     case DNS_SUCCEED:
-      if (!dnssec_require || dns_is_secure(&dnsa))
+      if (!dnssec_require || dns_is_secure(dnsa))
        break;
       DEBUG(D_host_lookup)
        debug_printf("dnssec fail on MX for %.256s", host->name);
@@ -2754,9 +2774,9 @@ host which is not the primary hostname. */
 
 last = NULL;    /* Indicates that not even the first item is filled yet */
 
-for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
      rr;
-     rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
+     rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == ind_type)
   {
   int precedence, weight;
   int port = PORT_NONE;
@@ -2781,7 +2801,7 @@ for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
 
   /* Get the name of the host pointed to. */
 
-  (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, s,
+  (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
     (DN_EXPAND_ARG4_TYPE)data, sizeof(data));
 
   /* Check that we haven't already got this host on the chain; if we have,
@@ -2837,7 +2857,7 @@ for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
   /* Make a new host item and seek the correct insertion place */
     {
     int sort_key = precedence * 1000 + weight;
-    host_item *next = store_get(sizeof(host_item));
+    host_item *next = store_get(sizeof(host_item), FALSE);
     next->name = string_copy_dnsdomain(data);
     next->address = NULL;
     next->port = port;
@@ -3152,6 +3172,79 @@ dns_init(FALSE, FALSE, FALSE);   /* clear the dnssec bit for getaddrbyname */
 return yield;
 }
 
+
+
+
+#ifdef SUPPORT_DANE
+/* Lookup TLSA record for host/port.
+Return:  OK            success with dnssec; DANE mode
+         DEFER         Do not use this host now, may retry later
+        FAIL_FORCED    No TLSA record; DANE not usable
+        FAIL           Do not use this connection
+*/
+
+int
+tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
+{
+uschar buffer[300];
+const uschar * fullname = buffer;
+int rc;
+BOOL sec;
+
+/* TLSA lookup string */
+(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
+
+rc = dns_lookup_timerwrap(dnsa, buffer, T_TLSA, &fullname);
+sec = dns_is_secure(dnsa);
+DEBUG(D_transport)
+  debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
+
+switch (rc)
+  {
+  case DNS_AGAIN:
+    return DEFER; /* just defer this TLS'd conn */
+
+  case DNS_SUCCEED:
+    if (sec)
+      {
+      DEBUG(D_transport)
+       {
+       dns_scan dnss;
+       for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+            rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+         if (rr->type == T_TLSA && rr->size > 3)
+           {
+           uint16_t payload_length = rr->size - 3;
+           uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
+
+           sp += sprintf(CS sp, "%d ", *p++); /* usage */
+           sp += sprintf(CS sp, "%d ", *p++); /* selector */
+           sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
+           while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+             sp += sprintf(CS sp, "%02x", *p++);
+
+           debug_printf(" %s\n", s);
+           }
+       }
+      return OK;
+      }
+    log_write(0, LOG_MAIN,
+      "DANE error: TLSA lookup for %s not DNSSEC", host->name);
+    /*FALLTRHOUGH*/
+
+  case DNS_NODATA:     /* no TLSA RR for this lookup */
+  case DNS_NOMATCH:    /* no records at all for this lookup */
+    return dane_required ? FAIL : FAIL_FORCED;
+
+  default:
+  case DNS_FAIL:
+    return dane_required ? FAIL : DEFER;
+  }
+}
+#endif /*SUPPORT_DANE*/
+
+
+
 /*************************************************
 **************************************************
 *             Stand-alone test program           *