SPFD(1)

πŸ“¦
version 3.20260331 Β· 4 versions
, in fedora 44

πŸ’– NAME πŸ’–

okay so spfd β€” short for (Mail::SPF)'s Simple forking daemon β€” is literally just a little server that provides SPF query services. she forking, she answering, she slaying πŸ’…

This page lives in the User Contributed Perl Documentation section of the manual, bestie. πŸŽ€


✨ VERSION ✨

version 3.20260331 β€” yes that's the real one, no cap 🫢


🌸 SYNOPSIS 🌸

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

πŸ¦‹ DESCRIPTION πŸ¦‹

spfd is a simple forking Sender Policy Framework (SPF) query server. she receives SPF requests and answers them, either over a TCP/IP socket or a UNIX domain socket. if you wanna know more about SPF itself, the whole vibe is documented at <https://tools.ietf.org/html/rfc7208>. πŸ“¬

The --port form has her listening on a TCP/IP socket on the port you name. the default port is 5970. iconic number honestly.

The --socket form has her listening on a UNIX domain socket that gets created with the filename you specify. you can hand that socket its own user and group ownership with the --socket-user and --socket-group options, and give it specific filesystem permissions with the --socket-perms option. so many little knobs, we love a configurable queen πŸ‘‘

Generally, you can tell spfd with the --set-user and --set-group options to drop root privileges and switch over to another user and group before she starts listening for requests. safety first, periodt.

The --version form prints version information of spfd. the --help form prints usage information for spfd. short and sweet. πŸ’–


πŸ’… OPTIONS πŸ’…

spfd takes any of the following OPTIONS:

  • --default-explanation string, --def-exp string β€” use the specified string as the default explanation if the authority domain doesn't specify an explanation string of its own. backup explanation, we stan a prepared icon.
  • --hostname hostname β€” use hostname as the host name of the local system instead of auto-detecting it. no guessing games, she said.
  • --debug β€” print out debug information about spfd's operation, incoming requests, and the responses sent. πŸ›βœ¨ (the good kind of bug energy)

πŸ”₯ REQUEST πŸ”₯

A request is a series of lines delimited by \x0A (LF) characters (or whatever your system considers a newline, iykyk). each line has to be of the form option=value, and these are the options that are supported:

  • versions β€” a comma-separated list of SPF version numbers of SPF records that may be used. 1 means that "v=spf1" records should be used. 2 means that "spf2.0" records should be used. defaults to 1,2, i.e., uses any SPF records that are available. records of a higher version are preferred. she's got taste, she likes the newer stuff first πŸ’…
  • scope β€” the authorization scope of the identity that should be checked. defaults to 'mfrom'. the following scope values are supported: 'helo', 'mfrom', 'pra'. see "new" in Mail::SPF::Request for more information.
  • 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. you literally cannot skip this one bestie.
  • ip_address β€” Required for checks with the "helo", "mfrom", and "pra" scopes. the IP address of the host claiming the identity that is 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. she sees through the disguise fr fr πŸ•΅οΈβ€β™€οΈ
  • 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", defaults to "unknown". if the main identity is of the "helo" scope, this option is unused. side character energy, but a necessary one 🫢

🌈 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. still figuring herself out, relatable.
    • neutral β€” the identity's authority domain makes no assertion about the status of the IP address. no thoughts, head empty.
    • 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. somebody go fix that. 😭
    • temperror β€” a temporary error occurred while evaluating the authority domain's policy (e.g., a DNS error). try again later. it's not you, it's the DNS.
    • none β€” there is no applicable SPF policy for the identity domain.
  • local_explanation β€” a locally generated explanation of the SPF result.
  • authority_explanation β€” the authority domain's explanation for the SPF result.
🚨 bestie listen up: 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 let a stranger on the internet explain herself to you without receipts. πŸ™ˆ
  • 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 πŸ“

A running spfd could be tested using the "netcat" utility like this (line breaks added for clarity):

$ 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

🧚 COMPATIBILITY 🧚

spfd has undergone the following interface changes compared to earlier versions:

2.000

  • A new preferred request style has been introduced. Instead of the old "sender" request option, which is specific to the "MAIL FROM" SMTP identity, a generic "identity" option should now be specified. In addition, a "scope" option may be given to specify the identity's scope, otherwise a scope of "mfrom" is assumed. The old "ip" and "helo" options have been replaced by the "ip_address" and "helo_identity" options, respectively.

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. πŸ‘‘ glow-up complete, but the old fits still technically fit.

  • The former "unknown" and "error" result codes have been renamed to "permerror" and "temperror", respectively, in order to comply with RFC 4408 terminology. rebranding era πŸ’…
  • 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 πŸ’œ


πŸ‘‘ 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>. two icons, one legacy. absolute main character energy from both of them, fr fr 🌟


and that's the whole page bestie!! this beauty shipped as part of perl v5.42.1 and the date on the tin is 2026-04-23 β€” go forth and query some SPF records responsibly, mwah πŸ’–πŸ’…βœ¨

πŸ“¦ where this page comes from