+/* Return the cached result of a known-bad lookup, or -1.
+*/
+static int
+dns_fail_cache_hit(const uschar * name, int type)
+{
+uschar node_name[DNS_FAILTAG_MAX];
+tree_node * previous;
+expiring_data * e;
+int val, rc;
+
+dns_fail_tag(node_name, name, type);
+if (!(previous = tree_search(tree_dns_fails, node_name)))
+ return -1;
+
+e = previous->data.ptr;
+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",
+ 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" : "??",
+ rc == -1 ? " past valid time" : "");
+
+return rc;
+}
+
+
+
+/* Return the TTL suitable for an NXDOMAIN result, which is given
+in the SOA. We hope that one was returned in the lookup, and do not
+bother doing a separate lookup; if not found return a forever TTL.
+*/
+
+time_t
+dns_expire_from_soa(dns_answer * dnsa)
+{
+const HEADER * h = (const HEADER *)dnsa->answer;
+dns_scan dnss;
+
+/* This is really gross. The successful return value from res_search() is
+the packet length, which is stored in dnsa->answerlen. If we get a
+negative DNS reply then res_search() returns -1, which causes the bounds
+checks for name decompression to fail when it is treated as a packet
+length, which in turn causes the authority search to fail. The correct
+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
+the packet length if the packet header looks plausible. */
+
+if ( h->qr == 1 && h->opcode == QUERY && h->tc == 0
+ && (h->rcode == NOERROR || h->rcode == NXDOMAIN)
+ && (ntohs(h->qdcount) == 1 || f.running_in_test_harness)
+ && ntohs(h->ancount) == 0
+ && ntohs(h->nscount) >= 1)
+ dnsa->answerlen = sizeof(dnsa->answer);
+
+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;
+}
+