@ -28,6 +28,8 @@
# include "resolver.h"
# include "hash.h"
# include "parse_dns.h"
# include <sys/socket.h>
# include <netdb.h>
# include <string.h>
@ -35,7 +37,7 @@
# include <resolv.h>
# include <arpa/inet.h>
# include <arpa/nameser.h>
# include <arpa/nameser_compat.h> // Darwin
//#include <arpa/nameser_compat.h> // Darwin only
# include <list>
@ -50,7 +52,6 @@ using std::list;
// (the limit is the # bits in dns_handle::srv_used)
# define MAX_SRV_RR (sizeof(unsigned int)*8)
struct ip_entry
: public dns_base_entry
{
@ -78,30 +79,43 @@ public:
: dns_entry ( )
{ }
int next_ip ( dns_handle * h , sockaddr_storage * sa ) ;
} ;
void init ( ) { } ;
int dns_ip_entry : : next_ip ( dns_handle * h , sockaddr_storage * sa )
{
if ( h - > ip_e ! = this ) {
if ( h - > ip_e ) dec_ref ( h - > ip_e ) ;
h - > ip_e = this ;
h - > ip_n = 0 ;
}
dns_base_entry * get_rr ( dns_record * rr , u_char * begin , u_char * end ) ;
int next_ip ( dns_handle * h , sockaddr_storage * sa )
{
if ( h - > ip_e ! = this ) {
if ( h - > ip_e ) dec_ref ( h - > ip_e ) ;
h - > ip_e = this ;
h - > ip_n = 0 ;
}
int & index = h - > ip_n ;
if ( index > = ( int ) ip_vec . size ( ) ) return - 1 ;
int & index = h - > ip_n ;
if ( index > = ( int ) ip_vec . size ( ) ) return - 1 ;
//copy address
( ( ip_entry * ) ip_vec [ index + + ] ) - > to_sa ( sa ) ;
//copy address
( ( ip_entry * ) ip_vec [ index + + ] ) - > to_sa ( sa ) ;
// reached the end?
if ( index > = ( int ) ip_vec . size ( ) ) {
index = - 1 ;
}
// reached the end?
if ( index > = ( int ) ip_vec . size ( ) ) {
index = - 1 ;
}
return 0 ;
}
return 0 ;
}
} ;
static bool srv_less ( const dns_base_entry * le , const dns_base_entry * re )
{
const srv_entry * l_srv = ( const srv_entry * ) le ;
const srv_entry * r_srv = ( const srv_entry * ) re ;
if ( l_srv - > p ! = r_srv - > p )
return l_srv - > p < r_srv - > p ;
else
return l_srv - > w < r_srv - > w ;
} ;
class dns_srv_entry
: public dns_entry
@ -111,106 +125,104 @@ public:
: dns_entry ( )
{ }
int next_ip ( dns_handle * h , sockaddr_storage * sa ) ;
} ;
int dns_srv_entry : : next_ip ( dns_handle * h , sockaddr_storage * sa )
{
int & index = h - > srv_n ;
if ( index > = ( int ) ip_vec . size ( ) ) return - 1 ;
if ( h - > srv_e ! = this ) {
if ( h - > srv_e ) dec_ref ( h - > srv_e ) ;
h - > srv_e = this ;
h - > srv_n = 0 ;
h - > srv_used = 0 ;
}
else if ( h - > ip_n ! = - 1 ) {
( ( sockaddr_in * ) sa ) - > sin_port = h - > port ;
return h - > ip_e - > next_ip ( h , sa ) ;
void init ( ) {
stable_sort ( ip_vec . begin ( ) , ip_vec . end ( ) , srv_less ) ;
}
// reset IP record
if ( h - > ip_e ) {
dec_ref ( h - > ip_e ) ;
h - > ip_e = NULL ;
h - > ip_n = 0 ;
}
list < pair < unsigned int , int > > srv_lst ;
int i = index ;
dns_base_entry * get_rr ( dns_record * rr , u_char * begin , u_char * end ) ;
// fetch current priority
unsigned short p = ( ( srv_entry * ) ip_vec [ i ] ) - > p ;
unsigned int w_sum = 0 ;
// and fetch records with same priority
// which have not been chosen yet
int srv_lst_size = 0 ;
unsigned int used_mask = ( 1 < < i ) ;
while ( ( p = = ( ( srv_entry * ) ip_vec [ i ] ) - > p ) ) {
int next_ip ( dns_handle * h , sockaddr_storage * sa )
{
int & index = h - > srv_n ;
if ( index > = ( int ) ip_vec . size ( ) ) return - 1 ;
DBG ( " used_mask & h->srv_used: %i & %i " , used_mask , h - > srv_used ) ;
if ( ! ( used_mask & h - > srv_used ) ) {
w_sum + = ( ( srv_entry * ) ip_vec [ i ] ) - > w ;
srv_lst. push_back ( std : : make_pair ( w_sum , i ) ) ;
srv_lst_size+ + ;
if ( h - > srv_e ! = this ) {
if ( h - > srv_e ) dec_ref ( h - > srv_e ) ;
h - > srv_e = this ;
h - > srv_n = 0 ;
h - > srv_used = 0 ;
}
if ( ( + + i > = ( int ) ip_vec . size ( ) ) | |
( i > = ( int ) MAX_SRV_RR ) ) {
break ;
else if ( h - > ip_n ! = - 1 ) {
( ( sockaddr_in * ) sa ) - > sin_port = h - > port ;
return h - > ip_e - > next_ip ( h , sa ) ;
}
DBG ( " (p==((srv_entry*)ip_vec[i])->p): %i, %i " , p , ( ( srv_entry * ) ip_vec [ i ] ) - > p ) ;
used_mask = used_mask < < 1 ;
}
srv_entry * e = NULL ;
DBG ( " srv_lst_size: %i " , srv_lst_size ) ;
if ( ( srv_lst_size > 1 ) & & w_sum ) {
// multiple records: apply weigthed load balancing
// - remember the entries which have already been used
unsigned int r = random ( ) % ( w_sum + 1 ) ;
DBG ( " random SRV lottery: %u / %u " , r , w_sum ) ;
list < pair < unsigned int , int > > : : iterator srv_lst_it = srv_lst . begin ( ) ;
while ( srv_lst_it ! = srv_lst . end ( ) ) {
if ( srv_lst_it - > first > = r ) {
DBG ( " h->srv_used: %i (before) " , h - > srv_used ) ;
h - > srv_used | = ( 1 < < ( srv_lst_it - > second ) ) ;
DBG ( " h->srv_used: %i (after) " , h - > srv_used ) ;
e = ( srv_entry * ) ip_vec [ srv_lst_it - > second ] ;
// reset IP record
if ( h - > ip_e ) {
dec_ref ( h - > ip_e ) ;
h - > ip_e = NULL ;
h - > ip_n = 0 ;
}
list < pair < unsigned int , int > > srv_lst ;
int i = index ;
// fetch current priority
unsigned short p = ( ( srv_entry * ) ip_vec [ i ] ) - > p ;
unsigned int w_sum = 0 ;
// and fetch records with same priority
// which have not been chosen yet
int srv_lst_size = 0 ;
unsigned int used_mask = ( 1 < < i ) ;
while ( ( p = = ( ( srv_entry * ) ip_vec [ i ] ) - > p ) ) {
if ( ! ( used_mask & h - > srv_used ) ) {
w_sum + = ( ( srv_entry * ) ip_vec [ i ] ) - > w ;
srv_lst . push_back ( std : : make_pair ( w_sum , i ) ) ;
srv_lst_size + + ;
}
if ( ( + + i > = ( int ) ip_vec . size ( ) ) | |
( i > = ( int ) MAX_SRV_RR ) ) {
break ;
}
+ + srv_lst_it ;
}
// should never trigger
assert ( e ) ;
}
else {
// single record or all weights == 0
e = ( srv_entry * ) ip_vec [ srv_lst . begin ( ) - > second ] ;
if ( ( i < ( int ) ip_vec . size ( ) ) & & ( i < ( int ) MAX_SRV_RR ) ) {
index = i ;
used_mask = used_mask < < 1 ;
}
else if ( ! w_sum ) {
index + + ;
srv_entry * e = NULL ;
if ( ( srv_lst_size > 1 ) & & w_sum ) {
// multiple records: apply weigthed load balancing
// - remember the entries which have already been used
unsigned int r = random ( ) % ( w_sum + 1 ) ;
list < pair < unsigned int , int > > : : iterator srv_lst_it = srv_lst . begin ( ) ;
while ( srv_lst_it ! = srv_lst . end ( ) ) {
if ( srv_lst_it - > first > = r ) {
h - > srv_used | = ( 1 < < ( srv_lst_it - > second ) ) ;
e = ( srv_entry * ) ip_vec [ srv_lst_it - > second ] ;
break ;
}
+ + srv_lst_it ;
}
// will only happen if the algorithm
// is broken
if ( ! e )
return - 1 ;
}
else {
index = - 1 ;
// single record or all weights == 0
e = ( srv_entry * ) ip_vec [ srv_lst . begin ( ) - > second ] ;
if ( ( i < ( int ) ip_vec . size ( ) ) & & ( i < ( int ) MAX_SRV_RR ) ) {
index = i ;
}
else if ( ! w_sum ) {
index + + ;
}
else {
index = - 1 ;
}
}
DBG ( " index = %i " , index ) ;
//TODO: find a solution for IPv6
h - > port = htons ( e - > port ) ;
( ( sockaddr_in * ) sa ) - > sin_port = h - > port ;
return resolver : : instance ( ) - > resolve_name ( e - > target . c_str ( ) , h , sa , IPv4 ) ;
}
//TODO: find a solution for IPv6
h - > port = htons ( e - > port ) ;
( ( sockaddr_in * ) sa ) - > sin_port = h - > port ;
return resolver : : instance ( ) - > resolve_name ( e - > target . c_str ( ) , h , sa , IPv4 ) ;
}
} ;
dns_entry : : dns_entry ( )
: dns_base_entry ( )
@ -219,7 +231,6 @@ dns_entry::dns_entry()
dns_entry : : ~ dns_entry ( )
{
DBG ( " ~dns_entry() " ) ;
for ( vector < dns_base_entry * > : : iterator it = ip_vec . begin ( ) ;
it ! = ip_vec . end ( ) ; + + it ) {
@ -227,6 +238,31 @@ dns_entry::~dns_entry()
}
}
dns_entry * dns_entry : : make_entry ( ns_type t )
{
switch ( t ) {
case ns_t_srv :
return new dns_srv_entry ( ) ;
case ns_t_a :
//case ns_t_aaaa:
return new dns_ip_entry ( ) ;
default :
return NULL ;
}
}
void dns_entry : : add_rr ( dns_record * rr , u_char * begin , u_char * end , long now )
{
dns_base_entry * e = get_rr ( rr , begin , end ) ;
if ( ! e ) return ;
e - > expire = rr - > ttl + now ;
if ( expire < e - > expire )
expire = e - > expire ;
ip_vec . push_back ( e ) ;
}
dns_bucket : : dns_bucket ( unsigned long id )
: dns_bucket_base ( id )
{
@ -294,17 +330,6 @@ dns_entry* dns_bucket::find(const string& name)
return e ;
}
_resolver : : _resolver ( )
: cache ( DNS_CACHE_SIZE )
{
start ( ) ;
}
_resolver : : ~ _resolver ( )
{
}
static void dns_error ( int error , const char * domain )
{
switch ( error ) {
@ -330,47 +355,11 @@ void ip_entry::to_sa(sockaddr_storage* sa)
memcpy ( & ( sa_in - > sin_addr ) , & addr , sizeof ( in_addr ) ) ;
}
static int collect_rr ( ns_msg * handle , ns_sect section , ns_type type ,
dns_entry * dns_e , long now ,
dns_base_entry * ( rr_to_entry ) ( ns_msg * , ns_rr * ) )
dns_base_entry * dns_ip_entry : : get_rr ( dns_record * rr , u_char * begin , u_char * end )
{
/*
* Look at all the resource records in this section .
*/
assert ( handle ) ;
assert ( dns_e ) ;
ns_rr rr ;
for ( int rrnum = 0 ; rrnum < ns_msg_count ( * handle , section ) ; rrnum + + ) {
/*
* Expand the resource record number rrnum into rr .
*/
if ( ns_parserr ( handle , section , rrnum , & rr ) ) {
ERROR ( " ns_parserr: %s \n " , strerror ( errno ) ) ;
continue ;
}
/*
* If the record type is correct , save the data into
* the proper entry .
*/
if ( ns_rr_type ( rr ) = = type ) {
dns_base_entry * new_entry = ( * rr_to_entry ) ( handle , & rr ) ;
if ( ! new_entry ) { continue ; }
new_entry - > expire = now + ns_rr_ttl ( rr ) ;
dns_e - > ip_vec . push_back ( new_entry ) ;
if ( dns_e - > expire < new_entry - > expire )
dns_e - > expire = new_entry - > expire ;
}
}
return 0 ;
}
if ( rr - > type ! = dns_r_a )
return NULL ;
static dns_base_entry * rr_to_a_entry ( ns_msg * , ns_rr * rr )
{
DBG ( " A: \t TTL=%i \t %s \t %i.%i.%i.%i \n " ,
ns_rr_ttl ( * rr ) ,
ns_rr_name ( * rr ) ,
@ -382,40 +371,36 @@ static dns_base_entry* rr_to_a_entry(ns_msg*, ns_rr* rr)
ip_entry * new_ip = new ip_entry ( ) ;
new_ip - > type = IPv4 ;
memcpy ( & ( new_ip - > addr ) , ns_rr_rdata ( * rr ) , sizeof ( in_addr ) ) ;
return new_ip ;
}
static int collect_a_rr ( ns_msg * handle , ns_sect section , dns_entry * dns_e , long now )
dns_base_entry * dns_srv_entry : : get_rr ( dns_record * rr , u_char * begin , u_char * end )
{
return collect_rr ( handle , section , ns_t_a , dns_e , now , rr_to_a_entry ) ;
}
if( rr - > type ! = dns_r_srv )
return NULL ;
static dns_base_entry * rr_to_srv_entry ( ns_msg * handle , ns_rr * rr )
{
char name_buf [ MAXDNAME ] ;
u_char name_buf [ NS_MAXDNAME ] ;
const u_char * rdata = ns_rr_rdata ( * rr ) ;
/* Expand the target's name */
if ( ns_name_uncompress (
ns_msg_base ( * handle ) , /* Start of the packet */
ns_msg_end ( * handle ) , /* End of the packet */
rdata + 6 , /* Position in the packet*/
name_buf , /* Result */
MAXDNAME ) /* Size of result buffer */
< 0 ) { /* Negative: error */
u_char * p = ( u_char * ) rdata + 6 ;
if ( dns_expand_name ( & p , begin , end ,
name_buf , /* Result */
NS_MAXDNAME ) /* Size of result buffer */
< 0 ) { /* Negative: error */
ERROR ( " ns_name_uncompress failed\n " ) ;
return NULL ;
ERROR ( " dns_expand_name failed \n " ) ;
return NULL ;
}
DBG ( " SRV: \t TTL=%i \t %s \t P=<%i> W=<%i> P=<%i> T=<%s> \n " ,
ns_rr_ttl ( * rr ) ,
ns_rr_name ( * rr ) ,
ns_get16 ( rdata ) ,
ns_get16 ( rdata + 2 ) ,
ns_get16 ( rdata + 4 ) ,
name_buf ) ;
ns_rr_ttl ( * rr ) ,
ns_rr_name ( * rr ) ,
ns_get16 ( rdata ) ,
ns_get16 ( rdata + 2 ) ,
ns_get16 ( rdata + 4 ) ,
name_buf ) ;
srv_entry * srv_r = new srv_entry ( ) ;
srv_r - > p = ns_get16 ( rdata ) ;
@ -426,90 +411,117 @@ static dns_base_entry* rr_to_srv_entry(ns_msg* handle, ns_rr* rr)
return srv_r ;
}
st atic bool srv_less ( const dns_base_entry * le , const dns_base_entry * re )
st ruct dns_entry_h
{
const srv_entry * l_srv = ( const srv_entry * ) le ;
const srv_entry * r_srv = ( const srv_entry * ) re ;
dns_entry * e ;
long now ;
} ;
if ( l_srv - > p ! = r_srv - > p )
return l_srv - > p < r_srv - > p ;
int rr_to_dns_entry ( dns_record * rr , dns_section_type t , u_char * begin , u_char * end , void * data )
{
dns_entry * dns_e = ( ( dns_entry_h * ) data ) - > e ;
long now = ( ( dns_entry_h * ) data ) - > now ;
if ( t = = dns_s_an )
dns_e - > add_rr ( rr , begin , end , now ) ;
// TODO: parse the additional section as well.
// there might be some A/AAAA records related to
// the SRV targets.
return 0 ;
}
dns_handle : : dns_handle ( )
: srv_e ( 0 ) , srv_n ( 0 ) , ip_e ( 0 ) , ip_n ( 0 )
{ }
dns_handle : : ~ dns_handle ( )
{
if ( ip_e )
dec_ref ( ip_e ) ;
if ( srv_e )
dec_ref ( srv_e ) ;
}
bool dns_handle : : valid ( )
{
return ( ip_e ) ;
}
bool dns_handle : : eoip ( )
{
if ( srv_e )
return ( srv_n = = - 1 ) & & ( ip_n = = - 1 ) ;
else
return l_srv - > w < r_srv - > w ;
} ;
return ( ip_n = = - 1 ) ;
}
static int collect_srv_rr ( ns_msg * handle , ns_sect section , dns_entry * dns_e , long now )
int dns_handle : : next_ip ( sockaddr_storage * sa )
{
int ret = collect_rr ( handle , section , ns_t_srv , dns_e , now , rr_to_srv_entry ) ;
if ( ! ret ) {
stable_sort ( dns_e - > ip_vec . begin ( ) , dns_e - > ip_vec . end ( ) , srv_less ) ;
}
if ( ! valid ( ) | | eoip ( ) ) return - 1 ;
return ret ;
if ( srv_e )
return srv_e - > next_ip ( this , sa ) ;
else
return ip_e - > next_ip ( this , sa ) ;
}
int _resolver : : query_dns ( const char * name , dns_entry * * e , long now )
_resolver : : _resolver ( )
: cache ( DNS_CACHE_SIZE )
{
start ( ) ;
}
_resolver : : ~ _resolver ( )
{
typedef union {
HEADER hdr ; /* defined in resolv.h */
u_char buf [ NS_PACKETSZ ] ; /* defined in arpa/nameser.h */
} dns_response ; /* response buffers */
}
int _resolver : : query_dns ( const char * name , dns_entry * * e , long now )
{
u_char dns_res [ NS_PACKETSZ ] ;
if ( ! name ) return - 1 ;
dns_response dns_res ;
ns_type t = ( name [ 0 ] = = ' _ ' ) ? ns_t_srv : ns_t_a ;
//TODO: add AAAA record support
int dns_res_len = res_search ( name , ns_c_in , t ,
( u_char * ) & dns_res . buf , sizeof ( dns_response ) ) ;
//TODO: add AAAA record support
int dns_res_len = res_search ( name , ns_c_in , t , dns_res , NS_PACKETSZ ) ;
if ( dns_res_len < 0 ) {
dns_error ( h_errno , name ) ;
return - 1 ;
}
* e = dns_entry : : make_entry ( t ) ;
/*
* Initialize a handle to this response . The handle will
* be used later to extract information from the response .
*/
ns_msg handle ;
if ( ns_initparse( dns_res . buf , dns_res_len , & handle ) < 0 ) {
ERROR( " ns_initparse: %s \n " , strerror ( errno ) ) ;
dns_entry_h dns_h = { * e , now } ;
if ( dns_msg_parse( dns_res , dns_res_len , rr_to_dns_entry , & dns_h ) < 0 ) {
DBG( " Could not parse DNS reply " ) ;
return - 1 ;
}
if ( ! ns_msg_count ( handle , ns_s_an ) ) {
// nothing in the answer section
* e = dns_h . e ;
if ( ! * e ) {
DBG ( " no dns_entry created " ) ;
return - 1 ;
}
int ret ;
switch ( t ) {
case ns_t_srv :
* e = new dns_srv_entry ( ) ;
ret = collect_srv_rr ( & handle , ns_s_an , * e , now ) ;
break ;
case ns_t_a :
* e = new dns_ip_entry ( ) ;
ret = collect_a_rr ( & handle , ns_s_an , * e , now ) ;
break ;
default :
ret = - 1 ;
break ;
}
// TODO: parse the additional section as well.
// there might be some A/AAAA records related to
// the SRV targets.
if ( ( ret < 0 ) | | ( * e ) - > ip_vec . empty ( ) ) {
delete * e ;
* e = NULL ;
return - 1 ;
if ( ( * e ) - > ip_vec . empty ( ) ) {
delete * e ;
* e = NULL ;
return - 1 ;
}
( * e ) - > init ( ) ;
inc_ref ( * e ) ;
return 0 ;
}
@ -552,13 +564,14 @@ int _resolver::resolve_name(const char* name,
return - 1 ;
}
// if ttl != 0
if ( e - > expire ! = tv_now . tv_sec ) {
// cache the new record
b - > insert ( name , e ) ;
}
if ( e ) {
// if ttl != 0
if ( e - > expire ! = tv_now . tv_sec ) {
// cache the new record
b - > insert ( name , e ) ;
}
// now we should have a valid IP
return e - > next_ip ( h , sa ) ;
}
@ -574,7 +587,6 @@ int _resolver::str2ip(const char* name,
if ( types & IPv4 ) {
int ret = inet_pton ( AF_INET , name , & ( ( sockaddr_in * ) sa ) - > sin_addr ) ;
if ( ret = = 1 ) {
DBG ( " inet_pton() succeeded " ) ;
( ( sockaddr_in * ) sa ) - > sin_family = AF_INET ;
return 1 ;
}
@ -587,7 +599,6 @@ int _resolver::str2ip(const char* name,
if ( types & IPv6 ) {
int ret = inet_pton ( AF_INET6 , name , & ( ( sockaddr_in6 * ) sa ) - > sin6_addr ) ;
if ( ret = = 1 ) {
DBG ( " inet_pton() succeeded " ) ;
( ( sockaddr_in6 * ) sa ) - > sin6_family = AF_INET6 ;
return 1 ;
}
@ -600,43 +611,6 @@ int _resolver::str2ip(const char* name,
return 0 ;
}
dns_handle : : dns_handle ( )
: srv_e ( 0 ) , srv_n ( 0 ) , ip_e ( 0 ) , ip_n ( 0 )
{ }
dns_handle : : ~ dns_handle ( )
{
DBG ( " ~dns_handle() " ) ;
if ( ip_e )
dec_ref ( ip_e ) ;
if ( srv_e )
dec_ref ( srv_e ) ;
}
bool dns_handle : : valid ( )
{
return ( ip_e ) ;
}
bool dns_handle : : eoip ( )
{
if ( srv_e )
return ( srv_n = = - 1 ) & & ( ip_n = = - 1 ) ;
else
return ( ip_n = = - 1 ) ;
}
int dns_handle : : next_ip ( sockaddr_storage * sa )
{
if ( ! valid ( ) | | eoip ( ) ) return - 1 ;
if ( srv_e )
return srv_e - > next_ip ( this , sa ) ;
else
return ip_e - > next_ip ( this , sa ) ;
}
void _resolver : : run ( )
{
for ( ; ; ) {
@ -660,14 +634,13 @@ void _resolver::run()
dns_bucket : : value_map : : iterator tmp_it = it ;
bool end_of_bucket = ( + + it = = bucket - > elmts . end ( ) ) ;
DBG ( " ########### expiring record %p ############# " , dns_e ) ;
bucket - > elmts . erase ( tmp_it ) ;
dec_ref ( dns_e ) ;
if ( end_of_bucket ) break ;
}
else {
DBG ( " ######### record %p expires in %li seconds ########## " , dns_e , it - > second - > expire - tv_now . tv_sec ) ;
//DBG(" ######### record %p expires in %li seconds ##########",dns_e,it->second->expire-tv_now.tv_sec);
}
}
bucket - > unlock ( ) ;