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 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 underscore character. This syntax mirrors the add_header
setting, except that tags cannot have parameters in parenthesis when
used in askdns templates (exceptions found below). Tag names may appear
anywhere in the template - each queried DNS zone prescribes how a query
should be formed.
Special supported tag HEADER() can be used to query any
header content, using same header names/modifiers that as header rules
support. For example _HEADER(Reply-To:addr:domain)_ can be used to query
the trimmed domain part of Reply-To address. See
Mail::SpamAssassin::Conf documentation about header rules.
A query template may contain any number of tag names including
none, although in the most common anticipated scenario exactly one tag
name would appear in each askdns rule. Specified tag names are
considered dependencies. Askdns rules with dependencies on the same set
of tags are grouped, and all queries in a group are launched as soon as
all their dependencies are met, i.e. when the last of the awaited tag
values becomes available by a call 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: query type and an 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
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 signed message.
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 .
A parameter rr_type following the query template is a
comma-separated list of expected DNS resource record (RR) types. Missing
rr_type parameter implies an 'A'. A DNS result may bring resource
records of multiple types, but only resource records of a type found in
the rr_type parameter list are considered, other resource records found
in the answer section of a DNS reply are ignored for this rule. A value
ANY in the rr_type parameter list matches any resource record type. An
empty DNS answer section does not match ANY.
The rr_type parameter not only provides a filter for RR types
found in the DNS answer, but also determines the DNS query type. If only
a single RR type is specified in the parameter (e.g. TXT), than this is
also the RR type of a query. When more than one RR type is specified
(e.g. A, AAAA, TXT) or if ANY is specified, then the DNS query type will
be ANY and the rr_type parameter will only act as a filter on a
result.
Currently recognized RR types in the rr_type parameter are:
ANY, A, AAAA, MX, TXT, PTR, NAPTR, NS, SOA, CERT, CNAME, DNAME, DHCID,
HINFO, MINFO, RP, HIP, IPSECKEY, KX, LOC, SRV, SSHFP, SPF.
https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
The last optional parameter of a rule is a filtering
expression, a.k.a. a 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, 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 7208, RFC 4871, RFC 5617.
Examples of a plain text filtering parameter: "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. 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 (an IPv4
address) is masked with a specified filtering value and tested to fall
within a 127.0.0.0/8 network range - the rule hits if the result is
nonzero: ((r & n) != 0) && ((r & 0xff000000) ==
0x7f000000). An example: 0x10 .
A pair of numerical values (each a decimal, hexadecimal or
quad-dotted) 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 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
https://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] .