You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by mm...@apache.org on 2010/12/15 03:34:38 UTC
svn commit: r1049394 - in /spamassassin/trunk/lib/Mail/SpamAssassin:
Message/Metadata.pm Plugin/AskDNS.pm
Author: mmartinec
Date: Wed Dec 15 02:34:38 2010
New Revision: 1049394
URL: http://svn.apache.org/viewvc?rev=1049394&view=rev
Log:
Bug 6518: Plugin::AskDNS now also accepts DNS rcode as a filtering subrule, making it possible to distinguish a NXDOMAIN from other failures; provide tags FIRSTTRUSTEDREVIP and LASTEXTERNALREVIP (first approx)
Modified:
spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AskDNS.pm
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm?rev=1049394&r1=1049393&r2=1049394&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm Wed Dec 15 02:34:38 2010
@@ -55,6 +55,7 @@ use re 'taint';
use Mail::SpamAssassin;
use Mail::SpamAssassin::Constants qw(:sa);
+use Mail::SpamAssassin::Util qw(reverse_ip_address);
use Mail::SpamAssassin::Message::Metadata::Received;
use Mail::SpamAssassin::Logger;
@@ -83,6 +84,18 @@ sub extract {
# pre-chew Received headers
$self->parse_received_headers ($permsgstatus, $msg);
+ foreach ( [$self->{relays_external}, 'LASTEXTERNALREVIP'],
+ [$self->{relays_untrusted}, 'FIRSTTRUSTEDREVIP'] ) {
+ my($rly, $tag) = @$_;
+ if (ref $rly && @$rly) {
+ my($r0, $ip, $revip);
+ $r0 = $rly->[0];
+ $ip = $r0->{ip} if ref $r0 && !$r0->{ip_private};
+ $revip = reverse_ip_address($ip) if defined $ip && $ip ne '';
+ $permsgstatus->set_tag($tag,$revip) if defined $revip && $revip ne '';
+ }
+ }
+
$permsgstatus->{main}->call_plugins("extract_metadata",
{ msg => $msg, permsgstatus => $permsgstatus,
conf => $permsgstatus->{main}->{conf} });
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AskDNS.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AskDNS.pm?rev=1049394&r1=1049393&r2=1049394&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AskDNS.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AskDNS.pm Wed Dec 15 02:34:38 2010
@@ -22,16 +22,16 @@ AskDNS - form a DNS query using tag valu
=head1 SYNOPSIS
loadplugin Mail::SpamAssassin::Plugin::AskDNS
- askdns DKIMDOMAIN_IN_DWL _DKIMDOMAIN_._vouch.dwl.spamhaus.org TXT /\ball\b/
+ askdns D_IN_DWL _DKIMDOMAIN_._vouch.dwl.spamhaus.org TXT /\b(transaction|list|all)\b/
=head1 DESCRIPTION
-Using a DNS query template as specified in a parameter of the askdns rule,
-the plugin replaces tag names as found in the template with their values as
-soon as they become available, and launches DNS queries. When DNS responses
-trickle in, filters them according the requested DNS resource record type
-and optional subrule filtering expression, yielding a rule hit if a response
-meets filtering conditions.
+Using a DNS query template as specified in a parameter of a askdns rule,
+the plugin replaces tag names as found in the template with their values
+and launches DNS queries, as soon as tag values become available. When DNS
+responses trickle in, filters them according the requested DNS resource
+record type and optional subrule filtering expression, yielding a rule hit
+if a response meets filtering conditions.
=head1 USER SETTINGS
@@ -39,8 +39,10 @@ meets filtering conditions.
=item rbl_timeout t [t_min] [zone] (default: 15 3)
-The rbl_timeout setting is common to all DNS querying rules. It can
-specify a DNS query timeout globally, or individually for each zone.
+The rbl_timeout setting is common to all DNS querying rules (as implemented
+by other plugins). It can specify a DNS query timeout globally, or individually
+for each zone. When the zone parameter is specified, the settings affects DNS
+queries when their query domain equals the specified zone, or is its subdomain.
See the C<Mail::SpamAssassin::Conf> POD for details on C<rbl_timeout>.
=back
@@ -53,12 +55,12 @@ See the C<Mail::SpamAssassin::Conf> POD
A query template is a string which will be expanded to produce a domain name
to be used in a DNS query. The template may include SpamAssassin tag names,
-which will be replaced with their values to form the final query domain.
-The final query domain must adhere to rules governing DNS domains, i.e. must
-consist of fields each up to 63 characters long, delimited by dots. There
-may be a trailing dot at the end, but it is redundant / carries no semantics,
-because SpamAssassin uses a Net::DSN::Resolver::send method for querying
-DNS, which ignores any 'search' or 'domain' DNS resolver options.
+which will be replaced by their values to form a final query domain.
+The final query domain must adhere to rules governing DNS domains, i.e.
+must consist of fields each up to 63 characters long, delimited by dots.
+There may be a trailing dot at the end, but it is redundant / carries
+no semantics, because SpamAssassin uses a Net::DSN::Resolver::send method
+for querying DNS, which ignores any 'search' or 'domain' DNS resolver options.
Domain names in DNS queries are case-insensitive.
A tag name is a string of capital letters, preceded and followed by an
@@ -76,74 +78,93 @@ i.e. when the last of the awaited tag va
to set_tag() from some other plugin or elsewhere in the SpamAssassin code.
Launched queries from all askdns rules are grouped too according to a pair
-of: RR type and expanded query domain name. Even if there are multiple rules
-producing the same type/domain pair, only one DNS query is launched, and
-a reply to such query contributes to all the constituent rules.
+of: query type and expanded query domain name. Even if there are multiple
+rules producing the same type/domain pair, only one DNS query is launched,
+and a reply to such query contributes to all the constituent rules.
-A tag may produce none, one or multiple values. Askdns rules waiting for
+A tag may produce none, one or multiple values. Askdns rules awaiting for
a tag which never receives its value never result in a DNS query. Tags which
produce multiple values will result in multiple queries launched, each with
an expanded template using one of the tag values. An example is a DKIMDOMAIN
tag which yields a list of signing domains, one for each valid signature in
a message signed by more than one domain.
-When more than one tag name appears in a template, each potentially resulting
-in multiple values, a Cartesian product is formed, and each tuple results in
-a launch of one DNS query (duplicates excluded). For example, a query template
-_A_._B_.example.com where tag A is a list (11,22) and B is (xx,yy,zz),
-will result in queries: 11.xx.example.com, 22.xx.example.com,
-11.yy.example.com, 22.yy.example.com, 11.zz.example.com, 22.zz.example.com .
+When more than one distinct tag name appears in a template, each potentially
+resulting in multiple values, a Cartesian product is formed, and each tuple
+results in a launch of one DNS query (duplicates excluded). For example,
+a query template _A_._B_.example._A_.com where tag A is a list (11,22)
+and B is (xx,yy,zz), will result in queries: 11.xx.example.11.com,
+22.xx.example.22.com, 11.yy.example.11.com, 22.yy.example.22.com,
+11.zz.example.11.com, 22.zz.example.22.com .
The parameter following the query template is a DNS resource record (RR)
type. A DNS result may bring resource records of multiple types, but only
those resource records matching the type specified in a rule are considered,
returned resource records with non-matching types are ignored for this rule.
-Currently the RR type parameter also determines the DNS query types (not
-just the filter for the result), although in future similar queries could
+Currently the RR type parameter determines the DNS query type as well as a
+filter for the resulting RR types, although in future similar queries could
be combined, launching a query of type 'ANY'. Currently allowed RR types
are: A, AAAA, MX, TXT, PTR, NS, SOA, CNAME, HINFO, MINFO, WKS, SRV, SPF.
The last optional parameter of a rule is filtering expression, a.k.a. a
-subrule. Its function is much like the subrule in URIDNSBL plugin rules
-(like in the uridnssub rules), or in the check_rbl eval rules. The main
-difference is that with askdns rules there is no need to manually group
-rules according to their queried zone, as the grouping is automatic and
-duplicate queries are implicitly eliminated.
+subrule. Its function is much like the subrule in URIDNSBL plugin rules,
+or in the check_rbl eval rules. The main difference is that with askdns
+rules there is no need to manually group rules according to their queried
+zone, as the grouping is automatic and duplicate queries are implicitly
+eliminated.
The subrule filtering parameter can be: a plain string, a regular expression,
-a single numerical value. or a pair of numerical values. Absence of the
-filtering parameter implies no filtering, i.e. any positive DNS response
-of the requested RR type will result in a rule hit, regardless of the RR
-value returned with the response.
-
-When a plain string is used as a filter, it must match the response exactly.
-Typical use is an exact text string for TXT queries.
+a single numerical value or a pair of numerical values, or a list of rcodes
+(DNS status codes of a response). Absence of the filtering parameter implies
+no filtering, i.e. any positive DNS response (rcode=NOERROR) of the requested
+RR type will result in a rule hit, regardless of the RR value returned with
+the response.
+
+When a plain string is used as a filter, it must be enclosed in single or
+double quotes. For the rule to hit, the response must match the filtering
+string exactly, and a RR type of a response must match the query type.
+Typical use is an exact text string for TXT queries, or an exact quad-dotted
+IPv4 address. In case of a TXT or SPF resource record which can return
+multiple character-strings (as defined in Section 3.3 of [RFC1035]), these
+strings are concatenated with no delimiters before comparing the result
+to the filtering string. This follows requirements of several documents,
+such as RFC 5518, RFC 4408, RFC 4871, RFC 5617. Examples: "127.0.0.1",
+"transaction", 'list' .
A regular expression follows a familiar perl syntax like /.../ or m{...}
optionally followed by regexp flags (such as 'i' for case-insensitivity).
If a DNS response matches the requested RR type and the regular expression,
-the rule hits. Typical use: /^127\.0\.0\.\d+$/ or m{\bdial up\b}i .
+the rule hits. Examples: /^127\.0\.0\.\d+$/, m{\bdial up\b}i .
A single numerical value can be a decimal number, or a hexadecimal number
prefixed by 0x. Such numeric filtering expression is typically used with
-RR type-A DNS queries. The returned value (IP address) is masked with the
-specified filtering value, and the rule hits if the result is nonzero:
+RR type-A DNS queries. The returned value (IPv4 address) is masked with
+a specified filtering value, and the rule hits if the result is nonzero:
(r & n) != 0 . An example: 0x10 .
A pair of numerical values (each a decimal, hexadecimal or quad-dotted)
-delimited by a '-' specifies an IP address range, and a pair of values
-delimited by a '/' specifies an IP address followed by a bitmask. Again,
+delimited by a '-' specifies an IPv4 address range, and a pair of values
+delimited by a '/' specifies an IPv4 address followed by a bitmask. Again,
this type of filtering expression is primarily intended with RR type-A
-DNS queries. The rule hits if the returned IP address falls within the
-specified range: (r >= n1 && r <= n2), or masked with a bitmask matches
-the specified value: (r & m) == (n & m) . As a shorthand notation,
-a single quad-dotted value is equivalent to a n/32 form, i.e. it must
-match the returned value exactly with all its bits.
+DNS queries. The rule hits if the RR type matches, and the returned IP
+address falls within the specified range: (r >= n1 && r <= n2), or
+masked with a bitmask matches the specified value: (r & m) == (n & m) .
+
+As a shorthand notation, a single quad-dotted value is equivalent to
+a n-n form, i.e. it must match the returned value exactly with all its bits.
Some typical examples of a numeric filtering parameter are: 127.0.1.2,
127.0.1.20-127.0.1.39, 127.0.1.0/255.255.255.0, 0.0.0.16/0.0.0.16,
0x10/0x10, 16, 0x10 .
+Lastly, the filtering parameter can be a comma-separated list of DNS status
+codes (rcode), enclosed in square brackets. Rcodes can be represented either
+by their numeric decimal values (0=NOERROR, 3=NXDOMAIN, ...), or their names.
+See http://www.iana.org/assignments/dns-parameters for the list of names. When
+testing for a rcode where rcode is nonzero, a RR type parameter is ignored
+as a filter, as there is typically no answer section in a DNS reply when
+rcode indicates an error. Example: [NXDOMAIN], or [FormErr,ServFail,4,5] .
+
=back
=cut
@@ -158,9 +179,16 @@ use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Util;
use Mail::SpamAssassin::Logger;
-use vars qw(@ISA);
+use vars qw(@ISA %rcode_value);
@ISA = qw(Mail::SpamAssassin::Plugin);
+%rcode_value = ( # http://www.iana.org/assignments/dns-parameters
+ NOERROR => 0, FORMERR => 1, SERVFAIL => 2, NXDOMAIN => 3, NOTIMP => 4,
+ REFUSED => 5, YXDOMAIN => 6, YXRRSET => 7, NXRRSET => 8, NOTAUTH => 9,
+ NOTZONE => 10, BADVERS => 16, BADSIG => 16, BADKEY => 17, BADTIME => 18,
+ BADMODE => 19, BADNAME => 20, BADALG => 21, BADTRUNC => 22,
+);
+
sub new {
my($class,$sa_main) = @_;
@@ -175,8 +203,10 @@ sub new {
# ---------------------------------------------------------------------------
-# Accepts argument as a regular expression (including its m() operator or
-# equivalent perl syntaxes), or in one of the following forms: m, n1-n2,
+# Accepts argument as a string in single or double quotes, or as a regular
+# expression in // or m{} notation, or as a numerical value or a pair of
+# numerical values, or as a bracketed and comma-separated list of DNS rcode
+# names or their numerical codes. Recognized numerical forms are: m, n1-n2,
# or n/m, where n,n1,n2,m can be any of: decimal digits, 0x followed by
# up to 8 hexadecimal digits, or an IPv4 address in quad-dotted notation.
# The argument is checked for syntax, undef is returned on syntax errors.
@@ -187,20 +217,24 @@ sub new {
# then components are reassembled into a string delimited by '-' or '/'.
# As a special backward compatibility measure, a single quad-dot (with no
# second number) is converted into n-n, to distinguish it from a traditional
-# mask-only form.
+# mask-only form. A list or rcodes is returned as a hashref, where keys
+# represent specified numerical rcodes.
#
-# In practice, arguments like the following are anticipated:
+# Arguments like the following are anticipated:
+# "127.0.0.1", "some text", 'some "other" text',
+# /regexp/flags, m{regexp}flags,
# 127.0.1.2 (same as 127.0.1.2-127.0.1.2 or 127.0.1.2/255.255.255.255)
# 127.0.1.20-127.0.1.39 (= 0x7f000114-0x7f000127 or 2130706708-2130706727)
# 0.0.0.16/0.0.0.16 (same as 0x10/0x10 or 16/0x10 or 16/16)
# 16 (traditional style mask-only, same as 0x10)
+# [NXDOMAIN], [FormErr,ServFail,4,5]
#
sub parse_and_canonicalize_subtest {
my($subtest) = @_;
my $result;
local($1,$2,$3);
- if ($subtest =~ m{^ / (.+) / ([msixo]*) \z}xs) {
+ if ( $subtest =~ m{^ / (.+) / ([msixo]*) \z}xs) {
$result = $2 ne '' ? qr{(?$2)$1} : qr{$1};
} elsif ($subtest =~ m{^ m \s* \( (.+) \) ([msixo]*) \z}xs) {
$result = $2 ne '' ? qr{(?$2)$1} : qr{$1};
@@ -212,6 +246,16 @@ sub parse_and_canonicalize_subtest {
$result = $2 ne '' ? qr{(?$2)$1} : qr{$1};
} elsif ($subtest =~ m{^ m \s* (\S) (.+) \1 ([msixo]*) \z}xs) {
$result = $2 ne '' ? qr{(?$2)$1} : qr{$1};
+ } elsif ($subtest =~ m{^ (["']) (.*) \1 \z}xs) { # quoted string
+ $result = $2;
+ } elsif ($subtest =~ m{^ \[ ( (?:[A-Z]+|\d+)
+ (?: \s* , \s* (?:[A-Z]+|\d+) )* ) \] \z}xis) {
+ # a comma-separated list of rcode names or their decimal values
+ my @rcodes = split(/\s*,\s*/, uc $1);
+ for (@rcodes) { $_ = $rcode_value{$_} if exists $rcode_value{$_} }
+ return undef if grep(!/^\d+\z/, @rcodes);
+ # a hashref indicates a list of DNS rcodes (stored as hash keys)
+ $result = { map( ($_,1), @rcodes) };
} elsif ($subtest =~ m{^ ([^/-]+) (?: ([/-]) (.+) )? \z}xs) {
my($n1,$delim,$n2) = ($1,$2,$3);
my $any_quad_dot;
@@ -241,7 +285,7 @@ sub set_config {
push(@cmds, {
setting => 'askdns',
- is_priv => 1,
+ is_admin => 1,
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
code => sub {
my($self, $key, $value, $line) = @_;
@@ -257,8 +301,7 @@ sub set_config {
my($rulename,$query_template,$query_type,$subtest) = ($1,$2,$3,$4);
$query_type = 'A' if !defined $query_type;
$query_type = uc $query_type;
- $subtest = '' if !defined $subtest;
- if ($subtest ne '') {
+ if (defined $subtest) {
$subtest = parse_and_canonicalize_subtest($subtest);
defined $subtest or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
@@ -432,54 +475,79 @@ OUTER:
sub process_response_packet {
my($self, $pms, $packet, $dnskey, $query_type, $query_domain) = @_;
my $conf = $pms->{conf};
+ my %rulenames_hit;
# map a dnskey back to info on queries which caused this DNS lookup
my $queries_ref = $pms->{askdns_map_dnskey_to_rules}{$dnskey};
- my %rulenames_hit;
- my @answer;
- @answer = $packet->answer if $packet;
+ my($header, @question, @answer, $qtype, $rcode);
+ if ($packet) {
+ @answer = $packet->answer;
+ $header = $packet->header;
+ @question = $packet->question;
+ $qtype = uc $question[0]->qtype if @question;
+ my $query_str = join(', ',map($_->qtype.' '.$_->qname, @question));
+ $rcode = uc $header->rcode if $header; # 'NOERROR', 'NXDOMAIN', ...
+ dbg("askdns: answer received, rcode %s, query %s, answer has %d records",
+ $rcode, $query_str, scalar @answer);
+ if (defined $rcode && exists $rcode_value{$rcode}) {
+ # Net::DNS return a rcode name for codes it knows about,
+ # and returns a number for the rest; we deal with numbers from here on
+ $rcode = $rcode_value{$rcode} if exists $rcode_value{$rcode};
+ }
+ }
+ if (!@answer) {
+ # a trick to make the following loop run at least once, so that we can
+ # evaluate also rules which only care for rcode status
+ @answer = ( undef );
+ }
for my $rr (@answer) {
- my $rr_type = $rr->type;
- $rr_type = '' if !defined $rr_type;
- $rr_type = uc $rr_type;
- my $rr_rdatastr;
- my $rdatanum;
- if ($rr_type eq 'TXT' || $rr_type eq 'SPF') {
- # RFC 5518: If the RDATA in the TXT record contains multiple
- # character-strings (as defined in Section 3.3 of [RFC1035]),
- # the code handling that reply from DNS MUST assemble all of these
- # marshaled text blocks into a single one before any syntactical
- # verification takes place.
- # The same goes for RFC 4408 (SPF), RFC 4871 (DKIM), RFC 5617 (ADSP) ...
- $rr_rdatastr = join('', $rr->char_str_list); # as per RFC 5518
+ my($rr_rdatastr, $rdatanum, $rr_type);
+ if (!$rr) {
+ # special case, no answer records, only rcode can be tested
} else {
- $rr_rdatastr = $rr->rdatastr;
- if ($rr_type eq 'A' &&
- $rr_rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) {
- $rdatanum = Mail::SpamAssassin::Util::my_inet_aton($rr_rdatastr);
+ $rr_type = uc $rr->type;
+ if ($rr_type eq 'TXT' || $rr_type eq 'SPF') {
+ # RFC 5518: If the RDATA in the TXT record contains multiple
+ # character-strings (as defined in Section 3.3 of [RFC1035]),
+ # the code handling that reply from DNS MUST assemble all of these
+ # marshaled text blocks into a single one before any syntactical
+ # verification takes place.
+ # The same goes for RFC 4408 (SPF), RFC 4871 (DKIM), RFC 5617 (ADSP) ...
+ $rr_rdatastr = join('', $rr->char_str_list); # as per RFC 5518
+ } else {
+ $rr_rdatastr = $rr->rdatastr;
+ if ($rr_type eq 'A' &&
+ $rr_rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) {
+ $rdatanum = Mail::SpamAssassin::Util::my_inet_aton($rr_rdatastr);
+ }
}
+ # decode DNS presentation format as returned by Net::DNS
+ $rr_rdatastr =~ s/\\([0-9]{3}|.)/length($1)==1 ? $1 : chr($1)/gse;
+ # dbg("askdns: received rr type %s, data: %s", $rr_type, $rr_rdatastr);
}
- # decode DNS presentation format as returned by Net::DNS
- $rr_rdatastr =~ s/\\([0-9]{3}|.)/length($1)==1 ? $1 : chr($1)/gse;
- # dbg("askdns: received rr type %s, data: %s", $rr_type, $rr_rdatastr);
my $j = 0;
for my $q_tuple (!ref $queries_ref ? () : @$queries_ref) {
next if !$q_tuple;
-
my($query_type, $rules) = @$q_tuple;
- next if $rr_type ne $query_type;
- $pms->{askdns_map_dnskey_to_rules}{$dnskey}[$j++] = undef; # mark it done
+ next if $query_type ne $qtype;
+
+ # mark rule as done
+ $pms->{askdns_map_dnskey_to_rules}{$dnskey}[$j++] = undef;
- local($1,$2,$3);
while (my($rulename,$subtest) = each %$rules) {
my $match;
- if (!defined $subtest || $subtest eq '') {
- $match = 1; # any response of the requested RR type matches
- } elsif (ref $subtest eq 'Regexp') {
+ local($1,$2,$3);
+ if (ref $subtest eq 'HASH') { # a list of DNS rcodes (as hash keys)
+ $match = 1 if $subtest->{$rcode};
+ } elsif ($rcode != 0 || $rr_type ne $query_type) {
+ # skip remaining tests on DNS error or wrong RR type
+ } elsif (!defined $subtest) {
+ $match = 1; # any valid response of the requested RR type matches
+ } elsif (ref $subtest eq 'Regexp') { # a regular expression
$match = 1 if $rr_rdatastr =~ $subtest;
- } elsif ($rr_rdatastr eq $subtest) {
+ } elsif ($rr_rdatastr eq $subtest) { # exact equality
$match = 1;
} elsif (defined $rdatanum &&
$subtest =~ m{^ (\d+) (?: ([/-]) (\d+) )? \z}x) {
@@ -491,7 +559,7 @@ sub process_response_packet {
: 0;
}
if ($match) {
- $self->askdns_hit($pms,$query_domain,$rr_type,$rr_rdatastr,$rulename);
+ $self->askdns_hit($pms,$query_domain,$qtype,$rr_rdatastr,$rulename);
$rulenames_hit{$rulename} = 1;
}
}
@@ -502,15 +570,16 @@ sub process_response_packet {
}
sub askdns_hit {
- my($self, $pms, $query_domain, $rr_type, $rr_rdatastr, $rulename) = @_;
+ my($self, $pms, $query_domain, $qtype, $rr_rdatastr, $rulename) = @_;
+ $rr_rdatastr = '' if !defined $rr_rdatastr; # e.g. with rules testing rcode
dbg('askdns: domain "%s" listed (%s): %s',
$query_domain, $rulename, $rr_rdatastr);
# only the first hit will show in the test log report, even if
# an answer section matches more than once - got_hit() handles this
$pms->clear_test_state;
- $pms->test_log(sprintf("%s %s:%s", $query_domain,$rr_type,$rr_rdatastr));
+ $pms->test_log(sprintf("%s %s:%s", $query_domain,$qtype,$rr_rdatastr));
$pms->got_hit($rulename, 'ASKDNS: ', ruletype => 'askdns'); # score=>$score
}