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;

Reply via email to