SPF| SRS
Navigation
› Home › Status › Download › Documentation › Support › FAQ
Validation
Validate the XHTML and CSS of this page.

Introduction

This documentation covers version 1.0.3 of libspf2. It does not yet cover all public functions; however, it contains more than enough information to effectively use this library. Read the header files for more information. Please submit corrections or requests for missing information to libspf2 [ta] rt.anarres.org.

Recipes

To init the library:
  1. Create a handle to the SPF library. (SPF_create_config)
  2. (optional) Set the debug level in the handle.
  3. Set the local hostname in the handle. SPF_set_rec_dom
  4. Compile and set the local policy. (SPF_init_c_results, SPF_compile_local_policy, SPF_set_local_policy)
  5. Compile and set the default explanation. (SPF_init_c_results, SPF_compile_exp, SPF_set_exp)
  6. Create a noncaching resolver. (SPF_dns_create_config_resolv)
  7. Create a caching resolver which uses the non-caching resolver. (SPF_dns_create_config_cache)
To perform a test:
  1. Set the client address in the handle. (SPF_set_ipv4, SPF_set_ipv4_str, SPF_set_ipv6, SPF_set_ipv6_str)
  2. Set the helo domain in the handle. (SPF_set_helo_dom)
  3. Set the mail from address in the handle. (SPF_set_env_from)
  4. Query the handle for a result. (SPF_result)
  5. Now the handle and resolvers may be reused.
To clean up:
  1. Free the caching resolver. (SPF_dns_destroy_config_cache)
  2. Free the noncaching resolver. (SPF_dns_destroy_config_resolv)
  3. Free the local policy and explanation. (SPF_free_c_results)
  4. Free the SPF library handle. (SPF_destroy_config)
  5. Call SPF_destroy_default_config
Notes on threads:

Headers

#include <spf2/spf.h>

The main spf2 header file must be included.

#include <spf2/spf_dns_resolv.h>

Include this for definitions relating to the basic SPF resolver.

#include <spf2/spf_dns_cache.h>

Include this for definitions relating to the caching SPF resolver.

Types

SPF_err_t

An error code: an integer. See 'Error Codes' below.

SPF_config_t

A handle to an instantiation of the SPF library.

SPF_dns_config_t

A handle to an SPF DNS resolver.

SPF_c_results_t

A struct which holds the output from the SPF bytecode compiler.

SPF_output_t

A response from the SPF engine. This is a struct containing the following fields:

  • result: One of SPF_RESULT_PASS, SPF_RESULT_FAIL, SPF_RESULT_SOFTFAIL, SPF_RESULT_NEUTRAL, SPF_RESULT_UNKNOWN, SPF_RESULT_ERROR, SPF_RESULT_NONE.
  • received_spf: A header to be inserted into the checked mail.
  • smtp_comment: An SMTP error message XXX(?)
  • err: The error message from the SPF library (justifying a failure?)

Functions

SPF_config_t SPF_create_config()

Construct a new handle for accessing the SPF library.

Return values:

  • (SPF_config_t)0: If construction of the handle fails.
  • (an opaque handle): If construction of the handle succeeds.
SPF_config_t SPF_dup_config(SPF_config_t handle)

Duplicate a handle for accessing the SPF library.

Return values:

  • (SPF_config_t)0: If construction of the handle fails.
  • (an opaque handle): If construction of the handle succeeds.
void SPF_destroy_config(SPF_config_t handle)

Destroy a handle for accessing the SPF library.

void SPF_set_debug(SPF_config_t handle, int debug)

Set the debugging level for an SPF handle. The SPF library allows a certain amount of debugging output to be generated for help in determining why things succeeded or failed. Currently, only the following debug levels are implemented:

  • 0: Be completely silent, no debugging information will be generated.
  • 1: Moderate amount of debugging information.
  • 2: Include some detailed information about the DNS lookups. Usually this is not needed.

SPF_dns_config_t SPF_dns_create_config_resolv(SPF_dns_config_t layer_below, int debug)

Create a handle for accessing a non-caching DNS resolver. The value passed for layer_below will be 0.

void SPF_dns_destroy_config_resolv(SPF_dns_config_t handle)

Destroy a handle for accessing a non-caching DNS resolver.

SPF_dns_config_t SPF_dns_create_config_cache(SPF_dns_config_t layer_below, int debug)

Create a handle for accessing a caching DNS resolver. The value passed for layer_below will be a handle to a non-caching DNS resolver created using SPF_dns_create_config_resolv.

void SPF_dns_destroy_config_cache(SPF_dns_config_t handle)

Destroy a handle for accessing a caching DNS resolver.

void SPF_init_c_results(SPF_c_results_t *data)

Initialize the given compiler results structure so that it may receive the output of a compilation. This must be freed when it is no longer to be used.

void SPF_free_c_results(SPF_c_results_t *data)

Frees the bytecode contained in the SPF_c_results_t structure.

SPF_err_t SPF_compile_local_policy(SPF_config_t handle, const char *spf_record, int use_default_whitelist, SPF_c_results_t *c_results)

Several of the SPF specifications support a "local policy" option. This is both very important, and not particularly obvious how it works.

Email may come from many sources, sometimes these sources are not direct, and not all of these indirect sources correctly rewrite the envelope-from to specify the new domain that is resending the email. This can happen on incorrectly configured mailing lists, or from people who have set up unix-like .forward files.

Often, you want to accept these emails, even if they would technically fail the SPF check. So, you can set up a "local policy" that lists these sources of known-ok emails. If a local policy is set, it will allow you to whitelist these sources. There is a default globally maintained whitelist of known trusted email forwarders that is generally a good idea to use.

SPF checks that pass due to local policies will be noted in the messages generated from SPF_result(). As such, it is best if the local policy option is check only right before the SPF check is sure to fail. SPF records that say that a domain never sends email should not do any checking of the local policy.

The exact spot in the evaluation of the SPF record was defined in a message sent to the SPF-devel mailing list. It said in part:

	Philip Gladstone says:
	Message-ID: <400B56AB.30702@gladstonefamily.net>
	Date: Sun, 18 Jan 2004 23:01:47 -0500

	I think that the localpolicy should only be inserted if the
	final mechanism is '-all', and it should be inserted after
	the last mechanism which is not '-'.

	Thus for the case of 'v=spf1 +a +mx -all', this would be
	interpreted as 'v=spf1 +a +mx +localpolicy -all'. Whereas
	'v=spf1 -all' would remain the same (no non-'-'
	mechanism). 'v=spf1 +a +mx -exists:%stuff -all' would
	become 'v=spf1 +a +mx +localpolicy -exists:%stuff -all'.
	This local policy string can be any string with macro variables
	included. It is first byte compiled, and then the result can be
	set in the configuration.

void SPF_set_local_policy(SPF_config_t handle, SPF_c_results_t c_results)

SPF_err_t SPF_compile_exp(SPF_config_t handle, const char *exp, SPF_c_results_t *c_results)

When the SPF check fails, an "explanation" string is generated for use by the MTA during the 4xx or 5xx reject code.

This explanation string can be any string with macro variables included. It is first byte compiled, and then the result can be set in the configuration. If an SPF record does not use the "exp=" modifier to specify a more appropriate explanation string, this default explanation string will be used.

int SPF_set_exp(SPF_config_t handle, SPF_c_results_t c_results)

Set the explanation string for the given SPF handle.

int SPF_set_rec_dom(SPF_config_t handle, const char *receiving_hostname)

Set the local hostname. Part of the Received-SPF: email header requires the domain name of the receiving MTA.

int SPF_set_ipv4(SPF_config_t handle, struct in_addr ipv4)

Set the IPv4 address of the SMTP client.

int SPF_set_ipv4_str(SPF_config_t handle, const char *ipv4_address)

Set the IPv4 address of the SMTP client.

int SPF_set_ipv6(SPF_config_t handle, struct in6_addr ipv6)

Set the IPv6 address of the SMTP client.

int SPF_set_ipv6_str(SPF_config_t handle, const char *ipv6_address)

Set the IPv6 address of the SMTP client.

int SPF_set_helo_dom(SPF_config_t handle, char *helohost)

Set the helo address of the SMTP client.

SPF needs both an IP address and a domain name to do its checking. The IP address is set by one of the above routines, but the domain name is not so simple.

The domain name is normally obtained from the envelope-from (SMTP MAIL FROM: command), but if that is null (MAIL FROM:<>), then the HELO domain is used (SMTP HELO or EHLO commands).

If there is no local part to the envelope-from email address, the name "postmaster" is used instead. This is the case when the HELO domain has to be used, but it might be able to happen with the envelope-from also, depending on how the MTA works.

Whatever the source of the domain name, the SPF spec defines this as the "current domain". Normally, you wouldn't set this directly, you would call the SPF_set_helo_dom() and SPF_set_env_from() routines. However, when an SPF record is being evaluated, the current domain is changed when an include or redirect mechanism is executed.

int SPF_set_env_from(SPF_config_t handle, char *from)

Set the 'MAIL FROM' address from the SMTP client.

SPF_output_t SPF_result(SPF_config_t handle, SPF_dns_config_t resolver)

Perform an SPF query based on the parameters specified in the handle, and return a result. The SPF_result() function does most of the real, important work.

SPF_result() checks the IP address and the envelope-from (or HELO domain) as was configured using the spfcid variable and sees if it is valid. It returns all the info that the caller will need to use the SPF check results. See the description of the structure SPF_output_t for details about the return value of SPF_result() and how they should be used.

It may use the DNS configuration to fetch additional information.

Actually, SPF_result() is just an easy-to-use wrapper around SPF_get_spf(), SPF_eval_id() and SPF_result_comments().

SPF_output_t SPF_result_2mx(SPF_config_t handle, SPF_dns_config_t resolver)

SPF_result_2mx() does everything that SPF_result() does, but it first checks to see if the sending system is a recognized MX secondary for the email recipient. If so, then it returns "pass" and does not perform the SPF query. Note that the sending system may be a MX secondary for some (but not all) of the recipients for a multi-recipient message, which is why SPF_result_2mx may be called many times with the final result being obtained from SPF_result_2mx_msg().

In effect, SPF_result_2mx() adds the mechanism "mx:" to the beginning of the SPF record for the mail from domain.

If you do not know what a secondary MX is, you probably don't have one. Use the SPF_result() function instead.

Error codes

SPF_E_SUCCESS
No errors
SPF_E_NO_MEMORY
Out of memory
SPF_E_NOT_SPF
Could not find a valid SPF record
SPF_E_SYNTAX
Syntax error
SPF_E_MOD_W_PREF
Modifiers can not have prefixes
SPF_E_INVALID_CHAR
Invalid character found
SPF_E_UNKNOWN_MECH
Unknown mechanism found
SPF_E_INVALID_OPT
Invalid option found
SPF_E_INVALID_CIDR
Invalid CIDR length
SPF_E_MISSING_OPT
Required option is missing
SPF_E_INTERNAL_ERROR
Internal programming error
SPF_E_INVALID_ESC
Invalid %-escape character
SPF_E_INVALID_VAR
Invalid macro variable
SPF_E_BIG_SUBDOM
Subdomain truncation depth too large
SPF_E_INVALID_DELIM
Invalid delimiter character
SPF_E_BIG_STRING
Option string too long
SPF_E_BIG_MECH
Too many mechanisms
SPF_E_BIG_MOD
Too many modifiers
SPF_E_BIG_DNS
Mechanisms used too many DNS lookups
SPF_E_INVALID_IP4
Invalid IPv4 address literal
SPF_E_INVALID_IP6
Invalid IPv6 address literal
SPF_E_INVALID_PREFIX
Invalid mechanism prefix
SPF_E_RESULT_UNKNOWN
SPF result is \"unknown\"
SPF_E_UNINIT_VAR
Uninitialized variable
SPF_E_MOD_NOT_FOUND
Modifier not found
SPF_E_NOT_CONFIG
Not configured
SPF_E_DNS_ERROR
DNS lookup failure
SPF_E_BAD_HOST_IP
Invalid hostname (possibly an IP address?)
SPF_E_BAD_HOST_TLD
Hostname has a missing or invalid TLD
SPF_E_MECH_AFTER_ALL
Mechanisms found after the "all:" mechanism will be ignored