/*
* 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"
/*
* helper functions
*/
void SPF_dns_destroy_config( SPF_dns_config_t spfdcid )
{
SPF_dcid2spfdic( spfdcid )->destroy( spfdcid );
}
SPF_dns_rr_t *SPF_dns_lookup( SPF_dns_config_t spfdcid,
const char *domain, ns_type rr_type, int should_cache )
{
SPF_dns_rr_t *spfrr;
spfrr = SPF_dcid2spfdic( spfdcid )->lookup( spfdcid, domain, rr_type, should_cache );
if ( spfrr == NULL )
SPF_error( "SPF DNS layer return NULL during a lookup." );
return spfrr;
}
SPF_dns_rr_t *SPF_dns_rlookup( SPF_dns_config_t spfdcid,
struct in_addr ipv4, ns_type rr_type, int should_cache )
{
union
{
struct in_addr ipv4;
unsigned char x[4];
} tmp;
char domain[ sizeof( "111.222.333.444.in-addr.arpa" ) ];
SPF_dns_rr_t *spfrr;
if ( spfdcid == NULL )
SPF_error( "spfdcid is NULL" );
/*
* make sure the scratch buffer is big enough
*/
tmp.ipv4 = ipv4;
snprintf( domain, sizeof( domain ), "%d.%d.%d.%d.in-addr.arpa",
tmp.x[3], tmp.x[2], tmp.x[1], tmp.x[0] );
spfrr = SPF_dcid2spfdic( spfdcid )->lookup( spfdcid, domain, rr_type, should_cache );
if ( spfrr == NULL )
SPF_error( "SPF DNS layer return NULL during a rlookup." );
return spfrr;
}
SPF_dns_rr_t *SPF_dns_rlookup6( SPF_dns_config_t spfdcid,
struct in6_addr ipv6, ns_type rr_type, int should_cache )
{
char domain[ sizeof( struct in6_addr ) * 4 + sizeof( ".ip6.arpa" ) + 1 ]; /* nibbles */
char *p, *p_end;
int i;
SPF_dns_rr_t *spfrr;
if ( spfdcid == NULL )
SPF_error( "spfdcid is NULL" );
p = domain;
p_end = p + sizeof( domain );
for( i = sizeof( struct in6_addr ) - 1; i >= 0; i-- )
{
p += snprintf( p, p_end - p, "%.1x.%.1x.",
ipv6.s6_addr[i] & 0xf,
ipv6.s6_addr[i] >> 4 );
}
/* squash the final '.' */
p += snprintf( p, p_end - p, "ip6.arpa" );
spfrr = SPF_dcid2spfdic( spfdcid )->lookup( spfdcid, domain, rr_type, should_cache );
if ( spfrr == NULL )
SPF_error( "SPF DNS layer return NULL during a rlookup6." );
return spfrr;
}
SPF_dns_rr_t *SPF_dns_make_rr( SPF_dns_config_t spfdcid, const char *domain,
ns_type rr_type, int ttl,
SPF_dns_stat_t herrno )
{
SPF_dns_rr_t *spfrr;
spfrr = SPF_dns_create_rr();
if ( spfrr == NULL )
return spfrr;
spfrr->source = spfdcid;
if ( domain )
{
spfrr->domain = strdup( domain );
if ( spfrr == NULL )
{
free( spfrr );
return NULL;
}
spfrr->domain_buf_len = strlen( domain ) + 1;
} else {
spfrr->domain = NULL;
spfrr->domain_buf_len = 0;
}
spfrr->rr_type = rr_type;
spfrr->ttl = ttl;
spfrr->herrno = herrno;
return spfrr;
}
SPF_dns_rr_t *SPF_dns_create_rr()
{
SPF_dns_rr_t *spfrr;
spfrr = calloc( 1, sizeof( *spfrr ) );
if ( spfrr == NULL )
return spfrr;
SPF_dns_reset_rr( spfrr );
return spfrr;
}
void SPF_dns_reset_rr( SPF_dns_rr_t *spfrr )
{
if ( spfrr == NULL )
return;
if ( spfrr->domain )
spfrr->domain[0] = '\0';
spfrr->rr_type = ns_t_invalid;
spfrr->num_rr = 0;
spfrr->ttl = 0;
spfrr->utc_ttl = 0;
spfrr->herrno = HOST_NOT_FOUND;
}
SPF_err_t SPF_dns_rr_buf_malloc( SPF_dns_rr_t *dst, int i, size_t len )
{
if ( dst->rr_buf_num <= i )
{
SPF_dns_rr_data_t **new_data;
size_t *new_buf_len;
int new_num;
int j;
/* allocate lots so we don't have to remalloc often */
new_num = dst->rr_buf_num + (i + (i >> 2) + 4 );
new_data = realloc( dst->rr, new_num * sizeof( *new_data ) );
if ( new_data == NULL )
return SPF_E_NO_MEMORY;
dst->rr = new_data;
new_buf_len = realloc( dst->rr_buf_len,
new_num * sizeof( *new_buf_len ) );
if ( new_buf_len == NULL )
return SPF_E_NO_MEMORY;
dst->rr_buf_len = new_buf_len;
for( j = dst->rr_buf_num; j < new_num; j++ )
{
dst->rr[j] = NULL;
dst->rr_buf_len[j] = 0;
}
dst->rr_buf_num = new_num;
}
if ( dst->rr_buf_len[i] >= len )
return SPF_E_SUCCESS;
dst->rr_buf_len[i] = len;
if ( dst->rr_buf_len[i] < sizeof( *dst->rr[i] ) )
dst->rr_buf_len[i] = sizeof( *dst->rr[i] );
dst->rr[i] = realloc( dst->rr[i], dst->rr_buf_len[i] );
if ( dst->rr[i] == NULL )
return SPF_E_NO_MEMORY;
return SPF_E_SUCCESS;
}
SPF_err_t SPF_dns_copy_rr( SPF_dns_rr_t *dst, SPF_dns_rr_t *src )
{
int i;
SPF_err_t err;
if ( src == NULL )
SPF_error( "src is NULL" );
if ( dst == NULL )
SPF_error( "dst is NULL" );
if ( src->domain && src->domain[0] != '\0' )
{
char *new_domain;
size_t new_len = strlen( src->domain ) + 1;
if ( dst->domain_buf_len < new_len )
{
new_domain = realloc( dst->domain, new_len );
if ( new_domain == NULL )
return SPF_E_NO_MEMORY;
dst->domain = new_domain;
dst->domain_buf_len = new_len;
}
strcpy( dst->domain, src->domain );
}
else if ( dst->domain )
dst->domain[0] = '\0';
dst->rr_type = src->rr_type;
dst->ttl = src->ttl;
dst->utc_ttl = src->utc_ttl;
dst->herrno = src->herrno;
dst->source = src->source;
dst->num_rr = src->num_rr;
for( i = dst->num_rr - 1; i >= 0; i-- )
{
switch( dst->rr_type )
{
case ns_t_a:
err = SPF_dns_rr_buf_malloc( dst, i, sizeof( *dst->rr[i] ) );
if ( err )
return err;
dst->rr[i]->a = src->rr[i]->a;
break;
case ns_t_ptr:
err = SPF_dns_rr_buf_malloc( dst, i,
strlen( src->rr[i]->ptr ) + 1 );
if ( err )
return err;
strcpy( dst->rr[i]->ptr, src->rr[i]->ptr );
break;
case ns_t_mx:
err = SPF_dns_rr_buf_malloc( dst, i,
strlen( src->rr[i]->mx ) + 1 );
if ( err )
return err;
strcpy( dst->rr[i]->mx, src->rr[i]->mx );
break;
case ns_t_txt:
err = SPF_dns_rr_buf_malloc( dst, i,
strlen( src->rr[i]->txt ) + 1 );
if ( err )
return err;
strcpy( dst->rr[i]->txt, src->rr[i]->txt );
break;
case ns_t_aaaa:
err = SPF_dns_rr_buf_malloc( dst, i, sizeof( *dst->rr[i] ) );
if ( err )
return err;
dst->rr[i]->aaaa = src->rr[i]->aaaa;
break;
default:
break;
}
}
return SPF_E_SUCCESS;
}
SPF_dns_rr_t *SPF_dns_dup_rr( SPF_dns_rr_t *orig )
{
SPF_err_t err;
SPF_dns_rr_t *spfrr;
if ( orig == NULL )
return NULL;
spfrr = SPF_dns_create_rr();
if ( spfrr == NULL )
return NULL;
err = SPF_dns_copy_rr( spfrr, orig );
if ( err )
{
SPF_dns_destroy_rr( spfrr );
return NULL;
}
return spfrr;
}
void SPF_dns_destroy_rr_var( SPF_dns_rr_t *spfrr )
{
int i;
SPF_dns_reset_rr( spfrr );
if ( spfrr->domain ) free( spfrr->domain );
if ( spfrr->rr )
{
for( i = 0; i < spfrr->rr_buf_num; i++ )
if ( spfrr->rr[i] ) free( spfrr->rr[i] );
free( spfrr->rr );
}
if ( spfrr->rr_buf_len ) free( spfrr->rr_buf_len );
if ( spfrr->hook )
free( spfrr->hook );
}
void SPF_dns_destroy_rr( SPF_dns_rr_t *spfrr )
{
SPF_dns_destroy_rr_var( spfrr );
free( spfrr );
}
/*
* Set the SMPT client domain name
*/
void SPF_set_client_dom( SPF_config_t spfcid, SPF_dns_config_t spfdcid )
{
SPF_iconfig_t *spfic = SPF_cid2spfic(spfcid);
SPF_dns_rr_t *rr_ptr;
SPF_dns_rr_t *rr_a;
SPF_dns_rr_t *rr_aaaa;
int i, j;
int max_ptr;
if ( spfcid == NULL )
SPF_error( "spfcid is NULL" );
if ( spfdcid == NULL )
SPF_error( "spfdcid is NULL" );
if ( spfic->client_dom )
return;
/*
* The "p" macro expands to the validated domain name of the SMTP
* client. The validation procedure is described in section 5.4. If
* there are no validated domain names, the word "unknown" is
* substituted. If multiple validated domain names exist, the first one
* returned in the PTR result is chosen.
*
*
* sending-host_names := ptr_lookup(sending-host_IP);
* for each name in (sending-host_names) {
* IP_addresses := a_lookup(name);
* if the sending-host_IP is one of the IP_addresses {
* validated_sending-host_names += name;
* } }
*/
if ( spfic->client_ver == AF_INET )
{
rr_ptr = SPF_dns_dup_rr( SPF_dns_rlookup( spfdcid, spfic->ipv4, ns_t_ptr, FALSE ) );
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, FALSE );
for( j = 0; j < rr_a->num_rr; j++ )
{
if ( rr_a->rr[j]->a.s_addr == spfic->ipv4.s_addr )
{
spfic->client_dom = strdup( rr_ptr->rr[i]->ptr );
SPF_dns_destroy_rr( rr_ptr );
return;
}
}
}
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, FALSE ) );
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, FALSE );
for( j = 0; j < rr_aaaa->num_rr; j++ )
{
if ( memcmp( &rr_aaaa->rr[j]->aaaa, &spfic->ipv6,
sizeof( spfic->ipv6 ) ) == 0 )
{
spfic->client_dom = strdup( rr_ptr->rr[i]->ptr );
SPF_dns_destroy_rr( rr_ptr );
return;
}
}
}
SPF_dns_destroy_rr( rr_ptr );
}
spfic->client_dom = strdup( "unknown" );
}
SPF_dns_rr_t SPF_dns_nxdomain =
{(char *)"", 0, ns_t_any, 0, NULL, NULL, 0, 0, 0, HOST_NOT_FOUND, NULL, NULL };
syntax highlighted by Code2HTML, v. 0.9.1