Mail::S...Iterator(3)
π Mail::SPF::Iterator π
okay bestie, welcome to the manual page for Mail::SPF::Iterator β it's part of the User Contributed Perl Documentation and it's genuinely one of those modules that makes you go "wait, that's actually so clever". it does iterative SPF lookup, and by the end of this page you're gonna understand it fr fr. π¦
β¨ NAME β¨
Mail::SPF::Iterator β iterative SPF lookup. that's it, that's the whole vibe. π
πΈ SYNOPSIS πΈ
no notes, just look at it:
use Net::DNS;
use Mail::SPF::Iterator;
use Mail::SPF::Iterator Debug =>1; # enable debugging
my $spf = Mail::SPF::Iterator->new(
$ip, # IP4|IP6 of client
$mailfrom, # from MAIL FROM:
$helo, # from HELO|EHLO
$myname, # optional: my hostname
{
default_spf => 'mx/24 ?all', # in case no record was found in DNS
pass_all => SPF_SoftFail, # treat records like '+all' as error
# rfc4408 => 1, # for compatibility only
}
);
# could be other resolvers too
my $resolver = Net::DNS::Resolver->new;
### with nonblocking, but still in loop
### (callbacks are preferred with non-blocking)
my ($result,@ans) = $spf->next; # initial query
while ( ! $result ) {
my @query = @ans;
die "no queries" if ! @query;
for my $q (@query) {
# resolve query
my $socket = $resolver->bgsend( $q );
... wait...
my $answer = $resolver->bgread($socket);
($result,@ans) = $spf->next(
$answer # valid answer
|| [ $q, $resolver->errorstring ] # or DNS problem
);
last if $result; # got final result
last if @ans; # got more DNS queries
}
}
### OR with blocking:
### ($result,@ans) = $spf->lookup_blocking( undef,$resolver );
### print mailheader
print "Received-SPF: ".$spf->mailheader;
# $result = Fail|Pass|...
# $ans[0] = comment for Received-SPF
# $ans[1] = %hash with infos for Received-SPF
# $ans[2] = explanation in case of Fail
like, the loop is doing the most and honestly it ate. π₯
π« DESCRIPTION π«
so this module gives you iterative resolving of SPF records. and here's the tea: unlike Mail::SPF, which does blocking DNS lookups (imagine waiting around, not very main character of it), this module just hands you the DNS queries and later expects the responses back. you're in charge, you're driving, you're the main character. π
the lookup of the DNS records happens outside of the module, and it can totally be done in an event driven way. you can also run many parallel SPF checks in parallel without needing multiple threads or processes. no cap, that's the whole point. β¨
this module can also make use of SenderID records for checking the "mfrom" part, but it will prefer SPF. it will only use DNS TXT records for looking up SPF policies unless compatibility with RFC 4408 is explicitly enabled.
see RFC 7208 (old RFC 4408) for SPF and RFC 4406 for SenderID. iykyk. π
π METHODS π
new( IP, MAILFROM, HELO, [ MYNAME ], [ \%OPT ] )
constructs a shiny new Mail::SPF::Iterator object, which maintains the state between the steps of the iteration. important: for each new SPF check you gotta create a new object. no recycling bestie, it doesn't work like that. π
IPβ the IP if the client as string (IP4orIP6).MAILFROMβ theuser@domainpart from the MAIL FROM handshake, e.g.'<','>'and any parameters removed. if only'<>'was given (like in bounces) the value is empty.HELOβ the string send within theHELO|EHLOdialog which should be a domain according to the RFC but often is not. π (we've all seen it)MYNAMEβ the name of the local host. it's only used if required by macros inside the SPF record.OPTβ used for additional arguments. currently:default_spfcan be used to set a default SPF record in case no SPF/TXT records are returned from DNS (useful values are for example'mx ?all'or'mx/24 ?all').rfc4408can be set to true in case stricter compatibility is needed with RFC 4408 instead of RFC 7208, i.e. lookup of DNS SPF records, no limit on void DNS lookups etc.pass_allcan be set to the expected outcome in case a SPF policy gets found, which would pass everything. such policies are common used domains used by spammers. π©
returns the new object. π
next([ ANSWER ])
"next" will be initially called with no arguments to get initial DNS queries, and then will be called with the DNS answers. it's giving "round trip" but make it DNS. βοΈ
ANSWER is either:
- a DNS packet with the response to a former query, or
[ QUERY, REASON ]on failures, whereQUERYis the DNS packet containing the failed query andREASONthe reason why the query failed (likeTIMEOUT). π΅
if a final result was achieved it will return ( RESULT, COMMENT, HASH, EXPLAIN ):
RESULTis the result, e.g."Fail","Pass",β¦.COMMENTis the comment for the Received-SPF header.HASHcontains information about problem, mechanism for the Received-SPF header.EXPLAINwill be set to the explain string ifRESULTisFail.
the following fields are in HASH:
client-ipβ the clients IP addressheloβ the helo string from the clientidentityβ how the identity of the sender was given, i.e. either"mailfrom"or"helo".envelope-fromβ the sender, either based on the mail from in the SMTP dialog (with"identity"being"mailfrom") or the HELO/EHLO.
if no final result was achieved yet it will either return:
(undef,@QUERIES)with a list of new queries to continue,('')in case theANSWERproduced an error but got ignored, because there are other queries open, or()in case theANSWERwas ignored because it did not match any open queries.
three different flavors of "not yet", pick your fighter. π
mailheader
creates value for Received-SPF header based on the final answer from next(). returns header as string (one line, no folding) or undef, if no final result was found. this creates only the value, not the 'Received-SPF' prefix. so don't forget to glue that on yourself. π
result
returns ( RESULT, COMMENT, HASH, EXPLAIN ) like the final "next" does, or () if the final result wasn't found yet.
heads up bestie: if the SPF record had an explain modifier, which needed DNS lookups to resolve, this method might return the result (although with incomplete explain) before "next" does it. so likeβ¦ read the room before you trust it fully. π
explain_default ( [ EXPLAIN ] )
sets default explanation string if EXPLAIN is given. if it's called as a class method the default explanation string for the class will be set, otherwise the default explanation string for the object. so it's giving "depends who's asking". π«Ά
returns the current default explanation string for the object, or β if non given, or if called as a class method β the default explanation string for the class.
lookup_blocking ( [ TIMEOUT, RESOLVER ] )
quick way to get the SPF status. this will simply call "next" until it gets a final result. the "i don't feel like managing state today" option. π
TIMEOUTlimits the lookup time and defaults to20.RESOLVERis aNet::DNS::Resolverobject (or similar) and defaults toNet::DNS::Resolver->new.
returns ( RESULT, COMMENT, HASH ) like the final "next" does.
warning bestie, red flag: this is not the preferred way to use this module, because it's blocking, so no lookups can be done in parallel in a single process/thread. it works, but it's giving "i chose violence against my own throughput". π
π¦ EXPORTED SYMBOLS π¦
for convenience the constants SPF_TempError, SPF_PermError, SPF_Pass, SPF_Fail, SPF_SoftFail, SPF_Neutral, SPF_None are by default exported, which have the values "TempError", "PermError" β¦ you get the pattern, slay. β¨
π Arguments to use/import π
the "SPF_*" symbols are available for import and are exported if no arguments are given to "use" or "import". same effect with adding ":DEFAULT" as an argument. additionally the following arguments are supported:
DebugFunc => \&coderefβ sets a custom debug function, which just takes on argument. if given it will be called on all debug messages when debugging is active. this function takes as the only argument the debug message.Debug => 1|0β switches debugging on/off. π
π AUTHOR π
Steffen Ullrich <sullr@cpan.org>
π COPYRIGHT π
copyright by Steffen Ullrich.
this module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
and that's the whole page bestie, you're officially educated π
β¨ this beauty is running under perl v5.42.0, dated 2025-07-25, and it lives at Mail::S...Iterator(3). go forth and resolve iteratively, you absolute icon π«Άπ
π¦ where this page comes from
- fedora 43: perl-Mail-SPF-Iterator 1.121-3.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms