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

#include "spf_alt/spf_sys_config.h"

#ifdef STDC_HEADERS
# include <stdio.h>        /* stdin / stdout */
# include <stdlib.h>       /* malloc / free */
#endif

#ifdef HAVE_STRING_H
# include <string.h>       /* strstr / strdup */
#else
# ifdef HAVE_STRINGS_H
#  include <strings.h>       /* strstr / strdup */
# endif
#endif

#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif


#include "spf_alt/spf.h"
#include "spf_alt/spf_dns.h"
#include "spf_alt/spf_internal.h"
#include "spf_alt/spf_dns_internal.h"


int SPF_mech_cidr( SPF_config_t spfcid, SPF_mech_t *mech )
{
    SPF_iconfig_t	*spfic = SPF_cid2spfic(spfcid);
    SPF_data_t		*data;
    
    if ( spfcid == NULL )
	SPF_error( "spfcid is NULL" );
    
    if ( mech == NULL )
	SPF_error( "mech is NULL" );
    
    switch( mech->mech_type )
    {
    case MECH_IP4:
    case MECH_IP6:
	return mech->parm_len;
	break;

    case MECH_A:
    case MECH_MX:
	data = SPF_mech_data( mech );
	if ( data <= SPF_mech_end_data( mech )
	     && data->dc.parm_type == PARM_CIDR )
	{
	    if ( spfic->client_ver == AF_INET )
		return data->dc.ipv4;
	    else if ( spfic->client_ver == AF_INET6 )
		return data->dc.ipv6;
	}
	break;
    }
	
    return 0;
}



int SPF_ip_match( SPF_config_t spfcid, SPF_mech_t *mech,
		  struct in_addr ipv4 )
{
    SPF_iconfig_t	*spfic = SPF_cid2spfic(spfcid);

    char	src_ip4_buf[ INET_ADDRSTRLEN ];
    char	dst_ip4_buf[ INET_ADDRSTRLEN ];
    char	mask_ip4_buf[ INET_ADDRSTRLEN ];

    const char	*p_err;


    struct in_addr	src_ipv4 = spfic->ipv4;
    int		cidr, mask;
	

    if ( spfic->client_ver != AF_INET )
	return FALSE;


    cidr = SPF_mech_cidr( spfcid, mech );
    if ( cidr == 0 )
	cidr = 32;
    mask = 0xffffffff << (32 - cidr);
    mask = htonl( mask );
	
    if ( spfic->debug )
    {
	p_err = inet_ntop( AF_INET, &src_ipv4.s_addr,
			   src_ip4_buf, sizeof( src_ip4_buf ) );
	if ( p_err == NULL )
	    snprintf( src_ip4_buf, sizeof( src_ip4_buf ), "ip-error" );

	p_err = inet_ntop( AF_INET, &ipv4.s_addr,
			   dst_ip4_buf, sizeof( dst_ip4_buf ) );
	if ( p_err == NULL )
	    snprintf( dst_ip4_buf, sizeof( dst_ip4_buf ), "ip-error" );

	p_err = inet_ntop( AF_INET, &mask,
			   mask_ip4_buf, sizeof( mask_ip4_buf ) );
	if ( p_err == NULL )
	    snprintf( mask_ip4_buf, sizeof( mask_ip4_buf ), "ip-error" );

	SPF_debugf( "ip_match:  %s == %s  (/%d %s):  %d",
		src_ip4_buf, dst_ip4_buf, cidr, mask_ip4_buf,
		(src_ipv4.s_addr & mask) == (ipv4.s_addr & mask));
    }

    return (src_ipv4.s_addr & mask) == (ipv4.s_addr & mask);
}


int SPF_ip_match6( SPF_config_t spfcid, SPF_mech_t *mech,
		   struct in6_addr ipv6 )
{
    SPF_iconfig_t	*spfic = SPF_cid2spfic(spfcid);

    char	src_ip6_buf[ INET6_ADDRSTRLEN ];
    char	dst_ip6_buf[ INET6_ADDRSTRLEN ];

    const char	*p_err;


    struct in6_addr	src_ipv6 = spfic->ipv6;
    int		cidr, mask;
    int		i;
    int		match;
    

    if ( spfic->client_ver != AF_INET6 )
	return FALSE;


    cidr = SPF_mech_cidr( spfcid, mech );
    if ( cidr == 0 )
	cidr = 128;

    match = TRUE;
    for( i = 0; i < array_elem( ipv6.s6_addr ) && match; i++ )
    {
	if ( cidr > 8 )
	    mask = 0xff;
	else if ( cidr > 0 )
	    mask = (0xff << (8 - cidr)) & 0xff;
	else
	    break;
	cidr -= 8;

	match = (src_ipv6.s6_addr[i] & mask) == (ipv6.s6_addr[i] & mask);
    }
    
	
    if ( spfic->debug )
    {
	p_err = inet_ntop( AF_INET6, &src_ipv6.s6_addr,
			   src_ip6_buf, sizeof( src_ip6_buf ) );
	if ( p_err == NULL )
	    snprintf( src_ip6_buf, sizeof( src_ip6_buf ), "ip-error" );

	p_err = inet_ntop( AF_INET6, &ipv6.s6_addr,
			   dst_ip6_buf, sizeof( dst_ip6_buf ) );
	if ( p_err == NULL )
	    snprintf( dst_ip6_buf, sizeof( dst_ip6_buf ), "ip-error" );

	SPF_debugf( "ip_match:  %s == %s  (/%d):  %d",
		src_ip6_buf, dst_ip6_buf, cidr, match );
    }

    return match;
}


#define done(result,reason,err) xdone( &output, result, reason, err, buf, &c_results )

static SPF_output_t xdone( SPF_output_t *output,
			  SPF_result_t result,
			  SPF_reason_t reason,
			  SPF_err_t err,
			  char *buf, SPF_c_results_t *c_results )
{
    output->result = result;
    output->reason = reason;
    output->err = err;
    if ( buf ) free( buf );
    SPF_free_c_results( c_results );
    return *output;
}


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_iconfig_t	*spfic = SPF_cid2spfic(spfcid);
    SPF_internal_t	*spfi = SPF_id2spfi(spfid);
    SPF_output_t	output;

    int		i, j;
    int		m;
    SPF_mech_t	*mech;
    SPF_mech_t	*local_policy;
    int		found_all;
    SPF_data_t	*data, *data_end;

    char	*buf = NULL;
    size_t	buf_len = 0;
    ns_type	fetch_ns_type;
    char	*lookup;

    SPF_dns_rr_t *rr_a;
    SPF_dns_rr_t *rr_aaaa;
    SPF_dns_rr_t *rr_ptr;
    SPF_dns_rr_t *rr_mx;

    SPF_err_t	err;

    char	*sender_dom, *sd, *cd;
    
    char	*pc, *ps;

    SPF_c_results_t c_results;

    SPF_output_t	inc_out;

    char	*save_cur_dom;
    int		local_num_dns_mech = 0;
    
    struct in_addr tmp_ipv4;
    struct in6_addr tmp_ipv6;
    
    int		max_ptr;
    int		max_mx;


    /*
     * make sure we were passed valid data to work with
     */
    if ( spfic == NULL )
	SPF_error( "spfcid is NULL" );

    if ( spfi == NULL )
	SPF_error( "spfid is NULL" );

    if ( spfdcid == NULL )
	SPF_error( "spfdcid is NULL" );



    SPF_init_c_results( &c_results );
    SPF_init_output( &output );
    


    if ( spfic->client_ver != AF_INET && spfic->client_ver != AF_INET6 )
	return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, SPF_E_NOT_CONFIG );
    
    if ( spfic->cur_dom == NULL )
	return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, SPF_E_NOT_CONFIG );
	
    sender_dom = spfic->dp_from;
    if ( sender_dom == NULL || use_helo ) sender_dom = spfic->helo_dom;

    if ( sender_dom == NULL )
	return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, SPF_E_NOT_CONFIG );


    /*
     * localhost always gets a free ride
     */

    if ( SPF_is_loopback( spfcid ) )
	return done( SPF_RESULT_PASS, SPF_REASON_LOCALHOST, SPF_E_SUCCESS );
    

    /*
     * Do some start up stuff if we haven't recursed yet
     */

    if ( num_dns_mech == NULL )
	num_dns_mech = &local_num_dns_mech;
    if ( *num_dns_mech < 0 )
	*num_dns_mech = 0;
    
    local_policy = NULL;

    if ( use_local_policy )
    {
	/*
	 * find the location for the whitelist execution
	 *
	 * Philip Gladstone says:
	 *
	 * 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'.
	 */
	
	if ( spfic->local_policy.spfid )
	{
	    mech = spfi->mech_first;

	    found_all = FALSE;
	    for( m = 0; m < spfi->header.num_mech; m++ )
	    {
		if ( mech->mech_type == MECH_ALL
		     && (mech->prefix_type == PREFIX_FAIL
			 || mech->prefix_type == PREFIX_UNKNOWN
			 || mech->prefix_type == PREFIX_SOFTFAIL
			 )
		    )
		    found_all = TRUE;
	    
		if ( mech->prefix_type != PREFIX_FAIL
		     && mech->prefix_type != PREFIX_SOFTFAIL
		    )
		    local_policy = mech;

		mech = SPF_next_mech( mech );
	    }
	
	    if ( !found_all )
		local_policy = NULL;
	}
	
    }
    

    /*
     * evaluate the mechanisms
     */

    mech = spfi->mech_first;
    for( m = 0; m < spfi->header.num_mech; m++ )
    {
	if ( *num_dns_mech > spfic->max_dns_mech
	     || *num_dns_mech > SPF_MAX_DNS_MECH )
	    return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, SPF_E_BIG_DNS );
		
	switch( mech->mech_type )
	{
	case MECH_A:
	    ++*num_dns_mech;

	    data = SPF_mech_data( mech );
	    data_end = SPF_mech_end_data( mech );

	    if ( data < data_end && data->dc.parm_type == PARM_CIDR )
		data = SPF_next_data( data );
	    
	    if ( data == data_end )
		lookup = SPF_get_cur_dom( spfcid );
	    else
	    {
		err = SPF_expand( spfcid, spfdcid,
				  data, mech->parm_len,
				  &buf, &buf_len );

		if ( err == SPF_E_NO_MEMORY )
		    return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
		else if ( err )
		    return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
		lookup = buf;
	    }
	    
	    if ( spfic->client_ver == AF_INET )
		fetch_ns_type = ns_t_a;
	    else
		fetch_ns_type = ns_t_aaaa;
	    
	    rr_a = SPF_dns_lookup( spfdcid, lookup, fetch_ns_type, TRUE );
    
	    if ( spfic->debug )
		SPF_debugf( "found %d A records for %s  (herrno: %d)",
			rr_a->num_rr, lookup, rr_a->herrno );
	    
	    if( rr_a->herrno == TRY_AGAIN )
		return done( SPF_RESULT_ERROR, SPF_REASON_MECH,
			     SPF_E_DNS_ERROR );
	    
	    for( i = 0; i < rr_a->num_rr; i++ )
	    {
		if ( rr_a->rr_type != fetch_ns_type )
		    continue;

		if ( spfic->client_ver == AF_INET )
		{
		    if ( SPF_ip_match( spfcid, mech, rr_a->rr[i]->a ) )
			return done( mech->prefix_type, SPF_REASON_MECH,
				     SPF_E_SUCCESS );
		}
		else
		{
		    if ( SPF_ip_match6( spfcid, mech, rr_a->rr[i]->aaaa ) )
			return done( mech->prefix_type, SPF_REASON_MECH,
				     SPF_E_SUCCESS );
		}
	    }
	    break;
	    
	case MECH_MX:
	    ++*num_dns_mech;

	    data = SPF_mech_data( mech );
	    data_end = SPF_mech_end_data( mech );

	    if ( data < data_end && data->dc.parm_type == PARM_CIDR )
		data = SPF_next_data( data );
	    
	    if ( data == SPF_mech_end_data( mech ) )
		lookup = SPF_get_cur_dom( spfcid );
	    else
	    {
		err = SPF_expand( spfcid, spfdcid,
				  data, mech->parm_len,
				  &buf, &buf_len );

		if ( err == SPF_E_NO_MEMORY )
		    return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
		else if ( err )
		    return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
		lookup = buf;
	    }
	    
	    rr_mx = SPF_dns_dup_rr( SPF_dns_lookup( spfdcid, lookup,
						    ns_t_mx, TRUE ) );
    
	    if ( spfic->debug )
		SPF_debugf( "found %d MX records for %s  (herrno: %d)",
			rr_mx->num_rr, lookup, rr_mx->herrno );
	    
	    if( rr_mx->herrno == TRY_AGAIN )
		return done( SPF_RESULT_ERROR, SPF_REASON_MECH,
			     SPF_E_DNS_ERROR );
	    
	    max_mx = rr_mx->num_rr;
	    if ( max_mx > spfic->max_dns_mx )
		max_mx = spfic->max_dns_mx;
	    if ( max_mx > SPF_MAX_DNS_MX )
		max_mx = SPF_MAX_DNS_MX;

	    for( j = 0; j < max_mx; j++ )
	    {
		if ( rr_mx->rr_type != ns_t_mx )
		    continue;

		if ( spfic->client_ver == AF_INET )
		    fetch_ns_type = ns_t_a;
		else
		    fetch_ns_type = ns_t_aaaa;
	    
		rr_a = SPF_dns_lookup( spfdcid, rr_mx->rr[j]->mx,
				       fetch_ns_type, TRUE );
    
		if ( spfic->debug )
		    SPF_debugf( "%d: found %d A records for %s  (herrno: %d)",
			    j, rr_a->num_rr, rr_mx->rr[j]->mx, rr_a->herrno );
		if( rr_a->herrno == TRY_AGAIN )
		    return done( SPF_RESULT_ERROR, SPF_REASON_MECH,
				 SPF_E_DNS_ERROR );
	    
		for( i = 0; i < rr_a->num_rr; i++ )
		{
		    if ( rr_a->rr_type != fetch_ns_type )
			continue;

		    if ( spfic->client_ver == AF_INET )
		    {
			if ( SPF_ip_match( spfcid, mech, rr_a->rr[i]->a ) )
			{
			    SPF_dns_destroy_rr( rr_mx );

			    return done( mech->prefix_type, SPF_REASON_MECH,
					 SPF_E_SUCCESS );
			}
		    }
		    else
		    {
			if ( SPF_ip_match6( spfcid, mech, rr_a->rr[i]->aaaa ) )
			{
			    SPF_dns_destroy_rr( rr_mx );

			    return done( mech->prefix_type, SPF_REASON_MECH,
					 SPF_E_SUCCESS );
			}
		    }
		}
	    }

	    SPF_dns_destroy_rr( rr_mx );
	    break;
	    
	case MECH_PTR:
	    ++*num_dns_mech;

	    if ( mech->parm_len == 0 )
		sd = sender_dom;
	    else
	    {
		err = SPF_expand( spfcid, spfdcid,
				  SPF_mech_data( mech ), mech->parm_len,
				  &buf, &buf_len );

		if ( err == SPF_E_NO_MEMORY )
		    return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
		else if ( err )
		    return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
		sd = buf;
	    }
	    
	    
	    if ( spfic->client_ver == AF_INET )
	    {
		rr_ptr = SPF_dns_dup_rr( SPF_dns_rlookup( spfdcid, spfic->ipv4, ns_t_ptr, TRUE ) );

		if ( spfic->debug )
		{
		    char	ip4_buf[ INET_ADDRSTRLEN ];
		    
		    const char	*p_err;
		    p_err = inet_ntop( AF_INET, &spfic->ipv4.s_addr,
				       ip4_buf, sizeof( ip4_buf ) );
		    if ( p_err == NULL )
			snprintf( ip4_buf, sizeof( ip4_buf ), "ip-error" );
		    
		    SPF_debugf( "found %d PTR records for %s  (herrno: %d)",
			    rr_ptr->num_rr, ip4_buf, rr_ptr->herrno );
		}
		if( rr_ptr->herrno == TRY_AGAIN )
		    return done( SPF_RESULT_ERROR, SPF_REASON_MECH, SPF_E_DNS_ERROR );
	    

		max_ptr = rr_ptr->num_rr;
		if ( max_ptr > spfic->max_dns_ptr )
		    max_ptr = spfic->max_dns_ptr;
		if ( max_ptr > SPF_MAX_DNS_PTR )
		    max_ptr = SPF_MAX_DNS_PTR;

		for( i = 0; i < max_ptr; i++ )
		{
		    rr_a = SPF_dns_lookup( spfdcid, rr_ptr->rr[i]->ptr, ns_t_a, TRUE );

		    if ( spfic->debug )
			SPF_debugf( "%d:  found %d A records for %s  (herrno: %d)",
				i, rr_a->num_rr, rr_ptr->rr[i]->ptr, rr_a->herrno );
		    if( rr_a->herrno == TRY_AGAIN )
			return done( SPF_RESULT_ERROR, SPF_REASON_MECH, SPF_E_DNS_ERROR );
	    
		    for( j = 0; j < rr_a->num_rr; j++ )
		    {
			if ( spfic->debug )
			{
			    char	ip4_buf[ INET_ADDRSTRLEN ];
		    
			    const char	*p_err;
			    p_err = inet_ntop( AF_INET, &rr_a->rr[j]->a.s_addr,
					       ip4_buf, sizeof( ip4_buf ) );
			    if ( p_err == NULL )
				snprintf( ip4_buf, sizeof( ip4_buf ), "ip-error" );
		    
			    SPF_debugf( "%d: %d:  found %s",
				    i, j, ip4_buf );
			}

			if ( rr_a->rr[j]->a.s_addr == spfic->ipv4.s_addr )
			{

			    cd = rr_ptr->rr[i]->ptr;
			    pc = cd + strlen( cd ) - 1;
			    ps = sd + strlen( sd ) - 1;

			    if ( spfic->debug)
				SPF_debugf( "%s == %s", sd, cd );
				
			    while ( pc != cd
				    && ps != sd
				    && *pc-- == *ps-- )
				;

			    if ( spfic->debug)
				SPF_debugf( "%s == %s", ps, pc );
				
			    if ( (ps == sd && pc == cd)
				 || ( ps == sd && *(pc-1) == '.' )
				)
			    {
				SPF_dns_destroy_rr( rr_ptr );
				return done( mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS );
			    }
			}
		    }
		}
		SPF_dns_destroy_rr( rr_ptr );
	    }
	    
	    else if ( spfic->client_ver == AF_INET6 )
	    {
		rr_ptr = SPF_dns_dup_rr( SPF_dns_rlookup6( spfdcid, spfic->ipv6, ns_t_ptr, TRUE ) );

		if ( spfic->debug )
		{
		    char	ip6_buf[ INET6_ADDRSTRLEN ];
		    
		    const char	*p_err;
		    p_err = inet_ntop( AF_INET6, &spfic->ipv6.s6_addr,
				       ip6_buf, sizeof( ip6_buf ) );
		    if ( p_err == NULL )
			snprintf( ip6_buf, sizeof( ip6_buf ), "ip-error" );
		    
		    SPF_debugf( "found %d PTR records for %s  (herrno: %d)",
			    rr_ptr->num_rr, ip6_buf, rr_ptr->herrno );
		}
		if( rr_ptr->herrno == TRY_AGAIN )
		    return done( SPF_RESULT_ERROR, SPF_REASON_MECH, SPF_E_DNS_ERROR );
	    

		max_ptr = rr_ptr->num_rr;
		if ( max_ptr > spfic->max_dns_ptr )
		    max_ptr = spfic->max_dns_ptr;
		if ( max_ptr > SPF_MAX_DNS_PTR )
		    max_ptr = SPF_MAX_DNS_PTR;

		for( i = 0; i < max_ptr; i++ )
		{
		    rr_aaaa = SPF_dns_lookup( spfdcid, rr_ptr->rr[i]->ptr, ns_t_aaaa, TRUE );

		    if ( spfic->debug )
			SPF_debugf( "%d:  found %d AAAA records for %s  (herrno: %d)",
				i, rr_aaaa->num_rr, rr_ptr->rr[i]->ptr, rr_aaaa->herrno );
		    if( rr_aaaa->herrno == TRY_AGAIN )
			return done( SPF_RESULT_ERROR, SPF_REASON_MECH, SPF_E_DNS_ERROR );
	    
		    for( j = 0; j < rr_aaaa->num_rr; j++ )
		    {
			if ( spfic->debug )
			{
			    char	ip6_buf[ INET6_ADDRSTRLEN ];
		    
			    const char	*p_err;
			    p_err = inet_ntop( AF_INET6, &rr_aaaa->rr[j]->aaaa.s6_addr,
					       ip6_buf, sizeof( ip6_buf ) );
			    if ( p_err == NULL )
				snprintf( ip6_buf, sizeof( ip6_buf ), "ip-error" );
		    
			    SPF_debugf( "%d: %d:  found %s",
				    i, j, ip6_buf );
			}

			if ( memcmp( &rr_aaaa->rr[j]->aaaa, &spfic->ipv6,
				     sizeof( spfic->ipv6 ) ) == 0 )
			{

			    cd = rr_ptr->rr[i]->ptr;
			    pc = cd + strlen( cd ) - 1;
			    ps = sd + strlen( sd ) - 1;

			    if ( spfic->debug)
				SPF_debugf( "%s == %s", sd, cd );
				
			    while ( pc != cd
				    && ps != sd
				    && *pc-- == *ps-- )
				;

			    if ( spfic->debug)
				SPF_debugf( "%s == %s", ps, pc );
				
			    if ( (ps == sd && pc == cd)
				 || ( ps == sd && *(pc-1) == '.' )
				)
			    {
				SPF_dns_destroy_rr( rr_ptr );

				return done( mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS );
			    }
			}
		    }
		}
		SPF_dns_destroy_rr( rr_ptr );
	    }

	    break;
	    
	case MECH_INCLUDE:
	    ++*num_dns_mech;

	    err = SPF_expand( spfcid, spfdcid,
			      SPF_mech_data( mech ), mech->parm_len,
			      &buf, &buf_len );

	    if ( err == SPF_E_NO_MEMORY )
		return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
	    else if ( err )
		return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
	    lookup = buf;
	    
	    /*
	     * get the (compiled) SPF record
	     */
    
	    err = SPF_get_spf( spfcid, spfdcid, lookup, &c_results );

	    if ( spfic->debug > 0 )
		SPF_debugf( "include:  getting SPF record:  %s",
			SPF_strerror( err ) );
		

	    if ( err == SPF_E_SUCCESS )
	    {
		/*
		 * find out whether this configuration passes
		 */
		
		save_cur_dom = strdup( SPF_get_cur_dom( spfcid ) );
		if ( save_cur_dom == NULL )
		    return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
		SPF_set_cur_dom( spfcid, lookup );

		inc_out = SPF_eval_id( spfcid, c_results.spfid, spfdcid,
				       FALSE, FALSE, num_dns_mech );

		SPF_set_cur_dom( spfcid, save_cur_dom );
		free( save_cur_dom );
		SPF_reset_c_results( &c_results );

		if ( spfic->debug > 0 )
		    SPF_debugf( "include:  executed SPF record:  %s  result: %s  reason: %s",
			    SPF_strerror( inc_out.err ),
			    SPF_strresult( inc_out.result ),
			    SPF_strreason( inc_out.reason ) );
		
		switch ( inc_out.result )
		{
		case SPF_RESULT_PASS:
		    err = inc_out.err;
		    SPF_free_output( &inc_out );

		    return done( mech->prefix_type, SPF_REASON_MECH, err );
		    break;
		    
		case SPF_RESULT_ERROR:
		    err = inc_out.err;
		    SPF_free_output( &inc_out );

		    return done( SPF_RESULT_ERROR, SPF_REASON_MECH, err );
		    break;

		case SPF_RESULT_NEUTRAL:
		case SPF_RESULT_SOFTFAIL:
		case SPF_RESULT_FAIL:
		    SPF_free_output( &inc_out );
		    break;

		case SPF_RESULT_NONE:
		case SPF_RESULT_UNKNOWN:
		default:
		    err = inc_out.err;
		    SPF_free_output( &inc_out );

		    return done( SPF_RESULT_UNKNOWN, SPF_REASON_MECH, err );
		    break;
		}
	    }
	    else if ( err == SPF_E_DNS_ERROR )
		return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
	    else
		return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
	    

	    
	    break;
	    
	case MECH_IP4:
	    memmove( &tmp_ipv4, SPF_mech_ip4_data( mech ), sizeof( tmp_ipv4 ) );
	    if ( SPF_ip_match( spfcid, mech, tmp_ipv4 ) )
		return done( mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS );
	    break;
	    
	case MECH_IP6:
	    memmove( &tmp_ipv6, SPF_mech_ip6_data( mech ), sizeof( tmp_ipv6 ) );
	    if ( SPF_ip_match6( spfcid, mech, tmp_ipv6 ) )
		return done( mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS );
	    break;
	    
	case MECH_EXISTS:
	    ++*num_dns_mech;

	    err = SPF_expand( spfcid, spfdcid,
			      SPF_mech_data( mech ), mech->parm_len,
			      &buf, &buf_len );

	    if ( err == SPF_E_NO_MEMORY )
		return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
	    else if ( err )
		return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
	    lookup = buf;
	    
	    rr_a = SPF_dns_lookup( spfdcid, lookup, ns_t_a, FALSE );
    
	    if ( spfic->debug )
		SPF_debugf( "found %d A records for %s  (herrno: %d)",
			rr_a->num_rr, lookup, rr_a->herrno );
	    
	    if( rr_a->herrno == TRY_AGAIN )
		return done( SPF_RESULT_ERROR, SPF_REASON_MECH, SPF_E_DNS_ERROR );
	    
	    
	    if ( rr_a->num_rr > 0 )
		return done( mech->prefix_type, SPF_REASON_MECH, SPF_E_SUCCESS );
	    
	    break;
	    
	case MECH_ALL:
	    if ( mech->prefix_type == SPF_RESULT_UNKNOWN )
		err = SPF_E_UNKNOWN_MECH;
	    else
		err = SPF_E_SUCCESS;

	    return done( mech->prefix_type, SPF_REASON_MECH, err );
	    break;
	    
	case MECH_REDIRECT:
	    ++*num_dns_mech;

	    err = SPF_expand( spfcid, spfdcid,
			      SPF_mech_data( mech ), mech->parm_len,
			      &buf, &buf_len );

	    if ( err == SPF_E_NO_MEMORY )
		return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
	    else if ( err )
		return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, err );
	    lookup = buf;
	    
	    /*
	     * get the (compiled) SPF record
	     */
    
	    err = SPF_get_spf( spfcid, spfdcid, lookup, &c_results );

	    if ( spfic->debug > 0 )
		SPF_debugf( "redirect:  getting SPF record:  %s",
			SPF_strerror( err ) );
		

	    if ( err == SPF_E_SUCCESS )
	    {
		/*
		 * find out whether this configuration passes
		 */
		
		SPF_set_cur_dom( spfcid, lookup );
		inc_out = SPF_eval_id( spfcid, c_results.spfid, spfdcid,
				       TRUE, FALSE, num_dns_mech );
		SPF_reset_c_results( &c_results );

		if ( spfic->debug > 0 )
		    SPF_debugf( "redirect:  executed SPF record:  %s  result: %s  reason: %s",
			    SPF_strerror( inc_out.err ),
			    SPF_strresult( inc_out.result ),
			    SPF_strreason( inc_out.reason ) );

		output = done( inc_out.result, inc_out.reason, inc_out.err );
		SPF_free_output( &inc_out );
		
		return output;
	    }
	    else if ( err == SPF_E_DNS_ERROR )
		return done( SPF_RESULT_ERROR, SPF_REASON_NONE, err );
	    else
		return done( mech->prefix_type, SPF_REASON_MECH, err );
	    
	    break;

	default:
	    return done( SPF_RESULT_UNKNOWN, SPF_REASON_NONE, SPF_E_UNKNOWN_MECH );
	    break;
	}
	    

	/*
	 * execute the local policy
	 */

	if ( mech == local_policy )
	{
	    inc_out = SPF_eval_id( spfcid, spfic->local_policy.spfid,
				   spfdcid, FALSE, FALSE, NULL );

	    if ( spfic->debug > 0 )
		SPF_debugf( "local_policy:  executed SPF record:  %s  result: %s  reason: %s",
			    SPF_strerror( inc_out.err ),
			    SPF_strresult( inc_out.result ),
			    SPF_strreason( inc_out.reason ) );
		
	    if ( inc_out.reason != SPF_REASON_DEFAULT )
	    {
		output = done( inc_out.result, SPF_REASON_LOCAL_POLICY,
			       inc_out.err );
		SPF_free_output( &inc_out );

		return output;
	    }
	    SPF_free_output( &inc_out );

	}
	
	mech = SPF_next_mech( mech );
    }

    /* falling off the end is the same as ?all */
    return done( SPF_RESULT_NEUTRAL, SPF_REASON_DEFAULT, SPF_E_SUCCESS );
}




syntax highlighted by Code2HTML, v. 0.9.1