/*
* 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_MEMORY_H
#include <memory.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# 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"
#include "spf_alt/spf_dns_zone.h"
/*
* this is really little more than a proof-of-concept static zone.
*
* The static zone shouldn't just be an unsorted list that must be
* completely searched each time. Rather something should be done to
* allow quicker access. For example, sorting/bsearch, or red-black
* trees, or perfect hashes, or something.
*
* Note that wildcards mean that a domain could match more than one
* record. The most specific record should match.
*
* Also, SPF records could be byte-compiled.
*/
typedef struct
{
SPF_dns_rr_t **zone;
int num_zone;
int zone_buf_len;
SPF_dns_rr_t nxdomain;
} SPF_dns_zone_config_t;
static inline SPF_dns_zone_config_t *SPF_voidp2spfhook( void *hook )
{ return (SPF_dns_zone_config_t *)hook; }
static inline void *SPF_spfhook2voidp( SPF_dns_zone_config_t *spfhook )
{ return (void *)spfhook; }
static SPF_dns_rr_t *SPF_dns_find_zone( SPF_dns_config_t spfdcid, const char *domain, ns_type rr_type )
{
SPF_dns_iconfig_t *spfdic = SPF_dcid2spfdic( spfdcid );
SPF_dns_zone_config_t *spfhook = SPF_voidp2spfhook( spfdic->hook );
int i;
if ( strncmp( domain, "*.", 2 ) == 0 )
{
for( i = 0; i < spfhook->num_zone; i++ )
{
if ( spfhook->zone[i]->rr_type == rr_type
&& strcmp( spfhook->zone[i]->domain, domain ) == 0 )
return spfhook->zone[i];
}
} else {
size_t domain_len = strlen( domain );
for( i = 0; i < spfhook->num_zone; i++ )
{
if ( spfhook->zone[i]->rr_type != rr_type
&& spfhook->zone[i]->rr_type != ns_t_any )
continue;
if ( strncmp( spfhook->zone[i]->domain, "*.", 2 ) == 0 )
{
size_t zdomain_len = strlen( spfhook->zone[i]->domain ) - 2;
if ( zdomain_len <= domain_len
&& strcmp( spfhook->zone[i]->domain + 2,
domain + (domain_len - zdomain_len) ) == 0 )
return spfhook->zone[i];
} else {
if ( strcmp( spfhook->zone[i]->domain, domain ) == 0 )
return spfhook->zone[i];
}
}
}
return NULL;
}
static SPF_dns_rr_t *SPF_dns_lookup_zone( SPF_dns_config_t spfdcid, const char *domain, ns_type rr_type, int should_cache )
{
SPF_dns_iconfig_t *spfdic = SPF_dcid2spfdic( spfdcid );
SPF_dns_zone_config_t *spfhook = SPF_voidp2spfhook( spfdic->hook );
SPF_dns_rr_t *spfrr;
spfrr = SPF_dns_find_zone( spfdcid, domain, rr_type );
if ( spfrr )
return spfrr;
if ( spfdic->layer_below )
return SPF_dcid2spfdic( spfdic->layer_below )->lookup( spfdic->layer_below, domain, rr_type, should_cache );
else
return &spfhook->nxdomain;
}
SPF_dns_rr_t *SPF_dns_zone_add_str( SPF_dns_config_t spfdcid, const char *domain, ns_type rr_type, SPF_dns_stat_t herrno, const char *data )
{
SPF_dns_iconfig_t *spfdic = SPF_dcid2spfdic( spfdcid );
SPF_dns_zone_config_t *spfhook = SPF_voidp2spfhook( spfdic->hook );
SPF_dns_rr_t *spfrr;
int err;
int cnt;
/* try to find an existing record */
spfrr = SPF_dns_find_zone( spfdcid, domain, rr_type );
/* create a new record */
if ( spfrr == NULL )
{
spfrr = SPF_dns_make_rr( spfdcid, domain, rr_type, 24*60*60, herrno );
if ( spfrr == NULL )
return NULL;
if ( spfhook->num_zone == spfhook->zone_buf_len )
{
int new_len;
SPF_dns_rr_t **new_zone;
int i;
new_len = spfhook->zone_buf_len
+ (spfhook->zone_buf_len >> 2) + 4;
new_zone = realloc( spfhook->zone,
new_len * sizeof( *new_zone ) );
if ( new_zone == NULL )
return NULL;
for( i = spfhook->zone_buf_len; i < new_len; i++ )
new_zone[i] = NULL;
spfhook->zone_buf_len = new_len;
spfhook->zone = new_zone;
}
spfhook->zone[ spfhook->num_zone ] = spfrr;
spfhook->num_zone++;
if ( rr_type == ns_t_any )
{
if ( data )
SPF_error( "RR type ANY can not have data.");
if ( herrno == NETDB_SUCCESS )
SPF_error( "RR type ANY must return a DNS error code.");
}
if ( herrno != NETDB_SUCCESS )
return spfrr;
}
/*
* initialize stuff
*/
cnt = spfrr->num_rr;
switch( rr_type )
{
case ns_t_a:
if ( SPF_dns_rr_buf_malloc( spfrr, cnt,
sizeof( spfrr->rr[cnt]->a ) ) != SPF_E_SUCCESS )
return spfrr;
err = inet_pton( AF_INET, data, &spfrr->rr[cnt]->a );
if ( err <= 0 )
return NULL;
break;
case ns_t_aaaa:
if ( SPF_dns_rr_buf_malloc( spfrr, cnt,
sizeof( spfrr->rr[cnt]->aaaa ) ) != SPF_E_SUCCESS )
return spfrr;
err = inet_pton( AF_INET6, data, &spfrr->rr[cnt]->aaaa );
if ( err <= 0 )
return NULL;
break;
case ns_t_mx:
if ( SPF_dns_rr_buf_malloc( spfrr, cnt,
strlen( data ) + 1 ) != SPF_E_SUCCESS )
return spfrr;
strcpy( spfrr->rr[cnt]->mx, data );
break;
case ns_t_txt:
if ( SPF_dns_rr_buf_malloc( spfrr, cnt,
strlen( data ) + 1 ) != SPF_E_SUCCESS )
return spfrr;
strcpy( spfrr->rr[cnt]->txt, data );
break;
case ns_t_ptr:
if ( SPF_dns_rr_buf_malloc( spfrr, cnt,
strlen( data ) + 1 ) != SPF_E_SUCCESS )
return spfrr;
strcpy( spfrr->rr[cnt]->ptr, data );
break;
case ns_t_any:
if ( data )
SPF_error( "RR type ANY can not have data.");
if ( herrno == NETDB_SUCCESS )
SPF_error( "RR type ANY must return a DNS error code.");
SPF_error( "RR type ANY can not have multiple RR.");
break;
default:
SPF_error( "Invalid RR type" );
break;
}
spfrr->num_rr = cnt + 1;
return spfrr;
}
SPF_dns_config_t SPF_dns_create_config_zone( SPF_dns_config_t layer_below, const char *name )
{
SPF_dns_iconfig_t *spfdic;
SPF_dns_zone_config_t *spfhook;
spfdic = malloc( sizeof( *spfdic ) );
if ( spfdic == NULL )
return NULL;
spfdic->hook = malloc( sizeof( SPF_dns_zone_config_t ) );
if ( spfdic->hook == NULL )
{
free( spfdic );
return NULL;
}
spfdic->destroy = SPF_dns_destroy_config_zone;
spfdic->lookup = SPF_dns_lookup_zone;
spfdic->get_spf = NULL;
spfdic->get_exp = NULL;
spfdic->add_cache = NULL;
spfdic->layer_below = layer_below;
if ( name )
spfdic->name = name;
else
spfdic->name = "zone";
spfhook = SPF_voidp2spfhook( spfdic->hook );
spfhook->zone_buf_len = 32;
spfhook->num_zone = 0;
spfhook->zone = calloc( spfhook->zone_buf_len, sizeof( *spfhook->zone ) );
if ( spfhook->zone == NULL )
{
free( spfdic );
return NULL;
}
spfhook->nxdomain = SPF_dns_nxdomain;
spfhook->nxdomain.source = SPF_spfdic2dcid( spfdic );
return SPF_spfdic2dcid( spfdic );
}
void SPF_dns_reset_config_zone( SPF_dns_config_t spfdcid )
{
SPF_dns_iconfig_t *spfdic = SPF_dcid2spfdic( spfdcid );
SPF_dns_zone_config_t *spfhook = SPF_voidp2spfhook( spfdic->hook );
int i;
if ( spfdcid == NULL )
SPF_error( "spfdcid is NULL" );
if ( spfhook == NULL )
SPF_error( "spfdcid.hook is NULL" );
if ( spfhook->zone == NULL )
SPF_error( "spfdcid.hook->zone is NULL" );
for( i = 0; i < spfhook->zone_buf_len; i++ )
{
if ( spfhook->zone[i] )
SPF_dns_reset_rr( spfhook->zone[i] );
}
}
void SPF_dns_destroy_config_zone( SPF_dns_config_t spfdcid )
{
SPF_dns_iconfig_t *spfdic = SPF_dcid2spfdic( spfdcid );
SPF_dns_zone_config_t *spfhook = SPF_voidp2spfhook( spfdic->hook );
int i;
if ( spfdcid == NULL )
SPF_error( "spfdcid is NULL" );
if ( spfhook )
{
for( i = 0; i < spfhook->zone_buf_len; i++ )
{
if ( spfhook->zone[i] )
SPF_dns_destroy_rr( spfhook->zone[i] );
}
if ( spfhook->zone ) free( spfhook->zone );
free( spfhook );
}
free( spfdic );
}
syntax highlighted by Code2HTML, v. 0.9.1