/* 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of either:
 * 
 *   a) The GNU Lesser General Public License as published by the Free
 *      Software Foundation; either version 2.1, or (at your option) any
 *      later version,
 * 
 *   OR
 * 
 *   b) The two-clause BSD license.
 *
 * These licenses can be found with the distribution in the file LICENSES
 */




#ifndef INC_SPF
#define INC_SPF

/*
 * we use the in_addr and in6_addr structs all over the place.  Every file
 * that includes spf.h needs them.
 */
/* #include <sys/types.h> */
/* #include <sys/socket.h> */
/* #include <arpa/inet.h> */


#include "spf_lib_version.h"


#define SPF_VERSION 1
#define SPF_VER_STR "v=spf1"


/*
 * object ids for SPF related data
 */

/* SPF record (byte-compiled)		*/
typedef struct SPF_id_struct *SPF_id_t;

/* SPF configuration (IP address, domain names, options, etc.)	*/
typedef struct SPF_config_struct *SPF_config_t;

/* SPF DNS layer configuration		*/
typedef struct SPF_dns_config_struct *SPF_dns_config_t;


/*
 * error codes returned by various SPF functions.  (including SPF_compile()
 * and SPF_id2str(), spf_result(), etc.).
 *
 * The function SPF_strerror() will return a longer explanation of these
 * errors.
 */

typedef int SPF_err_t;

#define SPF_E_SUCCESS		 0	/* No errors			*/
#define SPF_E_NO_MEMORY		 1	/* Out of memory		*/
#define SPF_E_NOT_SPF		 2	/* Could not find a valid SPF record */
#define SPF_E_SYNTAX		 3	/* Syntax error			*/
#define SPF_E_MOD_W_PREF	 4	/* Modifiers can not have prefixes */
#define SPF_E_INVALID_CHAR	 5	/* Invalid character found	*/
#define SPF_E_UNKNOWN_MECH	 6	/* Unknown mechanism found	*/
#define SPF_E_INVALID_OPT	 7	/* Invalid option found		*/
#define SPF_E_INVALID_CIDR	 8	/* Invalid CIDR length		*/
#define SPF_E_MISSING_OPT	 9	/* Required option is missing	*/
#define SPF_E_INTERNAL_ERROR	10	/* Internal programming error	*/
#define SPF_E_INVALID_ESC	11	/* Invalid %-escape character	*/
#define SPF_E_INVALID_VAR	12	/* Invalid macro variable	*/
#define SPF_E_BIG_SUBDOM	13	/* Subdomain truncation depth too large */
#define SPF_E_INVALID_DELIM	14	/* Invalid delimiter character	*/
#define SPF_E_BIG_STRING	15	/* Option string too long	*/
#define SPF_E_BIG_MECH		16	/* Too many mechanisms		*/
#define SPF_E_BIG_MOD		17	/* Too many modifiers		*/
#define SPF_E_BIG_DNS		18	/* Mechanisms used too many DNS lookups */
#define SPF_E_INVALID_IP4	19	/* Invalid IPv4 address literal	*/
#define SPF_E_INVALID_IP6	20	/* Invalid IPv6 address literal	*/
#define SPF_E_INVALID_PREFIX	21	/* Invalid mechanism prefix	*/
#define SPF_E_RESULT_UNKNOWN	22	/* SPF result is \"unknown\"	*/
#define SPF_E_UNINIT_VAR	23	/* Uninitialized variable	*/
#define SPF_E_MOD_NOT_FOUND	24	/* Modifier not found		*/
#define SPF_E_NOT_CONFIG	25	/* Not configured		*/
#define SPF_E_DNS_ERROR		26	/* DNS lookup failure		*/
#define SPF_E_BAD_HOST_IP	27	/* Invalid hostname (possibly an IP address?) */
#define SPF_E_BAD_HOST_TLD	28	/* Hostname has a missing or invalid TLD */
#define SPF_E_MECH_AFTER_ALL	29	/* Mechanisms found after the \"all:\" mechanism will be ignored */



/* ********************************************************************* */

/*
 * SPF record object management
 */

/*
 * These routines take care of creating/destroying/etc. the objects
 * that hold the byte-compiled SPF records.  spfid objects contain
 * malloc'ed data, so they must be destroyed when you are finished
 * with them, or you will leak memory.  This is true even if the spfid
 * was created for you by a subroutine, such as SPF_compile().
 */

SPF_id_t SPF_create_id( void );		/* create a new spfid		*/
void SPF_reset_id( SPF_id_t spfid );	/* reset the spfid to a newly   */
					/* created state */
void SPF_destroy_id( SPF_id_t spfid );	/* free all memory and clean up.*/
					/* spfid is invalid afterwards  */
SPF_id_t SPF_dup_id( SPF_id_t src_spfid ); /* create a duplicate of a spfid */



/*
 * SPF record byte-compiler
 */

typedef struct
{
    SPF_id_t	spfid;			/* byte compiled SPF record	*/
    SPF_err_t	err;			/* compiler error		*/
    char	*err_msg;		/* first compiler error message	*/
    size_t	err_msg_len;		/* malloc'ed size of err_msg	*/
    int		num_errs;		/* number of errors		*/
    char	**err_msgs;		/* all compilter error info	*/
    size_t	*err_msgs_len;		/* malloc'ed size of err_msgs	*/

    /*
     * these pointers refer to places in the text SPF record and will
     * become invalid when the caller of spf_compile chooses.  This
     * detailed info is available only for the first error message.
     *
     * don't use them if you don't control the text SPF record!
     */
    const char	*expression;	/* the complete mech/mod with the error	*/
    int		expression_len;
    const char	*token;		/* the token within the expression	*/
    int		token_len;
    const char	*error_loc;	/* exact char within the token		*/
} SPF_c_results_t;
    

/*
 * The SPF compiler translates the SPF record in text form (passed in
 * 'record' argument) into a byte-compiled form.  The compiled results are left
 * in the c_results structure as described above.
 *
 * The SPF configuration, while passed as a argument, doesn't effect
 * the byte-compiled results.  It is used only to determine how
 * certain errors are handled, such as the maximum DNS lookups and
 * whether the error messages should be sanitized.
 *
 * An SPF record that is byte-compiled using one configuration can be
 * safely evaluated with a completely different configuration.
 */
SPF_err_t SPF_compile( SPF_config_t spfcid, const char *record, SPF_c_results_t *c_results );

/* finds questionable constructs in the SPF record */
void SPF_lint( SPF_id_t spfid, SPF_c_results_t *c_results );

/* SPF_strerror() translates the SPF error number into a readable string */
const char *SPF_strerror( SPF_err_t spf_err );

/*
 * SPF_verify() can be used to make sure that the byte-compiled object is
 * valid.  This is important when the byte-compiled results are obtained
 * via an outside source, such as a file or via a DNS lookup.  Basically
 * any time you call the SPF_mem2id() function, you probably want to call
 * SPF_verify().
 */
SPF_err_t SPF_verify( SPF_config_t spfcid, SPF_id_t spfid );

/*
 * The SPF optimizer hasn't been written yet, but the intent is to do
 * things like converting 'a' and 'mx' mechanisms into the equivalent
 * ip4/ip6 mechanisms, inlining include mechanisms, etc.
 */
SPF_err_t SPF_optimize( SPF_config_t spfcid, SPF_id_t *dst_spfid, SPF_id_t src_spfid );


/*
 * These routines take care of initializing/freeing/etc. the compiler
 * results structure.  The c_results structure contains malloc'ed
 * variables (including an spfid), so it must be freed when you are
 * finished with it.
 */

void SPF_init_c_results( SPF_c_results_t *c_results );
void SPF_reset_c_results( SPF_c_results_t *c_results );
SPF_c_results_t SPF_dup_c_results( SPF_c_results_t c_results );
void SPF_free_c_results( SPF_c_results_t *c_results );


/* FYI only -- can't be changed without recompiling the library
 * Most error messages are under 80 characters and we don't want
 * bad/malicious input to cause huge error messages */
#define SPF_C_ERR_MSG_SIZE		(2*80)
#define SPF_SMTP_COMMENT_SIZE		(4*80)
#define SPF_RECEIVED_SPF_SIZE		(6*80)
#define SPF_SYSLOG_SIZE			(10*80)




/* ********************************************************************* */

/*
 * SPF configuration object management
 */

/*
 * These routines take care of creating/destroying/etc. the objects
 * that hold the SPF configuration.  spfcid objects contain
 * malloc'ed data, so they must be destroyed when you are finished
 * with them, or you will leak memory. 
 */
SPF_config_t SPF_create_config( void );
void SPF_reset_config( SPF_config_t spfcid );
void SPF_destroy_config( SPF_config_t spfcid );
SPF_config_t SPF_dup_config( SPF_config_t src_spfcid );


/*
 * SPF_destroy_default_config() is useful only at the very end of the
 * program to free up all internally malloc'ed variables.  These
 * variables include such things as the default explanation, that get
 * created automatically,  Calling this will keep valgrind quiet.
 */

void SPF_destroy_default_config( void );


/*
 * The following routines get or set the SPF configuration variables.
 *
 * All of the functions that set configuration return 0 or
 * SPF_E_SUCCESS if there were no errors encountered.
 */


/*
 * The 'ip' variables are for the IP address of the client MTA that is
 * trying to send you email.
 */

/* SPF_set_ip_str() calls the appropriate IPv4/IPv6 routine, depending
 * on the input */
int	SPF_set_ip_str( SPF_config_t spfcid, const char *ip_address );

int	SPF_set_ipv4_str( SPF_config_t spfcid, const char *ipv4_address );
int	SPF_set_ipv4( SPF_config_t spfcid, struct in_addr ipv4 );
struct in_addr  SPF_get_ipv4( SPF_config_t spfcid );

int	SPF_set_ipv6_str( SPF_config_t spfcid, const char *ipv6_address );
int	SPF_set_ipv6( SPF_config_t spfcid, struct in6_addr ipv6 );
struct in6_addr SPF_get_ipv6( SPF_config_t spfcid );

/* SPF_get_client_ver() returns AF_INET or AF_INET6, depending on which
 * kind of IP address was set. */
int	SPF_get_client_ver( SPF_config_t spfcid );


/*
 * SPF needs both an IP address and a domain name to do it's 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_helo_dom( SPF_config_t spfcid, const char *helo_domain );
char    *SPF_get_helo_dom( SPF_config_t spfcid );

int	SPF_set_env_from( SPF_config_t spfcid, const char *envelope_from );
char    *SPF_get_env_from( SPF_config_t spfcid );

int	SPF_set_cur_dom( SPF_config_t spfcid, const char *current_domain );
char	*SPF_get_cur_dom( SPF_config_t spfcid );


/*
 * While evaluating the SPF record, the number of mechanisms that
 * require DNS lookups is kept track of.  In order to prevent abusive
 * behavior, there is a limit to how many of these mechanisms can be
 * executed.  It is initial set to the maximum, but you might want to
 * lower it in some cases.
 */

int	SPF_set_max_dns_mech( SPF_config_t spfcid, int max_dns_mech );
int     SPF_get_max_dns_mech( SPF_config_t spfcid );
int	SPF_set_max_dns_ptr( SPF_config_t spfcid, int max_dns_ptr );
int     SPF_get_max_dns_ptr( SPF_config_t spfcid );
int	SPF_set_max_dns_mx( SPF_config_t spfcid, int max_dns_mx );
int     SPF_get_max_dns_mx( SPF_config_t spfcid );


/*
 * Some of the data for error messages and mail headers is obtained
 * from sources on the Internet.  In order to prevent accidental or
 * malicious strings from being passed on, there is an option to
 * sanitize all strings.  The default defined by SPF_DEFAULT_SANITIZE
 */
int	SPF_set_sanitize( SPF_config_t spfcid, int sanitize );
int     SPF_get_sanitize( SPF_config_t spfcid );


/*
 * Part of the Received-SPF: email header requires the domain name of
 * the receiving MTA.
 */

int	SPF_set_rec_dom( SPF_config_t spfcid, const char *receiving_hostname );
char    *SPF_get_rec_dom( SPF_config_t spfcid );


/*
 * 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.
 */

SPF_err_t SPF_compile_exp( SPF_config_t spfcid, const char *exp, SPF_c_results_t *c_results );
int SPF_set_exp( SPF_config_t spfcid, 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.  
 */
	

SPF_err_t SPF_compile_local_policy( SPF_config_t spfcid, const char *spf_record,
				    int use_default_whitelist,
				    SPF_c_results_t *c_results );
int	SPF_set_local_policy( SPF_config_t spfcid,
			  SPF_c_results_t c_results );
SPF_c_results_t SPF_get_local_policy( SPF_config_t spfcid );

/*
 * 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.
 */

int	SPF_set_debug( SPF_config_t spfcid, int debug_level );
int     SPF_get_debug( SPF_config_t spfcid );


/* FYI only -- defaults can't be changed without recompiling the library */
#define SPF_DEFAULT_EXP		  "Please see http://spf.pobox.com/why.html?sender=%{S}&ip=%{C}&receiver=%{R}"
#define SPF_DEFAULT_MAX_DNS_MECH 10	/* DoS limit on SPF mechanisms	*/
#define SPF_DEFAULT_MAX_DNS_PTR	  5	/* DoS limit on PTR records	*/
#define SPF_DEFAULT_MAX_DNS_MX	  5	/* DoS limit on MX records	*/
#define SPF_DEFAULT_SANITIZE	  1
#define SPF_DEFAULT_WHITELIST	  "include:spf.trusted-forwarder.org"



/* ********************************************************************* */

/*
 * Results from an SPF check
 */

/*
 * The results of the SPF check (as defined by the official SPF spec)
 *
 * To quote from doc/draft-mengwong-spf-00.txt Section 3:
 *
 *     3. SPF Record Evaluation
 *     
 *        An SPF client evaluates an SPF record and produces one of seven
 *        results:
 *     
 *          None: The domain does not publish SPF data.
 *     
 *          Neutral (?): The SPF client MUST proceed as if a domain did not
 *          publish SPF data.  This result occurs if the domain explicitly
 *          specifies a "?" value, or if processing "falls off the end" of
 *          the SPF record.
 *     
 *          Pass (+): the message meets the publishing domain's definition of
 *          legitimacy.  MTAs proceed to apply local policy and MAY accept or
 *          reject the message accordingly.
 *     
 *          Fail (-): the message does not meet a domain's definition of
 *          legitimacy.  MTAs MAY reject the message using a permanent
 *          failure reply code.  (Code 550 is RECOMMENDED.  See RFC2821
 *          section 7.1.)
 *     
 *          Softfail (~): the message does not meet a domain's strict
 *          definition of legitimacy, but the domain cannot confidently state
 *          that the message is a forgery.  MTAs SHOULD accept the message
 *          but MAY subject it to a higher transaction cost, deeper scrutiny,
 *          or an unfavourable score.
 *     
 *        There are two error conditions, one temporary and one permanent.
 *     
 *          Error: indicates an error during lookup; an MTA MAY reject the
 *          message using a transient failure code, such as 450.
 *     
 *          Unknown: indicates incomplete processing: an MTA MUST proceed as
 *          if a domain did not publish SPF data.
 *     
 *        When SPF-aware SMTP receivers accept a message, they SHOULD prepend a
 *        Received-SPF header.  See section 6.
 *     
 *        SPF clients MUST use the algorithm described in this section
 *        or its functional equivalent.
 *        
 *        If an SPF client encounters a syntax error in an
 *        SPF record, it must terminate processing and return a result
 *        of "unknown".
 *
 *
 * note: SPF_RESULT_* values are constrained by the internal PREFIX_* values
 */
typedef int SPF_result_t;

#define SPF_RESULT_PASS		0	/* +				*/
#define SPF_RESULT_FAIL		1	/* -				*/
#define SPF_RESULT_SOFTFAIL	2	/* ~				*/
#define SPF_RESULT_NEUTRAL	3	/* ?				*/
#define SPF_RESULT_UNKNOWN	4	/* permanent error		*/
#define SPF_RESULT_ERROR	5	/* temp error			*/
#define SPF_RESULT_NONE		6	/* no SPF record exists		*/


/*
 * The reason that the result was returned
 *
 * This is what triggered the SPF result.  Usually, it is a mechanism in the
 * SPF record that causes the result, but if it was something else, the
 * calling program will often want to take a different action or issue
 * a different message.
 */
typedef int SPF_reason_t;

#define SPF_REASON_NONE		0
#define SPF_REASON_LOCALHOST	1	/* localhost always gets a free ride */
#define SPF_REASON_LOCAL_POLICY	2	/* local policy caused the match */
#define SPF_REASON_MECH		3	/* mechanism caused the match	*/
#define SPF_REASON_DEFAULT	4	/* ran off the end of the rec	*/
#define SPF_REASON_2MX		5	/* sent from a secondary MX	*/


/* FIXME  the relationships between result, reason and err is too murky */


/*
 * The SPF_output_t structure contains the results from an SPF check.  These
 * results can then be used by the calling program to reject the email, or
 * add headers to the email, or whatever is appropriate.
 *
 * result	See SPF_result_t for details
 * reason	See SPF_reason_t for details
 * err		See SPF_err_t for details
 *
 * err_msg	The first error message from compiling/evaluating the record
 * num_errs	The number of error messages
 * err_msgs	All of the error messages from compiling/evaluating the record
 *
 * smtp_comment	The MTA should return this message when rejecting an email
 *		due to the SPF check failure.  This comment is created via
 *		the explanation modifier (exp=) or a default explanation
 *		if no modifier is found on the SPF record.  See section
 *		5.2 of the SPF spec for details.
 * received_spf The MTA should add this header to the email at the very top
 *		of the headers.  (Before the Received: header generated by
 *		this MTA)  See section 6.3 of the SPF spec for details.
 * header_comment  This is the "comment" part of the received_spf header.
 *              This is useful if you want to build your onw received_spf
 *              header.
 */

typedef struct
{
    SPF_result_t	result;		/* pass/fail/softfail/etc.	*/
    SPF_reason_t	reason;		/* mech match, local policy, etc*/
    SPF_err_t		err;		/* DNS error, out of memory, etc*/

    char		*err_msg;	/* first syntax error info	*/
    int			num_errs;	/* number of errors		*/
    char		**err_msgs;	/* all syntax error info	*/

    char		*smtp_comment;	/* MTA rejection explanation	*/
    char		*received_spf;	/* to be added to the email	*/
    char		*header_comment; /* comment part of received_spf*/
} SPF_output_t;
    
/*
 * These routines take care of initializing/freeing/etc. the SPF check
 * output structure.  The output structure contains malloc'ed
 * variables, so it must be freed when you are finished with it.
 */

void SPF_init_output( SPF_output_t *output );
SPF_output_t SPF_dup_output( SPF_output_t output );
void SPF_free_output( SPF_output_t *output );



/* SPF_result() just calls SPF_result_id() with the SPF record found
 * using the envelope from or HELO domain in the SPF_config_t. */

/*
 * 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( SPF_config_t spfcid, SPF_dns_config_t spfdcid );

/*
 * SPF_result_helo() is just like SPF_result(), only it checks the
 * HELO domain instead of the envelope from
 */
SPF_output_t SPF_result_helo( SPF_config_t spfcid, SPF_dns_config_t spfdcid );


/*
 * 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:<rcpt-to domain>" 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.
 * 
 */

SPF_output_t SPF_result_2mx( SPF_config_t spfcid, SPF_dns_config_t spfdcid,
			     const char *rcpt_to );
SPF_output_t SPF_result_2mx_msg( SPF_config_t spfcid,
				 SPF_dns_config_t spfdcid );

/*
 * SPF_eval_id() is a low-level runction that does the actual SPF
 * checking.
 *
 * It takes the IP address and domain, the SPF record, and a way to do
 * the DNS lookups that the SPF record may require, does the
 * evaluation and returns the results.
 *
 * The IP address and domain are found found in the SPF config.  The
 * SPF record is found in spfid variable.  The way to do the DNS
 * lookups is found in the DNS config.
 *
 * The three different inputs can be mixed and matched in any way you
 * want.  You can take the same SPF config and evaluate many different
 * SPF records with it, or you can take the same SPF record and
 * evaluate many different configurations with it.  (And the same goes
 * for changing the DNS config whenever you want.)
 *
 * In addition, SPF_eval_id() gets passed a few bookkeeping variables.
 *
 * The use_local_policy variable determines if the local_policy
 * mechanisms should be applied.  Normally, the top level call to
 * SPF_eval_id() would pass TRUE, but recursive calls caused by the
 * include: mechanism would pass FALSE.
 *
 * The use_helo variable determins if the "responsible sender" (as
 * defined in the SPF spec) should be the HELO domain instead of the
 * envelope-from.  Normally, the HELO domain is used as a fallback if
 * the envelope-from is empty, but this variable lets you explicitly
 * check the HELO domain.
 *
 * The "num_dns_mech" keeps track of the number of mechanisms that
 * have required DNS lookups.  Normally, the top level call to
 * SPF_eval_id() pass NULL for num_dns_mech, while recursive calls
 * caused by the include: mechanism pass an internal counter.
 */

SPF_output_t SPF_eval_id( SPF_config_t spfcid, SPF_id_t spfid,
			  SPF_dns_config_t spfdcid, 
			  int use_local_policy, int use_helo,
			  int *num_dns_mech );


/*
 * SPF_result_comments filles out the smtp_comment (if appropriate),
 * received_spf and header_comment fields of the SPF_output_t
 * structure.
 */
void SPF_result_comments( SPF_config_t spfcid, SPF_dns_config_t spfdcid,
			  SPF_c_results_t c_results, SPF_output_t *output );


/* SPF_smtp_comment() may requre DNS lookups, so call on when needed */
char *SPF_smtp_comment( SPF_config_t spfcid, SPF_id_t spfid, SPF_dns_config_t spfdcid, SPF_output_t output );

/* SPF_received_spf() depends on the header_comment being set */
char *SPF_received_spf( SPF_config_t spfcid, SPF_c_results_t c_results, SPF_output_t output );

char *SPF_header_comment( SPF_config_t spfcid, SPF_output_t output );


/* SPF_strresult() translates the SPF result number into a readable string */
const char *SPF_strresult( SPF_result_t result );
/* SPF_strreason() translates the SPF reason number into a readable string */
const char *SPF_strreason( SPF_reason_t reason );

/* Extract value of modifier from the SPF record.  The value will have
 * any macro-variables expanded. */
SPF_err_t SPF_find_mod_value( SPF_config_t spfcid, SPF_id_t spfid,
			      SPF_dns_config_t spfdcid, const char *mod_name,
			      char **buf, size_t *buf_len );
SPF_err_t SPF_find_mod_cidr( SPF_config_t spfcid, SPF_id_t spfid,
			     SPF_dns_config_t spfdcid, const char *mod_name,
			     int *ipv4_cidr, int *ipv6_cidr );


/* ********************************************************************* */

/*
 * misc functions
 */

/* converts the byte-compiled SPF record between the library format and
 * a network portable block of data */
SPF_err_t SPF_id2mem( void *dst_mem, int *dst_len, SPF_id_t src_spfid );
SPF_err_t SPF_mem2id( SPF_id_t dst_spfid, void *src_mem, int src_len );

/* convert the byte-compiled SPF record in the equivalent text format.
 * The results may not exactly match the original text record before
 * it was compiled due to changes in whitespaces, optional arguments,
 * etc. */
SPF_err_t SPF_id2str( char **dst_rec, size_t *dst_len, SPF_id_t src_spfid );
/* SPF_err_t SPF_str2id()   See: SPF_compile() */

/* print the byte-compiled SPF record, along with a little debugging info */
void SPF_print( SPF_id_t spfid );


/* Get an SPF record from the given domain and return the compiled results.
 * If the domain is NULL, SPF_get_spf() will use the envelope-from domain
 * if it exists, or the HELO domain as the last resort.
 */
SPF_err_t SPF_get_spf( SPF_config_t spfcid, SPF_dns_config_t spfdcid,
		       const char *domain, SPF_c_results_t *c_results );

/* Get an SPF explanation string from the given domain and return the
 * compiled results */ 
SPF_err_t SPF_get_exp( SPF_config_t spfcid, SPF_id_t spfid,
		       SPF_dns_config_t spfdcid,
 		       char **buf, size_t *buf_len );


/* The client domain is the validated domain name of the client IP
 * address.  This is not just the domain name(s) found in the reverse
 * DNS tree, but involves checking to make sure these name(s) use the
 * client IP address.  The complete validation procedure is described
 * in section 5.4 of the SPF spec.
 */
char *SPF_get_client_dom( SPF_config_t spfcid, SPF_dns_config_t spfdcid );

/* Since determining the client domain can be somewhat expensive, you
 * can explicitly make sure it is set, rather than just waiting until
 * SPF_get_client_dom() is called. */
void SPF_set_client_dom( SPF_config_t spfcid, SPF_dns_config_t spfdcid );


/* This returns the version information library.  Useful if the library
 * is a shared library and may differ from when the application was compiled.
 */
void SPF_get_lib_version( int *major, int *minor, int *patch );


/*
 * Error messages and warnings generated internally by the library call
 * these routines.  By default, the messages go to stderr, but you can
 * define your own routines to deal with the messages instead.
 */

#include <stdarg.h>


#define SPF_error(errmsg) SPF_errorx( __FILE__, __LINE__, "%s", errmsg )
void SPF_errorx( const char *file, int line, const char *format, ... ) __attribute__ ((noreturn)) __attribute__ ((format (printf, 3, 4)));
void SPF_errorx2( const char *format, ... );
void SPF_errorv( const char *file, int line, const char *format, va_list ap ) __attribute__ ((noreturn)) __attribute__ ((format (printf, 3, 0)));

#define SPF_warning(errmsg) SPF_warningx( __FILE__, __LINE__, "%s", errmsg )
void SPF_warningx( const char *file, int line, const char *format, ... ) __attribute__ ((format (printf, 3, 4)));
void SPF_warningx2( const char *format, ... );
void SPF_warningv( const char *file, int line, const char *format, va_list ap ) __attribute__ ((format (printf, 3, 0)));

#define SPF_info(errmsg) SPF_infox( __FILE__, __LINE__, "%s", errmsg )
void SPF_infox( const char *file, int line, const char *format, ... ) __attribute__ ((format (printf, 3, 4)));
void SPF_infox2( const char *format, ... );
void SPF_infov( const char *file, int line, const char *format, va_list ap ) __attribute__ ((format (printf, 3, 0)));

#define SPF_debug(errmsg) SPF_debugx( __FILE__, __LINE__, "%s", errmsg )
void SPF_debugx( const char *file, int line, const char *format, ... ) __attribute__ ((format (printf, 3, 4)));
void SPF_debugx2( const char *format, ... );
void SPF_debugv( const char *file, int line, const char *format, va_list ap ) __attribute__ ((format (printf, 3, 0)));


#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L

#define SPF_errorf(format, ... ) SPF_errorx( __FILE__, __LINE__, format, __VA_ARGS__ )
#define SPF_warningf(format, ... ) SPF_warningx( __FILE__, __LINE__, format, __VA_ARGS__ )
#define SPF_infof(format, ... ) SPF_infox( __FILE__, __LINE__, format, __VA_ARGS__ )
#define SPF_debugf(format, ... ) SPF_debugx( __FILE__, __LINE__, format, __VA_ARGS__ )

#elif defined( __GNUC__ )

#define SPF_errorf(format... ) SPF_errorx( __FILE__, __LINE__, format )
#define SPF_warningf(format... ) SPF_warningx( __FILE__, __LINE__, format )
#define SPF_infof(format... ) SPF_infox( __FILE__, __LINE__, format )
#define SPF_debugf(format... ) SPF_debugx( __FILE__, __LINE__, format )

#else

#define SPF_errorf	SPF_errorx2
#define SPF_warningf	SPF_warningx2
#define SPF_infof	SPF_infox2
#define SPF_debugf	SPF_debugx2

#endif


/* These message handler routines print to stderr or stdout, as appropriate. */

void SPF_error_stdio( const char *file, int line, const char *errmsg ) __attribute__ ((noreturn));
void SPF_warning_stdio( const char *file, int line, const char *errmsg );
void SPF_info_stdio( const char *file __attribute__ ((unused)), int line __attribute__ ((unused)), const char *errmsg );
void SPF_debug_stdio( const char *file, int line, const char *errmsg );


/* These message handler routines send messages to syslog */

void SPF_error_syslog( const char *file, int line, const char *errmsg ) __attribute__ ((noreturn));
void SPF_warning_syslog( const char *file, int line, const char *errmsg );
void SPF_info_syslog( const char *file __attribute__ ((unused)), int line __attribute__ ((unused)), const char *errmsg );
void SPF_debug_syslog( const char *file, int line, const char *errmsg );

#if 0
    /* to use the syslog routines, add code such as: */
    openlog(logPrefix,LOG_PID|LOG_CONS|LOG_NDELAY|LOG_NOWAIT,LOG_MAIL);

    SPF_error_handler = SPF_error_syslog;
    SPF_warning_handler = SPF_warning_syslog;
    SPF_info_handler = SPF_info_syslog;
    SPF_debug_handler = SPF_debug_syslog;
#endif

/* FYI only -- can't be changed without recompiling the library */
#define SPF_DEFAULT_ERROR_HANDLER	SPF_error_stdio
#define SPF_DEFAULT_WARNING_HANDLER	SPF_warning_stdio
#define SPF_DEFAULT_INFO_HANDLER	SPF_info_stdio
#define SPF_DEFAULT_DEBUG_HANDLER	SPF_debug_stdio


/*
 * You can assign these global function pointers to whatever routines
 * you want to handle the various types of messages.  Setting them to NULL
 * will cause the messages to be ignored.
 */
 
extern void (*SPF_error_handler)( const char *, int, const char * ) __attribute__ ((noreturn));
extern void (*SPF_warning_handler)( const char *, int, const char * );
extern void (*SPF_info_handler)( const char *, int, const char * );
extern void (*SPF_debug_handler)( const char *, int, const char * );



#endif
