SPFD(8p)

๐Ÿ“ฆ
version 3.20260331 ยท 4 versions
, in ubuntu stonking

๐Ÿ’– spfd โ€” bestie edition ๐Ÿ’–

okay so listen. this is the manual page for spfd from the 8p section (perl user-contributed docs, we love to see it). it's a whole server. it answers SPF questions. and honestly? iconic. let's get into it ๐Ÿซถ


โœจ NAME โœจ

spfd โ€” that's the Mail::SPF one โ€” a simple forking daemon to provide SPF query services. yeah. she forkin'. she servin'. periodt. ๐Ÿ’…

๐ŸŒธ VERSION ๐ŸŒธ

version 3.20260331

that's the one we're working with, no cap.

๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

here's how you summon her. three whole moods:

spfd --port|-p port [--set-user|-u uid|username] [--set-group|-g
gid|groupname] [OPTIONS]

spfd --socket|-s filename [--socket-user uid|username] [--socket-group
gid|groupname] [--socket-perms octal-perms] [--set-user|-u uid|username]
[--set-group|-g gid|groupname] [OPTIONS]

spfd --version|-V

spfd --help

TCP mood, UNIX-domain-socket mood, "who even are you" mood, and "help me" mood. all valid. ๐ŸŒˆ

๐Ÿ‘‘ DESCRIPTION ๐Ÿ‘‘

so spfd is a simple forking Sender Policy Framework (SPF) query server. she receives SPF requests and she answers them, over either a TCP/IP socket or a UNIX domain socket. if you want the whole SPF lore, it's at <https://tools.ietf.org/html/rfc7208>. go read it. i'll wait. ๐Ÿ“

the --port form means she's listening on a TCP/IP socket on the port you hand her. and babes, the default port is 5970. remember that number, you'll need it later. ๐Ÿ“Œ

the --socket form means she's listening on a UNIX domain socket created with the filename you specify. and here's the cute part โ€” you can give that socket its own user and group ownership with --socket-user and --socket-group, and its own filesystem permissions with --socket-perms. she's got a whole wardrobe. โœจ

and generally speaking, spfd can be told โ€” via --set-user and --set-group โ€” to drop root privileges and switch to a different user and group before she starts listening for requests. safety first, always. ๐Ÿ”

the --version form prints version info of spfd. the --help form prints usage info for spfd. that's it. that's the whole vibe check.


๐ŸŒŸ OPTIONS ๐ŸŒŸ

spfd takes any of the following OPTIONS:

  • --default-explanation string, --def-exp string โ€” use the given string as the default explanation when the authority domain doesn't have an explanation string of its own. backup explanation, bestie. ๐Ÿ’–
  • --hostname hostname โ€” use hostname as the host name of the local system instead of, like, auto-detecting it. you know better than the machine, sometimes. ๐Ÿ™ˆ
  • --debug โ€” print out debug info about spfd's operation, the incoming requests, and the responses she sends. she's telling you everything. gossip mode: ON. ๐Ÿ—ฃ๏ธ

๐ŸŽ€ REQUEST ๐ŸŽ€

a request is a series of lines, delimited by \x0A (LF) characters โ€” or whatever your system considers a newline, we don't judge.

each line has to be of the form option=value. here's what's supported:

  • versions โ€” a comma-separated list of SPF version numbers of SPF records that may be used. 1 means "v=spf1" records should be used. 2 means "spf2.0" records should be used. defaults to 1,2 โ€” i.e., use any SPF records that are available. records of a higher version are preferred. โœจ
  • scope โ€” the authorization scope of the identity that should be checked. defaults to 'mfrom'. the supported scope values are: 'helo', 'mfrom', 'pra'. for more info, see "new" in Mail::SPF::Request. ๐ŸŒธ
  • identity โ€” Required. the sender identity whose authorization should be checked. this is a domain name for the "helo" scope, and an e-mail address for the "mfrom" and "pra" scopes.
  • ip_address โ€” Required for checks with the "helo", "mfrom", and "pra" scopes. the IP address of the host claiming the identity that's being checked. can be either an IPv4 or an IPv6 address. an IPv4-mapped IPv6 address (e.g. '::ffff:192.168.0.1') is treated as an IPv4 address. noted. ๐Ÿ“
  • helo_identity โ€” the "HELO" SMTP transaction parameter, in the case that the main identity is of a scope other than "helo". this identity is then used merely for the expansion of "%{h}" macros during the policy evaluation of the main identity. if unspecified with a scope other than "helo", it defaults to "unknown". if the main identity is of the "helo" scope, this option is unused. so like, don't bother. ๐Ÿ’…

๐Ÿ’œ RESPONSE ๐Ÿ’œ

spfd responds to SPF requests with a similar series of lines of the form key=value. the most important response keys are:

  • result โ€” the result code of the SPF check:
    • pass โ€” the specified IP address is an authorized SMTP sender for the identity. ๐ŸŽ‰
    • fail โ€” the specified IP address is not an authorized SMTP sender for the identity. ๐Ÿ’€
    • softfail โ€” the specified IP address is not an authorized SMTP sender for the identity, however the authority domain is still testing out its SPF policy. it's giving "we're figuring it out".
    • neutral โ€” the identity's authority domain makes no assertion about the status of the IP address. she said nothing. ๐Ÿ˜
    • permerror โ€” a permanent error occurred while evaluating the authority domain's policy (e.g., a syntax error in the SPF record). manual intervention is required from the authority domain. not your problem, technically. ๐Ÿ˜ญ
    • temperror โ€” a temporary error occurred while evaluating the authority domain's policy (e.g., a DNS error). try again later. she's just having a moment.
    • none โ€” there is no applicable SPF policy for the identity domain. nothing to see here.
  • local_explanation โ€” a locally generated explanation of the SPF result.
  • authority_explanation โ€” the authority domain's explanation for the SPF result.
๐Ÿšจ bestie, READ THIS ONE: be aware that the authority domain may be a malicious party and thus the authority explanation should not be trusted blindly. see RFC 4408, 10.5, for a detailed discussion of this issue. do NOT trust the strangers on the internet with your whole heart. ๐Ÿ™…โ€โ™€๏ธ
  • received_spf_header โ€” an appropriate "Received-SPF" header field for the SPF result. ๐Ÿ“ฌ
  • spf_record โ€” the authority domain's SPF record that was used for the policy evaluation.

๐Ÿ”ฅ EXAMPLE ๐Ÿ”ฅ

you can test a running spfd using the "netcat" utility, like this (line breaks added for clarity, because we're being nice):

$ echo -e "identity=user@example.com\nip_address=1.2.3.4\n" \
    | nc localhost 5970
result=fail
local_explanation=example.com: Sender is not authorized by default to use
    'user@example.com' in 'mfrom' identity (mechanism '-all' matched)
authority_explanation=Rejected by SPF record.
received_spf_header=Received-SPF: fail (example.com: Sender is not
    authorized by default to use 'user@example.com' in 'mfrom' identity
    (mechanism '-all' matched)) receiver=localhost; identity=mfrom;
    envelope-from="user@example.com"; client-ip=1.2.3.4
spf_record=v=spf1 mx -all

look at her go. so clean. so functional. obsessed. ๐Ÿ˜Œโœจ


๐ŸŒˆ COMPATIBILITY ๐ŸŒˆ

spfd has undergone the following interface changes compared to earlier versions. lore drop time:

2.000

  • a new preferred request style has been introduced. instead of the old "sender" request option โ€” which was specific to the "MAIL FROM" SMTP identity โ€” a generic "identity" option should now be specified. plus, a "scope" option may be given to specify the identity's scope, otherwise a scope of "mfrom" is assumed. and the old "ip" and "helo" options have been replaced by the "ip_address" and "helo_identity" options, respectively. glow-up fr fr. ๐Ÿ’…

this is how legacy requests with the "mfrom" scope would translate to the new preferred request style:

 Legacy request style       | New request style
----------------------------+---------------------------------------
                            | scope=mfrom
 sender=<mfrom-identity>    | identity=<mfrom-identity>
 ip=<ip-address>            | ip_address=<ip-address>
 helo=<helo-identity>       | helo_identity=<helo-identity>

a new response style featuring new response values has also been introduced:

 Legacy response style      | New response style
----------------------------+---------------------------------------
 result=<result-code>       | result=<result-code>
 header_comment=<local-exp> | local_explanation=<local-exp>
 smtp_comment=<local-exp    | authority_explanation=<authority-exp>
   or authority-exp>        |
 spf_record=<spf-record>    | spf_record=<spf-record>
                            | received_spf_header=<header>

the legacy request style is deprecated but still supported for backwards compatibility. the legacy response values are still returned for backwards compatibility in addition to the new response values, but may be removed in the future. adjust your code to use the new request and response styles. don't get caught lacking when they delete it. ๐Ÿ‘€

  • the former "unknown" and "error" result codes have been renamed to "permerror" and "temperror", respectively, in order to comply with RFC 4408 terminology. rebranded. โœจ
  • in the case of an empty "MAIL FROM" SMTP transaction parameter ("MAIL FROM:<>"), the identity checked will be postmaster@helo name as specified in RFC 7208. ๐Ÿ“ฎ

๐Ÿงš SEE ALSO ๐Ÿงš

go read your homework, bestie. ๐Ÿ“š


๐Ÿ’ AUTHORS ๐Ÿ’

this version of spfd is a complete rewrite by Julian Mehnle <julian@mehnle.net>, based on an earlier version written by Meng Weng Wong <mengwong+spf@pobox.com>.

give them their flowers, they earned them. ๐Ÿ’๐Ÿซถ


and that's the whole page, babes. this was SPFD(8p), perl v5.40.1, dated 2026-04-05 โ€” she ate, she slayed, she forked. goodnight and go configure something responsibly. ๐Ÿ’…๐Ÿ’–๐ŸŒธ

๐Ÿ“ฆ where this page comes from