Package: mimedefang
Version: 2.84-3
Severity: important
Tags: patch
Dear maintainer,
The relay_is_blacklisted_multi function seems to be unaware that
Net::DNS::Resolver->bgread (and bgbusy) may change their $handle
parameter in the caller's scope via the $_[1] = $newvalue idiom under
certain conditions.
Indeed, such change happens when the reply to the UDP query is
truncated, and Net::DNS::Resolver has to re-issue the query as a TCP
query. This happens frequently with the bl.spamcop.net blacklist.
The result of this is that the lookup for the domain in the
$sock_to_domain hash fails, as $sock is no longer the original $sock
that it was before the call to bgread.
If this happens, you'll find entries such as the following in the log:
Jun 28 00:20:33 lll mimedefang-multiplexor[23337]: 05RMKV8R016512: Worker 5
stderr: Use of uninitialized value $domain in hash element at
/usr/bin/mimedefang.pl line 1714.
Jun 28 00:20:33 lll mimedefang-multiplexor[23337]: 05RMKV8R016512: Worker 5
stderr: Use of uninitialized value in string eq at /usr/bin/mimedefang.pl line
1714.
Jun 28 00:20:33 lll mimedefang-multiplexor[23337]: 05RMKV8R016512: Worker 5
stderr: Use of uninitialized value $domain in hash element at
/usr/bin/mimedefang.pl line 1717.
==> in order to be correct, $domain = $sock_to_domain{$sock}; has to
happen *before* $res->bgread($sock). Likewise, $sel->remove($sock) also
needs to happen before bgread.
Another issue is that if a TCP fallback was indeed performed, then
bgread may still block even though its handle was in the select's
can_read set.
==> so if we want to make sure that we parallelize all queries, rather
than blocking on a single one of them, we need to test
$res->bgbusy($sock). And if indeed busy, add $sock again to the
sock_to_domain hash and $sel (as it may have been changed by
Net::DNS::Resolver's bgbusy).
The attached patch accomplishes this.
Thanks,
Alain
--- mimedefang-orig.pl 2020-07-03 20:04:25.418013798 +0200
+++ mimedefang.pl 2020-07-05 21:01:38.566585418 +0200
@@ -1697,9 +1697,14 @@
my @ready;
@ready = $sel->can_read($expire);
foreach $sock (@ready) {
- my $pack = $res->bgread($sock);
$sel->remove($sock);
$domain = $sock_to_domain{$sock};
+ if($res->bgbusy($sock)) {
+ $sock_to_domain{$sock}=$domain;
+ $sel->add($sock);
+ next;
+ }
+ my $pack = $res->bgread($sock);
undef($sock);
my($rr, $rcode);
$rcode = $pack->header->rcode;