From 1d38781da934809e6ce0b8c3718c4b3bccdfe1d2 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 28 Dec 2022 19:39:06 +0000 Subject: [PATCH] Fix recursion on dns_again_means_nonexist. Bug 2911 --- doc/ChangeLog | 8 +++++ src/dns.c | 12 ++++++++ test/confs/2202 | 18 +++++++++-- test/scripts/2200-dnsdb/2202 | 8 +++++ test/stderr/2202 | 58 +++++++++++++++++++++++++++++++++++- test/stdout/2202 | 8 +++++ 6 files changed, 108 insertions(+), 4 deletions(-) --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -33,10 +33,18 @@ JH/14 Bug 2933: Fix regex substring matc JH/18 Fix a fencepost error in logging. Previously (since 4.92) when a log line was exactly sized compared to the log buffer, a crash occurred with the misleading message "bad memory reference; pool not found". Found and traced by Jasen Betts. +JH/19 Bug 2911: Fix a recursion in DNS lookups. Previously, if the main option + dns_again_means_nonexist included an element causing a DNS lookup which + iteslf returned DNS_AGAIN, unbounded recursion occurred. Possible results + included (though probably not limited to) a process crash from stack + memory limit, or from excessive open files. Replace this with a paniclog + whine (as this is likely a configuration error), and returning + DNS_NOMATCH. + Exim version 4.96 ----------------- --- a/src/dns.c +++ b/src/dns.c @@ -799,10 +799,11 @@ int dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type) { int rc; #ifndef STAND_ALONE const uschar * save_domain; +static BOOL try_again_recursion = FALSE; #endif /* DNS lookup failures of any kind are cached in a tree. This is mainly so that a timeout on one domain doesn't happen time and time again for messages that have many addresses in the same domain. We rely on the resolver and name server @@ -903,15 +904,26 @@ if (dnsa->answerlen < 0) switch (h_errno DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", name, dns_text_type(type)); /* Cut this out for various test programs */ #ifndef STAND_ALONE + if (try_again_recursion) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "dns_again_means_nonexist recursion seen for %s (assuming nonexist)", + name); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); + } + + try_again_recursion = TRUE; save_domain = deliver_domain; deliver_domain = string_copy(name); /* set $domain */ rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save_domain; + try_again_recursion = FALSE; + if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); return dns_fail_return(name, type, 0, DNS_AGAIN); }