From 5120770a4ea66238c95d884ac36a17df0c981fc3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 30 Aug 2010 17:53:45 +0200 Subject: [PATCH] introduced a new DNS cache - added aotmic_types for lock-free operations - simple DNS cache - support for SRV records (work in progress...) --- core/Makefile | 2 + core/sip/Makefile | 14 +- core/sip/atomic_types.h | 137 +++++++++ core/sip/hash_table.h | 110 +++++++- core/sip/resolver.cpp | 582 +++++++++++++++++++++++++++++++++++++-- core/sip/resolver.h | 97 ++++++- core/sip/sip_parser.h | 5 +- core/sip/sip_trans.cpp | 9 +- core/sip/sip_trans.h | 9 +- core/sip/trans_layer.cpp | 9 +- core/sip/trans_table.cpp | 6 +- core/sip/trans_table.h | 1 + core/sip/wheeltimer.cpp | 14 +- 13 files changed, 914 insertions(+), 81 deletions(-) create mode 100644 core/sip/atomic_types.h diff --git a/core/Makefile b/core/Makefile index 1ed45eaa..ec05a9dc 100644 --- a/core/Makefile +++ b/core/Makefile @@ -50,6 +50,8 @@ deps: $(DEPS) COREPATH=. include ../Makefile.defs +LDFLAGS += -lresolv +CPPFLAGS += -I$(COREPATH) # implicit rules %.o : %.cpp %.d ../Makefile.defs diff --git a/core/sip/Makefile b/core/sip/Makefile index 7208067c..a53790a8 100644 --- a/core/sip/Makefile +++ b/core/sip/Makefile @@ -5,8 +5,6 @@ HDRS=$(SRCS:.cpp=.h) OBJS=$(SRCS:.cpp=.o) DEPS=$(SRCS:.cpp=.d) -CPPFLAGS += -I.. - .PHONY: all all: ../../Makefile.defs -@$(MAKE) deps && \ @@ -20,17 +18,19 @@ clean: .PHONY: deps deps: $(DEPS) -COREPATH=.. -include ../../Makefile.defs +COREPATH = .. +include $(COREPATH)/../Makefile.defs + +CPPFLAGS += -I$(COREPATH) # implicit rules -%.o : %.cpp %.d ../../Makefile.defs +%.o : %.cpp %.d $(COREPATH)/../Makefile.defs $(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS) -%.d : %.cpp %.h ../../Makefile.defs +%.d : %.cpp %.h $(COREPATH)/../Makefile.defs $(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@ -$(LIBNAME): $(OBJS) ../../Makefile.defs +$(LIBNAME): $(OBJS) $(COREPATH)/../Makefile.defs -@echo "" -@echo "making $(LIBNAME)" $(AR) rvs $(LIBNAME) $(OBJS) diff --git a/core/sip/atomic_types.h b/core/sip/atomic_types.h new file mode 100644 index 00000000..8ecd1b47 --- /dev/null +++ b/core/sip/atomic_types.h @@ -0,0 +1,137 @@ +#ifndef _atomic_types_h_ +#define _atomic_types_h_ + +#if defined(__GNUC__) +# if defined(__GNUC_PATCHLEVEL__) +# define __GNUC_VERSION__ (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +# else +# define __GNUC_VERSION__ (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100) +# endif +#else +#error Unsupported compiler +#endif + +#if __GNUC_VERSION__ < 40101 +#error GCC version >= 4.1.1 is required for proper atomic operations +#endif + +#include +#include "log.h" + +// 32 bit unsigned integer +class atomic_int +{ + volatile unsigned int i; + +public: + atomic_int() : i(0) {} + + void set(unsigned int val) { + i = val; + } + + unsigned int get() { + return i; + } + + // ++i; + unsigned int inc() { + return __sync_add_and_fetch(&i,1); + } + + // --i; + unsigned int dec() { + return __sync_sub_and_fetch(&i,1); + } + + // return --ll != 0; + bool dec_and_test() { + return dec() == 0; + }; +}; + +// 64 bit unsigned integer +class atomic_int64 +{ + volatile unsigned long long ll; + +public: + void set(unsigned long long val) { +#ifndef __LP64__ || !__LP64__ + unsigned long long tmp_ll; + do { + tmp_ll = ll; + } + while(!__sync_bool_compare_and_swap(&ll, tmp_ll, val)); +#else + ll = val; +#endif + } + + unsigned long long get() { +#ifndef __LP64__ || !__LP64__ + unsigned long long tmp_ll; + do { + tmp_ll = ll; + } + while(!__sync_bool_compare_and_swap(&ll, tmp_ll, tmp_ll)); + + return tmp_ll; +#else + return ll; +#endif + } + + // returns ++ll; + unsigned long long inc() { + return __sync_add_and_fetch(&ll,1); + } + + // returns --ll; + unsigned long long dec() { + return __sync_sub_and_fetch(&ll,1); + } + + // return --ll == 0; + bool dec_and_test() { + return dec() == 0; + }; +}; + +class atomic_ref_cnt; +void inc_ref(atomic_ref_cnt* rc); +void dec_ref(atomic_ref_cnt* rc); + +class atomic_ref_cnt + : protected atomic_int +{ +protected: + atomic_ref_cnt() + : atomic_int() {} + + virtual ~atomic_ref_cnt() {} + + friend void inc_ref(atomic_ref_cnt* rc); + friend void dec_ref(atomic_ref_cnt* rc); +}; + +inline void inc_ref(atomic_ref_cnt* rc) +{ + assert(rc); + rc->inc(); + //DBG("after inc_ref(%p): ref_cnt = %u",rc,rc->get()); +} + +inline void dec_ref(atomic_ref_cnt* rc) +{ + assert(rc); + //DBG("before dec_ref(%p): ref_cnt = %u",rc,rc->get()); + if(rc->dec_and_test()) + delete rc; +} + + +#endif diff --git a/core/sip/hash_table.h b/core/sip/hash_table.h index dce8864c..5bd1fb60 100644 --- a/core/sip/hash_table.h +++ b/core/sip/hash_table.h @@ -29,12 +29,13 @@ #ifndef _hash_table_h #define _hash_table_h -#include "cstring.h" -#include "../AmThread.h" -#include "../log.h" +#include "AmThread.h" +#include "log.h" #include +#include using std::list; +using std::map; struct sip_trans; struct sip_msg; @@ -70,13 +71,12 @@ public: * if it was still present. */ void remove(Value* t) { - typename value_list::iterator it = find(t); + typename value_list::iterator it = find(t); - if(it != elmts.end()){ - elmts.erase(it); - delete t; - DBG("~sip_trans()\n"); - } + if(it != elmts.end()){ + elmts.erase(it); + delete t; + } } /** @@ -123,13 +123,88 @@ protected: value_list elmts; }; -template +template +class ht_map_bucket: public AmMutex +{ +public: + typedef map value_map; + + ht_map_bucket(unsigned long id) : id(id) {} + ~ht_map_bucket() {} + + /** + * Caution: The bucket MUST be locked before you can + * do anything with it. + */ + + /** + * Searches for the value ptr in this bucket. + * This is used to check if the value + * still exists. + * + * @return true if the value still exists. + */ + bool exist(const Key& k) { + return find(k) != elmts.end(); + } + + /** + * Remove the value from this bucket, + * if it was still present. + */ + void remove(const Key& k) { + typename value_map::iterator it = find(k); + + if(it != elmts.end()){ + Value* v = it->second; + elmts.erase(it); + delete v; + } + } + + /** + * Returns the bucket id, which should be an index + * into the corresponding hash table. + */ + unsigned long get_id() const { + return id; + } + + // debug method + void dump() const { + + if(elmts.empty()) + return; + + DBG("*** Bucket ID: %i ***\n",(int)get_id()); + + for(typename value_map::const_iterator it = elmts.begin(); it != elmts.end(); ++it) { + + (*it)->dump(); + } + } + +protected: + typename value_map::iterator find(const Key& k) + { + return elmts.find(k); + } + + unsigned long id; + value_map elmts; +}; + +template class hash_table { - Bucket* _table[size]; + unsigned long size; + Bucket** _table; public: - hash_table() { + hash_table(unsigned long size) + : size(size) + { + _table = new Bucket* [size]; for(unsigned long i=0; idump(); + } } }; diff --git a/core/sip/resolver.cpp b/core/sip/resolver.cpp index 7662b81a..16321d0d 100644 --- a/core/sip/resolver.cpp +++ b/core/sip/resolver.cpp @@ -26,16 +26,245 @@ */ #include "resolver.h" +#include "hash.h" +#include #include #include +#include +#include +#include +#include +#include // Darwin + +#include + +using std::pair; +using std::list; #include "log.h" -#include -#include +struct ip_entry + : public dns_base_entry +{ + address_type type; + in_addr addr; + + void to_sa(sockaddr_storage* sa); +}; + +struct srv_entry + : public dns_base_entry +{ + unsigned short p; + unsigned short w; + + unsigned short port; + string target; +}; + +class dns_ip_entry + : public dns_entry +{ +public: + dns_ip_entry() + : dns_entry() + {} + + int next_ip(dns_handle* h, sockaddr_storage* sa); +}; + +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; + } + + int& index = h->ip_n; + if(index >= (int)ip_vec.size()) return -1; + + //copy address + ((ip_entry*)ip_vec[index++])->to_sa(sa); + + // reached the end? + if(index >= (int)ip_vec.size()) { + index = -1; + } + + return 0; +} + +class dns_srv_entry + : public dns_entry +{ +public: + dns_srv_entry() + : 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; + } + else if(h->ip_n != -1){ + ((sockaddr_in*)sa)->sin_port = h->port; + return h->ip_e->next_ip(h,sa); + } + + // reset IP record + if(h->ip_e){ + dec_ref(h->ip_e); + h->ip_e = NULL; + h->ip_n = 0; + } + + list > srv_lst; + int i = index; + + // fetch current priority + unsigned short p = ((srv_entry*)ip_vec[i])->p; + unsigned int w_sum = ((srv_entry*)ip_vec[i])->w; + srv_lst.push_back(std::make_pair(w_sum,i)); + + // and fetch records with same priority + while( (++i != (int)ip_vec.size()) && + (p==((srv_entry*)ip_vec[i])->p) ){ + + w_sum += ((srv_entry*)ip_vec[i])->w; + srv_lst.push_back(std::make_pair(w_sum,i)); + } + + srv_entry* e=NULL; + if((i - index > 1) && w_sum){ + // multiple records: apply weigthed load balancing + + // TODO: + // - generate random number + // - pick the first record with cum. sum >= random number + + unsigned int r = rand() % (w_sum+1); + + list >::iterator srv_lst_it = srv_lst.begin(); + while(srv_lst_it != srv_lst.end()){ + if(srv_lst_it->first >= r){ + //TODO: add this entry to some "already tried" list + e = (srv_entry*)ip_vec[srv_lst_it->second]; + } + } + + // should never trigger + assert(e); + } + else { + // single record or all weights == 0 + e = (srv_entry*)ip_vec[index]; + if(++index >= (int)ip_vec.size()){ + h->srv_n = -1; + } + } + + //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() +{ +} + +dns_entry::~dns_entry() +{ + DBG("~dns_entry()"); + for(vector::iterator it = ip_vec.begin(); + it != ip_vec.end(); ++it) { + + delete *it; + } +} + +dns_bucket::dns_bucket(unsigned long id) + : dns_bucket_base(id) +{ +} + +bool dns_bucket::insert(const string& name, dns_entry* e) +{ + if(!e) return false; + + lock(); + if(!(elmts.insert(std::make_pair(name,e)).second)){ + // if insertion failed + unlock(); + return false; + } + + inc_ref(e); + unlock(); + + return true; +} + +bool dns_bucket::remove(const string& name) +{ + lock(); + value_map::iterator it = elmts.find(name); + if(it != elmts.end()){ + + dns_entry* e = it->second; + elmts.erase(it); + + dec_ref(e); + unlock(); + + return true; + } + + unlock(); + return false; +} + + +dns_entry* dns_bucket::find(const string& name) +{ + lock(); + value_map::iterator it = elmts.find(name); + if(it == elmts.end()){ + unlock(); + return NULL; + } + + dns_entry* e = it->second; + + timeval now; + gettimeofday(&now,NULL); + if(now.tv_sec >= e->expire){ + elmts.erase(it); + dec_ref(e); + unlock(); + return NULL; + } + + inc_ref(e); + unlock(); + return e; +} _resolver::_resolver() + : cache(DNS_CACHE_SIZE) { } @@ -45,41 +274,334 @@ _resolver::~_resolver() } -int _resolver::resolve_name(const char* name, sockaddr_storage* sa, - const address_type types, const proto_type protos) +static void dns_error(int error, const char* domain) { - struct addrinfo hints,*res=0; - memset(&hints,0,sizeof(hints)); + switch(error){ + case HOST_NOT_FOUND: + DBG("Unknown domain: %s\n", domain); + break; + case NO_DATA: + DBG("No records for %s\n", domain); + break; + case TRY_AGAIN: + DBG("No response for query (try again)\n"); + break; + default: + ERROR("Unexpected error\n"); + break; + } +} + +void ip_entry::to_sa(sockaddr_storage* sa) +{ + sockaddr_in* sa_in = (sockaddr_in*)sa; + sa_in->sin_family = AF_INET; + 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*)) +{ + /* + * 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; + } - int err=0; - if(types & IPv4){ + /* + * 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; +} - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; +static dns_base_entry* rr_to_a_entry(ns_msg*, ns_rr* rr) +{ + DBG("A:\tTTL=%i\t%s\t%i.%i.%i.%i\n", + ns_rr_ttl(*rr), + ns_rr_name(*rr), + ns_rr_rdata(*rr)[0], + ns_rr_rdata(*rr)[1], + ns_rr_rdata(*rr)[2], + ns_rr_rdata(*rr)[3]); + + 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) +{ + return collect_rr(handle,section,ns_t_a,dns_e,now,rr_to_a_entry); +} + +static dns_base_entry* rr_to_srv_entry(ns_msg* handle, ns_rr* rr) +{ + char name_buf[MAXDNAME]; + const u_char * rdata = ns_rr_rdata(*rr); - err = getaddrinfo(name,NULL,&hints,&res); - - if(err){ - switch(err){ - case EAI_AGAIN: - case EAI_NONAME: - ERROR("Could not resolve '%s'\n",name); - break; - default: - ERROR("getaddrinfo('%s'): %s\n", - name,gai_strerror(err)); - break; - } - - err = -1; + /* 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 */ + + ERROR("ns_name_uncompress failed\n"); + return NULL; + } + + printf("SRV:\tTTL=%i\t%s\tP=<%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); + + srv_entry* srv_r = new srv_entry(); + srv_r->p = ns_get16(rdata); + srv_r->w = ns_get16(rdata+2); + srv_r->port = ns_get16(rdata+4); + srv_r->target = (const char*)name_buf; + + return srv_r; +} + +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; +}; + +static int collect_srv_rr(ns_msg* handle, ns_sect section, dns_entry* dns_e, long now) +{ + 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); + } + + return ret; +} + + +int _resolver::query_dns(const char* name, dns_entry** e, long now) +{ + typedef union { + HEADER hdr; /* defined in resolv.h */ + u_char buf[NS_PACKETSZ]; /* defined in arpa/nameser.h */ + } dns_response; /* response buffers */ + + 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)); + + if(dns_res_len < 0){ + dns_error(h_errno,name); + return -1; + } + + /* + * 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)); + return -1; + } + + if(!ns_msg_count(handle,ns_s_an)) { + // nothing in the answer section + 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; + } + + if((ret < 0) || (*e)->ip_vec.empty()){ + delete *e; + *e = NULL; + + return -1; + } + + inc_ref(*e); + return 0; +} + +int _resolver::resolve_name(const char* name, + dns_handle* h, + sockaddr_storage* sa, + const address_type types) +{ + int ret; + + // already have a valid handle? + if(h->valid()){ + if(h->eoip()) return -1; + return h->next_ip(sa); + } + + // first try to detect if 'name' is already an IP address + ret = str2ip(name,sa,types); + if(ret == 1) { + h->ip_n = -1; // flag end of IP list + h->srv_n = -1; + return 0; // 'name' is an IP address + } + + // name is NOT an IP address -> try a cache look up + dns_bucket* b = cache.get_bucket(hashlittle(name,strlen(name),0)); + dns_entry* e = b->find(name); + + // first attempt to get a valid IP + // (from the cache) + if(e){ + return e->next_ip(h,sa); + } + + timeval tv_now; + gettimeofday(&tv_now,NULL); + + // no valid IP, query the DNS + if(query_dns(name,&e,tv_now.tv_sec) < 0) { + // DNS query failed + WARN("DNS query failed"); + return -1; + } + + // if ttl != 0 + if(e->expire != tv_now.tv_sec){ + // cache the new record + b->insert(name,e); + } + + if(e) { + // now we should have a valid IP + return e->next_ip(h,sa); + } + + // should not happen... + return -1; +} + +int _resolver::str2ip(const char* name, + sockaddr_storage* sa, + const address_type types) +{ + 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; } - else { - memcpy(sa,res->ai_addr,res->ai_addrlen); - freeaddrinfo(res); + else if(ret < 0) { + ERROR("while trying to detect an IPv4 address '%s': %s",name,strerror(errno)); + return ret; } } - return err; + + 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; + } + else if(ret < 0) { + ERROR("while trying to detect an IPv6 address '%s': %s",name,strerror(errno)); + return ret; + } + } + + 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_n) + 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); } /** EMACS ** diff --git a/core/sip/resolver.h b/core/sip/resolver.h index b90ac121..b91ec418 100644 --- a/core/sip/resolver.h +++ b/core/sip/resolver.h @@ -28,11 +28,21 @@ #define _resolver_h_ #include "singleton.h" +#include "hash_table.h" +#include "atomic_types.h" -struct sockaddr_storage; +#include +#include +using std::string; +using std::vector; + +#include + +#define DNS_CACHE_SIZE 128 enum address_type { + IPnone=0, IPv4=1, IPv6=2 }; @@ -43,16 +53,95 @@ enum proto_type { UDP=2 }; -class _resolver +struct dns_handle; + +struct dns_base_entry +{ + long int expire; + + dns_base_entry() + :expire(0) + {} + + virtual ~dns_base_entry() {} +}; + +class dns_entry + : public atomic_ref_cnt, + public dns_base_entry { public: - int resolve_name(const char* name, sockaddr_storage* sa, - const address_type types, const proto_type protos); + vector ip_vec; + + dns_entry(); + virtual ~dns_entry(); + + virtual int next_ip(dns_handle* h, sockaddr_storage* sa)=0; +}; + +typedef ht_map_bucket dns_bucket_base; + +class dns_bucket + : protected dns_bucket_base +{ +public: + dns_bucket(unsigned long id); + bool insert(const string& name, dns_entry* e); + bool remove(const string& name); + dns_entry* find(const string& name); +}; +typedef hash_table dns_cache; + +class dns_srv_entry; +class dns_ip_entry; + +struct dns_handle +{ + dns_handle(); + ~dns_handle(); + + bool valid(); + bool eoip(); + + int next_ip(sockaddr_storage* sa); + +private: + friend class _resolver; + friend class dns_entry; + friend class dns_srv_entry; + friend class dns_ip_entry; + + dns_srv_entry* srv_e; + int srv_n; + unsigned short port; + + dns_ip_entry* ip_e; + int ip_n; +}; + +class _resolver +{ +public: + int resolve_name(const char* name, + dns_handle* h, + sockaddr_storage* sa, + const address_type types); + protected: _resolver(); ~_resolver(); + int query_dns(const char* name, + dns_entry** e, + long now); + + int str2ip(const char* name, + sockaddr_storage* sa, + const address_type types); + +private: + dns_cache cache; }; typedef singleton<_resolver> resolver; diff --git a/core/sip/sip_parser.h b/core/sip/sip_parser.h index e60f89a0..6df13872 100644 --- a/core/sip/sip_parser.h +++ b/core/sip/sip_parser.h @@ -30,6 +30,7 @@ #include "cstring.h" #include "parse_uri.h" +#include "resolver.h" #include using std::list; @@ -41,6 +42,7 @@ struct sip_request; struct sip_reply; struct sip_header; struct sip_via_parm; +struct dns_handle; // // SIP message types: @@ -125,7 +127,8 @@ struct sip_msg sockaddr_storage local_ip; sockaddr_storage remote_ip; - + dns_handle h_dns; + sip_msg(); sip_msg(const char* msg_buf, int msg_len); ~sip_msg(); diff --git a/core/sip/sip_trans.cpp b/core/sip/sip_trans.cpp index e7c43373..bf6a4119 100644 --- a/core/sip/sip_trans.cpp +++ b/core/sip/sip_trans.cpp @@ -35,7 +35,14 @@ #include -int _timer_type_lookup[] = { -1, 0,1,2, 0,1,2, 0,1,2, 0,2 }; +int _timer_type_lookup[] = { + -1, // STIMER_INVALID + 0,1,2, // STIMER_A, STIMER_B, STIMER_D + 0,1,2, // STIMER_E, STIMER_F, STIMER_K + 0,1,2, // STIMER_G, STIMER_H, STIMER_I + 0, // STIMER_J + 2 // STIMER_L; shares the same slot as STIMER_D +}; inline timer** fetch_timer(unsigned int timer_type, timer** base) { diff --git a/core/sip/sip_trans.h b/core/sip/sip_trans.h index 1ae1b68f..f47ea9e9 100644 --- a/core/sip/sip_trans.h +++ b/core/sip/sip_trans.h @@ -86,7 +86,7 @@ enum sip_timer_type { // This timer is not defined by // RFC 3261. But it is needed // to handle 200 ACKs automatically - // in UAC transactions. + // in INVITE client transactions. STIMER_L // Terminated_200 -> Terminated }; @@ -128,10 +128,12 @@ class sip_trans sent/received reply */ int reply_status; - /** Transaction state */ int state; + /** used by UAS only; keeps RSeq of last sent reliable 1xx */ + unsigned int last_rseq; + /** * Retransmission buffer * - UAC transaction: ACK @@ -142,9 +144,6 @@ class sip_trans /** Length of the retransmission buffer */ int retr_len; - /** used by UAS only; keeps RSeq of last sent reliable 1xx */ - unsigned int last_rseq; - /** Destination for retransmissions */ sockaddr_storage retr_addr; diff --git a/core/sip/trans_layer.cpp b/core/sip/trans_layer.cpp index f0e9f271..7fc34433 100644 --- a/core/sip/trans_layer.cpp +++ b/core/sip/trans_layer.cpp @@ -533,7 +533,6 @@ int _trans_layer::set_next_hop(sip_msg* msg) list& route_hdrs = msg->route; cstring& r_uri = msg->u.request->ruri_str; - sockaddr_storage* remote_ip = &msg->remote_ip; cstring next_hop; unsigned short next_port = 0; @@ -682,13 +681,15 @@ int _trans_layer::set_next_hop(sip_msg* msg) DBG("next_hop:next_port is <%.*s:%u>\n", next_hop.len, next_hop.s, next_port); - err = resolver::instance()->resolve_name(c2stlstr(next_hop).c_str(),remote_ip,IPv4,UDP); + err = resolver::instance()->resolve_name(c2stlstr(next_hop).c_str(), + &(msg->h_dns), + &(msg->remote_ip),IPv4); if(err < 0){ ERROR("Unresolvable Request URI\n"); return -1; } - ((sockaddr_in*)remote_ip)->sin_port = htons(next_port); + ((sockaddr_in*)&(msg->remote_ip))->sin_port = htons(next_port); return 0; } @@ -804,6 +805,7 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt) } memcpy(&p_msg->remote_ip,&msg->remote_ip,sizeof(sockaddr_storage)); + memcpy(&p_msg->h_dns,&msg->h_dns,sizeof(dns_handle)); DBG("Sending to %s:%i <%.*s...>\n", get_addr_str(((sockaddr_in*)&p_msg->remote_ip)->sin_addr).c_str(), @@ -1283,6 +1285,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* t->state = TS_COMPLETED; t->clear_timer(STIMER_E); + t->clear_timer(STIMER_F); t->reset_timer(STIMER_K, K_TIMER, bucket->get_id()); if(t->msg->u.request->method != sip_request::CANCEL) diff --git a/core/sip/trans_table.cpp b/core/sip/trans_table.cpp index f6777b76..2216e806 100644 --- a/core/sip/trans_table.cpp +++ b/core/sip/trans_table.cpp @@ -44,7 +44,7 @@ // Global transaction table // //trans_bucket _trans_table[H_TABLE_ENTRIES]; -hash_table _trans_table; +hash_table _trans_table(H_TABLE_ENTRIES); trans_bucket::trans_bucket(unsigned long id) : ht_bucket::ht_bucket(id) @@ -494,13 +494,13 @@ void compute_branch(char* branch/*[8]*/, const cstring& callid, const cstring& c trans_bucket* get_trans_bucket(const cstring& callid, const cstring& cseq_num) { - return /*&*/_trans_table[hash(callid,cseq_num)]; + return _trans_table[hash(callid,cseq_num)]; } trans_bucket* get_trans_bucket(unsigned int h) { assert(h < H_TABLE_ENTRIES); - return /*&*/_trans_table[h]; + return _trans_table[h]; } void dumps_transactions() diff --git a/core/sip/trans_table.h b/core/sip/trans_table.h index 3e7dd501..c934b943 100644 --- a/core/sip/trans_table.h +++ b/core/sip/trans_table.h @@ -2,6 +2,7 @@ #define _trans_table_h_ #include "hash_table.h" +#include "cstring.h" #define H_TABLE_POWER 10 #define H_TABLE_ENTRIES (1<