Browse Source

- dns cache support (ser will cache both positive and negative responses if
turned on, see doc/dns.txt for more details & config options)
- dns failover support: when a name resolves to more then 1 ip (either
multiple A or AAAA records or multiple SRVs) and sending to the first
ip fails, ser will retry with the others. By default is off. See
doc/dns.txt for more details/config options a.s.o.
- destination blacklist: when sending to some destination (defined by
ip:port and protocol) fails, ser will temporarily add this destination
in a blacklist giving future sends the opportunity of immediately
failing. Destination are also added to the blacklist on tm invite
timeouts (when no response is received in the fr_timer interval).
By default is off, see doc/dst_blacklist.txt form more details/config
options.
- small makefile fixes (in mode=debug)
- resolver get_record api changes (updated enum)

WARNING: there are a lot of changes in tm

Andrei Pelinescu-Onciul 19 years ago
parent
commit
dcb59e67b4
37 changed files with 4772 additions and 176 deletions
  1. 15 2
      Makefile.defs
  2. 24 3
      NEWS
  3. 12 18
      action.c
  4. 42 0
      cfg.lex
  5. 61 1
      cfg.y
  6. 12 1
      clist.h
  7. 43 0
      core_cmd.c
  8. 2436 0
      dns_cache.c
  9. 287 0
      dns_cache.h
  10. 41 0
      dns_wrappers.h
  11. 173 0
      doc/dns.txt
  12. 69 0
      doc/dst_blacklist.txt
  13. 494 0
      dst_blacklist.c
  14. 55 0
      dst_blacklist.h
  15. 2 0
      error.h
  16. 165 42
      forward.c
  17. 2 1
      forward.h
  18. 18 0
      globals.h
  19. 50 4
      main.c
  20. 20 0
      modules/tm/h_table.c
  21. 9 3
      modules/tm/h_table.h
  22. 20 5
      modules/tm/t_funcs.c
  23. 236 17
      modules/tm/t_fwd.c
  24. 8 0
      modules/tm/t_fwd.h
  25. 30 3
      modules/tm/t_msgbuilder.c
  26. 45 9
      modules/tm/t_reply.c
  27. 54 2
      modules/tm/timer.c
  28. 29 1
      modules/tm/uac.c
  29. 125 12
      modules/tm/ut.h
  30. 1 1
      msg_translator.h
  31. 75 32
      resolve.c
  32. 32 9
      resolve.h
  33. 26 8
      socket_info.c
  34. 9 1
      socket_info.h
  35. 23 0
      ut.h
  36. 9 0
      utils/sercmd/sercmd.c
  37. 20 1
      version.h

+ 15 - 2
Makefile.defs

@@ -66,7 +66,7 @@ MAIN_NAME=ser
 VERSION = 0
 VERSION = 0
 PATCHLEVEL = 10
 PATCHLEVEL = 10
 SUBLEVEL =   99
 SUBLEVEL =   99
-EXTRAVERSION = -dev42
+EXTRAVERSION = -dev43-dns_cache
 
 
 SER_VER = $(shell expr $(VERSION) \* 1000000 + $(PATCHLEVEL) \* 1000 + \
 SER_VER = $(shell expr $(VERSION) \* 1000000 + $(PATCHLEVEL) \* 1000 + \
 			$(SUBLEVEL) )
 			$(SUBLEVEL) )
@@ -339,6 +339,14 @@ endif
 #		compiles in checks and use for maddr parameter in uri.
 #		compiles in checks and use for maddr parameter in uri.
 #		Required to support Windows Messenger 5.x over TCP connection
 #		Required to support Windows Messenger 5.x over TCP connection
 #		which (mis)uses this parameter.
 #		which (mis)uses this parameter.
+# -DUSE_DNS_CACHE
+#		use an internal dns cache instead of making dns requests each time
+# -DUSE_DNS_FAILOVER
+#		if the destination resolves to multiple ips, on send error fall back
+#		to the others
+# -DUSE_DST_BLACKLIST
+#		blacklist bad destination (timeout, failed to connect, error sending
+#        a.s.o)
 
 
 # Sometimes is needes correct non-quoted $OS. HACK: gcc translates known OS to number ('linux'), so there is added underscore
 # Sometimes is needes correct non-quoted $OS. HACK: gcc translates known OS to number ('linux'), so there is added underscore
 
 
@@ -356,6 +364,9 @@ DEFS+= $(extra_defs) \
 	 -DDISABLE_NAGLE \
 	 -DDISABLE_NAGLE \
 	 -DHAVE_RESOLV_RES \
 	 -DHAVE_RESOLV_RES \
 	 -DDBG_QM_MALLOC \
 	 -DDBG_QM_MALLOC \
+	 -DUSE_DNS_CACHE \
+	 -DUSE_DNS_FAILOVER \
+	 -DUSE_DST_BLACKLIST \
 	 #-DF_MALLOC \
 	 #-DF_MALLOC \
 	 #-DDBG_F_MALLOC \
 	 #-DDBG_F_MALLOC \
 	 #-DNO_DEBUG \
 	 #-DNO_DEBUG \
@@ -1092,7 +1103,6 @@ $(error 			Unsupported compiler ($(CC):$(CC_NAME)), try gcc)
 endif		#CC_NAME, gcc
 endif		#CC_NAME, gcc
 endif	#ARCH, ppc 
 endif	#ARCH, ppc 
 
 
-CFLAGS+= $(CC_EXTRA_OPTS)
 
 
 
 
 # setting LDFLAGS
 # setting LDFLAGS
@@ -1122,6 +1132,7 @@ endif
 else	#mode,release
 else	#mode,release
 ifeq	($(CC_NAME), gcc)
 ifeq	($(CC_NAME), gcc)
 		CFLAGS=-g -Wcast-align $(PROFILE)
 		CFLAGS=-g -Wcast-align $(PROFILE)
+		DEFS+=-DCC_GCC_LIKE_ASM
 ifeq		($(ARCH), sparc64)
 ifeq		($(ARCH), sparc64)
 			DEFS+=SPARC64_MODE
 			DEFS+=SPARC64_MODE
 			CFLAGS+= -mcpu=ultrasparc -m64
 			CFLAGS+= -mcpu=ultrasparc -m64
@@ -1138,6 +1149,7 @@ else
 endif
 endif
 endif
 endif
 ifeq	($(CC_NAME), icc)
 ifeq	($(CC_NAME), icc)
+		DEFS+=-DCC_GCC_LIKE_ASM
 		CFLAGS=-g  $(PROFILE)
 		CFLAGS=-g  $(PROFILE)
 		LDFLAGS+=-g -Wl,-E $(PROFILE)
 		LDFLAGS+=-g -Wl,-E $(PROFILE)
 		MOD_LDFLAGS=-shared $(LDFLAGS)
 		MOD_LDFLAGS=-shared $(LDFLAGS)
@@ -1150,6 +1162,7 @@ endif
 
 
 endif #mode=release
 endif #mode=release
 
 
+CFLAGS+= $(CC_EXTRA_OPTS)
 
 
 #*FLAGS used for compiling the modules
 #*FLAGS used for compiling the modules
 ifeq	($(CC_NAME), gcc)
 ifeq	($(CC_NAME), gcc)

+ 24 - 3
NEWS

@@ -24,7 +24,8 @@ modules:
                 hashing after an uri (to, from or request uri)
                 hashing after an uri (to, from or request uri)
               - improved uri hashing (password is ignored, port is used only
               - improved uri hashing (password is ignored, port is used only
                 if != 5060 or 5061)
                 if != 5060 or 5061)
- - tm        - migrated to the new timers (tm timers completely rewritten)
+ - tm        - dns failover and dst blacklist support
+             - migrated to the new timers (tm timers completely rewritten)
              - improved speed and less memory usage
              - improved speed and less memory usage
              - much more precise reptransmissions timing
              - much more precise reptransmissions timing
              - params: - retr_timer1p1, retr_timer1p2, retr_timer1p3 removed
              - params: - retr_timer1p1, retr_timer1p2, retr_timer1p3 removed
@@ -49,6 +50,11 @@ modules:
               Vias a.s.o) and not on the original message
               Vias a.s.o) and not on the original message
  
  
 core:
 core:
+ - dns cache and dns failover support added (see doc/dns.txt)
+ - destination blacklist added -- destinations to which forwarding fails
+   (send error, tm timeout a.s.o) are temporarily added to a blacklist which 
+    is consulted before each send => faster send error detection
+    in the near future (see doc/dst_blacklist.txt)
  - default log level switched to 0 (only messages < L_WARN will be printed
  - default log level switched to 0 (only messages < L_WARN will be printed
    by default)
    by default)
  - separate memdbg log level which controls the memory/malloc related
  - separate memdbg log level which controls the memory/malloc related
@@ -113,7 +119,7 @@ core:
  - tcp: - improved  performance (io event handling), using OS specific
  - tcp: - improved  performance (io event handling), using OS specific
            optimizations
            optimizations
         - 1024 connections limit removed (see tcp_max_connections)
         - 1024 connections limit removed (see tcp_max_connections)
--  resolver: - timeouts, retries a.s.o can no be set from ser.cfg
+-  resolver: - timeouts, retries a.s.o can now be set from ser.cfg
              (see below dns_* and man resolv.conf(6)).
              (see below dns_* and man resolv.conf(6)).
              The maximum time a dns request can take (before failing) is:
              The maximum time a dns request can take (before failing) is:
               (dns_retr_time*dns_retr_no)*(search_list_domains)
               (dns_retr_time*dns_retr_no)*(search_list_domains)
@@ -130,6 +136,21 @@ core:
    are available (see tm docs)
    are available (see tm docs)
 - avps directly accessible from script with %avp_name (variable style)
 - avps directly accessible from script with %avp_name (variable style)
 new config variables:
 new config variables:
+   use_dns_cache = on | off  (default on)  
+   use_dns_failover = on | off (default off)
+   dns_cache_flags = number (default 0)
+   dns_cache_negative_ttl = number in seconds (default 60)
+   dns_cache_min_ttl = time in seconds (default 0)
+   dns_cache_max_ttl = time in seconds (default MAXINT)
+   dns_cache_mem = maximum memory used for the dns cache in Kb (default 500 K)
+   dns_cache_gc_interval = interval in seconds after which the dns cache is
+      garbage collected (default: 120 s)
+   use_dst_blacklist = on | off (default off)
+   dst_blacklist_expire = time in s (default 60)
+   dst_blacklist_mem = maximum memory used for the blacklist in Kb (default 250
+      K)
+   dst_blacklist_gc_interval = interval in seconds after which the destination 
+      blacklist is garbage collected (default 60)
    tos = number  - ip type of service (TOS) value
    tos = number  - ip type of service (TOS) value
    dns_try_ipv6 = yes/no - if yes and a dns lookup fails, it will retry it
    dns_try_ipv6 = yes/no - if yes and a dns lookup fails, it will retry it
       for ipv6 (AAAA record). Default: yes
       for ipv6 (AAAA record). Default: yes
@@ -138,7 +159,7 @@ new config variables:
       (usually 5s).
       (usually 5s).
    dns_retr_no = no. - number of dns retransmissions before giving up.
    dns_retr_no = no. - number of dns retransmissions before giving up.
       Default: see above (usually 4)
       Default: see above (usually 4)
-   dns_server_no = no. - how many dns servers from the ones defined in 
+   dns_servers_no = no. - how many dns servers from the ones defined in 
       /etc/resolv.conf will be used. Default: all of them.
       /etc/resolv.conf will be used. Default: all of them.
    dns_use_search_list= yes/no - if no, the search list in /etc/resolv.conf
    dns_use_search_list= yes/no - if no, the search list in /etc/resolv.conf
       will be ignored (=> fewer lookups => gives up faster). Default: yes.
       will be ignored (=> fewer lookups => gives up faster). Default: yes.

+ 12 - 18
action.c

@@ -41,6 +41,7 @@
  *  2005-12-12  return & drop/exit differentiation (andrei)
  *  2005-12-12  return & drop/exit differentiation (andrei)
  *  2005-12-19  select framework (mma)
  *  2005-12-19  select framework (mma)
  *  2006-04-12  updated *_send() calls to use a struct dest_info (andrei)
  *  2006-04-12  updated *_send() calls to use a struct dest_info (andrei)
+ *  2006-07-27  dns cache and dns based send address failover support (andrei)
  */
  */
 
 
 
 
@@ -107,6 +108,7 @@ int do_action(struct action* a, struct sip_msg* msg)
 	unsigned short port;
 	unsigned short port;
 	unsigned short flags;
 	unsigned short flags;
 	int_str name, value;
 	int_str name, value;
+	str* dst_host;
 
 
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
 	   functions to return with error (status<0) and not setting it
 	   functions to return with error (status<0) and not setting it
@@ -208,32 +210,24 @@ int do_action(struct action* a, struct sip_msg* msg)
 #endif
 #endif
 				}
 				}
 
 
-#ifdef HONOR_MADDR
-				if (u->maddr_val.s && u->maddr_val.len) {
-					if (sip_hostport2su(&dst.to, &u->maddr_val, port, dst.proto)<0){
-						LOG(L_ERR, "ERROR:  bad maddr param in uri,"
-								" dropping packet\n");
-						ret=E_BAD_ADDRESS;
-						goto error_fwd_uri;
-					}
-				} else
+#ifdef HONOR_MADDR				
+				if (u->maddr_val.s && u->maddr_val.len)
+					dst_host=&u->maddr_val;
+				else
 #endif
 #endif
-				if (sip_hostport2su(&dst.to, &u->host, port, dst.proto)<0){
-					LOG(L_ERR, "ERROR:  bad host name in uri,"
-							" dropping packet\n");
-					ret=E_BAD_ADDRESS;
-					goto error_fwd_uri;
-				}
+					dst_host=&u->host;
 #ifdef USE_COMP
 #ifdef USE_COMP
 				dst.comp=u->comp;
 				dst.comp=u->comp;
 #endif
 #endif
-				ret=forward_request(msg, &dst);
-				if (ret>=0) ret=1;
+				ret=forward_request(msg, dst_host, port, &dst);
+				if (ret>=0){
+					ret=1;
+				}
 			}else if ((a->val[0].type==PROXY_ST) && (a->val[1].type==NUMBER_ST)){
 			}else if ((a->val[0].type==PROXY_ST) && (a->val[1].type==NUMBER_ST)){
 				if (dst.proto==PROTO_NONE)
 				if (dst.proto==PROTO_NONE)
 					dst.proto=msg->rcv.proto;
 					dst.proto=msg->rcv.proto;
 				proxy2su(&dst.to,  (struct proxy_l*)a->val[0].u.data);
 				proxy2su(&dst.to,  (struct proxy_l*)a->val[0].u.data);
-				ret=forward_request(msg, &dst);
+				ret=forward_request(msg, 0, 0, &dst);
 				if (ret>=0){
 				if (ret>=0){
 					ret=1;
 					ret=1;
 					proxy_mark((struct proxy_l*)a->val[0].u.data, ret);
 					proxy_mark((struct proxy_l*)a->val[0].u.data, ret);

+ 42 - 0
cfg.lex

@@ -60,6 +60,8 @@
  *              to_{ip,port} (andrei)
  *              to_{ip,port} (andrei)
  *  2005-12-12  separated drop, exit, break, return, added RETCODE (andrei)
  *  2005-12-12  separated drop, exit, break, return, added RETCODE (andrei)
  *  2005-12-19  select framework (mma)
  *  2005-12-19  select framework (mma)
+ * 2006-09-11  added dns cache (use, flags, ttls, mem ,gc) & dst blacklist
+ *              options (andrei)
  */
  */
 
 
 
 
@@ -228,6 +230,22 @@ DNS_RETR_TIME	dns_retr_time
 DNS_RETR_NO		dns_retr_no
 DNS_RETR_NO		dns_retr_no
 DNS_SERVERS_NO	dns_servers_no
 DNS_SERVERS_NO	dns_servers_no
 DNS_USE_SEARCH	dns_use_search_list
 DNS_USE_SEARCH	dns_use_search_list
+/* dns cache */
+DNS_USE_CACHE	use_dns_cache
+DNS_USE_FAILOVER	use_dns_failover
+DNS_CACHE_FLAGS		dns_cache_flags
+DNS_CACHE_NEG_TTL	dns_cache_negative_ttl
+DNS_CACHE_MIN_TTL	dns_cache_min_ttl
+DNS_CACHE_MAX_TTL	dns_cache_max_ttl
+DNS_CACHE_MEM		dns_cache_mem
+DNS_CACHE_GC_INT	dns_cache_gc_interval
+/* blacklist */
+USE_DST_BLST		use_dst_blacklist
+DST_BLST_MEM		dst_blacklist_mem
+DST_BLST_TTL		dst_blacklist_expire|dst_blacklist_ttl
+DST_BLST_GC_INT		dst_blacklist_gc_interval
+
+
 PORT	port
 PORT	port
 STAT	statistics
 STAT	statistics
 MAXBUFFER maxbuffer
 MAXBUFFER maxbuffer
@@ -422,6 +440,30 @@ EAT_ABLE	[\ \t\b\r]
 								return DNS_SERVERS_NO; }
 								return DNS_SERVERS_NO; }
 <INITIAL>{DNS_USE_SEARCH}	{ count(); yylval.strval=yytext;
 <INITIAL>{DNS_USE_SEARCH}	{ count(); yylval.strval=yytext;
 								return DNS_USE_SEARCH; }
 								return DNS_USE_SEARCH; }
+<INITIAL>{DNS_USE_CACHE}	{ count(); yylval.strval=yytext;
+								return DNS_USE_CACHE; }
+<INITIAL>{DNS_USE_FAILOVER}	{ count(); yylval.strval=yytext;
+								return DNS_USE_FAILOVER; }
+<INITIAL>{DNS_CACHE_FLAGS}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_FLAGS; }
+<INITIAL>{DNS_CACHE_NEG_TTL}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_NEG_TTL; }
+<INITIAL>{DNS_CACHE_MIN_TTL}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_MIN_TTL; }
+<INITIAL>{DNS_CACHE_MAX_TTL}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_MAX_TTL; }
+<INITIAL>{DNS_CACHE_MEM}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_MEM; }
+<INITIAL>{DNS_CACHE_GC_INT}	{ count(); yylval.strval=yytext;
+								return DNS_CACHE_GC_INT; }
+<INITIAL>{USE_DST_BLST}	{ count(); yylval.strval=yytext;
+								return USE_DST_BLST; }
+<INITIAL>{DST_BLST_MEM}	{ count(); yylval.strval=yytext;
+								return DST_BLST_MEM; }
+<INITIAL>{DST_BLST_TTL}	{ count(); yylval.strval=yytext;
+								return DST_BLST_TTL; }
+<INITIAL>{DST_BLST_GC_INT}	{ count(); yylval.strval=yytext;
+								return DST_BLST_GC_INT; }
 <INITIAL>{PORT}	{ count(); yylval.strval=yytext; return PORT; }
 <INITIAL>{PORT}	{ count(); yylval.strval=yytext; return PORT; }
 <INITIAL>{STAT}	{ count(); yylval.strval=yytext; return STAT; }
 <INITIAL>{STAT}	{ count(); yylval.strval=yytext; return STAT; }
 <INITIAL>{MAXBUFFER}	{ count(); yylval.strval=yytext; return MAXBUFFER; }
 <INITIAL>{MAXBUFFER}	{ count(); yylval.strval=yytext; return MAXBUFFER; }

+ 61 - 1
cfg.y

@@ -72,6 +72,8 @@
  * 2006-02-02  named flags support (andrei)
  * 2006-02-02  named flags support (andrei)
  * 2006-02-06  named routes support (andrei)
  * 2006-02-06  named routes support (andrei)
  * 2006-05-30  avp flags (tma)
  * 2006-05-30  avp flags (tma)
+ * 2006-09-11  added dns cache (use, flags, ttls, mem ,gc) & dst blacklist
+ *              options (andrei)
  */
  */
 
 
 %{
 %{
@@ -117,6 +119,26 @@
 		if (rt!=ONSEND_ROUTE) yyerror( s " allowed only in onsend_routes");\
 		if (rt!=ONSEND_ROUTE) yyerror( s " allowed only in onsend_routes");\
 	}while(0)
 	}while(0)
 
 
+
+#ifdef USE_DNS_CACHE
+	#define IF_DNS_CACHE(x) x
+#else
+	#define IF_DNS_CACHE(x) warn("dns cache support not compiled in")
+#endif
+
+#ifdef USE_DNS_FAILOVER
+	#define IF_DNS_FAILOVER(x) x
+#else
+	#define IF_DNS_FAILOVER(x) warn("dns failover support not compiled in")
+#endif
+
+#ifdef USE_DST_BLACKLIST
+	#define IF_DST_BLACKLIST(x) x
+#else
+	#define IF_DST_BLACKLIST(x) warn("dst blacklist support not compiled in")
+#endif
+
+
 extern int yylex();
 extern int yylex();
 static void yyerror(char* s);
 static void yyerror(char* s);
 static char* tmp;
 static char* tmp;
@@ -234,6 +256,20 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token DNS_RETR_NO
 %token DNS_RETR_NO
 %token DNS_SERVERS_NO
 %token DNS_SERVERS_NO
 %token DNS_USE_SEARCH
 %token DNS_USE_SEARCH
+%token DNS_USE_CACHE
+%token DNS_USE_FAILOVER
+%token DNS_CACHE_FLAGS
+%token DNS_CACHE_NEG_TTL
+%token DNS_CACHE_MIN_TTL
+%token DNS_CACHE_MAX_TTL
+%token DNS_CACHE_MEM
+%token DNS_CACHE_GC_INT
+/*blacklist*/
+%token USE_DST_BLST
+%token DST_BLST_MEM
+%token DST_BLST_TTL
+%token DST_BLST_GC_INT
+
 %token PORT
 %token PORT
 %token STAT
 %token STAT
 %token CHILDREN
 %token CHILDREN
@@ -512,6 +548,30 @@ assign_stm:
 	| DNS_SERVERS_NO error { yyerror("number expected"); }
 	| DNS_SERVERS_NO error { yyerror("number expected"); }
 	| DNS_USE_SEARCH EQUAL NUMBER   { dns_search_list=$3; }
 	| DNS_USE_SEARCH EQUAL NUMBER   { dns_search_list=$3; }
 	| DNS_USE_SEARCH error { yyerror("boolean value expected"); }
 	| DNS_USE_SEARCH error { yyerror("boolean value expected"); }
+	| DNS_USE_CACHE EQUAL NUMBER   { IF_DNS_CACHE(use_dns_cache=$3); }
+	| DNS_USE_CACHE error { yyerror("boolean value expected"); }
+	| DNS_USE_FAILOVER EQUAL NUMBER   { IF_DNS_FAILOVER(use_dns_failover=$3);}
+	| DNS_USE_FAILOVER error { yyerror("boolean value expected"); }
+	| DNS_CACHE_FLAGS EQUAL NUMBER   { IF_DNS_CACHE(dns_flags=$3); }
+	| DNS_CACHE_FLAGS error { yyerror("boolean value expected"); }
+	| DNS_CACHE_NEG_TTL EQUAL NUMBER   { IF_DNS_CACHE(dns_neg_cache_ttl=$3); }
+	| DNS_CACHE_NEG_TTL error { yyerror("boolean value expected"); }
+	| DNS_CACHE_MAX_TTL EQUAL NUMBER   { IF_DNS_CACHE(dns_cache_max_ttl=$3); }
+	| DNS_CACHE_MAX_TTL error { yyerror("boolean value expected"); }
+	| DNS_CACHE_MIN_TTL EQUAL NUMBER   { IF_DNS_CACHE(dns_cache_min_ttl=$3); }
+	| DNS_CACHE_MIN_TTL error { yyerror("boolean value expected"); }
+	| DNS_CACHE_MEM EQUAL NUMBER   { IF_DNS_CACHE(dns_cache_max_mem=$3); }
+	| DNS_CACHE_MEM error { yyerror("boolean value expected"); }
+	| DNS_CACHE_GC_INT EQUAL NUMBER   { IF_DNS_CACHE(dns_timer_interval=$3); }
+	| DNS_CACHE_GC_INT error { yyerror("boolean value expected"); }
+	| USE_DST_BLST EQUAL NUMBER   { IF_DST_BLACKLIST(use_dst_blacklist=$3); }
+	| USE_DST_BLST error { yyerror("boolean value expected"); }
+	| DST_BLST_MEM EQUAL NUMBER   { IF_DST_BLACKLIST(blst_max_mem=$3); }
+	| DST_BLST_MEM error { yyerror("boolean value expected"); }
+	| DST_BLST_TTL EQUAL NUMBER   { IF_DST_BLACKLIST(blst_timeout=$3); }
+	| DST_BLST_TTL error { yyerror("boolean value expected"); }
+	| DST_BLST_GC_INT EQUAL NUMBER { IF_DST_BLACKLIST(blst_timer_interval=$3);}
+	| DST_BLST_GC_INT error { yyerror("boolean value expected"); }
 	| PORT EQUAL NUMBER   { port_no=$3; }
 	| PORT EQUAL NUMBER   { port_no=$3; }
 	| STAT EQUAL STRING {
 	| STAT EQUAL STRING {
 		#ifdef STATS
 		#ifdef STATS
@@ -1828,7 +1888,7 @@ static void warn(char* s)
 {
 {
 	LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", line, startcolumn,
 	LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", line, startcolumn,
 			column, s);
 			column, s);
-	cfg_errors++;
+	cfg_warnings++;
 }
 }
 
 
 static void yyerror(char* s)
 static void yyerror(char* s)

+ 12 - 1
clist.h

@@ -46,6 +46,10 @@
 
 
 /* adds an entire sublist { s,e } (including s & e )
 /* adds an entire sublist { s,e } (including s & e )
  * after head
  * after head
+ * WARNING: clist_insert_sublist(head, n, n->prev) won't work,
+ *          same for clist_insert_sublist(head, n->next, n)
+ *  (macro!), use  e=n->prev; clist_insert_sublist(head, n, e, ...)
+ *  instead!
  */
  */
 #define clist_insert_sublist(head, s, e, next, prev) \
 #define clist_insert_sublist(head, s, e, next, prev) \
 	do{ \
 	do{ \
@@ -59,6 +63,9 @@
 
 
 /* appends an entire sublist { s,e } (including s & e )
 /* appends an entire sublist { s,e } (including s & e )
  * at the end of the list
  * at the end of the list
+ * WARNING: clist_append_sublist(head, n, n->prev, ...) won't work,
+ *  (macro!), use  e=n->prev; clist_append_sublist(head, n, e, ...)
+ *  instead!
  */
  */
 #define clist_append_sublist(head, s, e, next, prev) \
 #define clist_append_sublist(head, s, e, next, prev) \
 	do{ \
 	do{ \
@@ -70,9 +77,13 @@
 
 
 
 
 
 
+
 /* remove sublist { s,e } (including s & e )
 /* remove sublist { s,e } (including s & e )
  * always, if start is the beginning of the list use
  * always, if start is the beginning of the list use
- * clist_rm_sublist(head->next, e, next, prev ) */
+ * clist_rm_sublist(head->next, e, next, prev )
+ * WARNING: clist_rm_sublist(n, n->prev, ...) won't work,
+ *  (macro!), use  e=n->prev; clist_rm_sublist(n, e, ...)
+ *  instead! */
 #define clist_rm_sublist(s, e, next, prev) \
 #define clist_rm_sublist(s, e, next, prev) \
 	do{\
 	do{\
 		(s)->prev->next=(e)->next;  \
 		(s)->prev->next=(e)->next;  \

+ 43 - 0
core_cmd.c

@@ -39,6 +39,40 @@
 #include "tcp_info.h"
 #include "tcp_info.h"
 #include "core_cmd.h"
 #include "core_cmd.h"
 
 
+#ifdef USE_DNS_CACHE
+void dns_cache_debug(rpc_t* rpc, void* ctx);
+void dns_cache_debug_all(rpc_t* rpc, void* ctx);
+void dns_cache_mem_info(rpc_t* rpc, void* ctx);
+
+static const char* dns_cache_mem_info_doc[] = {
+	"dns cache memory info.",    /* Documentation string */
+	0                      /* Method signature(s) */
+};
+static const char* dns_cache_debug_doc[] = {
+	"dns debug  info.",    /* Documentation string */
+	0                      /* Method signature(s) */
+};
+
+static const char* dns_cache_debug_all_doc[] = {
+	"complete dns debug  dump",    /* Documentation string */
+	0                              /* Method signature(s) */
+};
+#endif
+#ifdef USE_DST_BLACKLIST
+void dst_blst_debug(rpc_t* rpc, void* ctx);
+void dst_blst_mem_info(rpc_t* rpc, void* ctx);
+
+static const char* dst_blst_mem_info_doc[] = {
+	"dst blacklist memory usage info.",  /* Documentation string */
+	0                                    /* Method signature(s) */
+};
+static const char* dst_blst_debug_doc[] = {
+	"dst blacklist  debug  info.",  /* Documentation string */
+	0                               /* Method signature(s) */
+};
+#endif
+
+
 
 
 #define MAX_CTIME_LEN 128
 #define MAX_CTIME_LEN 128
 
 
@@ -312,6 +346,15 @@ rpc_export_t core_rpc_methods[] = {
 	{"core.kill",              core_kill,              core_kill_doc,              0        },
 	{"core.kill",              core_kill,              core_kill_doc,              0        },
 	{"core.shmmem",            core_shmmem,            core_shmmem_doc,            0	},
 	{"core.shmmem",            core_shmmem,            core_shmmem_doc,            0	},
 	{"core.tcp_info",          core_tcpinfo,           core_tcpinfo_doc,          0	},
 	{"core.tcp_info",          core_tcpinfo,           core_tcpinfo_doc,          0	},
+#ifdef USE_DNS_CACHE
+	{"dns.mem_info",          dns_cache_mem_info,     dns_cache_mem_info_doc,     0	},
+	{"dns.debug",          dns_cache_debug,           dns_cache_debug_doc,        0	},
+	{"dns.debug_all",      dns_cache_debug_all,       dns_cache_debug_all_doc,        0	},
+#endif
+#ifdef USE_DST_BLACKLIST
+	{"dst_blacklist.mem_info",  dst_blst_mem_info,     dst_blst_mem_info_doc,     0	},
+	{"dst_blacklist.debug",    dst_blst_debug,         dst_blst_debug_doc,        0	},
+#endif
 	{0, 0, 0, 0}
 	{0, 0, 0, 0}
 };
 };
 
 

+ 2436 - 0
dns_cache.c

@@ -0,0 +1,2436 @@
+/*
+ * $Id$
+ *
+ * resolver related functions
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* History:
+ * --------
+ *  2006-07-13  created by andrei
+ */
+
+#ifdef USE_DNS_CACHE
+
+#include "globals.h"
+#include "dns_cache.h"
+#include "dns_wrappers.h"
+#include "mem/shm_mem.h"
+#include "hashes.h"
+#include "clist.h"
+#include "locking.h"
+#include "atomic_ops.h"
+#include "ut.h"
+#include "timer.h"
+#include "timer_ticks.h"
+#include "error.h"
+#include "rpc.h"
+
+
+
+#define DNS_CACHE_DEBUG /* extra sanity checks and debugging */
+
+
+#ifndef MAX
+	#define MAX(a,b) ( ((a)>(b))?(a):(b))
+#endif
+
+#define MAX_DNS_RECORDS 255  /* maximum dns records number  received in a 
+							   dns answer*/
+
+#define DNS_HASH_SIZE	1024 /* must be <= 65535 */
+#define DEFAULT_DNS_NEG_CACHE_TTL 60 /* 1 min. */
+#define DEFAULT_DNS_CACHE_MIN_TTL 0 /* (disabled) */
+#define DEFAULT_DNS_CACHE_MAX_TTL ((unsigned int)(-1)) /* (maxint) */
+#define DEFAULT_DNS_MAX_MEM 500 /* 500 Kb */
+#define DEFAULT_DNS_TIMER_INTERVAL 120  /* 2 min. */
+#define DNS_HE_MAX_ADDR 10  /* maxium addresses returne in a hostent struct */
+#define MAX_CNAME_CHAIN  10 
+
+
+static gen_lock_t* dns_hash_lock=0;
+static volatile unsigned int *dns_cache_mem_used=0; /* current mem. use */
+unsigned int dns_cache_max_mem=DEFAULT_DNS_MAX_MEM; /* maximum memory used for
+													 the cached entries */
+unsigned int dns_neg_cache_ttl=DEFAULT_DNS_NEG_CACHE_TTL; /* neg. cache ttl */
+unsigned int dns_cache_max_ttl=DEFAULT_DNS_CACHE_MAX_TTL; /* maximum ttl */
+unsigned int dns_cache_min_ttl=DEFAULT_DNS_CACHE_MIN_TTL; /* minimum ttl */
+unsigned int dns_timer_interval=DEFAULT_DNS_TIMER_INTERVAL; /* in s */
+int dns_flags=0; /* default flags used for the  dns_*resolvehost 
+                    (compatibility wrappers) */
+
+#define LOCK_DNS_HASH()		lock_get(dns_hash_lock)
+#define UNLOCK_DNS_HASH()	lock_release(dns_hash_lock)
+
+#define FIX_TTL(t)  (((t)<dns_cache_min_ttl)?dns_cache_min_ttl: \
+						(((t)>dns_cache_max_ttl)?dns_cache_max_ttl:(t)))
+
+
+struct dns_hash_head{
+	struct dns_hash_entry* next;
+	struct dns_hash_entry* prev;
+};
+
+#ifdef DNS_LU_LST
+struct dns_lu_lst* dns_last_used_lst=0;
+#endif
+
+static struct dns_hash_head* dns_hash=0;
+
+
+static struct timer_ln* dns_timer_h=0;
+
+
+
+static const char* dns_str_errors[]={
+	"no error",
+	"no more records", /* not an error, but and end condition */
+	"unknown error",
+	"internal error",
+	"bad SRV entry",
+	"unresolvable SRV request",
+	"bad A or AAAA entry",
+	"unresovlable A or AAAA request",
+	"invalid ip in A or AAAA record",
+	"blacklisted ip",
+	"name too long ", /* try again with a shorter name */
+	"ip AF mismatch", /* address family mismatch */
+	"bug - critical error"
+};
+
+
+
+/* param: err (negative error number) */
+const char* dns_strerror(int err)
+{
+	err=-err;
+	if ((err>=0) && (err<sizeof(dns_str_errors)/sizeof(char*)))
+		return dns_str_errors[err];
+	return "bug -- bad error number";
+}
+
+
+
+/* "internal" only, don't use unless you really know waht you're doing */
+inline static void dns_destroy_entry(struct dns_hash_entry* e)
+{
+#ifdef DNS_CACHE_DEBUG
+	memset(e, 0, e->total_size);
+#endif
+	shm_free(e); /* nice having it in one block isn't it? :-) */
+}
+
+
+/* "internal" only, same as above, asumes shm_lock() held (tm optimization) */
+inline static void dns_destroy_entry_shm_unsafe(struct dns_hash_entry* e)
+{
+#ifdef DNS_CACHE_DEBUG
+	memset(e, 0, e->total_size);
+#endif
+	shm_free_unsafe(e); /* nice having it in one block isn't it? :-) */
+}
+
+
+
+/* dec. the internal refcnt and if 0 deletes the entry */
+void dns_hash_put(struct dns_hash_entry* e)
+{
+	if(e && atomic_dec_and_test(&e->refcnt)){
+		/* atomic_sub_long(dns_cache_total_used, e->total_size); */
+		dns_destroy_entry(e);
+	}
+}
+
+
+
+/* same as above but uses dns_destroy_unsafe (assumes shm_lock held -- tm
+ *  optimization) */
+void dns_hash_put_shm_unsafe(struct dns_hash_entry* e)
+{
+	if(e && atomic_dec_and_test(&e->refcnt)){
+		/* atomic_sub_long(dns_cache_total_used, e->total_size); */
+		dns_destroy_entry_shm_unsafe(e);
+	}
+}
+
+
+inline static int dns_cache_clean(unsigned int no, int expired_only);
+inline static int dns_cache_free_mem(unsigned int target, int expired_only);
+
+static ticks_t dns_timer(ticks_t ticks, struct timer_ln* tl, void* data)
+{
+	if (*dns_cache_mem_used>12*(dns_cache_max_mem/16)){ /* ~ 75% used */
+		dns_cache_free_mem(dns_cache_max_mem/2, 1); 
+	}else{
+		dns_cache_clean(-1, 1); /* all the table, only expired entries */
+		/* TODO: better strategy? */
+	}
+	return (ticks_t)(-1);
+}
+
+
+
+void destroy_dns_cache()
+{
+	if (dns_timer_h){
+		timer_del(dns_timer_h);
+		timer_free(dns_timer_h);
+		dns_timer_h=0;
+	}
+	if (dns_hash_lock){
+		lock_destroy(dns_hash_lock);
+		lock_dealloc(dns_hash_lock);
+		dns_hash_lock=0;
+	}
+	if (dns_hash){
+		shm_free(dns_hash);
+		dns_hash=0;
+	}
+#ifdef DNS_LU_LST
+	if (dns_last_used_lst){
+		shm_free(dns_last_used_lst);
+		dns_last_used_lst=0;
+	}
+#endif
+	if (dns_cache_mem_used){
+		shm_free((void*)dns_cache_mem_used);
+		dns_cache_mem_used=0;
+	}
+}
+
+
+
+int init_dns_cache()
+{
+	int r;
+	int ret;
+	
+	ret=0;
+	/* sanity check */
+	if (E_DNS_CRITICAL>=sizeof(dns_str_errors)/sizeof(char*)){
+		LOG(L_CRIT, "BUG: dns_cache_init: bad dns error table\n");
+		ret=E_BUG;
+		goto error;
+	}
+	dns_cache_mem_used=shm_malloc(sizeof(*dns_cache_mem_used));
+	if (dns_cache_mem_used==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+#ifdef DNS_LU_LST
+	dns_last_used_lst=shm_malloc(sizeof(*dns_last_used_lst));
+	if (dns_last_used_lst==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	clist_init(dns_last_used_lst, next, prev);
+#endif
+	dns_hash=shm_malloc(sizeof(struct dns_hash_head)*DNS_HASH_SIZE);
+	if (dns_hash==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	for (r=0; r<DNS_HASH_SIZE; r++)
+		clist_init(&dns_hash[r], next, prev);
+	
+	dns_hash_lock=lock_alloc();
+	if (dns_hash_lock==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	if (lock_init(dns_hash_lock)==0){
+		lock_dealloc(dns_hash_lock);
+		dns_hash_lock=0;
+		ret=-1;
+		goto error;
+	}
+	
+	/* fix options */
+	dns_cache_max_mem<<=10; /* Kb */ /* TODO: test with 0 */
+	/* fix flags */
+	if (dns_try_ipv6==0){
+		dns_flags|=DNS_IPV4_ONLY;
+	}
+	if (dns_flags & DNS_IPV4_ONLY){
+		dns_flags&=~(DNS_IPV6_ONLY|DNS_IPV6_FIRST);
+	}
+			;
+	dns_timer_h=timer_alloc();
+	if (dns_timer_h==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	if (dns_timer_interval){
+		timer_init(dns_timer_h, dns_timer, 0, 0); /* "slow" timer */
+		if (timer_add(dns_timer_h, S_TO_TICKS(dns_timer_interval))<0){
+			LOG(L_CRIT, "BUG: dns_cache_init: failed to add the timer\n");
+			timer_free(dns_timer_h);
+			dns_timer_h=0;
+			goto error;
+		}
+	}
+	
+	return 0;
+error:
+	destroy_dns_cache();
+	return ret;
+}
+
+
+/* hash function, based on get_hash1_raw, but case insensitive
+ * type is not used (obsolete)
+ * returns the hash value
+ */
+inline static unsigned int dns_hash_no(char* s, int len, int type)
+{
+	char* p;
+	char* end;
+	
+	register unsigned v;
+	register unsigned h;
+	
+	h=0;
+	
+	hash_update_str(s, s+len, p, v, h);
+	end=s+len;
+	for (p=s; p<=(end-4); p+=4){
+		v=((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3])|0x20202020;
+		h+=v^(v>>3);
+	}
+	v=0;
+	for (;p<end; p++){ v<<=8; v+=*p|0x20;}
+	h+=v^(v>>3);
+	return hash_finish(h) % DNS_HASH_SIZE;
+}
+
+
+
+#ifdef DNS_CACHE_DEBUG
+#define DEBUG_LU_LST
+#ifdef DEBUG_LU_LST
+
+#include <stdlib.h> /* abort() */
+#define check_lu_lst(l) ((((l)->next==(l)) || ((l)->prev==(l))) && \
+							((l)!=dns_last_used_lst))
+
+#define dbg_lu_lst(txt, l) \
+		LOG(L_CRIT, "BUG: %s: crt(%p, %p, %p)," \
+					" prev(%p, %p, %p), next(%p, %p, %p)\n", txt, \
+					(l), (l)->next, (l)->prev, \
+					(l)->prev, (l)->prev->next, (l)->prev->prev, \
+					(l)->next, (l)->next->next, (l)->next->prev \
+				) 
+
+#define debug_lu_lst( txt, l) \
+	do{ \
+		if (check_lu_lst((l))){  \
+			dbg_lu_lst(txt  " crt:", (l)); \
+			abort(); \
+		} \
+		if (check_lu_lst((l)->next)){ \
+			dbg_lu_lst(txt  " next:",  (l)); \
+			abort(); \
+		} \
+		if (check_lu_lst((l)->prev)){ \
+			dbg_lu_lst(txt  " prev:", (l)); \
+			abort(); \
+		} \
+	}while(0)
+
+#endif
+#endif /* DNS_CACHE_DEBUG */
+
+
+/* must be called with the DNS_LOCK hold
+ * remove and entry from the hash, dec. its refcnt and if not referenced
+ * anymore deletes it */
+void _dns_hash_remove(struct dns_hash_entry* e)
+{
+	clist_rm(e, next, prev);
+#ifdef DNS_CACHE_DEBUG
+	e->next=e->prev=0;
+#endif
+#ifdef DNS_LU_LST
+#ifdef DEBUG_LU_LST
+	debug_lu_lst("_dns_hash_remove: pre rm:", &e->last_used_lst);
+#endif
+	clist_rm(&e->last_used_lst, next, prev);
+#ifdef DEBUG_LU_LST
+	debug_lu_lst("_dns_hash_remove: post rm:", &e->last_used_lst);
+#endif
+#ifdef DNS_CACHE_DEBUG
+	e->last_used_lst.next=e->last_used_lst.prev=0;
+#endif
+#endif
+	*dns_cache_mem_used-=e->total_size;
+	dns_hash_put(e);
+}
+
+
+
+/* non locking  version (the dns hash must _be_ locked externally)
+ * returns 0 when not found, or the entry on success (an entry with a
+ * similar name but with a CNAME type will always match).
+ * it doesn't increase the internal refcnt
+ * returns the entry when found, 0 when not found and sets *err to !=0
+ *  on error (e.g. recursive cnames)
+ * WARNING: - internal use only
+ *          - always check if the returned entry type is CNAME */
+inline static struct dns_hash_entry* _dns_hash_find(str* name, int type,
+														int* h, int* err)
+{
+	struct dns_hash_entry* e;
+	struct dns_hash_entry* tmp;
+	struct dns_hash_entry* ret;
+	ticks_t now;
+	int cname_chain;
+	str cname;
+	
+	cname_chain=0;
+	ret=0;
+	now=get_ticks_raw();
+	*err=0;
+again:
+	*h=dns_hash_no(name->s, name->len, type);
+	DBG("dns_hash_find(%.*s(%d), %d), h=%d\n", name->len, name->s,
+												name->len, type, *h);
+	clist_foreach_safe(&dns_hash[*h], e, tmp, next){
+		/* automatically remove expired elements */
+		if ((s_ticks_t)(now-e->expire)>=0){
+				_dns_hash_remove(e);
+		}else if ((e->type==type) && (e->name_len==name->len) &&
+			(strncasecmp(e->name, name->s, e->name_len)==0)){
+			e->last_used=now;
+#ifdef DNS_LU_LST
+			/* add it at the end */
+#ifdef DEBUG_LU_LST
+			debug_lu_lst("_dns_hash_find: pre rm:", &e->last_used_lst);
+#endif
+			clist_rm(&e->last_used_lst, next, prev);
+			clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
+#ifdef DEBUG_LU_LST
+			debug_lu_lst("_dns_hash_find: post append:", &e->last_used_lst);
+#endif
+#endif
+			return e;
+		}else if ((e->type==T_CNAME) && (e->name_len==name->len) &&
+			(strncasecmp(e->name, name->s, e->name_len)==0)){
+			e->last_used=now;
+#ifdef DNS_LU_LST
+			/* add it at the end */
+#ifdef DEBUG_LU_LST
+			debug_lu_lst("_dns_hash_find: cname: pre rm:", &e->last_used_lst);
+#endif
+			clist_rm(&e->last_used_lst, next, prev);
+			clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
+#ifdef DEBUG_LU_LST
+			debug_lu_lst("_dns_hash_find: cname: post append:",
+							&e->last_used_lst);
+#endif
+#endif		
+			ret=e; /* if this is an unfinished cname chain, we try to
+					  return the last cname */
+			/* this is a cname => retry using its value */
+			if (cname_chain> MAX_CNAME_CHAIN){
+				LOG(L_ERR, "ERROR: _dns_hash_find: cname chain too long "
+						"or recursive (\"%.*s\")\n", name->len, name->s);
+				ret=0; /* error*/
+				*err=-1;
+				break;
+			}
+			cname_chain++;
+			cname.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
+			cname.len= ((struct cname_rdata*)e->rr_lst->rdata)->name_len;
+			name=&cname;
+			goto again;
+		}
+	}
+	return ret;
+}
+
+
+
+/* frees cache entries, if expired_only=0 only expired entries will be 
+ * removed, else all of them
+ * it will process maximum no entries (to process all of them use -1)
+ * returns the number of deleted entries
+ * This should be called from a timer process*/
+inline static int dns_cache_clean(unsigned int no, int expired_only)
+{
+	struct dns_hash_entry* e;
+	ticks_t now;
+	unsigned int n;
+	unsigned int deleted;
+#ifdef DNS_LU_LST
+	struct dns_lu_lst* l;
+	struct dns_lu_lst* tmp;
+#else
+	struct dns_hash_entry* t;
+	unsigned int h;
+	static unsigned int start=0;
+#endif
+	
+	n=0;
+	deleted=0;
+	now=get_ticks_raw();
+	LOCK_DNS_HASH();
+#ifdef DNS_LU_LST
+	clist_foreach_safe(dns_last_used_lst, l, tmp, next){
+		e=(struct dns_hash_entry*)(((char*)l)-
+				(char*)&((struct dns_hash_entry*)(0))->last_used_lst);
+		if (!expired_only || ((s_ticks_t)(now-e->expire)>=0)){
+				_dns_hash_remove(e);
+				deleted++;
+		}
+		n++;
+		if (n>=no) break;
+	}
+#else
+	for(h=start; h!=(start+DNS_HASH_SIZE); h++){
+		clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
+			if  ((s_ticks_t)(now-e->expire)>=0){
+				_dns_hash_remove(e);
+				deleted++;
+			}
+			n++;
+			if (n>=no) break;
+		}
+	}
+	/* not fair, but faster then random() */
+	if (!expired_only){
+		for(h=start; h!=(start+DNS_HASH_SIZE); h++){
+			clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
+				if  ((s_ticks_t)(now-e->expire)>=0){
+					_dns_hash_remove(e);
+					deleted++;
+				}
+				n++;
+				if (n>=no) goto skip;
+			}
+		}
+	}
+skip:
+	start=h;
+#endif
+	UNLOCK_DNS_HASH();
+	return deleted;
+}
+
+
+
+/* frees cache entries, if expired_only=0 only expired entries will be 
+ * removed, else all of them
+ * it will stop when the dns cache used memory reaches target (to process all 
+ * of them use 0)
+ * returns the number of deleted entries */
+inline static int dns_cache_free_mem(unsigned int target, int expired_only)
+{
+	struct dns_hash_entry* e;
+	ticks_t now;
+	unsigned int deleted;
+#ifdef DNS_LU_LST
+	struct dns_lu_lst* l;
+	struct dns_lu_lst* tmp;
+#else
+	struct dns_hash_entry* t;
+	unsigned int h;
+	static unsigned int start=0;
+#endif
+	
+	deleted=0;
+	now=get_ticks_raw();
+	LOCK_DNS_HASH();
+#ifdef DNS_LU_LST
+	clist_foreach_safe(dns_last_used_lst, l, tmp, next){
+		if (*dns_cache_mem_used<=target) break;
+		e=(struct dns_hash_entry*)(((char*)l)-
+				(char*)&((struct dns_hash_entry*)(0))->last_used_lst);
+		if (!expired_only || ((s_ticks_t)(now-e->expire)>=0)){
+				_dns_hash_remove(e);
+				deleted++;
+		}
+	}
+#else
+	for(h=start; h!=(start+DNS_HASH_SIZE); h++){
+		clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
+			if (*dns_cache_mem_used<=target) 
+				goto skip;
+			if  ((s_ticks_t)(now-e->expire)>=0){
+				_dns_hash_remove(e);
+				deleted++;
+			}
+		}
+	}
+	/* not fair, but faster then random() */
+	if (!expired_only){
+		for(h=start; h!=(start+DNS_HASH_SIZE); h++){
+			clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
+				if (*dns_cache_mem_used<=target) 
+					goto skip;
+				if  ((s_ticks_t)(now-e->expire)>=0){
+					_dns_hash_remove(e);
+					deleted++;
+				}
+			}
+		}
+	}
+skip:
+	start=h;
+#endif
+	UNLOCK_DNS_HASH();
+	return deleted;
+}
+
+
+
+/* locking  version (the dns hash must _not_be locked externally)
+ * returns 0 when not found, the searched entry on success (with CNAMEs
+ *  followed) or the last CNAME entry from an unfinished CNAME chain, 
+ *  if the search matches a CNAME. On error sets *err (e.g. recursive CNAMEs).
+ * it increases the internal refcnt => when finished dns_hash_put() must
+ *  be called on the returned entry
+ *  WARNING: - the return might be a CNAME even if type!=CNAME, see above */
+inline static struct dns_hash_entry* dns_hash_get(str* name, int type, int* h,
+													int* err)
+{
+	struct dns_hash_entry* e;
+	
+	LOCK_DNS_HASH();
+	e=_dns_hash_find(name, type, h, err);
+	if (e){
+		atomic_inc(&e->refcnt);
+	}
+	UNLOCK_DNS_HASH();
+	return e;
+}
+
+
+
+/* adds a fully created and init. entry (see dns_cache_mk_entry()) to the hash
+ * table
+ * returns 0 on success, -1 on error */
+inline static int dns_cache_add(struct dns_hash_entry* e)
+{
+	int h;
+	
+	/* check space */
+	/* atomic_add_long(dns_cache_total_used, e->size); */
+	if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
+		LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
+		/* free ~ 12% of the cache */
+		dns_cache_free_mem(*dns_cache_mem_used/16*14, 1);
+		if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
+			LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
+			return -1;
+		}
+	}
+	atomic_inc(&e->refcnt);
+	h=dns_hash_no(e->name, e->name_len, e->type);
+	DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
+			e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
+	LOCK_DNS_HASH();
+		*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
+										 only from within a lock */
+		clist_append(&dns_hash[h], e, next, prev);
+#ifdef DNS_LU_LST
+		clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
+#endif
+	UNLOCK_DNS_HASH();
+	return 0;
+}
+
+
+
+/* same as above, but it must be called with the dns hash lock held
+ * returns 0 on success, -1 on error */
+inline static int dns_cache_add_unsafe(struct dns_hash_entry* e)
+{
+	int h;
+	
+	/* check space */
+	/* atomic_add_long(dns_cache_total_used, e->size); */
+	if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
+		LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
+		/* free ~ 12% of the cache */
+		UNLOCK_DNS_HASH();
+		dns_cache_free_mem(*dns_cache_mem_used/16*14, 1);
+		LOCK_DNS_HASH();
+		if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
+			LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
+			return -1;
+		}
+	}
+	atomic_inc(&e->refcnt);
+	h=dns_hash_no(e->name, e->name_len, e->type);
+	DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
+			e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
+	*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
+										 only from within a lock */
+	clist_append(&dns_hash[h], e, next, prev);
+#ifdef DNS_LU_LST
+	clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
+#endif
+	return 0;
+}
+
+
+
+/* creates a "negative" entry which will be valid for ttl seconds */
+inline static struct dns_hash_entry* dns_cache_mk_bad_entry(str* name,
+															int type,
+															int ttl,
+															int flags)
+{
+	struct dns_hash_entry* e;
+	int size;
+	ticks_t now;
+	
+	DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
+									type, ttl, flags);
+	size=sizeof(struct dns_hash_entry)+name->len-1+1;
+	e=shm_malloc(size);
+	if (e==0){
+		LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
+		return 0;
+	}
+	memset(e, 0, size); /* init with 0*/
+	e->total_size=size;
+	e->name_len=name->len;
+	e->type=type;
+	now=get_ticks_raw();
+	e->last_used=now;
+	e->expire=now+S_TO_TICKS(ttl);
+	memcpy(e->name, name->s, name->len);
+	e->err_flags=flags;
+	return e;
+}
+
+
+
+/* create a a/aaaa hash entry from a name and ip address
+ * returns 0 on error */
+inline static struct dns_hash_entry* dns_cache_mk_ip_entry(str* name,
+															struct ip_addr* ip)
+{
+	struct dns_hash_entry* e;
+	int size;
+	ticks_t now;
+	
+	/* everything is allocated in one block: dns_hash_entry + name +
+	 * + dns_rr + rdata;  dns_rr must start at an aligned adress,
+	 * hence we need to round dns_hash_entry+name size to a sizeof(long)
+	 * multiple.
+	 * Memory image:
+	 * struct dns_hash_entry
+	 * name (name_len+1 bytes)
+	 * padding to multiple of sizeof(long)
+	 * dns_rr
+	 * rdata  (no padding needed, since for ip is just an array of chars)
+	  */
+	size=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1)+ 
+			sizeof(struct dns_rr)+ ip->len;
+	e=shm_malloc(size);
+	if (e==0){
+		LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
+		return 0;
+	}
+	memset(e, 0, size); /* init with 0*/
+	e->total_size=size;
+	e->name_len=name->len;
+	e->type=(ip->af==AF_INET)?T_A:T_AAAA;
+	now=get_ticks_raw();
+	e->last_used=now;
+	e->expire=now-1; /* maximum expire */
+	memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
+	e->rr_lst=(void*)((char*)e+
+				ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
+	e->rr_lst->rdata=(void*)((char*)e->rr_lst+sizeof(struct dns_rr));
+	e->rr_lst->expire=now-1; /* maximum expire */
+	/* no need to align rr_lst->rdata for a or aaaa records */
+	memcpy(e->rr_lst->rdata, ip->u.addr, ip->len);
+	return e;
+}
+
+
+
+/* create a dns hash entry from a name and a rdata list (pkg_malloc'ed)
+ * (it will use only the type records with the name "name" from the
+ *  rdata list with one exception: if a matching CNAME with the same
+ *  name is found, the search will stop and this will be the record used)
+ * returns 0 on error and removes the used elements from the rdata list*/
+inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
+														struct rdata** rd_lst)
+{
+	struct dns_hash_entry* e;
+	struct dns_rr* rr;
+	struct dns_rr** tail_rr;
+	struct rdata** p;
+	struct rdata* tmp_lst;
+	struct rdata** tail;
+	struct rdata* l;
+	int size;
+	ticks_t now;
+	unsigned int max_ttl;
+	unsigned int ttl;
+	
+#define rec_matches(rec, t, n) /*(struct rdata* record, int type, str* name)*/\
+	(	((rec)->name_len==(n)->len) && ((rec)->type==(t)) && \
+		(strncasecmp((rec)->name, (n)->s, (n)->len)==0))
+	/* init */
+	tmp_lst=0;
+	tail=&tmp_lst;
+	
+	
+	/* everything is allocated in one block: dns_hash_entry + name +
+	 * + dns_rr + rdata_raw+ ....;  dns_rr must start at an aligned adress,
+	 * hence we need to round dns_hash_entry+name size to a sizeof(long)
+	 * multiple. If rdata type requires it, rdata_raw might need to be also
+	 * aligned.
+	 * Memory image:
+	 * struct dns_hash_entry  (e)
+	 * name (name_len+1 bytes)  (&e->name[0])
+	 * padding to multiple of sizeof(char*)
+	 * dns_rr1 (e->rr_lst)
+	 * possible padding: no padding for a_rdata or aaaa_rdata, 
+	 *                   multipe of sizeof(short) for srv_rdata,
+	 *                   multiple of sizeof(long) for naptr_rdata and others
+	 * dns_rr1->rdata  (e->rr_lst->rdata)
+	 * padding to multipe of sizeof long
+	 * dns_rr2 (e->rr_lst->next)
+	 * ....
+	 *
+	 */
+	size=0;
+	if (*rd_lst==0)
+		return 0;
+	/* find the first matching rr, if it's a CNAME use CNAME as type,
+	 * if not continue with the original type */
+	for(p=rd_lst; *p; p=&(*p)->next){
+		if (((*p)->name_len==name->len) &&
+				(((*p)->type==type) || ((*p)->type==T_CNAME)) &&
+				(strncasecmp((*p)->name, name->s, name->len)==0)){
+			type=(*p)->type;
+			break;
+		}
+	}
+	/* continue, we found the type we are looking for */
+	switch(type){
+		case T_A:
+			for(; *p;){
+				if (!rec_matches((*p), type, name)){ 
+					/* skip this record */
+					p=&(*p)->next; /* advance */
+					continue;
+				}
+				size+=ROUND_POINTER(sizeof(struct dns_rr)+
+										sizeof(struct a_rdata));
+				/* add it to our tmp. lst */
+				*tail=*p;
+				tail=&(*p)->next;
+				/* detach it from the rd list */
+				*p=(*p)->next;
+				/* don't advance p, because the crt. elem. has
+				 * just been elimintated */
+			}
+			break;
+		case T_AAAA:
+			for(; *p;){
+				if (!rec_matches((*p), type, name)){ 
+					/* skip this record */
+					p=&(*p)->next; /* advance */
+					continue;
+				}
+				/* no padding */
+				size+=ROUND_POINTER(sizeof(struct dns_rr)+
+											sizeof(struct aaaa_rdata));
+				/* add it to our tmp. lst */
+				*tail=*p;
+				tail=&(*p)->next;
+				/* detach it from the rd list */
+				*p=(*p)->next;
+				/* don't advance p, because the crt. elem. has
+				 * just been elimintated */
+			}
+			break;
+		case T_SRV:
+			for(; *p;){
+				if (!rec_matches((*p), type, name)){ 
+					/* skip this record */
+					p=&(*p)->next; /* advance */
+					continue;
+				}
+				/* padding to short */
+				size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
+						SRV_RDATA_SIZE(*(struct srv_rdata*)(*p)->rdata));
+				/* add it to our tmp. lst */
+				*tail=*p;
+				tail=&(*p)->next;
+				/* detach it from the rd list */
+				*p=(*p)->next;
+				/* don't advance p, because the crt. elem. has
+				 * just been elimintated */
+			}
+			break;
+		case T_NAPTR:
+			for(; *p;){
+				if (!rec_matches((*p), type, name)){ 
+					/* skip this record */
+					p=&(*p)->next; /* advance */
+					continue;
+				}
+				/* padding to char* */
+				size+=ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
+						NAPTR_RDATA_SIZE(*(struct naptr_rdata*)(*p)->rdata));
+				/* add it to our tmp. lst */
+				*tail=*p;
+				tail=&(*p)->next;
+				/* detach it from the rd list */
+				*p=(*p)->next;
+				/* don't advance p, because the crt. elem. has
+				 * just been elimintated */
+			}
+			break;
+		case T_CNAME:
+			for(; *p;){
+				if (!rec_matches((*p), type, name)){ 
+					/* skip this record */
+					p=&(*p)->next; /* advance */
+					continue;
+				}
+				/* no padding */
+				size+=ROUND_POINTER(sizeof(struct dns_rr)+
+						CNAME_RDATA_SIZE(*(struct cname_rdata*)(*p)->rdata));
+				/* add it to our tmp. lst */
+				*tail=*p;
+				tail=&(*p)->next;
+				/* detach it from the rd list */
+				*p=(*p)->next;
+				/* don't advance p, because the crt. elem. has
+				 * just been elimintated */
+			}
+			break;
+		default:
+			LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
+							"supported\n", type);
+			/* we don't know what to do with it, so don't
+			 * add it to the tmp_lst */
+			return 0; /* error */
+	}
+	*tail=0; /* mark the end of our tmp_lst */
+	if (size==0){
+		DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
+				name->len, name->s, type);
+		return 0;
+	}
+	/* compute size */
+	size+=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1);
+	e=shm_malloc(size);
+	if (e==0){
+		LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
+		return 0;
+	}
+	memset(e, 0, size); /* init with 0 */
+	e->total_size=size;
+	e->name_len=name->len;
+	e->type=type;
+	now=get_ticks_raw();
+	e->last_used=now;
+	memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
+	e->rr_lst=(struct dns_rr*)((char*)e+
+				ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
+	tail_rr=&(e->rr_lst);
+	rr=e->rr_lst;
+	max_ttl=0;
+	/* copy the actual data */
+	switch(type){
+		case T_A:
+			for(l=tmp_lst; l; l=l->next){
+				ttl=FIX_TTL(l->ttl);
+				rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				max_ttl=MAX(max_ttl, ttl);
+				rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
+				memcpy(rr->rdata, l->rdata, sizeof(struct a_rdata));
+				rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
+							sizeof(struct a_rdata)));
+				tail_rr=&(rr->next);
+				rr=rr->next;
+			}
+			break;
+		case T_AAAA:
+			for(l=tmp_lst; l; l=l->next){
+				ttl=FIX_TTL(l->ttl);
+				rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				max_ttl=MAX(max_ttl, ttl);
+				rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
+				memcpy(rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
+				rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
+							sizeof(struct aaaa_rdata)));
+				tail_rr=&(rr->next);
+				rr=rr->next;
+			}
+			break;
+		case T_SRV:
+			for(l=tmp_lst; l; l=l->next){
+				ttl=FIX_TTL(l->ttl);
+				rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				max_ttl=MAX(max_ttl, ttl);
+				rr->rdata=(void*)((char*)rr+
+								ROUND_SHORT(sizeof(struct dns_rr)));
+				/* copy the whole srv_rdata block*/
+				memcpy(rr->rdata, l->rdata, 
+						SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
+				rr->next=(void*)((char*)rr+
+							ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
+										SRV_RDATA_SIZE(
+											*(struct srv_rdata*)l->rdata)));
+				tail_rr=&(rr->next);
+				rr=rr->next;
+			}
+			break;
+		case T_NAPTR:
+			for(l=tmp_lst; l; l=l->next){
+				ttl=FIX_TTL(l->ttl);
+				rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				max_ttl=MAX(max_ttl, ttl);
+				rr->rdata=(void*)((char*)rr+
+								ROUND_POINTER(sizeof(struct dns_rr)));
+				/* copy the whole srv_rdata block*/
+				memcpy(rr->rdata, l->rdata, 
+						NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
+				/* adjust the string pointer */
+				((struct naptr_rdata*)rr->rdata)->flags=
+					translate_pointer((char*)rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->flags));
+				((struct naptr_rdata*)rr->rdata)->services=
+					translate_pointer((char*)rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->services));
+				((struct naptr_rdata*)rr->rdata)->regexp=
+					translate_pointer((char*)rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->regexp));
+				((struct naptr_rdata*)rr->rdata)->repl=
+					translate_pointer((char*)rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->repl));
+				rr->next=(void*)((char*)rr+
+							ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
+										NAPTR_RDATA_SIZE(
+											*(struct naptr_rdata*)l->rdata)));
+				tail_rr=&(rr->next);
+			}
+			break;
+		case T_CNAME:
+			for(l=tmp_lst; l; l=l->next){
+				ttl=FIX_TTL(l->ttl);
+				rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				max_ttl=MAX(max_ttl, ttl);
+				rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
+				memcpy(rr->rdata, l->rdata, 
+							CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
+				rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
+							CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
+				tail_rr=&(rr->next);
+				rr=rr->next;
+			}
+			break;
+		default:
+			/* do nothing */
+			LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: create: type %d not "
+							"supported\n", type);
+				;
+	}
+	*tail_rr=0; /* terminate the list */
+	e->expire=now+S_TO_TICKS(max_ttl);
+	free_rdata_list(tmp_lst);
+	return e;
+}
+
+
+
+/* structure used only inside dns_cache_mk_rd_entry2 to break
+ *  the list of records into records of the same type */
+struct tmp_rec{
+	struct rdata* rd;
+	struct dns_hash_entry* e;
+	struct dns_rr* rr;
+	struct dns_rr** tail_rr;
+	int max_ttl;
+	int size;
+};
+
+
+
+/* create several dns hash entries from a list of rdata structs
+ * returns 0 on error */
+inline static struct dns_hash_entry* dns_cache_mk_rd_entry2(struct rdata* rd)
+{
+	struct rdata* l;
+	ticks_t now;
+	struct tmp_rec rec[MAX_DNS_RECORDS];
+	int rec_idx[MAX_DNS_RECORDS];
+	int r, i;
+	int no_records; /* number of different records */
+	unsigned int ttl;
+	
+	
+	no_records=0; 
+	rec[0].e=0;
+	/* everything is allocated in one block: dns_hash_entry + name +
+	 * + dns_rr + rdata_raw+ ....;  dns_rr must start at an aligned adress,
+	 * hence we need to round dns_hash_entry+name size to a sizeof(long)
+	 * multiple. If rdata type requires it, rdata_raw might need to be also
+	 * aligned.
+	 * Memory image:
+	 * struct dns_hash_entry  (e)
+	 * name (name_len+1 bytes)  (&e->name[0])
+	 * padding to multiple of sizeof(char*)
+	 * dns_rr1 (e->rr_lst)
+	 * possible padding: no padding for a_rdata or aaaa_rdata, 
+	 *                   multipe of sizeof(short) for srv_rdata,
+	 *                   multiple of sizeof(long) for naptr_rdata and others
+	 * dns_rr1->rdata  (e->rr_lst->rdata)
+	 * padding to multipe of sizeof long
+	 * dns_rr2 (e->rr_lst->next)
+	 * ....
+	 *
+	 */
+	/* compute size */
+	for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
+		for (r=0; r<no_records; r++){
+			if ((l->type==rec[r].rd->type) && 
+					(l->name_len==rec[r].rd->name_len)
+				&& (strncasecmp(l->name, rec[r].rd->name, l->name_len)==0)){
+				/* found */
+				goto found;
+			}
+		}
+		/* not found, create new */
+		if (no_records<MAX_DNS_RECORDS){
+			rec[r].rd=l;
+			rec[r].e=0;
+			rec[r].size=ROUND_POINTER(sizeof(struct dns_hash_entry)+
+							rec[r].rd->name_len-1+1);
+			no_records++;
+		}else{
+			LOG(L_ERR, "ERROR: dns_cache_mk_rd_entry2: too many records: %d\n",
+						no_records);
+			/* skip */
+			continue;
+		}
+found:
+		rec_idx[i]=r;
+		switch(l->type){
+			case T_A:
+				/* no padding */
+				rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
+										sizeof(struct a_rdata));
+				break;
+			case T_AAAA:
+				/* no padding */
+				rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
+												sizeof(struct aaaa_rdata));
+				break;
+			case T_SRV:
+				/* padding to short */
+				rec[r].size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
+								SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata));
+				break;
+			case T_NAPTR:
+					/* padding to char* */
+				rec[r].size+=ROUND_POINTER(ROUND_POINTER(
+												sizeof(struct dns_rr))+
+							NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata));
+				break;
+			case T_CNAME:
+					/* no padding */
+				rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
+							CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
+				break;
+			default:
+				LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
+							"supported\n", l->type);
+		}
+	}
+	
+	now=get_ticks_raw();
+	/* alloc & init the entries */
+	for (r=0; r<no_records; r++){
+		rec[r].e=shm_malloc(rec[r].size);
+		if (rec[r].e==0){
+			LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
+			goto error;
+		}
+		memset(rec[r].e, 0, rec[r].size); /* init with 0*/
+		rec[r].e->total_size=rec[r].size;
+		rec[r].e->name_len=rec[r].rd->name_len;
+		rec[r].e->type=rec[r].rd->type;
+		rec[r].e->last_used=now;
+		/* memset makes sure is 0-term. */
+		memcpy(rec[r].e->name, rec[r].rd->name, rec[r].rd->name_len); 
+		rec[r].e->rr_lst=(struct dns_rr*)((char*)rec[r].e+
+				ROUND_POINTER(sizeof(struct dns_hash_entry)+rec[r].e->name_len
+								 -1+1));
+		rec[r].tail_rr=&(rec[r].e->rr_lst);
+		rec[r].rr=rec[r].e->rr_lst;
+		rec[r].max_ttl=0;
+		/* link them in a list */
+		if (r==0){
+			clist_init(rec[r].e, next, prev);
+		}else{
+			clist_append(rec[0].e, rec[r].e, next, prev);
+		}
+	}
+	/* copy the actual data */
+	for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
+		r=rec_idx[i];
+		ttl=FIX_TTL(l->ttl);
+		switch(l->type){
+			case T_A:
+				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
+				rec[r].rr->rdata=(void*)((char*)rec[r].rr+
+									sizeof(struct dns_rr));
+				memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct a_rdata));
+				rec[r].rr->next=(void*)((char*)rec[r].rr+
+									ROUND_POINTER(sizeof(struct dns_rr)+
+									sizeof(struct a_rdata)));
+				rec[r].tail_rr=&(rec[r].rr->next);
+				rec[r].rr=rec[r].rr->next;
+				break;
+			case T_AAAA:
+				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
+				rec[r].rr->rdata=(void*)((char*)rec[r].rr+
+									sizeof(struct dns_rr));
+				memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
+				rec[r].rr->next=(void*)((char*)rec[r].rr+
+									ROUND_POINTER(sizeof(struct dns_rr)+
+									sizeof(struct aaaa_rdata)));
+				rec[r].tail_rr=&(rec[r].rr->next);
+				rec[r].rr=rec[r].rr->next;
+				break;
+			case T_SRV:
+				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
+				rec[r].rr->rdata=(void*)((char*)rec[r].rr+
+								ROUND_SHORT(sizeof(struct dns_rr)));
+				/* copy the whole srv_rdata block*/
+				memcpy(rec[r].rr->rdata, l->rdata, 
+						SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
+				rec[r].rr->next=(void*)((char*)rec[r].rr+
+							ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
+										SRV_RDATA_SIZE(
+											*(struct srv_rdata*)l->rdata)));
+				rec[r].tail_rr=&(rec[r].rr->next);
+				rec[r].rr=rec[r].rr->next;
+				break;
+			case T_NAPTR:
+				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
+				rec[r].rr->rdata=(void*)((char*)rec[r].rr+
+								ROUND_POINTER(sizeof(struct dns_rr)));
+				/* copy the whole srv_rdata block*/
+				memcpy(rec[r].rr->rdata, l->rdata, 
+						NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
+				/* adjust the string pointer */
+				((struct naptr_rdata*)rec[r].rr->rdata)->flags=
+					translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->flags));
+				((struct naptr_rdata*)rec[r].rr->rdata)->services=
+					translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->services));
+				((struct naptr_rdata*)rec[r].rr->rdata)->regexp=
+					translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->regexp));
+				((struct naptr_rdata*)rec[r].rr->rdata)->repl=
+					translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
+							(((struct naptr_rdata*)l->rdata)->repl));
+				rec[r].rr->next=(void*)((char*)rec[r].rr+
+							ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
+										NAPTR_RDATA_SIZE(
+											*(struct naptr_rdata*)l->rdata)));
+				rec[r].tail_rr=&(rec[r].rr->next);
+				break;
+			case T_CNAME:
+				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
+				rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
+				rec[r].rr->rdata=(void*)((char*)rec[r].rr
+									+sizeof(struct dns_rr));
+				memcpy(rec[r].rr->rdata, l->rdata,
+							CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
+				rec[r].rr->next=(void*)((char*)rec[r].rr+
+							ROUND_POINTER(sizeof(struct dns_rr)+
+							CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
+				rec[r].tail_rr=&(rec[r].rr->next);
+				rec[r].rr=rec[r].rr->next;
+				break;
+			default:
+				/* do nothing */
+				;
+		}
+	}
+	for (r=0; r<no_records; r++){
+		*rec[r].tail_rr=0; /* terminate the list */
+		rec[r].e->expire=now+S_TO_TICKS(rec[r].max_ttl);
+	}
+	return rec[0].e;
+error:
+	for (r=0; r<no_records; r++){
+		dns_destroy_entry(rec[r].e);
+	}
+	return 0;
+}
+
+
+
+inline static struct dns_hash_entry* dns_get_entry(str* name, int type);
+
+
+#define CACHE_RELEVANT_RECS_ONLY
+
+#ifdef CACHE_RELEVANT_RECS_ONLY
+/* internal only: gets related entries from a rdata list, appends them
+ * to e (list) and returns:
+ *  - e if e is of the requested type
+ *  -  if e is a CNAME, tries to get to the end of the CNAME chain and returns
+ *      the final entry if the types match or 0 if the chain is unfinished
+ *  - 0 on error/not found
+ * records is modified (the used records are removed from the list and freed)
+ *
+ * WARNING: - records must be pkg_malloc'ed
+ * Notes:   - if the return is 0 and e->type==T_CNAME, the list will contain
+ *            the CNAME chain (the last element being the last CNAME)
+ *  */
+inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
+														int type,
+														struct rdata** records)
+{
+	struct dns_hash_entry* ret;
+	struct dns_hash_entry* l;
+	struct dns_hash_entry* t;
+	struct dns_hash_entry* lst_end;
+	struct dns_rr* rr;
+	static int cname_chain_len=0;
+	str tmp;
+	
+	ret=0;
+	l=e;
+	DBG("dns_get_related(%p (%.*s, %d), %d, *%p) (%d)\n", e,
+			e->name_len, e->name, e->type, type, *records, cname_chain_len);
+	clist_init(l, next, prev);
+	if (type==e->type){
+		ret=e;
+		switch(e->type){
+			case T_SRV:
+				for (rr=e->rr_lst; rr && *records; rr=rr->next){
+					tmp.s=((struct srv_rdata*)rr->rdata)->name;
+					tmp.len=((struct srv_rdata*)rr->rdata)->name_len;
+					if (!(dns_flags&DNS_IPV6_ONLY)){
+						t=dns_cache_mk_rd_entry(&tmp, T_A, records);
+						if (t){
+							if ((t->type==T_CNAME) && *records)
+								dns_get_related(t, T_A, records);
+							clist_append(l, t, next, prev);
+						}
+					}
+					if (!(dns_flags&DNS_IPV4_ONLY)){
+						t=dns_cache_mk_rd_entry(&tmp, T_AAAA, records);
+						if (t){
+							if ((t->type==T_CNAME) && *records)
+								dns_get_related(t, T_AAAA, records);
+							clist_append(l, t, next, prev);
+						}
+					}
+				}
+				break;
+			default:
+				/* nothing extra */
+				break;
+		}
+	}else if ((e->type==T_CNAME) && (cname_chain_len<MAX_CNAME_CHAIN)){
+		/* only one cname is allowed (rfc2181), so we ignore
+		 * the others (we take only the first one) */
+		tmp.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
+		tmp.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
+		t=dns_cache_mk_rd_entry(&tmp, type, records);
+		if (t){
+			if (*records){
+				cname_chain_len++;
+				ret=dns_get_related(t, type, records);
+				cname_chain_len--;
+				lst_end=t->prev;
+				clist_append_sublist(l, t, lst_end, next, prev);
+			}else{
+				clist_append(l, t, next, prev);
+			}
+		}
+	}
+	return ret;
+}
+#endif
+
+
+
+/* calls the external resolver and populates the cache with the result
+ * returns: 0 on error, pointer to hash entry on success
+ * WARNING: make sure you use dns_hash_entry_put() when you're
+ *  finished with the result)
+ * */
+inline static struct dns_hash_entry* dns_cache_do_request(str* name, int type)
+{
+	struct rdata* records;
+	struct dns_hash_entry* e;
+	struct dns_hash_entry* l;
+	struct dns_hash_entry* r;
+	struct dns_hash_entry* t;
+	struct ip_addr* ip;
+	str cname_val;
+	char name_buf[MAX_DNS_NAME];
+	
+	e=0;
+	l=0;
+	cname_val.s=0;
+	
+	if (type==T_A){
+		if ((ip=str2ip(name))!=0){
+				e=dns_cache_mk_ip_entry(name, ip);
+				if (e)
+					atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
+				goto end; /* we do not cache obvious stuff */
+		}
+	}else if (type==T_AAAA){
+		if ((ip=str2ip6(name))!=0){
+				e=dns_cache_mk_ip_entry(name, ip);
+				if (e)
+					atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
+				goto end;/* we do not cache obvious stuff */
+		}
+	}
+	if (name->len>=MAX_DNS_NAME){
+		LOG(L_ERR, "ERROR: dns_cache_do_request: name too long (%d chars)\n",
+					name->len);
+		goto end;
+	}
+	/* null terminate the string, needed by get_record */
+	memcpy(name_buf, name->s, name->len);
+	name_buf[name->len]=0;
+	records=get_record(name_buf, type, RES_AR);
+	if (records){
+#ifdef CACHE_RELEVANT_RECS_ONLY
+		e=dns_cache_mk_rd_entry(name, type, &records);
+		if (e){
+			l=e;
+			e=dns_get_related(l, type, &records);
+			/* e should contain the searched entry (if found) and l
+			 * all the entries (e and related) */
+			if (e){
+				atomic_set(&e->refcnt, 1); /* 1 because we return a 
+												ref. to it */
+			}else{
+				/* e==0 => l contains a  cname list => we use the last
+				 * cname from the chain for a new resolve attempt (l->prev) */
+				/* only one cname record is allowed (rfc2181), so we ignore 
+				 * the others (we take only the first one) */
+				cname_val.s=
+					((struct cname_rdata*)l->prev->rr_lst->rdata)->name;
+				cname_val.len=
+					((struct cname_rdata*)l->prev->rr_lst->rdata)->name_len;
+				DBG("dns_cache_do_request: cname detected: %.*s (%d)\n",
+						cname_val.len, cname_val.s, cname_val.len);
+			}
+			/* add all the records to the hash */
+			l->prev->next=0; /* we break the double linked list for easier
+								searching */
+			LOCK_DNS_HASH(); /* optimization */
+			for (r=l; r; r=t){
+				t=r->next;
+				dns_cache_add_unsafe(r); /* refcnt++ inside */
+				if (atomic_get(&r->refcnt)==0){
+					/* if cache adding failed and nobody else is interested
+					 * destroy this entry */
+					dns_destroy_entry(r);
+				}
+			}
+			UNLOCK_DNS_HASH();
+			/* if only cnames found => try to resolve the last one */
+			if (cname_val.s){ 
+				DBG("dns_cache_do_request: dns_get_entry(cname: %.*s (%d))\n",
+						cname_val.len, cname_val.s, cname_val.len);
+				e=dns_get_entry(&cname_val, type);
+			}
+		}
+#else
+		l=dns_cache_mk_rd_entry2(records);
+#endif
+		free_rdata_list(records);
+	}else if (dns_neg_cache_ttl){
+		e=dns_cache_mk_bad_entry(name, type, dns_neg_cache_ttl, DNS_BAD_NAME);
+		atomic_set(&e->refcnt, 1); /* 1 because we return a ref. to it */
+		dns_cache_add(e); /* refcnt++ inside*/
+		goto end;
+	}
+#ifndef CACHE_RELEVANT_RECS_ONLY
+	if (l){
+		/* add all the records to the cache, but return only the record
+		 * we are looking for */
+		l->prev->next=0; /* we break the double linked list for easier
+							searching */
+		LOCK_DNS_HASH(); /* optimization */
+		for (r=l; r; r=t){
+			t=r->next;
+			if (e==0){ /* no entry found yet */
+				if (r->type==T_CNAME){
+					if ((r->name_len==name->len) && (r->rr_lst) &&
+							(strncasecmp(r->name, name->s, name->len)==0)){
+						/* update the name with the name from the cname rec. */
+						cname_val.s=
+								((struct cname_rdata*)r->rr_lst->rdata)->name;
+						cname_val.len=
+							((struct cname_rdata*)r->rr_lst->rdata)->name_len;
+						name=&cname_val;
+					}
+				}else if ((r->type==type) && (r->name_len==name->len) &&
+							(strncasecmp(r->name, name->s, name->len)==0)){
+					e=r;
+					atomic_set(&e->refcnt, 1); /* 1 because we return a ref. 
+												  to it */
+				}
+			}
+			dns_cache_add_unsafe(r); /* refcnt++ inside */
+			if (atomic_get(&r->refcnt)==0){
+				/* if cache adding failed and nobody else is interested
+				 * destroy this entry */
+				dns_destroy_entry(r);
+			}
+		}
+		UNLOCK_DNS_HASH();
+		if ((e==0) && (cname_val.s)){ /* not found, but found a cname */
+			/* only one cname is allowed (rfc2181), so we ignore the
+			 * others (we take only the first one) */
+			e=dns_get_entry(&cname_val, type);
+		}
+	}
+#endif
+end:
+	return e;
+}
+
+
+
+/* tries to lookup (name, type) in the hash and if not found tries to make
+ *  a dns request
+ *  return: 0 on error, pointer to a dns_hash_entry on success
+ *  WARNING: when *   not needed anymore dns_hash_put() must be called! */
+inline static struct dns_hash_entry* dns_get_entry(str* name, int type)
+{
+	int h;
+	struct dns_hash_entry* e;
+	str cname_val;
+	int err;
+	static int rec_cnt=0; /* recursion protection */
+	
+	e=0;
+	if (rec_cnt>MAX_CNAME_CHAIN){
+		LOG(L_WARN, "WARNING: dns_get_entry: CNAME chain too long or"
+				" recursive CNAMEs (\"%.*s\")\n", name->len, name->s);
+		goto error;
+	}
+	rec_cnt++;
+	e=dns_hash_get(name, type, &h, &err);
+	if ((e==0) && ((err) || ((e=dns_cache_do_request(name, type))==0))){
+		goto error;
+	}else if ((e->type==T_CNAME) && (type!=T_CNAME)){
+		/* cname found instead which couldn't be resolved with the cached
+		 * info => try a dns request */
+		/* only one cname record is allowed (rfc2181), so we ignore 
+		 * the others (we take only the first one) */
+		cname_val.s= ((struct cname_rdata*)e->rr_lst->rdata)->name;
+		cname_val.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
+		dns_hash_put(e); /* not interested in the cname anymore */
+		if ((e=dns_cache_do_request(&cname_val, type))==0)
+			goto error; /* could not resolve cname */
+	}
+	/* found */
+	if ((e->rr_lst==0) || e->err_flags){
+		/* negative cache => not resolvable */
+		dns_hash_put(e);
+		e=0;
+	}
+error:
+	rec_cnt--;
+	return e;
+}
+
+
+
+/* gets the first non-expired, good record starting with record no
+ * from the dns_hash_entry struct e
+ * params:       e   - dns_hash_entry struct
+ *               *no - it must contain the start record number (0 initially);
+ *                      it will be filled with the returned record number
+ *               now - current time/ticks value
+ * returns pointer to the rr on success and sets no to the rr number
+ *         0 on error and fills the error flags
+ *
+ * Example usage:
+ * list all non-expired non-bad-marked ips for name:
+ * e=dns_get_entry(name, T_A);
+ * if (e){
+ *    *no=0;
+ *    now=get_ticks_raw();
+ *    while(rr=dns_entry_get_rr(e, no, now){
+ *       DBG("address %d\n", *no);
+ *       *no++;  ( get the next address next time )
+ *     }
+ *  }
+ */
+inline static struct dns_rr* dns_entry_get_rr(	struct dns_hash_entry* e,
+											 unsigned char* no, ticks_t now)
+{
+	struct dns_rr* rr;
+	int n;
+	int flags;
+	
+	flags=0;
+	for(rr=e->rr_lst, n=0;rr && (n<*no);rr=rr->next, n++);/* skip *no records*/
+	for(;rr;rr=rr->next){
+		if ((s_ticks_t)(now-e->expire)>=0) /* expired entry */
+			continue;
+		if (rr->err_flags){ /* bad rr */
+			continue;
+		}
+		/* everything is ok now */
+		*no=n;
+		return rr;
+	}
+	*no=n;
+	return 0;
+}
+
+
+
+/* gethostbyname compatibility: converts a dns_hash_entry structure 
+ * to a statical internal hostent structure
+ * returns a pointer to the internal hostent structure on success or
+ *          0 on error 
+ */
+struct hostent* dns_entry2he(struct dns_hash_entry* e)
+{
+	static struct hostent he;
+	static char hostname[256];
+	static char* p_aliases[1];
+	static char* p_addr[DNS_HE_MAX_ADDR+1];
+	static char address[16*DNS_HE_MAX_ADDR]; /* max 10 ipv6 addresses */
+	int af, len;
+	struct dns_rr* rr;
+	unsigned char rr_no;
+	ticks_t now;
+	int i;
+	
+	switch(e->type){
+		case T_A:
+			af=AF_INET;
+			len=4;
+			break;
+		case T_AAAA:
+			af=AF_INET6;
+			len=16;
+			break;
+		default:
+			LOG(L_CRIT, "BUG: dns_entry2he: wrong entry type %d for %.*s\n",
+					e->type, e->name_len, e->name);
+			return 0;
+	}
+	
+	
+	rr_no=0;
+	now=get_ticks_raw();
+	rr=dns_entry_get_rr(e, &rr_no, now);
+	for(i=0; rr && (i<DNS_HE_MAX_ADDR); i++, 
+							rr=dns_entry_get_rr(e, &rr_no, now)){
+				p_addr[i]=&address[i*len];
+				memcpy(p_addr[i], ((struct a_rdata*)rr->rdata)->ip, len);
+	}
+	if (i==0){
+		DBG("DEBUG: dns_entry2he: no good records found (%d) for %.*s (%d)\n",
+				rr_no, e->name_len, e->name, e->type);
+		return 0; /* no good record found */
+	}
+	
+	p_addr[i]=0; /* mark the end of the addresses */
+	p_aliases[0]=0; /* no aliases */
+	memcpy(hostname, e->name, e->name_len);
+	hostname[e->name_len]=0;
+	
+	he.h_addrtype=af;
+	he.h_length=len;
+	he.h_addr_list=p_addr;
+	he.h_aliases=p_aliases;
+	he.h_name=hostname;
+	
+	return &he;
+}
+
+
+
+/* gethostbyname compatibility: performs an a_lookup and returns a pointer 
+ * to a statical internal hostent structure
+ * returns 0 on success, <0 on error (see the error codes)
+ */
+struct hostent* dns_a_get_he(str* name)
+{
+	struct dns_hash_entry* e;
+	struct ip_addr* ip;
+	struct hostent* he;
+	
+	e=0;
+	if ((ip=str2ip(name))!=0){
+		return ip_addr2he(name, ip);
+	}
+	if ((e=dns_get_entry(name, T_A))==0)
+		return 0;
+	/* found */
+	he=dns_entry2he(e);
+	dns_hash_put(e);
+	return he;
+}
+
+
+
+/* gethostbyname compatibility: performs an aaaa_lookup and returns a pointer 
+ * to a statical internal hostent structure
+ * returns 0 on success, <0 on error (see the error codes)
+ */
+struct hostent* dns_aaaa_get_he(str* name)
+{
+	struct dns_hash_entry* e;
+	struct ip_addr* ip;
+	struct hostent* he;
+	
+	e=0;
+	if ((ip=str2ip6(name))!=0){
+		return ip_addr2he(name, ip);
+	}
+	if ((e=dns_get_entry(name, T_AAAA))==0)
+			return 0;
+	/* found */
+	he=dns_entry2he(e);
+	dns_hash_put(e);
+	return he;
+}
+
+
+
+/* returns 0 on success, -1 on error (rr type does not contain an ip) */
+inline static int dns_rr2ip(int type, struct dns_rr* rr, struct ip_addr* ip)
+{
+	switch(type){
+		case T_A:
+			ip->af=AF_INET;
+			ip->len=4;
+			memcpy(ip->u.addr, ((struct a_rdata*)rr->rdata)->ip, 4);
+			return 0;
+			break;
+		case T_AAAA:
+			ip->af=AF_INET6;
+			ip->len=16;
+			memcpy(ip->u.addr, ((struct aaaa_rdata*)rr->rdata)->ip6, 16);
+			return 0;
+			break;
+	}
+	return -1;
+}
+
+
+
+/* gethostbyname compatibility:
+ * performs an a or aaaa dns lookup, returns 0 on error and a pointer to a
+ *          static hostent structure on success
+ *  flags:  - none set: tries first an a_lookup and if it fails an aaaa_lookup
+ *          - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
+ *          - DNS_IPV4_ONLY: tries only an a_lookup
+ *          - DNS_IPV6_ONLY: tries only an aaaa_lookup
+ */
+struct hostent* dns_get_he(str* name, int flags)
+{
+	struct hostent* he;
+	
+	
+	if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
+		he=dns_aaaa_get_he(name);
+		if (he) return he;
+	}else{
+		he=dns_a_get_he(name);
+		if (he) return he;
+	}
+	if (flags&DNS_IPV6_FIRST){
+		he=dns_a_get_he(name);
+	}else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
+		he=dns_aaaa_get_he(name);
+	}
+	return he;
+}
+
+
+
+/* sip_resolvehost helper: gets the first good  hostent/port combination
+ * returns 0 on error, pointer to static hostent structure on success
+ *           (and sets port)*/
+struct hostent* dns_srv_get_he(str* name, unsigned short* port, int flags)
+{
+	struct dns_hash_entry* e;
+	struct dns_rr* rr;
+	str rr_name;
+	struct hostent* he;
+	ticks_t now;
+	unsigned char rr_no;
+	
+	rr=0;
+	he=0;
+	now=get_ticks_raw();
+	if ((e=dns_get_entry(name, T_SRV))==0)
+			goto error;
+	/* look inside the RRs for a good one (not expired or marked bad)  */
+	rr_no=0;
+	while( (rr=dns_entry_get_rr(e, &rr_no, now))!=0){
+		/* everything is ok now, we can try to resolve the ip */
+		rr_name.s=((struct srv_rdata*)rr->rdata)->name;
+		rr_name.len=((struct srv_rdata*)rr->rdata)->name_len;
+		if ((he=dns_get_he(&rr_name, flags))!=0){
+				/* success, at least one good ip found */
+				*port=((struct srv_rdata*)rr->rdata)->port;
+				goto end;
+		}
+		rr_no++; /* try from the next record, the current one was not good */
+	}
+	/* if we reach this point => error, we couldn't find any good rr */
+end:
+	if (e) dns_hash_put(e);
+error:
+	return he;
+}
+
+
+
+struct hostent* dns_resolvehost(char* name)
+{
+	str host;
+	
+	if ((use_dns_cache==0) || (dns_hash==0)){ /* not init yet */
+		return _resolvehost(name);
+	}
+	host.s=name;
+	host.len=strlen(name);
+	return dns_get_he(&host, dns_flags);
+}
+
+
+
+/* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
+ * if *port!=0.
+ * when performing SRV lookup (*port==0) it will use proto to look for
+ * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
+ * returns: hostent struct & *port filled with the port from the SRV record;
+ *  0 on error
+ */
+struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
+{
+	struct hostent* he;
+	struct ip_addr* ip;
+	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
+	int len;
+	str srv_name;
+
+	if ((use_dns_cache==0) || (dns_hash==0)){ 
+		/* not init or off => use normal, non-cached version */
+		return _sip_resolvehost(name, port, proto);
+	}
+	len=0;
+	/* try SRV if no port specified (draft-ietf-sip-srv-06) */
+	if ((port)&&(*port==0)){
+		*port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we don't
+														find another */
+		if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
+			LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
+						" (%d), unable to perform SRV lookup\n", name->len);
+		}else{
+			/* check if it's an ip address */
+			if ( ((ip=str2ip(name))!=0)
+#ifdef	USE_IPV6
+				  || ((ip=str2ip6(name))!=0)
+#endif
+				){
+				/* we are lucky, this is an ip address */
+				return ip_addr2he(name,ip);
+			}
+			
+			switch(proto){
+				case PROTO_NONE: /* no proto specified, use udp */
+					goto skip_srv;
+				case PROTO_UDP:
+					memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
+					memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
+					tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
+					len=SRV_UDP_PREFIX_LEN + name->len;
+					break;
+				case PROTO_TCP:
+					memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
+					memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
+					tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
+					len=SRV_TCP_PREFIX_LEN + name->len;
+					break;
+				case PROTO_TLS:
+					memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
+					memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
+					tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
+					len=SRV_TLS_PREFIX_LEN + name->len;
+					break;
+				default:
+					LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
+							proto);
+					return 0;
+			}
+
+			srv_name.s=tmp;
+			srv_name.len=len;
+			if ((he=dns_srv_get_he(&srv_name, port, dns_flags))!=0)
+				return he;
+		}
+	}
+skip_srv:
+	if (name->len >= MAX_DNS_NAME) {
+		LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
+		return 0;
+	}
+	he=dns_get_he(name, dns_flags);
+	return he;
+}
+
+
+
+/* performs an a lookup, fills the dns_entry pointer and the ip addr.
+ *  (with the first good ip). if *e ==0 does the a lookup, and changes it
+ *   to the result, if not it uses the current value and tries to use 
+ *   the rr_no record from it.
+ * params:  e - must contain the "in-use" dns_hash_entry pointer (from
+ *               a previous call) or *e==0 (for the first call)
+ *          name - host name for which we do the lookup (required only
+ *                  when *e==0)
+ *          ip   - will be filled with the first good resolved ip started
+ *                 at *rr_no
+ *          rr_no - record number to start searching for a good ip from
+ *                  (e.g. value from previous call + 1), filled on return
+ *                  with the number of the record corresponding to the 
+ *                  returned ip
+ * returns 0 on success, <0 on error (see the error codes),
+ *         fills e, ip and rr_no
+ *          On end of records (when use to iterate on all the ips) it
+ *          will return E_DNS_EOR (you should not log an error for this
+ *          value, is just a signal that the address list end has been reached)
+ * WARNING: dns_hash_put(*e) must be called when you don't need
+ *          the entry anymore and *e!=0 (failling to do so => mem. leak)
+ * Example:
+ *  dns_entry=0;
+ *  ret=dns_a_get_ip(&dns_entry, name, &ip, &rr_no);  -- get the first rr.
+ *  ...
+ *  rr_no++;
+ *  while((ret>=0) && dns_entry)
+ *     dns_a_get_ip(&dns_entry, name, &ip, &rr_no); -- get the next rr
+ *   if (ret!=-E_DNS_EOR) ERROR(....);
+ *  ...
+ *  dns_hash_put(dns_entry); -- finished with the entry
+ */
+int dns_a_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
+					str* name, struct ip_addr* ip)
+{
+	struct dns_rr* rr;
+	int ret;
+	ticks_t now;
+	struct ip_addr* tmp;
+	
+	rr=0;
+	ret=-E_DNS_NO_IP; 
+	if (*e==0){ /* do lookup */
+		/* if ip don't set *e */
+		if ((tmp=str2ip(name))!=0){
+			*ip=*tmp;
+			*rr_no=0;
+			return 0;
+		}
+		if ((*e=dns_get_entry(name, T_A))==0)
+			goto error;
+		/* found */
+		*rr_no=0;
+		ret=-E_DNS_BAD_IP_ENTRY;
+	}
+	now=get_ticks_raw();
+	rr=dns_entry_get_rr(*e, rr_no, now);
+	if (rr){
+		/* everything is ok now, we can try to "convert" the ip */
+		dns_rr2ip((*e)->type, rr, ip);
+		ret=0;
+	}else{
+		ret=-E_DNS_EOR;
+	}
+error:
+	DBG("dns_a_resovle(%.*s, %d) returning %d\n",
+			name->len, name->s, *rr_no, ret);
+	return ret;
+}
+
+
+
+/* lookup, fills the dns_entry pointer and the ip addr.
+ *  (with the first good ip). if *e ==0 does the a lookup, and changes it
+ *   to the result, if not it uses the current value and tries to use 
+ * Same as dns_a_resolve but for aaaa records (see above).
+ */
+int dns_aaaa_resolve(struct dns_hash_entry** e, unsigned char* rr_no, 
+						str* name, struct ip_addr* ip)
+{
+	struct dns_rr* rr;
+	int ret;
+	ticks_t now;
+	struct ip_addr* tmp;
+	
+	rr=0;
+	ret=-E_DNS_NO_IP; 
+	if (*e==0){ /* do lookup */
+		/* if ip don't set *e */
+		if ((tmp=str2ip6(name))!=0){
+			*ip=*tmp;
+			*rr_no=0;
+			return 0;
+		}
+		if ((*e=dns_get_entry(name, T_AAAA))==0)
+			goto error;
+		/* found */
+		*rr_no=0;
+		ret=-E_DNS_BAD_IP_ENTRY;
+	}
+	now=get_ticks_raw();
+	rr=dns_entry_get_rr(*e, rr_no, now);
+	if (rr){
+		/* everything is ok now, we can try to "convert" the ip */
+		dns_rr2ip((*e)->type, rr, ip);
+		ret=0;
+	}else{
+		ret=-E_DNS_EOR; /* no more records */
+	}
+error:
+	return ret;
+}
+
+
+
+/* performs an a or aaaa dns lookup, returns <0 on error (see the
+ *  dns error codes) and 0 on success
+ *  flags:  - none set: tries first an a_lookup and if it fails an aaaa_lookup
+ *          - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
+ *          - DNS_IPV4_ONLY: tries only an a_lookup
+ *          - DNS_IPV6_ONLY: tries only an aaaa_lookup
+ *  see dns_a_resolve() for the rest of the params., examples a.s.o
+ *  WARNING: don't forget dns_hash_put(*e) when e is not needed anymore
+ */
+int dns_ip_resolve(struct dns_hash_entry** e, unsigned char* rr_no, 
+					str* name, struct ip_addr* ip, int flags)
+{
+	int ret;
+	
+	if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
+		ret=dns_aaaa_resolve(e, rr_no, name, ip);
+		if (ret>=0) return ret;
+	}else{
+		ret=dns_a_resolve(e, rr_no, name, ip);
+		if (ret>=0) return ret;
+	}
+	if (flags&DNS_IPV6_FIRST){
+		ret=dns_a_resolve(e, rr_no, name, ip);
+	}else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
+		ret=dns_aaaa_resolve(e, rr_no, name, ip);
+	}
+	return ret;
+}
+
+
+
+/*  gets the first srv record starting at rr_no
+ *  (similar to dns_a_resolve but for srv, sets host, port)
+ */
+int dns_srv_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
+					str* name, str* host, unsigned short* port)
+{
+	struct dns_rr* rr;
+	int ret;
+	ticks_t now;
+	
+	rr=0;
+	ret=-E_DNS_NO_SRV; 
+	if (*e==0){
+		if ((*e=dns_get_entry(name, T_SRV))==0)
+			goto error;
+		/* found it */
+		*rr_no=0;
+		ret=-E_DNS_BAD_SRV_ENTRY;
+	}
+	now=get_ticks_raw();
+	rr=dns_entry_get_rr(*e, rr_no, now);
+	if (rr){
+		host->s=((struct srv_rdata*)rr->rdata)->name;
+		host->len=((struct srv_rdata*)rr->rdata)->name_len;
+		*port=((struct srv_rdata*)rr->rdata)->port;
+		ret=0;
+	}else{
+		ret=-E_DNS_EOR; /* no more records */
+	}
+error:
+	return ret;
+}
+
+
+
+/*  gets the first srv record starting at h->srv_no, resolve it
+ *   and get the first ip address (starting at h->ip_no)
+ *  (similar to dns_a_resolve but for srv, sets host, port)
+ *  WARNING: don't forget to init h prior to calling this function the first
+ *   time and dns_srv_handle_put(h), even if error is returned
+ */
+int dns_srv_resolve_ip(struct dns_srv_handle* h,
+					str* name, struct ip_addr* ip, unsigned short* port,
+					int flags)
+{
+	int ret;
+	str host;
+	
+	host.len=0;
+	host.s=0;
+	do{
+		if (h->a==0){ 
+			if ((ret=dns_srv_resolve(&h->srv, &h->srv_no,
+													name, &host, port))<0)
+				goto error;
+			h->port=*port; /* store new port */
+		}else{
+			*port=h->port; /* return the stored port */
+		}
+		if ((ret=dns_ip_resolve(&h->a, &h->ip_no, &host, ip, flags))<0){
+			/* couldn't find any good ip for this record, try the next one */
+			h->srv_no++;
+			if (h->a){
+				dns_hash_put(h->a);
+				h->a=0;
+			}
+		}else if (h->a==0){
+			/* this was an ip, try the next srv record in the future */
+			h->srv_no++;
+		}
+	}while(ret<0);
+error:
+	DBG("dns_srv_resolve_ip(\"%.*s\", %d, %d), ret=%d, ip=%s\n", 
+			name->len, name->s, h->srv_no, h->ip_no, ret, ip_addr2a(ip));
+	return ret;
+}
+
+
+
+/* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
+ * if *port!=0.
+ * when performing SRV lookup (*port==0) it will use proto to look for
+ * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
+ * dns_res_h must be initialized prior to  calling this function and can be
+ * used to get the subsequent ips
+ * returns:  <0 on error
+ *            0 on success and it fills *ip, *port, dns_sip_resolve_h
+ * WARNING: when finished, dns_sip_resolve_put(dns_res_h) must be called!
+ */
+int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
+						struct ip_addr* ip, unsigned short* port, int proto,
+						int flags)
+{
+	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
+	int len;
+	str srv_name;
+	struct ip_addr* tmp_ip;
+	int ret;
+	struct hostent* he;
+
+	if (dns_hash==0){ /* not init => use normal, non-cached version */
+		LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
+					" initialization\n");
+		h->srv=h->a=0;
+		he=_sip_resolvehost(name, port, proto);
+		if (he){
+			hostent2ip_addr(ip, he, 0);
+			return 0;
+		}
+		return -E_DNS_NO_SRV;
+	}
+	len=0;
+	if ((h->srv==0) && (h->a==0)){
+		h->port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
+														don't find another */
+		if ((port) && (*port==0)){
+			/* try SRV if initial call & no port specified
+			 * (draft-ietf-sip-srv-06) */
+			*port=h->port;
+			if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
+				LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too"
+							" long (%d), unable to perform SRV lookup\n",
+							name->len);
+			}else{
+				/* check if it's an ip address */
+				if ( ((tmp_ip=str2ip(name))!=0)
+#ifdef	USE_IPV6
+					  || ((tmp_ip=str2ip6(name))!=0)
+#endif
+					){
+					/* we are lucky, this is an ip address */
+#ifdef	USE_IPV6
+					if (((flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6)) ||
+						((flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
+						return -E_DNS_AF_MISMATCH;
+					}
+#endif
+					*ip=*tmp_ip;
+					return 0;
+				}
+				
+				switch(proto){
+					case PROTO_NONE: /* no proto specified, use udp */
+						goto skip_srv;
+					case PROTO_UDP:
+						memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
+						memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
+						tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
+						len=SRV_UDP_PREFIX_LEN + name->len;
+						break;
+					case PROTO_TCP:
+						memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
+						memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
+						tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
+						len=SRV_TCP_PREFIX_LEN + name->len;
+						break;
+					case PROTO_TLS:
+						memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
+						memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
+						tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
+						len=SRV_TLS_PREFIX_LEN + name->len;
+						break;
+					default:
+						LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
+									proto);
+						return -E_DNS_CRITICAL;
+				}
+				srv_name.s=tmp;
+				srv_name.len=len;
+				
+				if ((ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags))>=0)
+				{
+					DBG("dns_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n", 
+						name->len, name->s, h->srv_no, h->ip_no, ret);
+					return ret;
+				}
+			}
+		}
+	}else if (h->srv){
+			srv_name.s=h->srv->name;
+			srv_name.len=h->srv->name_len;
+			/* continue srv resolving */
+			ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags);
+			DBG("dns_sip_resolve(%.*s, %d, %d), srv, ret=%d\n", 
+					name->len, name->s, h->srv_no, h->ip_no, ret);
+			return ret;
+	}
+skip_srv:
+	if (name->len >= MAX_DNS_NAME) {
+		LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
+		return -E_DNS_NAME_TOO_LONG;
+	}
+	ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
+	if (port)
+		*port=h->port;
+	DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n", 
+			name->len, name->s, h->srv_no, h->ip_no, ret);
+	return ret;
+}
+
+
+
+/* performs an a lookup and fills ip with the first good ip address
+ * returns 0 on success, <0 on error (see the error codes)
+ */
+int dns_a_get_ip(str* name, struct ip_addr* ip)
+{
+	struct dns_hash_entry* e;
+	int ret;
+	unsigned char rr_no;
+	
+	e=0;
+	rr_no=0;
+	ret=dns_a_resolve(&e, &rr_no, name, ip);
+	if (e) dns_hash_put(e);
+	return ret;
+}
+
+
+
+int dns_aaaa_get_ip(str* name, struct ip_addr* ip)
+{
+	struct dns_hash_entry* e;
+	int ret;
+	unsigned char rr_no;
+	
+	e=0;
+	rr_no=0;
+	ret=dns_aaaa_resolve(&e, &rr_no, name, ip);
+	if (e) dns_hash_put(e);
+	return ret;
+}
+
+
+
+/* performs an a or aaaa dns lookup, returns <0 on error (see the
+ *  dns error codes) and 0 on success
+ *  flags:  - none set: tries first an a_lookup and if it fails an aaaa_lookup
+ *          - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
+ *          - DNS_IPV4_ONLY: tries only an a_lookup
+ *          - DNS_IPV6_ONLY: tries only an aaaa_lookup
+ */
+int dns_get_ip(str* name, struct ip_addr* ip, int flags)
+{
+	int ret;
+	struct dns_hash_entry* e;
+	unsigned char rr_no;
+	
+	e=0;
+	rr_no=0;
+	ret=dns_ip_resolve(&e, &rr_no, name, ip, flags);
+	if (e)
+		dns_hash_put(e);
+	return ret;
+}
+
+
+
+/* fast "inline" version, gets the first good ip:port */
+int dns_srv_get_ip(str* name, struct ip_addr* ip, unsigned short* port,
+						int flags)
+{
+	int ret;
+	struct dns_srv_handle h;
+	
+	dns_srv_handle_init(&h);
+	ret=dns_srv_resolve_ip(&h, name, ip, port, flags);
+	dns_srv_handle_put(&h);
+	return ret;
+}
+
+
+
+/* rpc functions */
+void dns_cache_mem_info(rpc_t* rpc, void* ctx)
+{
+	rpc->add(ctx, "dd",  *dns_cache_mem_used, dns_cache_max_mem);
+}
+
+
+void dns_cache_debug(rpc_t* rpc, void* ctx)
+{
+	int h;
+	struct dns_hash_entry* e;
+	ticks_t now;
+	
+	now=get_ticks_raw();
+	LOCK_DNS_HASH();
+		for (h=0; h<DNS_HASH_SIZE; h++){
+			clist_foreach(&dns_hash[h], e, next){
+				rpc->add(ctx, "sdddddd", 
+								e->name, e->type, e->total_size, e->refcnt.val,
+								(s_ticks_t)(e->expire-now)<0?-1:
+									TICKS_TO_S(e->expire-now),
+								TICKS_TO_S(now-e->last_used),
+								e->err_flags);
+			}
+		}
+	UNLOCK_DNS_HASH();
+}
+
+
+
+/* rpc functions */
+void dns_cache_debug_all(rpc_t* rpc, void* ctx)
+{
+	int h;
+	struct dns_hash_entry* e;
+	struct dns_rr* rr;
+	struct ip_addr ip;
+	int i;
+	ticks_t now;
+	
+	now=get_ticks_raw();
+	LOCK_DNS_HASH();
+		for (h=0; h<DNS_HASH_SIZE; h++){
+			clist_foreach(&dns_hash[h], e, next){
+				for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
+					rpc->add(ctx, "sddddddd", 
+								e->name, e->type, i, e->total_size,
+								e->refcnt.val,
+								(s_ticks_t)(e->expire-now)<0?-1:
+									TICKS_TO_S(e->expire-now),
+								TICKS_TO_S(now-e->last_used),
+								e->err_flags);
+					switch(e->type){
+						case T_A:
+						case T_AAAA:
+							if (dns_rr2ip(e->type, rr, &ip)==0){
+								rpc->add(ctx, "ss", "ip", ip_addr2a(&ip) );
+							}else{
+								rpc->add(ctx, "ss", "ip", "<error: bad rr>");
+							}
+							break;
+						case T_SRV:
+							rpc->add(ctx, "ss", "srv", 
+									((struct srv_rdata*)(rr->rdata))->name);
+							break;
+						case T_NAPTR:
+							rpc->add(ctx, "ss", "naptr", 
+									((struct naptr_rdata*)(rr->rdata))->flags);
+							break;
+						case T_CNAME:
+							rpc->add(ctx, "ss", "cname", 
+									((struct cname_rdata*)(rr->rdata))->name);
+							break;
+						default:
+							rpc->add(ctx, "ss", "unknown", "?");
+					}
+					rpc->add(ctx, "dd",
+								(s_ticks_t)(rr->expire-now)<0?-1:
+									TICKS_TO_S(rr->expire-now),
+							rr->err_flags);
+				}
+			}
+		}
+	UNLOCK_DNS_HASH();
+}
+
+
+#endif

+ 287 - 0
dns_cache.h

@@ -0,0 +1,287 @@
+/*
+ * $Id$
+ *
+ * resolver/dns related functions, dns cache and failover
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* History:
+ * --------
+ *  2006-07-13  created by andrei
+ */
+
+
+#ifndef __dns_cache_h
+#define __dns_cache_h
+
+#include "str.h"
+#include "timer.h"
+#include "ip_addr.h"
+#include "atomic_ops.h"
+#include "resolve.h"
+
+
+#if defined(USE_DNS_FAILOVER) && !defined(USE_DNS_CACHE)
+#error "DNS FAILOVER requires DNS CACHE support (define USE_DNS_CACHE)"
+#endif
+
+#define DNS_LU_LST
+
+/* dns functions return them as negative values (e.g. return -E_DNS_NO_IP)
+ * listed in the order of importance ( if more errors, only the most important
+ * is returned)
+ */
+enum dns_errors{
+					E_DNS_OK=0,
+					E_DNS_EOR, /* no more records (not an error)
+					              -- returned only by the dns_resolve* 
+								  functions when called iteratively,; it
+								  signals the end of the ip/records list */
+					E_DNS_UNKNOWN /* unkown error */,
+					E_DNS_INTERNAL_ERR /* internal error */,
+					E_DNS_BAD_SRV_ENTRY,
+					E_DNS_NO_SRV /* unresolvable srv record */,
+					E_DNS_BAD_IP_ENTRY,
+					E_DNS_NO_IP /* unresolvable a or aaaa records*/,
+					E_DNS_BAD_IP /* the ip is invalid */, 
+					E_DNS_BLACKLIST_IP /* the ip is blacklisted */, 
+					E_DNS_NAME_TOO_LONG /* try again with a shorter name */,
+					E_DNS_AF_MISMATCH /* ipv4 or ipv6 only requested, but 
+										 name contains an ip addr. of the
+										 opossite type */ ,
+					E_DNS_CRITICAL /* critical error, marks the end
+									  of the error table (always last) */
+};
+
+
+extern int dns_flags; /* default flags used for dns lookup */
+
+/* return a short string, printable error description (err <=0) */
+const char* dns_strerror(int err);
+
+/* dns entry error flags */
+#define DNS_BAD_NAME     1 /* unresolvable */
+
+/* dns requests flags */
+#define DNS_NO_FLAGS	0
+#define DNS_IPV4_ONLY	1
+#define DNS_IPV6_ONLY	2
+#define DNS_IPV6_FIRST	4
+
+
+/* ip blacklist error flags */
+#define IP_ERR_BAD_DST      2 /* destination is marked as bad (e.g. bad ip) */
+#define IP_ERR_SND          3 /* send error while using this as destination */
+#define IP_ERR_TIMEOUT      4 /* timeout waiting for a response */
+#define IP_ERR_TCP_CON      5 /* could not establish tcp connection */
+
+
+/* stripped down dns rr */
+struct dns_rr{
+	struct dns_rr* next;
+	void* rdata; /* depends on the type */
+	/* name, type and class are not needed, contained in struct dns_query */
+	ticks_t expire; /* = ttl + crt_time */
+	unsigned char err_flags; /* if 0 everything is ok */
+
+};
+
+
+
+#ifdef DNS_LU_LST
+struct dns_lu_lst{  /* last used ordered list */
+	struct dns_lu_lst* next;
+	struct dns_lu_lst* prev;
+};
+#endif
+
+struct dns_hash_entry{
+	/* hash table links */
+	struct dns_hash_entry* next;
+	struct dns_hash_entry* prev;
+#ifdef DNS_LU_LST
+	struct dns_lu_lst last_used_lst;
+#endif
+	struct dns_rr* rr_lst;
+	atomic_t refcnt;
+	ticks_t last_used;
+	ticks_t expire; /* when the whole entry will expire */
+	int total_size;
+	unsigned short type;
+	unsigned char err_flags;
+	unsigned char name_len; /* can be maximum 255 bytes */
+	char name[1]; /* variable length, name, null terminated 
+	                 (actual lenght = name_len +1)*/
+};
+
+
+
+struct dns_srv_handle{
+	struct dns_hash_entry* srv; /* srv entry */
+	struct dns_hash_entry* a;   /* a or aaaa current entry */
+	unsigned short port; /* current port */
+	unsigned char srv_no; /* current record no. in the srv entry */
+	unsigned char ip_no;   /* current record no. in the a/aaaa entry */
+};
+
+
+
+const char* dns_strerror(int err);
+
+int init_dns_cache();
+void destroy_dns_cache();
+
+
+void dns_hash_put(struct dns_hash_entry* e);
+void dns_hash_put_shm_unsafe(struct dns_hash_entry* e);
+
+inline static void dns_srv_handle_put(struct dns_srv_handle* h)
+{
+	if (h){
+		if (h->srv){
+			dns_hash_put(h->srv);
+			h->srv=0;
+		}
+		if (h->a){
+			dns_hash_put(h->a);
+			h->a=0;
+		}
+	}
+}
+
+
+
+/* use it when copying, it manually increases the ref cound */
+inline static void dns_srv_handle_ref(struct dns_srv_handle *h)
+{
+	if (h){
+		if (h->srv)
+			atomic_inc(&h->srv->refcnt);
+		if (h->a)
+			atomic_inc(&h->a->refcnt);
+	}
+}
+
+
+
+/* safe copy increases the refcnt, src must not change while in this function
+ * WARNING: the copy must be dns_srv_handle_put ! */
+inline static void dns_srv_handle_cpy(struct dns_srv_handle* dst,
+										struct dns_srv_handle* src)
+{
+	dns_srv_handle_ref(src);
+	*dst=*src;
+}
+
+
+
+/* same as above but assume shm_lock held (for internal tm use only) */
+inline static void dns_srv_handle_put_shm_unsafe(struct dns_srv_handle* h)
+{
+	if (h){
+		if (h->srv){
+			dns_hash_put_shm_unsafe(h->srv);
+			h->srv=0;
+		}
+		if (h->a){
+			dns_hash_put_shm_unsafe(h->a);
+			h->a=0;
+		}
+	}
+}
+
+
+
+/* get "next" ip next time a dns_srv_handle function is called
+ * params: h   - struct dns_srv_handler
+ *         err - return code of the last dns_*_resolve* call 
+ * returns: 0 if it doesn't make sense to try another record,
+ * 1 otherwise
+ */
+inline static int dns_srv_handle_next(struct dns_srv_handle* h, int err)
+{
+	if (err<0) return 0;
+	h->ip_no++;
+	return (h->srv || h->a);
+}
+
+
+
+inline static void dns_srv_handle_init(struct dns_srv_handle* h)
+{
+	h->srv=h->a=0;
+	h->srv_no=h->ip_no=0;
+}
+
+
+
+/* performes a srv query on name
+ * Params:  name  - srv query target (e.g. _sip._udp.foo.bar)
+ *          ip    - result: first good ip found
+ *          port  - result: corresponding port number
+ *          flags - resolve options (like ipv4 only, ipv6 prefered a.s.o)
+ * Returns: < 0 on error (can be passed to dns_strerror(), 0 on success
+ */
+int dns_srv_get_ip(str* name, struct ip_addr* ip, unsigned short* port,
+					int flags);
+
+/* performs an A, AAAA (or both) query/queries
+ * Params:  name  - query target (e.g. foo.bar)
+ *          ip    - result: first good ip found
+ *          flags - resolve options (like ipv4 only, ipv6 prefered a.s.o)
+ * Returns: < 0 on error (can be passed to dns_strerror(), 0 on success
+ */
+int dns_get_ip(str* name, struct ip_addr* ip, int flags);
+
+struct hostent* dns_srv_get_he(str* name, unsigned short* port, int flags);
+struct hostent* dns_get_he(str* name, int flags);
+
+
+/* resolve name to an ip, using srv record. Can be called multiple times
+ * to iterate on all the possible ips, e.g :
+ * dns_srv_handle_init(h);
+ * ret_code=dns_sip_resolve(h,...);
+ *  while( dns_srv_handle_next(h, ret_code){ ret_code=dns_sip_resolve(h...); }
+ * dns_srv_handle_put(h);
+ * WARNING: dns_srv_handle_init() must be called to initialize h and
+ *  dns_srv_handle_put(h) must be called when h is no longer needed
+ */
+int dns_sip_resolve(struct dns_srv_handle* h,  str* name, struct ip_addr* ip,
+					unsigned short* port, int proto, int flags);
+
+/* same as above, but fills su intead of changing port and filling an ip */ 
+inline static int dns_sip_resolve2su(struct dns_srv_handle* h,
+									 union sockaddr_union* su,
+									 str* name, unsigned short port,
+									 int proto, int flags)
+{
+	struct ip_addr ip;
+	int ret;
+	
+	ret=dns_sip_resolve(h, name, &ip, &port, proto, flags);
+	if (ret>=0)
+		init_su(su, &ip, port);
+	return ret;
+}
+#endif

+ 41 - 0
dns_wrappers.h

@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * resolver related functions
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* History:
+ * --------
+ *  2006-07-23  created by andrei
+ */
+
+#ifndef __dns_wrappers_h
+#define __dns_wrappers_h
+
+struct hostent* dns_resolvehost(char* name);
+struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
+										int proto);
+
+#endif

+ 173 - 0
doc/dns.txt

@@ -0,0 +1,173 @@
+# $Id$
+#
+# History:
+# --------
+# 2006-09-08  created by andrei
+#
+
+Overview
+
+ The dns subsystem in ser can either directly use libresolv and a combination
+  of the locally configured dns server, /etc/hosts and the local Network 
+  Information Service (NIS/YP a.s.o) or cache the query results (both positive
+  and negative) and look first in its internal cache.
+ When its internal dns cache is enabled, ser can also use dns failover: if
+  one destination resolves to multiple addresses ser can try all of them until
+  it finds one to which it can succesfully send the packet or it exhausts all 
+  of them. ser (tm to be more precise) uses the dns failover also when the
+  destination host doesn't send any reply to a forwarded invite within the
+  sip timeout interval (whose value can be configured using the tm fr_timer
+   parameter).
+
+
+DNS Cache and Failover Drawbacks
+
+ Using the dns cache and the dns failover has also some drawbacks: 
+
+  1. only the locally configured dns server (usually in /etc/resolv.conf) is
+  used for the requests (/etc/hosts and the local Network Information Service
+  are ignored). 
+     Workarround: disable the dns cache (use_dns_cache=off or
+  compile without -DUSE_DNS_CACHE).
+
+  2. the dns cache uses extra memory
+      Workarround: disable the dns cache.
+
+  3. the dns failover introduces a very small performance penalty 
+     Workarround: disable the dns failover (use_dns_failover=off).
+
+  4. the dns failover increases the memory usage (the internal structures
+  used to represent the transaction are bigger when the dns failover support is
+  compiled).
+     Workarround: compile without dns failover support (-DUSE_DNS_FAILOVER).
+  Turning it off from the config file is not enough in this case (the extra
+   memory will still be used).
+
+
+DNS Resolver Options
+
+ The DNS resolver options control how ser will interact with the external
+ DNS servers. These options (with the dns_try_ipv6 exception) are passed to
+ libresolv and are used each time a dns request is made.
+ The default values are system specific and generally depend on the
+ /etc/resolv.conf content. For servers doing a lot of DNS requests it is
+ highly recommended to change the default values in the ser config file
+  (even if using ser's internal dns cache).
+
+   dns_try_ipv6 = on | off - if on and ser listens on at least one ipv6 socket,
+      ipv6 (AAAA) lookups will be performed if the ipv4 (A) lookups fail. 
+      If off only ipv4 (A) lookups will be used.
+      Default: on if ser is compiled with ipv6 support.
+
+   dns_retr_time = time - time in s before retrying a dns request.
+      Default: system specific, depends also on the/etc/resolv.conf content
+      (usually 5 s).
+
+   dns_retr_no = no. - number of dns retransmissions before giving up.
+      Default: see above (usually 4)
+
+   dns_servers_no = no. - how many dns servers from the ones defined in 
+      /etc/resolv.conf will be used. Default: all of them.
+
+   dns_use_search_list= yes/no - if no, the search list in /etc/resolv.conf
+      will be ignored (=> fewer lookups => gives up faster).
+      Default: yes.
+      HINT: even if you don't have a search list defined, setting this option
+      to "no" will still be "faster", because an empty search list is in 
+      fact search "" (so even if the search list is empty/missing there will
+      still be 2 dns queries, eg. foo+'.' and foo+""+'.')
+
+ The maximum time a dns request can take (before failing) is:
+ (dns_retr_time*dns_retr_no)*(search_list_domains) If dns_try_ipv6 is yes,
+ mutliply it again by 2.
+
+ The option combination that produces the "fastest" dns resolver config
+  (the "faster" in the sense that it gives up the quickest) is:
+
+      dns_try_ipv6=no
+      dns_retr_time=1
+      dns_retr_no=1
+      dns_servers_no=1
+      dns_use_search_list=no
+
+ The recommended dns configuration is to have a "close" dns caching recursive
+ server configured in /etc/resolv.conf, set the dns resolver options in ser's
+ config as in the above example and enable the dns cache (in ser).
+ Pay particular attention to dns_servers_no and dns_use_search_list. It's a
+ good ideea to make sure you don't need / use the search list or more then one
+ dns server (to avoid unnecessary extra lookups).
+
+
+DNS Cache and Failover Config Variables
+
+   use_dns_cache = on | off - if off the dns cache won't be used (all dns
+      lookups will result into a dns request).  When on all the dns request
+      results will be cached.
+      WARNING: when enabled /etc/hosts will be completely bypassed, all the dns
+      request will go directly to the system configured (resolv.conf) dns
+      server.
+      Default: on.
+
+   use_dns_failover = on |off - if on and sending a request fails (due to not
+      being allowed from an onsend_route, send failure, blacklisted destination
+      or, when using tm, invite timeout), and the destination resolves to
+      multiple ip addresses and/or multiple SRV records, the send will be
+      re-tried using the next ip/record. In tm's case a new branch will be
+      created for each new send attempt.
+      Default: off.
+   Depends on use_dns_cache being on. If tm is used along with dns failover is
+   recommended to also turn on dst_blacklist.
+
+   dns_cache_flags = dns cache specific resolver flags, used for overriding
+     the default behaviour (low level).
+      Possible values:
+         1 - ipv4 only: only DNS A requests are performed, even if ser listens
+                        also on ipv6 addresses.
+         2 - ipv6 only: only DNS AAAA requests are performed. Ignored if
+                        dns_try_ipv6 is off or ser doesn't listen on any ipv6
+                        address.
+         4 - prefer ipv6: try first to resolve a host name to an ipv6 address
+                          (DNS AAAA request) and only if this fails try an ipv4
+                          address (DNS A request).
+                          By default the ipv4 addresses are preferred.
+      Default: 0
+
+   dns_cache_negative_ttl = time to live for negative results ("not found") in
+      seconds. Use 0 to disable.
+      Default: 60 s.
+
+   dns_cache_min_ttl = minimum accepted time to live for a record, in seconds.
+      If a record has a lower ttl, its value will be discarded and
+      dns_cache_min_ttl will be used instead.
+      Default: 0
+
+   dns_cache_max_ttl = maximum accepted time to live for a record, in seconds.
+      If a record has a higher ttl, its value will be discarded and
+      dns_cache_max_ttl will be used instead.
+      Default: MAXINT
+
+   dns_cache_mem = maximum memory used for the dns cache in Kb.
+      Default: 500 Kb
+
+   dns_cache_gc_interval = how often (in s) the dns cache will be garbage 
+      collected.
+      Default:  120 s.
+
+
+DNS Cache Compile Options
+
+   USE_DNS_CACHE - if defined the dns cache support will be compiled in 
+      (default). If not needed/wanted the dns_cache can be disabled from the
+      ser's config file. The only advantages for not compiling the dns cache
+      support is a slight decrease of the executable size and an extremely 
+      small performance increase (1 less comparison per dns request).
+
+   USE_DNS_FAILOVER - if defined the dns failover support will be compiled in.
+      (default). Compiling the dns failover support has a few disadvantages,
+      see the "Drawbacks" section.
+ 
+ Note: To remove a compile options,  edit ser's Makefile.defs and remove it 
+   form DEFS list. To add a compile options add it to the make command line,
+     e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER
+   or for a permanent solution, edit Makefile.defs and add it to DEFS 
+   (don't foget to prefix it with -D).

+ 69 - 0
doc/dst_blacklist.txt

@@ -0,0 +1,69 @@
+# $Id$
+#
+# History:
+# --------
+# 2006-09-08  created by andrei
+#
+
+Overview
+
+ The destination blacklist (dst_blacklist) is used to try to mark bad
+  destination and avoid possible future expensive send operation to them.
+ A destination is added to the blacklist when trying to send to it fails (e.g.
+ timeout while trying to send or connect on tcp), or when a sip timeout occurs
+ while trying to forward statefully an invite (using tm) and the remote side
+ doesn't send back any response.
+ The blacklist (if enabled) is checked before any send attempt.
+
+
+Drawbacks
+
+
+ Using the destination blacklist will cause some performance degradation,
+ especially on multi cpu machines. If you don't need it you can easily
+  disable it, either in ser's config or at compile time. Disabling it at
+  compile time is sligthly better (but not in a "measurable" way) then
+   disabling it at runtime, from the config file.
+ Whether the destination blacklist is better to be on or off depends a lot
+  on the setup. In general is better to turn it on when:
+   - sending to clients that don't respond is expensive (e.g. lots of clients
+   use tcp and they have the habit of silently discarding tcp traffic from time
+   to time)
+   - statefull forwarding is used (tm) and lower memory usage is desired
+   (a transaction will fail immediately if the destination is already 
+   blacklisted by a previous transaction to the same destination that failed
+   due to timeout)
+   - faster dns failover is desired, especially when statefull forwarding (tm)
+   and udp are used
+   - better chances of DOS survival are important
+
+
+Config Variables
+
+ use_dst_blacklist = on | off (default off) - enable the destination blacklist:
+  if on each failed send attempt will cause the destination to be blacklisted.
+  Before any send this blacklist will be checked and if a match is found the
+  send is no longer attempted (an error is returned immediately).
+  Note: using the blacklist incurs a small performance penalty.
+
+ dst_blacklist_mem = size in Kb (default 250 Kb) - maximum
+  shared memory ammount used for keeping the blacklisted destinations.
+
+ dst_blacklist_expire = time in s (default 60 s) - how much time a 
+  blacklisted destination will be kept in the blacklist (w/o any update).
+
+ dst_blacklist_gc_interval = time in s (default 60 s) - how often the 
+  garbage collection will run (eliminating old, expired entries).
+
+
+Compile Options
+
+ USE_DST_BLACKLIST - if defined the blacklist support will be compiled-in
+  (default).
+
+
+ Note: To remove a compile options,  edit ser's Makefile.defs and remove it 
+   form DEFS list. To add a compile options add it to the make command line,
+     e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER
+   or for a permanent solution, edit Makefile.defs and add it to DEFS 
+   (don't foget to prefix it with -D).

+ 494 - 0
dst_blacklist.c

@@ -0,0 +1,494 @@
+/*
+ * $Id$
+ *
+ * resolver related functions
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* History:
+ * --------
+ *  2006-07-29  created by andrei
+ */
+
+
+#ifdef USE_DST_BLACKLIST
+
+#include "dst_blacklist.h"
+#include "mem/shm_mem.h"
+#include "hashes.h"
+#include "locking.h"
+#include "timer.h"
+#include "timer_ticks.h"
+#include "ip_addr.h"
+#include "error.h"
+#include "rpc.h"
+
+
+
+
+struct dst_blst_entry{
+	struct dst_blst_entry* next;
+	ticks_t expire;
+	unsigned short port;
+	unsigned char proto;
+	unsigned char flags; /* contains the address type + error flags */
+	unsigned char ip[4]; /* 4 for ipv4, 16 for ipv6 */ 
+};
+
+#define DST_BLST_ENTRY_SIZE(b) \
+		(sizeof(struct dst_blst_entry)+((b).flags&BLST_IS_IPV6)*12)
+
+
+#define DST_BLST_HASH_SIZE		1024
+#define DEFAULT_BLST_TIMEOUT		60  /* 1 min. */
+#define DEFAULT_BLST_MAX_MEM	250 /* 1 Kb FIXME (debugging)*/
+#define DEFAULT_BLST_TIMER_INTERVAL		60 /* 1 min */
+
+
+static gen_lock_t* blst_lock=0;
+static struct timer_ln* blst_timer_h=0;
+
+static volatile unsigned int* blst_mem_used=0;
+unsigned int  blst_max_mem=DEFAULT_BLST_MAX_MEM; /* maximum memory used
+													for the blacklist entries*/
+unsigned int blst_timeout=DEFAULT_BLST_TIMEOUT;
+unsigned int blst_timer_interval=DEFAULT_BLST_TIMER_INTERVAL;
+struct dst_blst_entry** dst_blst_hash=0;
+
+
+#define LOCK_BLST()		lock_get(blst_lock)
+#define UNLOCK_BLST()	lock_release(blst_lock)
+
+
+inline static void blst_destroy_entry(struct dst_blst_entry* e)
+{
+	shm_free(e);
+}
+
+
+static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data);
+
+
+inline static void dst_blst_entry2ip(struct ip_addr* ip,
+										struct dst_blst_entry* e)
+{
+	if (e->flags & BLST_IS_IPV6){
+		ip->af=AF_INET6;
+		ip->len=16;
+	}else{
+		ip->af=AF_INET;
+		ip->len=4;
+	}
+	memcpy(ip->u.addr, e->ip, ip->len);
+}
+
+
+
+inline static unsigned short dst_blst_hash_no(unsigned char proto,
+											  struct ip_addr* ip,
+											  unsigned short port)
+{
+	str s1;
+	str s2;
+	
+	s1.s=(char*)ip->u.addr;
+	s1.len=ip->len;
+	s2.s=(char*)&port;
+	s2.len=sizeof(unsigned short);
+	return get_hash2_raw(&s1, &s2)%DST_BLST_HASH_SIZE;
+}
+
+
+
+void destroy_dst_blacklist()
+{
+	if (blst_timer_h){
+		timer_del(blst_timer_h);
+		timer_free(blst_timer_h);
+		blst_timer_h=0;
+	}
+	if (blst_lock){
+		lock_destroy(blst_lock);
+		lock_dealloc(blst_lock);
+		blst_lock=0;
+	}
+	if (dst_blst_hash){
+		shm_free(dst_blst_hash);
+		dst_blst_hash=0;
+	}
+	if (blst_mem_used){
+		shm_free((void*)blst_mem_used);
+		blst_mem_used=0;
+	}
+}
+
+
+
+int init_dst_blacklist()
+{
+	int ret;
+	
+	ret=-1;
+	blst_mem_used=shm_malloc(sizeof(*blst_mem_used));
+	if (blst_mem_used==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	dst_blst_hash=shm_malloc(sizeof(struct dst_blst_entry*) *
+											DST_BLST_HASH_SIZE);
+	if (dst_blst_hash==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	blst_lock=lock_alloc();
+	if (blst_lock==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	if (lock_init(blst_lock)==0){
+		lock_dealloc(blst_lock);
+		blst_lock=0;
+		ret=-1;
+		goto error;
+	}
+	blst_timer_h=timer_alloc();
+	if (blst_timer_h==0){
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	/* fix options */
+	blst_max_mem<<=10; /* in Kb */ /* TODO: test with 0 */
+	if (blst_timer_interval){
+		timer_init(blst_timer_h, blst_timer, 0 ,0); /* slow timer */
+		if (timer_add(blst_timer_h, S_TO_TICKS(blst_timer_interval))<0){
+			LOG(L_CRIT, "BUG: init_dst_blacklist: failed to add the timer\n");
+			timer_free(blst_timer_h);
+			blst_timer_h=0;
+			goto error;
+		}
+	}
+	return 0;
+error:
+	destroy_dst_blacklist();
+	return ret;
+}
+
+
+/* must be called with the lock held
+ * struct dst_blst_entry** head, struct dst_blst_entry* e */
+#define dst_blacklist_lst_add(head, e)\
+do{ \
+	(e)->next=*(head); \
+	*(head)=(e); \
+}while(0)
+
+
+
+/* must be called with the lock held
+ * returns a pointer to the blacklist entry if found, 0 otherwise
+ * it also deletes expired elements (expire<=now) as it searches
+ * proto==PROTO_NONE = wildcard */
+inline static struct dst_blst_entry* _dst_blacklist_lst_find(
+												struct dst_blst_entry** head,
+												struct ip_addr* ip,
+												unsigned char proto,
+												unsigned short port,
+												ticks_t now)
+{
+	struct dst_blst_entry** crt;
+	struct dst_blst_entry** tmp;
+	struct dst_blst_entry* e;
+	unsigned char type;
+	
+	type=(ip->af==AF_INET6)*BLST_IS_IPV6;
+	for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
+		e=*crt;
+		/* remove old expired entries */
+		if ((s_ticks_t)(now-(*crt)->expire)>=0){
+			*crt=(*crt)->next;
+			*blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
+			blst_destroy_entry(e);
+		}else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
+				((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
+					(e->proto==proto)) && 
+					(memcmp(ip->u.addr, e->ip, ip->len)==0)){
+			return e;
+		}
+	}
+	return 0;
+}
+
+
+
+/* frees all the expired entries until either there are no more of them
+ *  or the total memory used is <= target (to free all of them use -1 for 
+ *  targer)
+ *  It must be called with LOCK_BLST held (or call dst_blacklist_clean_expired
+ *   instead).
+ *  params:   target  - free expired entries until no more then taget memory 
+ *                      is used  (use 0 to free all of them)
+ *            delta   - consider an entry expired if it expires after delta
+ *                      ticks from now
+ *            timeout - exit after timeout ticks
+ *
+ *  returns: number of deleted entries
+ *  This function should be called periodically from a timer
+ */
+inline static int _dst_blacklist_clean_expired_unsafe(unsigned int target,
+												ticks_t delta,
+												ticks_t timeout)
+{
+	static unsigned short start=0;
+	unsigned short h;
+	struct dst_blst_entry** crt;
+	struct dst_blst_entry** tmp;
+	struct dst_blst_entry* e;
+	ticks_t start_time;
+	ticks_t now;
+	int no=0;
+	
+	now=start_time=get_ticks_raw();
+	for(h=start; h!=(start+DST_BLST_HASH_SIZE); h++){
+		for (crt=&dst_blst_hash[h%DST_BLST_HASH_SIZE], tmp=&(*crt)->next;
+				*crt; crt=tmp, tmp=&(*crt)->next){
+			e=*crt;
+			if ((s_ticks_t)(now+delta-(*crt)->expire)>=0){
+				*crt=(*crt)->next;
+				*blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
+				blst_destroy_entry(e);
+				no++;
+				if (*blst_mem_used<=target)
+					goto skip;
+			}
+		}
+		/* check for timeout only "between" hash cells */
+		now=get_ticks_raw();
+		if ((now-start_time)>=timeout){
+			DBG("_dst_blacklist_clean_expired_unsafe: timeout: %d > %d\n",
+					TICKS_TO_MS(now-start_time), TICKS_TO_MS(timeout));
+			goto skip;
+		}
+	}
+skip:
+	start=h; /* next time we start where we left */
+	return no;
+}
+
+
+
+/* frees all the expired entries until either there are no more of them
+ *  or the total memory used is <= target (to free all of them use -1 for 
+ *  targer)
+ *  params:   target  - free expired entries until no more then taget memory 
+ *                      is used  (use 0 to free all of them)
+ *            delta   - consider an entry expired if it expires after delta
+ *                      ticks from now
+ *            timeout - exit after timeout ticks
+ *
+ *  returns: number of deleted entries
+ *  This function should be called periodically from a timer
+ */
+inline static int dst_blacklist_clean_expired(unsigned int target,
+												ticks_t delta,
+												ticks_t timeout)
+{
+	int no;
+	
+	LOCK_BLST();
+		no=_dst_blacklist_clean_expired_unsafe(target, delta, timeout);
+	UNLOCK_BLST();
+	if (no){
+		DBG("dst_blacklist_clean_expired, %d entries removed\n", no);
+	}
+	return no;
+}
+
+
+
+/* timer */
+static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
+{
+	dst_blacklist_clean_expired(0, 0, 2); /*spend max. 2 ticks*/
+	return (ticks_t)(-1);
+}
+
+
+
+/* adds a proto ip:port combination to the blacklist
+ * returns 0 on success, -1 on error (blacklist full -- would use more then
+ *  blst:_max_mem, or out of shm. mem.)
+ */
+inline static int dst_blacklist_add_ip(unsigned char err_flags, 
+									unsigned char proto,
+									struct ip_addr* ip, unsigned short port)
+{
+	int size;
+	struct dst_blst_entry* e;
+	unsigned short hash;
+	ticks_t now;
+	int ret;
+	
+	ret=0;
+	if (ip->af==AF_INET){
+		err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */
+		size=sizeof(struct dst_blst_entry);
+	}else{
+		err_flags|=BLST_IS_IPV6;
+		size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */;
+	}
+	now=get_ticks_raw();
+	hash=dst_blst_hash_no(proto, ip, port);
+	/* check if the entry already exists */
+	LOCK_BLST();
+		e=_dst_blacklist_lst_find(&dst_blst_hash[hash], ip, proto, port, now);
+		if (e){
+			e->flags|=err_flags;
+			e->expire=now+S_TO_TICKS(blst_timeout); /* update the timeout */
+		}else{
+			if ((*blst_mem_used+size)>=blst_max_mem){
+				/* first try to free some memory  (~ 12%), but don't
+				 * spend more then 250 ms*/
+				_dst_blacklist_clean_expired_unsafe(*blst_mem_used/16*14, 0, 
+															MS_TO_TICKS(250));
+				if (*blst_mem_used+size>=blst_max_mem){
+					ret=-1;
+					goto error;
+				}
+			}
+			e=shm_malloc(size);
+			if (e==0){
+				ret=E_OUT_OF_MEM;
+				goto error;
+			}
+			*blst_mem_used+=size;
+			e->flags=err_flags;
+			e->proto=proto;
+			e->port=port;
+			memcpy(e->ip, ip->u.addr, ip->len);
+			e->expire=now+S_TO_TICKS(blst_timeout); /* update the timeout */
+			e->next=0;
+			dst_blacklist_lst_add(&dst_blst_hash[hash], e);
+		}
+error:
+	UNLOCK_BLST();
+	return ret;
+}
+
+
+
+/* if no blacklisted returns 0, else returns the blacklist flags */
+inline static int dst_is_blacklisted_ip(unsigned char proto,
+										struct ip_addr* ip,
+										unsigned short port)
+{
+	struct dst_blst_entry* e;
+	unsigned short hash;
+	ticks_t now;
+	int ret=0;
+	
+	ret=0;
+	now=get_ticks_raw();
+	hash=dst_blst_hash_no(proto, ip, port);
+	LOCK_BLST();
+		e=_dst_blacklist_lst_find(&dst_blst_hash[hash], ip, proto, port, now);
+		if (e){
+			ret=e->flags;
+		}
+	UNLOCK_BLST();
+	return ret;
+}
+
+
+
+int dst_blacklist_add(unsigned char err_flags,  struct dest_info* si)
+{
+	struct ip_addr ip;
+	su2ip_addr(&ip, &si->to);
+	return dst_blacklist_add_ip(err_flags, si->proto, &ip,
+								su_getport(&si->to));
+}
+
+
+
+int dst_is_blacklisted(struct dest_info* si)
+{
+	struct ip_addr ip;
+	su2ip_addr(&ip, &si->to);
+	return dst_is_blacklisted_ip(si->proto, &ip, su_getport(&si->to));
+}
+
+
+
+/* rpc functions */
+void dst_blst_mem_info(rpc_t* rpc, void* ctx)
+{
+	rpc->add(ctx, "dd",  *blst_mem_used, blst_max_mem);
+}
+
+
+
+static char* get_proto_name(unsigned char proto)
+{
+	switch(proto){
+		case PROTO_NONE:
+			return "*";
+		case PROTO_UDP:
+			return "udp";
+		case PROTO_TCP:
+			return "tcp";
+		case PROTO_TLS:
+			return "tls";
+		default:
+			return "unknown";
+	}
+}
+
+
+
+/* only for debugging, it helds the lock too long for "production" use */
+void dst_blst_debug(rpc_t* rpc, void* ctx)
+{
+	int h;
+	struct dst_blst_entry* e;
+	ticks_t now;
+	struct ip_addr ip;
+	
+	now=get_ticks_raw();
+	LOCK_BLST();
+		for(h=0; h<DST_BLST_HASH_SIZE; h++){
+			for(e=dst_blst_hash[h]; e; e=e->next){
+				dst_blst_entry2ip(&ip, e);
+				rpc->add(ctx, "ssddd", get_proto_name(e->proto), 
+										ip_addr2a(&ip), e->port, 
+										(s_ticks_t)(now-e->expire)<=0?
+										TICKS_TO_S(e->expire-now):
+										-TICKS_TO_S(now-e->expire) ,
+										e->flags);
+			}
+		}
+	UNLOCK_BLST();
+}
+
+#endif /* USE_DST_BLACKLIST */
+

+ 55 - 0
dst_blacklist.h

@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * resolver related functions
+ *
+ * Copyright (C) 2006 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* History:
+ * --------
+ *  2006-07-29  created by andrei
+ */
+
+#ifndef dst_black_list_h
+#define dst_black_list_h
+
+#include "ip_addr.h"
+
+/* flags: */
+#define BLST_IS_IPV6		1		/* set if the address is ipv6 */
+#define BLST_ERR_SEND		(1<<1)	/* set if  send is denied/failed */
+#define BLST_ERR_CONNECT	(1<<2)	/* set if connect failed (tcp/tls) */
+#define BLST_ICMP_RCVD		(1<<3)	/* set if icmp error */
+#define BLST_ERR_TIMEOUT	(1<<4)	/* set if sip timeout */
+#define BLST_RESERVED		(1<<5)	/* not used yet */
+#define BLST_ADM_PROHIBITED	(1<<6)	/* administratively prohibited */
+#define BLST_PERMANENT		(1<<7)  /* never deleted, never expires */
+
+int init_dst_blacklist();
+void destroy_dst_blacklist();
+
+int dst_blacklist_add(unsigned char err_flags, struct dest_info* si);
+
+int dst_is_blacklisted(struct dest_info* si);
+#endif

+ 2 - 0
error.h

@@ -72,6 +72,8 @@
 
 
 /* error in server */
 /* error in server */
 #define E_BAD_SERVER	  -500
 #define E_BAD_SERVER	  -500
+#define E_ADM_PROHIBITED  -510
+#define E_BLACKLISTED	  -520
 
 
 
 
 #define MAX_REASON_LEN	128
 #define MAX_REASON_LEN	128

+ 165 - 42
forward.c

@@ -50,6 +50,8 @@
  *              pkg_malloc'ed (andrei)
  *              pkg_malloc'ed (andrei)
  *  2006-04-12  forward_{request,reply} use now struct dest_info (andrei)
  *  2006-04-12  forward_{request,reply} use now struct dest_info (andrei)
  *  2006-04-21  basic comp via param support (andrei)
  *  2006-04-21  basic comp via param support (andrei)
+ *  2006-07-31  forward_request can resolve destination on its own, uses the 
+ *              dns cache and falls back on send error to other ips (andrei)
  */
  */
 
 
 
 
@@ -80,6 +82,13 @@
 #include "name_alias.h"
 #include "name_alias.h"
 #include "socket_info.h"
 #include "socket_info.h"
 #include "onsend.h"
 #include "onsend.h"
+#include "resolve.h"
+#ifdef USE_DNS_FAILOVER
+#include "dns_cache.h"
+#endif
+#ifdef USE_DST_BLACKLIST
+#include "dst_blacklist.h"
+#endif
 
 
 #ifdef DEBUG_DMALLOC
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 #include <dmalloc.h>
@@ -265,32 +274,64 @@ found:
 /* forwards a request to dst
 /* forwards a request to dst
  * parameters:
  * parameters:
  *   msg       - sip msg
  *   msg       - sip msg
+ *   dst       - destination name, if non-null it will be resolved and
+ *               send_info updated with the ip/port. Even if dst is non
+ *               null send_info must contain the protocol and if a non
+ *               default port or non srv. lookup is desired, the port must
+ *               be !=0 
+ *   port      - used only if dst!=0 (else the port in send_info->to is used)
  *   send_info - filled dest_info structure:
  *   send_info - filled dest_info structure:
  *               if the send_socket memeber is null, a send_socket will be 
  *               if the send_socket memeber is null, a send_socket will be 
  *               choosen automatically
  *               choosen automatically
- * WARNING: don' forget to zero-fill all the  unused members (a non-zero 
+ * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
  * random id along with proto==PROTO_TCP can have bad consequences, same for
  * random id along with proto==PROTO_TCP can have bad consequences, same for
- *   a bogus send_socket vaule)
+ *   a bogus send_socket value)
  */
  */
-int forward_request(struct sip_msg* msg, struct dest_info* send_info)
+int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
+							struct dest_info* send_info)
 {
 {
 	unsigned int len;
 	unsigned int len;
 	char* buf;
 	char* buf;
 	char md5[MD5_LEN];
 	char md5[MD5_LEN];
+	struct socket_info* orig_send_sock; /* initial send_sock */
+	int ret;
+	struct ip_addr ip; /* debugging only */
+#ifdef USE_DNS_FAILOVER
+	struct socket_info* prev_send_sock;
+	int err;
+	struct dns_srv_handle dns_srv_h;
 	
 	
-	buf=0;
+	prev_send_sock=0;
+	err=0;
+#endif
 	
 	
-	if (send_info->send_sock==0)
-		send_info->send_sock=get_send_socket(msg, &send_info->to,
-												send_info->proto);
-	if (send_info->send_sock==0){
-		LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
-				"no corresponding listening socket\n",
-				send_info->to.s.sa_family, send_info->proto);
-		ser_error=E_NO_SOCKET;
-		goto error;
-	}
-
+	
+	buf=0;
+	orig_send_sock=send_info->send_sock;
+	ret=0;
+
+	if(dst){
+#ifdef USE_DNS_FAILOVER
+		if (use_dns_failover){
+			dns_srv_handle_init(&dns_srv_h);
+			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
+									send_info->proto, dns_flags);
+			if (err!=0){
+				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
+						" failed: %s [%d]\n", dst->len, ZSW(dst->s),
+						dns_strerror(err), err);
+				ret=E_BAD_ADDRESS;
+				goto error;
+			}
+		}else
+#endif
+		if (sip_hostport2su(&send_info->to, dst, port, send_info->proto)<0){
+			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
+						" dropping packet\n", dst->len, ZSW(dst->s));
+			ret=E_BAD_ADDRESS;
+			goto error;
+		}
+	}/* dst */
 	/* calculate branch for outbound request;  if syn_branch is turned off,
 	/* calculate branch for outbound request;  if syn_branch is turned off,
 	   calculate is from transaction key, i.e., as an md5 of From/To/CallID/
 	   calculate is from transaction key, i.e., as an md5 of From/To/CallID/
 	   CSeq exactly the same way as TM does; good for reboot -- than messages
 	   CSeq exactly the same way as TM does; good for reboot -- than messages
@@ -306,48 +347,130 @@ int forward_request(struct sip_msg* msg, struct dest_info* send_info)
 	} else {
 	} else {
 		if (!char_msg_val( msg, md5 )) 	{ /* parses transaction key */
 		if (!char_msg_val( msg, md5 )) 	{ /* parses transaction key */
 			LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
 			LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
+			ret=E_UNSPEC;
 			goto error;
 			goto error;
 		}
 		}
 		msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
 		msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
 		if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */,
 		if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */,
 					msg->add_to_branch_s, &msg->add_to_branch_len )) {
 					msg->add_to_branch_s, &msg->add_to_branch_len )) {
 			LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
 			LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
+			ret=E_UNSPEC;
 			goto error;
 			goto error;
 		}
 		}
 	}
 	}
-
-	buf = build_req_buf_from_sip_req(msg, &len, send_info);
-	if (!buf){
-		LOG(L_ERR, "ERROR: forward_request: building failed\n");
-		goto error;
-	}
-	 /* send it! */
-	DBG("Sending:\n%.*s.\n", (int)len, buf);
-	DBG("orig. len=%d, new_len=%d, proto=%d\n",
-			msg->len, len, send_info->proto );
+	/* try to send the message until success or all the ips are exhausted
+	 *  (if dns lookup is peformed && the dns cache used ) */
+#ifdef USE_DNS_FAILOVER
+	do{
+#endif
+		if (orig_send_sock==0) /* no forced send_sock => find it **/
+			send_info->send_sock=get_send_socket(msg, &send_info->to,
+												send_info->proto);
+		if (send_info->send_sock==0){
+			LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
+						"no corresponding listening socket\n",
+						send_info->to.s.sa_family, send_info->proto);
+			ret=ser_error=E_NO_SOCKET;
+#ifdef USE_DNS_FAILOVER
+			/* continue, maybe we find a socket for some other ip */
+			continue;
+#else
+			goto error;
+#endif
+		}
 	
 	
-	if (run_onsend(msg, send_info, buf, len)==0){
-		LOG(L_INFO, "forward_request: request dropped (onsend_route)\n");
-		STATS_TX_DROPS;
-		ser_error=E_OK; /* no error */
-		goto error; /* error ? */
-	}
-	if (msg_send(send_info, buf, len)<0){
-		ser_error=E_SEND;
-		STATS_TX_DROPS;
+#ifdef USE_DNS_FAILOVER
+		if (prev_send_sock!=send_info->send_sock){
+			/* rebuild the message only if the send_sock changed */
+			prev_send_sock=send_info->send_sock;
+#endif
+			if (buf) pkg_free(buf);
+			buf = build_req_buf_from_sip_req(msg, &len, send_info);
+			if (!buf){
+				LOG(L_ERR, "ERROR: forward_request: building failed\n");
+				ret=E_OUT_OF_MEM; /* most probable */
+				goto error;
+			}
+#ifdef USE_DNS_FAILOVER
+		}
+#endif
+		 /* send it! */
+		DBG("Sending:\n%.*s.\n", (int)len, buf);
+		DBG("orig. len=%d, new_len=%d, proto=%d\n",
+				msg->len, len, send_info->proto );
+	
+		if (run_onsend(msg, send_info, buf, len)==0){
+			su2ip_addr(&ip, &send_info->to);
+			LOG(L_INFO, "forward_request: request to %s:%d(%d) dropped"
+					" (onsend_route)\n", ip_addr2a(&ip),
+						su_getport(&send_info->to), send_info->proto);
+			ser_error=E_OK; /* no error */
+			ret=E_ADM_PROHIBITED;
+#ifdef USE_DNS_FAILOVER
+			continue; /* try another ip */
+#else
+			goto error; /* error ? */
+#endif
+		}
+#ifdef USE_DST_BLACKLIST
+		if (use_dst_blacklist){
+			if (dst_is_blacklisted(send_info)){
+				su2ip_addr(&ip, &send_info->to);
+				LOG(L_ERR, "ERROR: blacklisted destination:%s:%d (%d)\n",
+							ip_addr2a(&ip), su_getport(&send_info->to),
+							send_info->proto);
+				ret=ser_error=E_SEND;
+#ifdef USE_DNS_FAILOVER
+				continue; /* try another ip */
+#else
+				goto error;
+#endif
+			}
+		}
+#endif
+		if (msg_send(send_info, buf, len)<0){
+			ret=ser_error=E_SEND;
+#ifdef USE_DST_BLACKLIST
+			if (use_dst_blacklist)
+				dst_blacklist_add(BLST_ERR_SEND, send_info);
+#endif
+#ifdef USE_DNS_FAILOVER
+			continue; /* try another ip */
+#else
+			goto error;
+#endif
+		}else{
+			ret=ser_error=E_OK;
+			/* sent requests stats */
+			STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
+			/* exit succcesfully */
+			goto end;
+		}
+#ifdef USE_DNS_FAILOVER
+	}while(dst && use_dns_failover && dns_srv_handle_next(&dns_srv_h, err) && 
+			((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
+								  send_info->proto, dns_flags))==0));
+	if ((err!=0) && (err!=-E_DNS_EOR)){
+		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
+							" failed: %s [%d] (dropping packet)\n",
+									dst->len, ZSW(dst->s),
+									dns_strerror(err), err);
+		ret=ser_error=E_BAD_ADDRESS;
 		goto error;
 		goto error;
 	}
 	}
+#endif
 	
 	
-	/* sent requests stats */
-	STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
-	
-	pkg_free(buf);
-	/* received_buf & line_buf will be freed in receive_msg by free_lump_list*/
-	return 0;
-
 error:
 error:
+	STATS_TX_DROPS;
+end:
+#ifdef USE_DNS_FAILOVER
+	if (dst && use_dns_failover){
+				dns_srv_handle_put(&dns_srv_h);
+	}
+#endif
 	if (buf) pkg_free(buf);
 	if (buf) pkg_free(buf);
-	return -1;
+	/* received_buf & line_buf will be freed in receive_msg by free_lump_list*/
+	return ret;
 }
 }
 
 
 
 

+ 2 - 1
forward.h

@@ -60,7 +60,8 @@ struct socket_info* get_send_socket(struct sip_msg* msg,
 									union sockaddr_union* su, int proto);
 									union sockaddr_union* su, int proto);
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto);
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto);
 int check_self(str* host, unsigned short port, unsigned short proto);
 int check_self(str* host, unsigned short port, unsigned short proto);
-int forward_request( struct sip_msg* msg,  struct dest_info* send_info);
+int forward_request( struct sip_msg* msg, str* dst,  unsigned short port,
+						struct dest_info* send_info);
 int update_sock_struct_from_via( union sockaddr_union* to,
 int update_sock_struct_from_via( union sockaddr_union* to,
 								 struct sip_msg* msg,
 								 struct sip_msg* msg,
 								 struct via_body* via );
 								 struct via_body* via );

+ 18 - 0
globals.h

@@ -123,6 +123,7 @@ extern int tos;
 /* extern int *pids; -moved to pt.h */
 /* extern int *pids; -moved to pt.h */
 
 
 extern int cfg_errors;
 extern int cfg_errors;
+extern int cfg_warnings;
 extern unsigned int msg_no;
 extern unsigned int msg_no;
 
 
 extern unsigned long shm_mem_size;
 extern unsigned long shm_mem_size;
@@ -168,5 +169,22 @@ extern int dns_retr_time;
 extern int dns_retr_no;
 extern int dns_retr_no;
 extern int dns_servers_no;
 extern int dns_servers_no;
 extern int dns_search_list;
 extern int dns_search_list;
+#ifdef USE_DNS_CACHE
+extern int use_dns_cache; /* 1 if the cache is enabled, 0 otherwise */
+extern int use_dns_failover; /* 1 if failover is enabled, 0 otherwise */
+unsigned int dns_cache_max_mem; /* maximum memory used for the cached entries*/
+unsigned int dns_neg_cache_ttl; /* neg. cache ttl */
+unsigned int dns_cache_max_ttl; /* maximum ttl */
+unsigned int dns_cache_min_ttl; /* minimum ttl */
+unsigned int dns_timer_interval; /* gc timer interval in s */
+int dns_flags; /* default flags used for the  dns_*resolvehost 
+                    (compatibility wrappers) */
+#endif
+#ifdef USE_DST_BLACKLIST
+extern int use_dst_blacklist; /* 1 if the blacklist is enabled */
+unsigned int  blst_max_mem; /* maximum memory used for the blacklist entries*/
+unsigned int blst_timeout; /* blacklist entry ttl */
+unsigned int blst_timer_interval; /* blacklist gc timer interval (in s)*/
+#endif
 
 
 #endif
 #endif

+ 50 - 4
main.c

@@ -61,6 +61,7 @@
  *               parent & once in the child to avoid a short window when one
  *               parent & once in the child to avoid a short window when one
  *               of them might use it "unset" (andrei)
  *               of them might use it "unset" (andrei)
  *  2005-07-25  use sigaction for setting the signal handlers (andrei)
  *  2005-07-25  use sigaction for setting the signal handlers (andrei)
+ *  2006-07-13  added dns cache/failover init. (andrei)
  */
  */
 
 
 
 
@@ -129,6 +130,12 @@
 #include "core_cmd.h"
 #include "core_cmd.h"
 #include "flags.h"
 #include "flags.h"
 #include "atomic_ops_init.h"
 #include "atomic_ops_init.h"
+#ifdef USE_DNS_CACHE
+#include "dns_cache.h"
+#endif
+#ifdef USE_DST_BLACKLIST
+#include "dst_blacklist.h"
+#endif
 
 
 #include "stats.h"
 #include "stats.h"
 
 
@@ -299,6 +306,13 @@ int reply_to_via=0;
 int mcast_loopback = 0;
 int mcast_loopback = 0;
 int mcast_ttl = -1; /* if -1, don't touch it, use the default (usually 1) */
 int mcast_ttl = -1; /* if -1, don't touch it, use the default (usually 1) */
 #endif /* USE_MCAST */
 #endif /* USE_MCAST */
+#ifdef USE_DNS_CACHE
+int use_dns_cache=1; /* 1 if the cache is enabled, 0 otherwise */
+int use_dns_failover=0; /* 1 if failover is enabled, 0 otherwise */
+#endif
+#ifdef USE_DST_BLACKLIST
+int use_dst_blacklist=0; /* 1 if the blacklist is enabled */
+#endif
 
 
 int tos = IPTOS_LOWDELAY;
 int tos = IPTOS_LOWDELAY;
 
 
@@ -355,6 +369,8 @@ int process_count = 0;
 
 
 /* cfg parsing */
 /* cfg parsing */
 int cfg_errors=0;
 int cfg_errors=0;
+int cfg_warnings=0;
+
 
 
 /* shared memory (in MB) */
 /* shared memory (in MB) */
 unsigned long shm_mem_size=SHM_MEM_SIZE * 1024 * 1024;
 unsigned long shm_mem_size=SHM_MEM_SIZE * 1024 * 1024;
@@ -386,6 +402,12 @@ void cleanup(show_status)
 					 some process crashed and let it locked; this will
 					 some process crashed and let it locked; this will
 					 allow an almost gracious shutdown */
 					 allow an almost gracious shutdown */
 	destroy_modules();
 	destroy_modules();
+#ifdef USE_DNS_CACHE
+	destroy_dns_cache();
+#endif
+#ifdef USE_DST_BLACKLIST
+	destroy_dst_blacklist();
+#endif
 #ifdef USE_TCP
 #ifdef USE_TCP
 	destroy_tcp();
 	destroy_tcp();
 #endif
 #endif
@@ -1210,13 +1232,16 @@ int main(int argc, char** argv)
 	int ret;
 	int ret;
 	unsigned int seed;
 	unsigned int seed;
 	int rfd;
 	int rfd;
-	int debug_save, debug_flag = 0;
-	int dont_fork_cnt = 0;
+	int debug_save, debug_flag;
+	int dont_fork_cnt;
+	int socket_types;
 
 
 	/*init*/
 	/*init*/
 	creator_pid = getpid();
 	creator_pid = getpid();
 	ret=-1;
 	ret=-1;
 	my_argc=argc; my_argv=argv;
 	my_argc=argc; my_argv=argv;
+	debug_flag=0;
+	dont_fork_cnt=0;
 
 
 	/*init pkg mallocs (before parsing cfg or cmd line !)*/
 	/*init pkg mallocs (before parsing cfg or cmd line !)*/
 	if (init_pkg_mallocs()==-1)
 	if (init_pkg_mallocs()==-1)
@@ -1361,6 +1386,9 @@ try_again:
 		fprintf(stderr, "ERROR: bad config file (%d errors)\n", cfg_errors);
 		fprintf(stderr, "ERROR: bad config file (%d errors)\n", cfg_errors);
 		goto error;
 		goto error;
 	}
 	}
+	if (cfg_warnings){
+		fprintf(stderr, "%d config warnings\n", cfg_warnings);
+	}
 	if (debug_flag) debug = debug_save;
 	if (debug_flag) debug = debug_save;
 	print_rls();
 	print_rls();
 
 
@@ -1517,10 +1545,15 @@ try_again:
 			goto error;
 			goto error;
 		}
 		}
 	}
 	}
-	if (fix_all_socket_lists()!=0){
+	if (fix_all_socket_lists(&socket_types)!=0){
 		fprintf(stderr,  "failed to initialize list addresses\n");
 		fprintf(stderr,  "failed to initialize list addresses\n");
 		goto error;
 		goto error;
 	}
 	}
+	if (dns_try_ipv6 && !(socket_types & SOCKET_T_IPV6)){
+		/* if we are not listening on any ipv6 address => no point
+		 * to try to resovle ipv6 addresses */
+		dns_try_ipv6=0;
+	}
 	/* print all the listen addresses */
 	/* print all the listen addresses */
 	printf("Listening on \n");
 	printf("Listening on \n");
 	print_all_socket_lists();
 	print_all_socket_lists();
@@ -1560,7 +1593,20 @@ try_again:
 		LOG(L_CRIT, "could not initialize timer, exiting...\n");
 		LOG(L_CRIT, "could not initialize timer, exiting...\n");
 		goto error;
 		goto error;
 	}
 	}
-
+#ifdef USE_DNS_CACHE
+	if (init_dns_cache()<0){
+		LOG(L_CRIT, "could not initialize the dns cache, exiting...\n");
+		goto error;
+	}
+	if (use_dns_cache==0)
+		use_dns_failover=0; /* cannot work w/o dns_cache support */
+#endif
+#ifdef USE_DST_BLACKLIST
+	if (init_dst_blacklist()<0){
+		LOG(L_CRIT, "could not initialize the dst blacklist, exiting...\n");
+		goto error;
+	}
+#endif
 	if (init_avps()<0) goto error;
 	if (init_avps()<0) goto error;
 	if (rpc_init_time() < 0) goto error;
 	if (rpc_init_time() < 0) goto error;
 
 

+ 20 - 0
modules/tm/h_table.c

@@ -41,6 +41,7 @@
  *             timer_link.payload removed (bogdan)
  *             timer_link.payload removed (bogdan)
  * 2004-08-23  avp support added - move and remove avp list to/from
  * 2004-08-23  avp support added - move and remove avp list to/from
  *             transactions (bogdan)
  *             transactions (bogdan)
+ * 2006-08-11  dns failover support (andrei)
  */
  */
 
 
 #include <stdlib.h>
 #include <stdlib.h>
@@ -148,6 +149,22 @@ void free_cell( struct cell* dead_cell )
 		if (rpl && rpl!=FAKED_REPLY && rpl->msg_flags&FL_SHM_CLONE) {
 		if (rpl && rpl!=FAKED_REPLY && rpl->msg_flags&FL_SHM_CLONE) {
 			sip_msg_free_unsafe( rpl );
 			sip_msg_free_unsafe( rpl );
 		}
 		}
+#ifdef USE_DNS_FAILOVER
+		if (dead_cell->uac[i].dns_h.a){
+			DBG("branch %d -> dns_h.srv (%.*s) ref=%d,"
+							" dns_h.a (%.*s) ref=%d\n", i,
+					dead_cell->uac[i].dns_h.srv?
+								dead_cell->uac[i].dns_h.srv->name_len:0,
+					dead_cell->uac[i].dns_h.srv?
+								dead_cell->uac[i].dns_h.srv->name:"",
+					dead_cell->uac[i].dns_h.srv?
+								dead_cell->uac[i].dns_h.srv->refcnt.val:0,
+					dead_cell->uac[i].dns_h.a->name_len,
+					dead_cell->uac[i].dns_h.a->name,
+					dead_cell->uac[i].dns_h.a->refcnt.val);
+		}
+		dns_srv_handle_put_shm_unsafe(&dead_cell->uac[i].dns_h);
+#endif
 	}
 	}
 
 
 	/* collected to tags */
 	/* collected to tags */
@@ -220,6 +237,9 @@ static void inline init_branches(struct cell *t)
 		uac->request.branch = i;
 		uac->request.branch = i;
 		init_rb_timers(&uac->request);
 		init_rb_timers(&uac->request);
 		uac->local_cancel=uac->request;
 		uac->local_cancel=uac->request;
+#ifdef USE_DNS_FAILOVER
+		dns_srv_handle_init(&uac->dns_h);
+#endif
 	}
 	}
 }
 }
 
 

+ 9 - 3
modules/tm/h_table.h

@@ -36,6 +36,7 @@
  *             with flags (bogdan)
  *             with flags (bogdan)
  * 2004-08-23  avp support added - avp list linked in transaction (bogdan)
  * 2004-08-23  avp support added - avp list linked in transaction (bogdan)
  * 2005-11-03  updated to the new timer interface (dropped tm timers) (andrei)
  * 2005-11-03  updated to the new timer interface (dropped tm timers) (andrei)
+ * 2006-08-11  dns failover support (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -65,7 +66,9 @@ struct retr_buf;
 #include "sip_msg.h"
 #include "sip_msg.h"
 #include "t_reply.h"
 #include "t_reply.h"
 #include "t_hooks.h"
 #include "t_hooks.h"
-#include "../../timer.h"
+#ifdef USE_DNS_FAILOVER
+#include "../../dns_cache.h"
+#endif
 
 
 #define LOCK_HASH(_h) lock_hash((_h))
 #define LOCK_HASH(_h) lock_hash((_h))
 #define UNLOCK_HASH(_h) unlock_hash((_h))
 #define UNLOCK_HASH(_h) unlock_hash((_h))
@@ -146,6 +149,8 @@ typedef struct ua_server
 
 
 typedef struct ua_client
 typedef struct ua_client
 {
 {
+	/* if we store a reply (branch picking), this is where it is */
+	struct sip_msg  *reply;
 	struct retr_buf  request;
 	struct retr_buf  request;
 	/* we maintain a separate copy of cancel rather than
 	/* we maintain a separate copy of cancel rather than
 	   reuse the structure for original request; the 
 	   reuse the structure for original request; the 
@@ -156,9 +161,10 @@ typedef struct ua_client
 	struct retr_buf local_cancel;
 	struct retr_buf local_cancel;
 	/* pointer to retransmission buffer where uri is printed;
 	/* pointer to retransmission buffer where uri is printed;
 	   good for generating ACK/CANCEL */
 	   good for generating ACK/CANCEL */
+#ifdef USE_DNS_FAILOVER
+	struct dns_srv_handle dns_h;
+#endif
 	str              uri;
 	str              uri;
-	/* if we store a reply (branch picking), this is where it is */
-	struct sip_msg  *reply;
 	/* if we don't store, we at least want to know the status */
 	/* if we don't store, we at least want to know the status */
 	int             last_received;
 	int             last_received;
 }ua_client_type;
 }ua_client_type;

+ 20 - 5
modules/tm/t_funcs.c

@@ -44,6 +44,9 @@
  *              desable variable timer feature (bogdan)
  *              desable variable timer feature (bogdan)
  *  2005-12-11  t_relay doesn't return 0 (stop script) on send error 
  *  2005-12-11  t_relay doesn't return 0 (stop script) on send error 
  *              anymore (andrei)
  *              anymore (andrei)
+ *  2006-08-11  updated forward_request usage (andrei)
+ *              t_relay_to releases the transaction if t_forward_non_ack
+ *              fails and t_kill fails or this is a failed replication (andrei)
  */
  */
 
 
 #include <limits.h>
 #include <limits.h>
@@ -198,11 +201,13 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 {
 {
 	int ret;
 	int ret;
 	int new_tran;
 	int new_tran;
-	str *uri;
 	int reply_ret;
 	int reply_ret;
 	/* struct hdr_field *hdr; */
 	/* struct hdr_field *hdr; */
 	struct cell *t;
 	struct cell *t;
 	struct dest_info dst;
 	struct dest_info dst;
+	unsigned short port;
+	str host;
+	short comp;
 
 
 	ret=0;
 	ret=0;
 	
 	
@@ -233,19 +238,26 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 	if ( p_msg->REQ_METHOD==METHOD_ACK) {
 	if ( p_msg->REQ_METHOD==METHOD_ACK) {
 		DBG( "SER: forwarding ACK  statelessly \n");
 		DBG( "SER: forwarding ACK  statelessly \n");
 		if (proxy==0) {
 		if (proxy==0) {
-			uri = GET_RURI(p_msg);
-			if (uri2dst(&dst, p_msg, GET_NEXT_HOP(p_msg), proto)==0){
+			init_dest_info(&dst);
+			dst.proto=proto;
+			if (get_uri_send_info(GET_NEXT_HOP(p_msg), &host, &port,
+									&dst.proto, &comp)!=0){
 				ret=E_BAD_ADDRESS;
 				ret=E_BAD_ADDRESS;
 				goto done;
 				goto done;
 			}
 			}
-			ret=forward_request( p_msg , &dst) ;
+#ifdef USE_COMP
+			dst.comp=comp;
+#endif
+			/* dst->send_sock not set, but forward_request will take care
+			 * of it */
+			ret=forward_request(p_msg, &host, port, &dst);
 		} else {
 		} else {
 			init_dest_info(&dst);
 			init_dest_info(&dst);
 			dst.proto=get_proto(proto, proxy->proto);
 			dst.proto=get_proto(proto, proxy->proto);
 			proxy2su(&dst.to, proxy);
 			proxy2su(&dst.to, proxy);
 			/* dst->send_sock not set, but forward_request will take care
 			/* dst->send_sock not set, but forward_request will take care
 			 * of it */
 			 * of it */
-			ret=forward_request( p_msg , &dst) ;
+			ret=forward_request( p_msg , 0, 0, &dst) ;
 		}
 		}
 		goto done;
 		goto done;
 	}
 	}
@@ -282,7 +294,10 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 			}  else {
 			}  else {
 				DBG("ERROR: generation of a stateful reply "
 				DBG("ERROR: generation of a stateful reply "
 					"on error failed\n");
 					"on error failed\n");
+				t_release_transaction(t);
 			}
 			}
+		}else{
+			t_release_transaction(t); /* kill it  silently */
 		}
 		}
 	} else {
 	} else {
 		DBG( "SER: new transaction fwd'ed\n");
 		DBG( "SER: new transaction fwd'ed\n");

+ 236 - 17
modules/tm/t_fwd.c

@@ -50,6 +50,10 @@
  *  2006-02-07  named routes support (andrei)
  *  2006-02-07  named routes support (andrei)
  *  2006-04-18  add_uac simplified + switched to struct dest_info (andrei)
  *  2006-04-18  add_uac simplified + switched to struct dest_info (andrei)
  *  2006-04-20  pint_uac_request uses now struct dest_info (andrei)
  *  2006-04-20  pint_uac_request uses now struct dest_info (andrei)
+ *  2006-08-11  dns failover support (andrei)
+ *              t_forward_non_ack won't start retransmission on send errors
+ *               anymore (WARNING: callers should release/kill the transaction
+ *               if error is returned) (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -76,6 +80,14 @@
 #include "t_fwd.h"
 #include "t_fwd.h"
 #include "fix_lumps.h"
 #include "fix_lumps.h"
 #include "config.h"
 #include "config.h"
+#ifdef USE_DNS_FAILOVER
+#include "../../dns_cache.h"
+#endif
+#ifdef USE_DST_BLACKLIST
+#include "../../dst_blacklist.h"
+#endif
+
+
 
 
 static int goto_on_branch = 0, branch_route = 0;
 static int goto_on_branch = 0, branch_route = 0;
 
 
@@ -105,8 +117,14 @@ static char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 	char *buf, *shbuf;
 	char *buf, *shbuf;
 	str* msg_uri;
 	str* msg_uri;
 	struct lump* add_rm_backup, *body_lumps_backup;
 	struct lump* add_rm_backup, *body_lumps_backup;
+	struct sip_uri parsed_uri_bak;
+	int parsed_uri_ok_bak;
+	str msg_uri_bak;
 
 
 	shbuf=0;
 	shbuf=0;
+	msg_uri_bak.s=0; /* kill warnings */
+	msg_uri_bak.len=0;
+	parsed_uri_ok_bak=0;
 
 
 	/* ... we calculate branch ... */	
 	/* ... we calculate branch ... */	
 	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
 	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
@@ -119,6 +137,9 @@ static char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 	/* ... update uri ... */
 	/* ... update uri ... */
 	msg_uri=GET_RURI(i_req);
 	msg_uri=GET_RURI(i_req);
 	if ((msg_uri->s!=uri->s) || (msg_uri->len!=uri->len)){
 	if ((msg_uri->s!=uri->s) || (msg_uri->len!=uri->len)){
+		msg_uri_bak=i_req->new_uri;
+		parsed_uri_ok_bak=i_req->parsed_uri_ok;
+		parsed_uri_bak=i_req->parsed_uri;
 		i_req->new_uri=*uri;
 		i_req->new_uri=*uri;
 		i_req->parsed_uri_ok=0;
 		i_req->parsed_uri_ok=0;
 	}
 	}
@@ -173,6 +194,12 @@ error01:
 	     /* Restore the lists from backups */
 	     /* Restore the lists from backups */
 	i_req->add_rm = add_rm_backup;
 	i_req->add_rm = add_rm_backup;
 	i_req->body_lumps = body_lumps_backup;
 	i_req->body_lumps = body_lumps_backup;
+	/* restore the new_uri from the backup */
+	if ((msg_uri->s!=uri->s) || (msg_uri->len!=uri->len)){
+		i_req->new_uri=msg_uri_bak;
+		i_req->parsed_uri=parsed_uri_bak;
+		i_req->parsed_uri_ok=parsed_uri_ok_bak;
+	}
 
 
  error00:
  error00:
 	return shbuf;
 	return shbuf;
@@ -257,9 +284,15 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 		get_send_socket( request, &t->uac[branch].request.dst.to,
 		get_send_socket( request, &t->uac[branch].request.dst.to,
 								t->uac[branch].request.dst.proto);
 								t->uac[branch].request.dst.proto);
 	}else {
 	}else {
+#ifdef USE_DNS_FAILOVER
+		if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
+					request, next_hop?next_hop:uri, proto) == 0)
+#else
 		/* dst filled from the uri & request (send_socket) */
 		/* dst filled from the uri & request (send_socket) */
 		if (uri2dst(&t->uac[branch].request.dst, request,
 		if (uri2dst(&t->uac[branch].request.dst, request,
-						next_hop ? next_hop: uri, proto)==0){
+						next_hop ? next_hop: uri, proto)==0)
+#endif
+		{
 			ret=E_BAD_ADDRESS;
 			ret=E_BAD_ADDRESS;
 			goto error;
 			goto error;
 		}
 		}
@@ -295,7 +328,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 	if (proxy){
 	if (proxy){
 		proxy_mark(proxy, 1);
 		proxy_mark(proxy, 1);
 	}
 	}
-	/* done! */	
+	/* done! */
 	ret=branch;
 	ret=branch;
 		
 		
 error01:
 error01:
@@ -303,6 +336,62 @@ error:
 	return ret;
 	return ret;
 }
 }
 
 
+
+
+#ifdef USE_DNS_FAILOVER
+/* introduce a new uac to transaction, based on old_uac and a possible
+ *  new ip address (if the dns name resolves to more ips). If no more
+ *   ips are found => returns -1.
+ *  returns its branch id (>=0)
+   or error (<0); it doesn't send a message yet -- a reply to it
+   might interfere with the processes of adding multiple branches
+   if lock_replies is 1 replies will be locked for t until the new branch
+   is added (to prevent add branches races). Use 0 if the reply lock is
+   already held, e.g. in failure route/handlers (WARNING: using 1 in a 
+   failure route will cause a deadlock).
+*/
+int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
+									struct ua_client* old_uac,
+									int lock_replies)
+{
+	int ret;
+	
+	ret=-1;
+	if (use_dns_failover && dns_srv_handle_next(&old_uac->dns_h, 0)){
+			if (lock_replies){
+				/* use reply lock to guarantee nobody is adding a branch
+				 * in the same time */
+				LOCK_REPLIES(t);
+			}
+			if (t->nr_of_outgoings >= MAX_BRANCHES){
+				LOG(L_ERR, "ERROR: add_uac_dns_fallback: maximum number of "
+							"branches exceeded\n");
+				if (lock_replies)
+					UNLOCK_REPLIES(t);
+				return E_CFG;
+			}
+			/* copy the dns handle into the new uac */
+			dns_srv_handle_cpy(&t->uac[t->nr_of_outgoings].dns_h,
+								&old_uac->dns_h);
+			/* add_uac will use dns_h => next_hop will be ignored.
+			 * Unfortunately we can't reuse the old buffer, the branch id
+			 *  must be changed and the send_socket might be different =>
+			 *  re-create the whole uac */
+			ret=add_uac(t,  msg, &old_uac->uri, 0, 0, 
+							old_uac->request.dst.proto);
+			if (ret<0){
+				/* failed, delete the copied dns_h */
+				dns_srv_handle_put(&t->uac[t->nr_of_outgoings].dns_h);
+			}
+			if (lock_replies){
+				UNLOCK_REPLIES(t);
+			}
+	}
+	return ret;
+}
+
+#endif
+
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, 
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, 
 	struct cell *t_invite, int branch )
 	struct cell *t_invite, int branch )
 {
 {
@@ -310,11 +399,16 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
 	char *shbuf;
 	char *shbuf;
 	unsigned int len;
 	unsigned int len;
 
 
+	ret=-1;
 	if (t_cancel->uac[branch].request.buffer) {
 	if (t_cancel->uac[branch].request.buffer) {
 		LOG(L_CRIT, "ERROR: e2e_cancel_branch: buffer rewrite attempt\n");
 		LOG(L_CRIT, "ERROR: e2e_cancel_branch: buffer rewrite attempt\n");
 		ret=ser_error=E_BUG;
 		ret=ser_error=E_BUG;
 		goto error;
 		goto error;
-	}	
+	}
+	if (t_invite->uac[branch].request.buffer==0){
+		/* inactive / deleted  branch */
+		goto error;
+	}
 
 
 	/* note -- there is a gap in proxy stats -- we don't update 
 	/* note -- there is a gap in proxy stats -- we don't update 
 	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
 	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
@@ -440,6 +534,134 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 }
 }
 
 
 
 
+
+/* sends one uac/branch buffer and fallbacks to other ips if
+ *  the destination resolves to several addresses
+ *  Takes care of starting timers a.s.o. (on send success)
+ *  returns: -2 on error, -1 on drop,  current branch id on success,
+ *   new branch id on send error/blacklist, when failover is possible
+ *    (ret>=0 && ret!=branch)
+ *    if lock_replies is 1, the replies for t will be locked when adding
+ *     new branches (to prevent races). Use 0 from failure routes or other
+ *     places where the reply lock is already held, to avoid deadlocks. */
+int t_send_branch( struct cell *t, int branch, struct sip_msg* p_msg ,
+					struct proxy_l * proxy, int lock_replies)
+{
+	struct ip_addr ip; /* debugging */
+	int ret;
+	struct ua_client* uac;
+	
+	uac=&t->uac[branch];
+	ret=branch;
+	if (run_onsend(p_msg,	&uac->request.dst, uac->request.buffer,
+					uac->request.buffer_len)==0){
+		/* disable the current branch: set a "fake" timeout
+		 *  reply code but don't set uac->reply, to avoid overriding 
+		 *  a higly unlikely, perfectly timed fake reply (to a message
+		 *   we never sent).
+		 * (code=final reply && reply==0 => t_pick_branch won't ever pick it)*/
+			uac->last_received=408;
+			su2ip_addr(&ip, &uac->request.dst.to);
+			DBG("t_send_branch: onsend_route dropped msg. to %s:%d (%d)\n",
+							ip_addr2a(&ip), su_getport(&uac->request.dst.to),
+							uac->request.dst.proto);
+#ifdef USE_DNS_FAILOVER
+			/* if the destination resolves to more ips, add another
+			 *  branch/uac */
+			if (use_dns_failover){
+				ret=add_uac_dns_fallback(t, p_msg, uac, lock_replies);
+				if (ret>=0){
+					su2ip_addr(&ip, &uac->request.dst.to);
+					DBG("t_send_branch: send on branch %d failed "
+							"(onsend_route), trying another ip %s:%d (%d)\n",
+							branch, ip_addr2a(&ip),
+							su_getport(&uac->request.dst.to),
+							uac->request.dst.proto);
+					/* success, return new branch */
+					return ret;
+				}
+			}
+#endif /* USE_DNS_FAILOVER*/
+		return -1; /* drop, try next branch */
+	}
+#ifdef USE_DST_BLACKLIST
+	if (use_dst_blacklist){
+		if (dst_is_blacklisted(&uac->request.dst)){
+			su2ip_addr(&ip, &uac->request.dst.to);
+			DBG("t_send_branch: blacklisted destination: %s:%d (%d)\n",
+							ip_addr2a(&ip), su_getport(&uac->request.dst.to),
+							uac->request.dst.proto);
+			/* disable the current branch: set a "fake" timeout
+			 *  reply code but don't set uac->reply, to avoid overriding 
+			 *  a higly unlikely, perfectly timed fake reply (to a message
+			 *   we never sent).  (code=final reply && reply==0 => 
+			 *   t_pick_branch won't ever pick it)*/
+			uac->last_received=408;
+#ifdef USE_DNS_FAILOVER
+			/* if the destination resolves to more ips, add another
+			 *  branch/uac */
+			if (use_dns_failover){
+				ret=add_uac_dns_fallback(t, p_msg, uac, lock_replies);
+				if (ret>=0){
+					su2ip_addr(&ip, &uac->request.dst.to);
+					DBG("t_send_branch: send on branch %d failed (blacklist),"
+							" trying another ip %s:%d (%d)\n", branch,
+							ip_addr2a(&ip), su_getport(&uac->request.dst.to),
+							uac->request.dst.proto);
+					/* success, return new branch */
+					return ret;
+				}
+			}
+#endif /* USE_DNS_FAILOVER*/
+			return -1; /* don't send */
+		}
+	}
+#endif /* USE_DST_BLACKLIST */
+	if (SEND_BUFFER( &uac->request)==-1) {
+		/* disable the current branch: set a "fake" timeout
+		 *  reply code but don't set uac->reply, to avoid overriding 
+		 *  a higly unlikely, perfectly timed fake reply (to a message
+		 *  we never sent).
+		 * (code=final reply && reply==0 => t_pick_branch won't ever pick it)*/
+		uac->last_received=408;
+		su2ip_addr(&ip, &uac->request.dst.to);
+		DBG("t_send_branch: send to %s:%d (%d) failed\n",
+							ip_addr2a(&ip), su_getport(&uac->request.dst.to),
+							uac->request.dst.proto);
+#ifdef USE_DST_BLACKLIST
+		if (use_dst_blacklist)
+			dst_blacklist_add(BLST_ERR_SEND, &uac->request.dst);
+#endif
+#ifdef USE_DNS_FAILOVER
+		/* if the destination resolves to more ips, add another
+		 *  branch/uac */
+		if (use_dns_failover){
+			ret=add_uac_dns_fallback(t, p_msg, uac, lock_replies);
+			if (ret>=0){
+				/* success, return new branch */
+				DBG("t_send_branch: send on branch %d failed, adding another"
+						" branch with another ip\n", branch);
+				return ret;
+			}
+		}
+#endif
+		LOG(L_ERR, "ERROR: t_send_branch: sending request on branch %d "
+				"failed\n", branch);
+		if (proxy) { proxy->errors++; proxy->ok=0; }
+		return -2;
+	} else {
+		/* start retr. only if the send succeeded */
+		if (start_retr( &uac->request )!=0){
+			LOG(L_CRIT, "BUG: t_send_branch: retr. already started for %p\n",
+					&uac->request);
+			return -2;
+		}
+	}
+	return ret;
+}
+
+
+
 /* function returns:
 /* function returns:
  *       1 - forward successful
  *       1 - forward successful
  *      -1 - error during forward
  *      -1 - error during forward
@@ -458,6 +680,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	struct cell *t_invite;
 	struct cell *t_invite;
 	int success_branch;
 	int success_branch;
 	int try_new;
 	int try_new;
+	int lock_replies;
 	str dst_uri;
 	str dst_uri;
 	struct socket_info* si, *backup_si;
 	struct socket_info* si, *backup_si;
 
 
@@ -545,7 +768,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	/* things went wrong ... no new branch has been fwd-ed at all */
 	/* things went wrong ... no new branch has been fwd-ed at all */
 	if (added_branches==0) {
 	if (added_branches==0) {
 		if (try_new==0) {
 		if (try_new==0) {
-			LOG(L_ERR, "ERROR: t_forward_nonack: no branched for forwarding\n");
+			LOG(L_ERR, "ERROR: t_forward_nonack: no branches for forwarding\n");
 			return -1;
 			return -1;
 		}
 		}
 		LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n");
 		LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n");
@@ -554,30 +777,26 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 
 
 	/* send them out now */
 	/* send them out now */
 	success_branch=0;
 	success_branch=0;
+	lock_replies= ! ((rmode==MODE_ONFAILURE) && (t==get_t()));
 	for (i=first_branch; i<t->nr_of_outgoings; i++) {
 	for (i=first_branch; i<t->nr_of_outgoings; i++) {
 		if (added_branches & (1<<i)) {
 		if (added_branches & (1<<i)) {
-			if (run_onsend(p_msg,	&t->uac[i].request.dst,
-									t->uac[i].request.buffer,
-									t->uac[i].request.buffer_len)==0)
-				continue; /* if onsend drop, try next branch */
 			
 			
-			if (SEND_BUFFER( &t->uac[i].request)==-1) {
-				LOG(L_ERR, "ERROR: t_forward_nonack: sending request failed\n");
-				if (proxy) { proxy->errors++; proxy->ok=0; }
-			} else {
-				success_branch++;
+			branch_ret=t_send_branch(t, i, p_msg , proxy, lock_replies);
+			if (branch_ret>=0){ /* some kind of success */
+				if (branch_ret==i) /* success */
+					success_branch++;
+				else /* new branch added */
+					added_branches |= 1<<branch_ret;
 			}
 			}
-			if (start_retr( &t->uac[i].request )!=0)
-				LOG(L_CRIT, "BUG: t_forward_non_ack: "
-						"failed to start retr. for %p\n", &t->uac[i].request);
 		}
 		}
 	}
 	}
 	if (success_branch<=0) {
 	if (success_branch<=0) {
 		ser_error=E_SEND;
 		ser_error=E_SEND;
+		/* the caller should take care and delete the transaction */
 		return -1;
 		return -1;
 	}
 	}
 	return 1;
 	return 1;
-}	
+}
 
 
 int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy, int proto )
 int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy, int proto )
 {
 {

+ 8 - 0
modules/tm/t_fwd.h

@@ -38,6 +38,7 @@
 #include "defs.h"
 #include "defs.h"
 
 
 #include "../../proxy.h"
 #include "../../proxy.h"
+#include "h_table.h"
 
 
 typedef int (*tfwd_f)(struct sip_msg* p_msg , struct proxy_l * proxy );
 typedef int (*tfwd_f)(struct sip_msg* p_msg , struct proxy_l * proxy );
 typedef int (*taddblind_f)( /*struct cell *t */ );
 typedef int (*taddblind_f)( /*struct cell *t */ );
@@ -53,10 +54,17 @@ void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch );
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch );
 int add_uac(	struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 int add_uac(	struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 				struct proxy_l *proxy, int proto );
 				struct proxy_l *proxy, int proto );
+#ifdef USE_DNS_FAILOVER
+int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg, 
+									struct ua_client* old_uac,
+									int lock_replies);
+#endif
 int add_blind_uac( /* struct cell *t */ );
 int add_blind_uac( /* struct cell *t */ );
 int t_forward_nonack( struct cell *t, struct sip_msg* p_msg,
 int t_forward_nonack( struct cell *t, struct sip_msg* p_msg,
 						struct proxy_l * p, int proto);
 						struct proxy_l * p, int proto);
 int t_forward_ack( struct sip_msg* p_msg );
 int t_forward_ack( struct sip_msg* p_msg );
+int t_send_branch( struct cell *t, int branch, struct sip_msg* p_msg ,
+					struct proxy_l * proxy, int lock_replies);
 
 
 
 
 #endif
 #endif

+ 30 - 3
modules/tm/t_msgbuilder.c

@@ -40,6 +40,8 @@
  * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan)
  * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan)
  * 2006-04-21  build_uac_req, assemble_via use struct dest_info now;
  * 2006-04-21  build_uac_req, assemble_via use struct dest_info now;
  *              uri2sock replaced with uri2dst (andrei)
  *              uri2sock replaced with uri2dst (andrei)
+ * 2006-08-11  build_dlg_ack: use the first dns ip for which a send_sock
+ *              is found (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -57,6 +59,9 @@
 #include "../../parser/contact/parse_contact.h"
 #include "../../parser/contact/parse_contact.h"
 #include "t_msgbuilder.h"
 #include "t_msgbuilder.h"
 #include "uac.h"
 #include "uac.h"
+#ifdef USE_DNS_FAILOVER
+#include "../../dns_cache.h"
+#endif
 
 
 
 
 #define ROUTE_PREFIX "Route: "
 #define ROUTE_PREFIX "Route: "
@@ -359,7 +364,7 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
       * The function creates an ACK to 200 OK. Route set will be created
       * The function creates an ACK to 200 OK. Route set will be created
       * and parsed and next_hop parameter will contain the uri to which the
       * and parsed and next_hop parameter will contain the uri to which the
       * request should be send. The function is used by tm when it generates
       * request should be send. The function is used by tm when it generates
-      * local ACK to 200 OK (on behalf of applications using uac
+      * local ACK to 200 OK (on behalf of applications using uac)
       */
       */
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch,
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch,
 		    str* to, unsigned int *len, str *next_hop)
 		    str* to, unsigned int *len, str *next_hop)
@@ -373,6 +378,9 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch
 	struct rte* list;
 	struct rte* list;
 	str contact, ruri, *cont;
 	str contact, ruri, *cont;
 	struct dest_info dst;
 	struct dest_info dst;
+#ifdef USE_DNS_FAILOVER
+	struct dns_srv_handle dns_h;
+#endif
 	
 	
 	if (get_contact_uri(rpl, &contact) < 0) {
 	if (get_contact_uri(rpl, &contact) < 0) {
 		return 0;
 		return 0;
@@ -399,11 +407,30 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch
 	*len += ruri.len;
 	*len += ruri.len;
 	
 	
 	
 	
-	     /* via */
-	if ((uri2dst(&dst, rpl, next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){
+	 /* via */
+#ifdef USE_DNS_FAILOVER
+	if (use_dns_failover){
+		dns_srv_handle_init(&dns_h);
+		if ((uri2dst(&dns_h , &dst, rpl, next_hop, PROTO_NONE)==0) ||
+				(dst.send_sock==0)){
+			dns_srv_handle_put(&dns_h);
+			LOG(L_ERR, "build_dlg_ack: no socket found\n");
+			goto error;
+		}
+		dns_srv_handle_put(&dns_h); /* not needed any more */
+	}else{
+		if ((uri2dst(0 , &dst, rpl, next_hop, PROTO_NONE)==0) ||
+				(dst.send_sock==0)){
+			LOG(L_ERR, "build_dlg_ack: no socket found\n");
+			goto error;
+		}
+	}
+#else
+	if ( (uri2dst( &dst, rpl, next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){
 			LOG(L_ERR, "build_dlg_ack: no socket found\n");
 			LOG(L_ERR, "build_dlg_ack: no socket found\n");
 		goto error;
 		goto error;
 	}
 	}
+#endif
 	
 	
 	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
 	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
 	branch_str.s = branch_buf;
 	branch_str.s = branch_buf;

+ 45 - 9
modules/tm/t_reply.c

@@ -68,6 +68,9 @@
  *               in t_retransmit_reply & reply_light (andrei)
  *               in t_retransmit_reply & reply_light (andrei)
  *  2005-11-09  updated to the new timers interface (andrei)
  *  2005-11-09  updated to the new timers interface (andrei)
  *  2006-02-07  named routes support (andrei)
  *  2006-02-07  named routes support (andrei)
+ *  2006-09-13  t_pick_branch will skip also over branches with empty reply 
+ *              t_should_relay_response will re-pick the branch if failure 
+ *               route /handlers added new branches (andrei)
  */
  */
 
 
 
 
@@ -317,15 +320,37 @@ static int send_local_ack(struct sip_msg* msg, str* next_hop,
 							char* ack, int ack_len)
 							char* ack, int ack_len)
 {
 {
 	struct dest_info dst;
 	struct dest_info dst;
+#ifdef USE_DNS_FAILOVER
+	struct dns_srv_handle dns_h;
+#endif
 
 
 	if (!next_hop) {
 	if (!next_hop) {
 		LOG(L_ERR, "send_local_ack: Invalid parameter value\n");
 		LOG(L_ERR, "send_local_ack: Invalid parameter value\n");
 		return -1;
 		return -1;
 	}
 	}
+#ifdef USE_DNS_FAILOVER
+	if (use_dns_failover){
+		dns_srv_handle_init(&dns_h);
+		if ((uri2dst(&dns_h, &dst, msg,  next_hop, PROTO_NONE)==0) || 
+				(dst.send_sock==0)){
+			dns_srv_handle_put(&dns_h);
+			LOG(L_ERR, "send_local_ack: no socket found\n");
+			return -1;
+		}
+		dns_srv_handle_put(&dns_h); /* not needed anymore */
+	}else{
+		if ((uri2dst(0, &dst, msg,  next_hop, PROTO_NONE)==0) || 
+				(dst.send_sock==0)){
+			LOG(L_ERR, "send_local_ack: no socket found\n");
+			return -1;
+		}
+	}
+#else
 	if ((uri2dst(&dst, msg,  next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){
 	if ((uri2dst(&dst, msg,  next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){
 		LOG(L_ERR, "send_local_ack: no socket found\n");
 		LOG(L_ERR, "send_local_ack: no socket found\n");
 		return -1;
 		return -1;
 	}
 	}
+#endif
 	return msg_send(&dst, ack, ack_len);
 	return msg_send(&dst, ack, ack_len);
 }
 }
 
 
@@ -701,7 +726,8 @@ int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code)
 		/* there is still an unfinished UAC transaction; wait now! */
 		/* there is still an unfinished UAC transaction; wait now! */
 		if ( t->uac[b].last_received<200 )
 		if ( t->uac[b].last_received<200 )
 			return -2;
 			return -2;
-		if ( t->uac[b].last_received<lowest_s ) {
+		/* if reply is null => t_send_branch "faked" reply, skip over it */
+		if ( t->uac[b].last_received<lowest_s && t->uac[b].reply ) {
 			lowest_b =b;
 			lowest_b =b;
 			lowest_s = t->uac[b].last_received;
 			lowest_s = t->uac[b].last_received;
 		}
 		}
@@ -729,6 +755,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 	int branch_cnt;
 	int branch_cnt;
 	int picked_branch;
 	int picked_branch;
 	int picked_code;
 	int picked_code;
+	int new_branch;
 	int inv_through;
 	int inv_through;
 
 
 	/* note: this code never lets replies to CANCEL go through;
 	/* note: this code never lets replies to CANCEL go through;
@@ -807,9 +834,8 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 		/* run ON_FAILURE handlers ( route and callbacks) */
 		/* run ON_FAILURE handlers ( route and callbacks) */
 		if ( has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
 		if ( has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
 		|| Trans->on_negative ) {
 		|| Trans->on_negative ) {
-			run_failure_handlers( Trans,
-				Trans->uac[picked_branch].reply,
-				picked_code);
+			run_failure_handlers( Trans, Trans->uac[picked_branch].reply,
+									picked_code);
 		}
 		}
 
 
 		/* now reset it; after the failure logic, the reply may
 		/* now reset it; after the failure logic, the reply may
@@ -834,11 +860,21 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 			return RPS_COMPLETED;
 			return RPS_COMPLETED;
 		}
 		}
 		/* look if the callback/failure_route introduced new branches ... */
 		/* look if the callback/failure_route introduced new branches ... */
-		if (branch_cnt<Trans->nr_of_outgoings)  {
-			/* await then result of new branches */
-			*should_store=1;
-			*should_relay=-1;
-			return RPS_STORE;
+		if (branch_cnt<Trans->nr_of_outgoings){
+			/* the new branches might be already "finished" => we
+			 * must use t_pick_branch again */
+			new_branch=t_pick_branch(branch, new_code, Trans, &picked_code);
+			if (new_branch<0){
+				if (new_branch==-2) { /* branches open yet */
+					*should_store=1;
+					*should_relay=-1;
+					return RPS_STORE;
+				}
+				/* error, use the old picked_branch */
+			}else{
+				/* found a new_branch */
+				picked_branch=new_branch;
+			}
 		}
 		}
 
 
 		/* really no more pending branches -- return lowest code */
 		/* really no more pending branches -- return lowest code */

+ 54 - 2
modules/tm/timer.c

@@ -101,6 +101,8 @@
  *  2005-10-03  almost completely rewritten to use the new timers (andrei)
  *  2005-10-03  almost completely rewritten to use the new timers (andrei)
  *  2005-12-12  on final response marked the rb as removed to avoid deleting
  *  2005-12-12  on final response marked the rb as removed to avoid deleting
  *              it from the timer handle; timer_allow_del()  (andrei)
  *              it from the timer handle; timer_allow_del()  (andrei)
+ *  2006-08-11  final_response_handler dns failover support for timeout-ed
+ *              invites (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -123,6 +125,12 @@
 #include "t_funcs.h"
 #include "t_funcs.h"
 #include "t_reply.h"
 #include "t_reply.h"
 #include "t_cancel.h"
 #include "t_cancel.h"
+#ifdef USE_DNS_FAILOVER
+#include "t_fwd.h" /* t_send_branch */
+#endif
+#ifdef USE_DST_BLACKLIST
+#include "../../dst_blacklist.h"
+#endif
 
 
 
 
 
 
@@ -221,6 +229,9 @@ inline static ticks_t  delete_cell( struct cell *p_cell, int unlock )
 
 
 
 
 
 
+
+/* generate a fake reply
+ * it assumes the REPLY_LOCK is already held and returns unlocked */
 static void fake_reply(struct cell *t, int branch, int code )
 static void fake_reply(struct cell *t, int branch, int code )
 {
 {
 	branch_bm_t cancel_bitmap;
 	branch_bm_t cancel_bitmap;
@@ -311,6 +322,13 @@ inline static void final_response_handler(	struct retr_buf* r_buf,
 {
 {
 	int silent;
 	int silent;
 	int reply_code;
 	int reply_code;
+#ifdef USE_DNS_FAILOVER
+	/*int i; 
+	int added_branches;
+	*/
+	int branch_ret;
+	int prev_branch;
+#endif
 
 
 #	ifdef EXTRA_DEBUG
 #	ifdef EXTRA_DEBUG
 	if (t->flags & T_IN_AGONY) 
 	if (t->flags & T_IN_AGONY) 
@@ -380,11 +398,45 @@ inline static void final_response_handler(	struct retr_buf* r_buf,
 	if (is_invite(t) && 
 	if (is_invite(t) && 
 	    r_buf->branch < MAX_BRANCHES && /* r_buf->branch is always >=0 */
 	    r_buf->branch < MAX_BRANCHES && /* r_buf->branch is always >=0 */
 	    t->uac[r_buf->branch].last_received > 0) {
 	    t->uac[r_buf->branch].last_received > 0) {
-		reply_code = 480; /* Request Terminated */
+		reply_code = 480; /* Temporarily Unavailable */
 	} else {
 	} else {
 		reply_code = 408; /* Request Timeout */
 		reply_code = 408; /* Request Timeout */
+#ifdef USE_DST_BLACKLIST
+		if (use_dst_blacklist)
+			dst_blacklist_add( BLST_ERR_TIMEOUT, &r_buf->dst);
+#endif
+#ifdef USE_DNS_FAILOVER
+		/* if this is an invite, the destination resolves to more ips, and
+		 *  it still hasn't passed more than fr_inv_timeout since we
+		 *  started, add another branch/uac */
+		if (is_invite(t) && use_dns_failover &&
+				((get_ticks_raw()-(r_buf->fr_expire-t->fr_timeout)) <
+				 	t->fr_inv_timeout)){
+			branch_ret=add_uac_dns_fallback(t, t->uas.request,
+												&t->uac[r_buf->branch], 0);
+			prev_branch=-1;
+			while((branch_ret>=0) &&(branch_ret!=prev_branch)){
+				prev_branch=branch_ret;
+				branch_ret=t_send_branch(t, branch_ret, t->uas.request , 0, 0);
+			}
+#if 0
+			if (branch_ret>=0){
+				added_branches=1<<branch_ret;
+				/* success */
+				for (i=branch_ret; i<t->nr_of_outgoings; i++) {
+					if (added_branches & (1<<i)) {
+						branch_ret=t_send_branch(t, i, t->uas.request , 0, 0);
+						if ((branch_ret>=0) && (branch_ret!=i)){
+							/* no send, but new branch */
+							added_branches |= 1<<branch_ret;
+						}
+					}
+				}
+			}
+#endif
+		}
+#endif
 	}
 	}
-
 	fake_reply(t, r_buf->branch, reply_code );
 	fake_reply(t, r_buf->branch, reply_code );
 }
 }
 
 

+ 29 - 1
modules/tm/uac.c

@@ -52,6 +52,7 @@
  *  2004-08-23  avp support in t_uac (bogdan)
  *  2004-08-23  avp support in t_uac (bogdan)
  *  2005-12-16  t_uac will set the new_cell timers to the default values,
  *  2005-12-16  t_uac will set the new_cell timers to the default values,
  *               fixes 0 fr_timer bug (andrei)
  *               fixes 0 fr_timer bug (andrei)
+ *  2006-08-11  t_uac uses dns failover until it finds a send socket (andrei)
  */
  */
 
 
 #include <string.h>
 #include <string.h>
@@ -173,6 +174,9 @@ static inline int t_uac_prepare(str* method, str* headers, str* body, dlg_t* dia
 	char* buf;
 	char* buf;
         int buf_len, ret, flags;
         int buf_len, ret, flags;
 	unsigned int hi;
 	unsigned int hi;
+#ifdef USE_DNS_FAILOVER
+	struct dns_srv_handle dns_h;
+#endif
 
 
 	ret=-1;
 	ret=-1;
 	/*if (dst_req) *dst_req = NULL;*/
 	/*if (dst_req) *dst_req = NULL;*/
@@ -186,13 +190,36 @@ static inline int t_uac_prepare(str* method, str* headers, str* body, dlg_t* dia
 	DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
 	DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
 			dialog->hooks.next_hop->s);
 			dialog->hooks.next_hop->s);
 	/* it's a new message, so we will take the default socket */
 	/* it's a new message, so we will take the default socket */
+#ifdef USE_DNS_FAILOVER
+	if (use_dns_failover){
+		dns_srv_handle_init(&dns_h);
+		if ((uri2dst(&dns_h, &dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0)
+				|| (dst.send_sock==0)){
+			dns_srv_handle_put(&dns_h);
+			ser_error = E_NO_SOCKET;
+			ret=ser_error;
+			LOG(L_ERR, "t_uac: no socket found\n");
+			goto error2;
+		}
+		dns_srv_handle_put(&dns_h); /* not needed anymore */
+	}else{
+		if ((uri2dst(0, &dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0) ||
+				(dst.send_sock==0)){
+			ser_error = E_NO_SOCKET;
+			ret=ser_error;
+			LOG(L_ERR, "t_uac: no socket found\n");
+			goto error2;
+		}
+	}
+#else
 	if ((uri2dst(&dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0) ||
 	if ((uri2dst(&dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0) ||
 			(dst.send_sock==0)){
 			(dst.send_sock==0)){
 		ser_error = E_NO_SOCKET;
 		ser_error = E_NO_SOCKET;
 		ret=ser_error;
 		ret=ser_error;
 		LOG(L_ERR, "t_uac: no socket found\n");
 		LOG(L_ERR, "t_uac: no socket found\n");
 		goto error2;
 		goto error2;
-	}	
+	}
+#endif
 
 
 	new_cell = build_cell(0); 
 	new_cell = build_cell(0); 
 	if (!new_cell) {
 	if (!new_cell) {
@@ -254,6 +281,7 @@ static inline int t_uac_prepare(str* method, str* headers, str* body, dlg_t* dia
 	new_cell->nr_of_outgoings++;
 	new_cell->nr_of_outgoings++;
 	
 	
 	if (dst_req) *dst_req = request;
 	if (dst_req) *dst_req = request;
+	
 	return 1;
 	return 1;
 
 
  error1:
  error1:

+ 125 - 12
modules/tm/ut.h

@@ -36,6 +36,8 @@
  *  2003-07-07  get_proto takes now two protos as arguments (andrei)
  *  2003-07-07  get_proto takes now two protos as arguments (andrei)
  *              tls/sips support for get_proto & uri2proxy (andrei)
  *              tls/sips support for get_proto & uri2proxy (andrei)
  *  2006-04-13  added uri2dst(), simplified uri2sock() (andrei)
  *  2006-04-13  added uri2dst(), simplified uri2sock() (andrei)
+ *  2006-08-11  dns failover support: uri2dst uses the dns cache and tries to 
+ *               get the first ip for which there is a send sock. (andrei)
  */
  */
 
 
 
 
@@ -54,6 +56,9 @@
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/msg_parser.h"
 #include "../../resolve.h"
 #include "../../resolve.h"
+#ifdef USE_DNS_FAILOVER
+#include "../../dns_cache.h"
+#endif
 
 
 /* a forced_proto takes precedence if != PROTO_NONE */
 /* a forced_proto takes precedence if != PROTO_NONE */
 inline static enum sip_protos get_proto(enum sip_protos force_proto,
 inline static enum sip_protos get_proto(enum sip_protos force_proto,
@@ -145,23 +150,94 @@ inline static struct proxy_l *uri2proxy( str *uri, int proto )
 
 
 
 
 
 
+/*
+ * parse uri and return send related information
+ * params: uri - uri in string form
+ *         host - filled with the uri host part
+ *         port - filled with the uri port
+ *         proto - if != PROTO_NONE, this protocol will be forced over the
+ *                 uri_proto, otherwise the uri proto will be used 
+ *                 (value/return)
+ *         comp - compression (if used)
+ * returns 0 on success, < 0 on error
+ */
+inline static int get_uri_send_info(str* uri, str* host, unsigned short* port,
+									short* proto, short* comp)
+{
+	struct sip_uri parsed_uri;
+	enum sip_protos uri_proto;
+	
+	if (parse_uri(uri->s, uri->len, &parsed_uri) < 0) {
+		LOG(L_ERR, "ERROR: get_uri_send_info: bad_uri: %.*s\n",
+					uri->len, uri->s );
+		return -1;
+	}
+	
+	if (parsed_uri.type==SIPS_URI_T){
+		if ((parsed_uri.proto!=PROTO_TCP) && (parsed_uri.proto!=PROTO_NONE)){
+			LOG(L_ERR, "ERROR: get_uri_send_info: bad transport  for"
+						" sips uri: %d\n", parsed_uri.proto);
+			return -1;
+		}else
+			uri_proto=PROTO_TLS;
+	}else
+		uri_proto=parsed_uri.proto;
+	
+	*proto= get_proto(*proto, uri_proto);
+#ifdef USE_COMP
+	*comp=parsed_uri.comp;
+#endif
+#ifdef HONOR_MADDR
+	if (parsed_uri.maddr_val.s && parsed_uri.maddr_val.len) {
+		*host=parsed_uri.maddr;
+		DBG("maddr dst: %.*s:%d\n", parsed_uri.maddr_val.len, 
+				parsed_uri.maddr_val.s, parsed_uri.port_no);
+	} else
+#endif
+		*host=parsed_uri.host;
+	*port=parsed_uri.port_no;
+	return 0;
+}
+
+
+
 /*
 /*
  * Convert a URI into a dest_info structure
  * Convert a URI into a dest_info structure
- * params: msg - sip message used to set dst->send_sock, if 0 dst->send_sock
- *               will be set to the default w/o using msg->force_send_socket 
- *               (see get_send_socket()) 
- *         dst - will be filled
- *         uri - uri in str form
+ * If the uri host resolves to multiple ips and dns_h!=0 the first ip for 
+ *  which a send socket is found will be used. If no send_socket are found,
+ *  the first ip is selected.
+ *
+ * params: dns_h - pointer to a valid dns_srv_handle structure (intialized!) or
+ *                 null. If null or use_dns_failover==0 normal dns lookup will
+ *                 be performed (no failover).
+ *         dst   - will be filled
+ *         msg   -  sip message used to set dst->send_sock, if 0 dst->send_sock
+ *                 will be set to the default w/o using msg->force_send_socket 
+ *                 (see get_send_socket()) 
+ *         uri   - uri in str form
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *                 uri_proto, otherwise the uri proto will be used
  *                 uri_proto, otherwise the uri proto will be used
  * returns 0 on error, dst on success
  * returns 0 on error, dst on success
  */
  */
+#ifdef USE_DNS_FAILOVER
+inline static struct dest_info *uri2dst(struct dns_srv_handle* dns_h,
+										struct dest_info* dst,
+										struct sip_msg *msg, str *uri, 
+											int proto )
+#else
 inline static struct dest_info *uri2dst(struct dest_info* dst,
 inline static struct dest_info *uri2dst(struct dest_info* dst,
 										struct sip_msg *msg, str *uri, 
 										struct sip_msg *msg, str *uri, 
 											int proto )
 											int proto )
+#endif
 {
 {
 	struct sip_uri parsed_uri;
 	struct sip_uri parsed_uri;
 	enum sip_protos uri_proto;
 	enum sip_protos uri_proto;
+	str* host;
+#ifdef USE_DNS_FAILOVER
+	int ip_found;
+	union sockaddr_union to;
+	int err;
+#endif
 
 
 	if (parse_uri(uri->s, uri->len, &parsed_uri) < 0) {
 	if (parse_uri(uri->s, uri->len, &parsed_uri) < 0) {
 		LOG(L_ERR, "ERROR: uri2dst: bad_uri: %.*s\n",
 		LOG(L_ERR, "ERROR: uri2dst: bad_uri: %.*s\n",
@@ -186,16 +262,53 @@ inline static struct dest_info *uri2dst(struct dest_info* dst,
 #endif
 #endif
 #ifdef HONOR_MADDR
 #ifdef HONOR_MADDR
 	if (parsed_uri.maddr_val.s && parsed_uri.maddr_val.len) {
 	if (parsed_uri.maddr_val.s && parsed_uri.maddr_val.len) {
-		sip_hostport2su(&dst->to, &parsed_uri.maddr_val, parsed_uri.port_no, dst->proto);
-		DBG("maddr dst: %.*s:%d\n", parsed_uri.maddr_val.len, parsed_uri.maddr_val.s, parsed_uri.port_no);
+		host=&parsed_uri.maddr_val;
+		DBG("maddr dst: %.*s:%d\n", parsed_uri.maddr_val.len, 
+								parsed_uri.maddr_val.s, parsed_uri.port_no);
 	} else
 	} else
 #endif
 #endif
-	sip_hostport2su(&dst->to, &parsed_uri.host, parsed_uri.port_no,
-						dst->proto);
+		host=&parsed_uri.host;
+#ifdef USE_DNS_FAILOVER
+	if (use_dns_failover && dns_h){
+		ip_found=0;
+		do{
+			/* try all the ips until we find a good send socket */
+			err=dns_sip_resolve2su(dns_h, &to, host,
+									parsed_uri.port_no, dst->proto, dns_flags);
+			if (err!=0){
+				if (ip_found==0){
+					LOG(L_ERR, "ERROR: uri2dst: failed to resolve \"%.*s\" :"
+								"%s (%d)\n", host->len, ZSW(host->s),
+									dns_strerror(err), err);
+					return 0; /* error, no ip found */
+				}
+				break;
+			}
+			if (ip_found==0){
+				dst->to=to;
+				ip_found=1;
+			}
+			dst->send_sock = get_send_socket(msg, &to, dst->proto);
+			if (dst->send_sock){
+				dst->to=to;
+				return dst; /* found a good one */
+			}
+		}while(dns_srv_handle_next(dns_h, err));
+		LOG(L_ERR, "ERROR: uri2sock: no corresponding socket for \"%.*s\" "
+					"af %d\n", host->len, ZSW(host->s), dst->to.s.sa_family);
+		/* try to continue */
+		return dst;
+	}
+#endif
+	if (sip_hostport2su(&dst->to, host, parsed_uri.port_no, dst->proto)!=0){
+		LOG(L_ERR, "ERROR: uri2dst: failed to resolve \"%.*s\"\n",
+					host->len, ZSW(host->s));
+		return 0;
+	}
 	dst->send_sock = get_send_socket(msg, &dst->to, dst->proto);
 	dst->send_sock = get_send_socket(msg, &dst->to, dst->proto);
 	if (dst->send_sock==0) {
 	if (dst->send_sock==0) {
 		LOG(L_ERR, "ERROR: uri2sock: no corresponding socket for af %d\n", 
 		LOG(L_ERR, "ERROR: uri2sock: no corresponding socket for af %d\n", 
-				dst->to.s.sa_family);
+					dst->to.s.sa_family);
 		/* ser_error = E_NO_SOCKET;*/
 		/* ser_error = E_NO_SOCKET;*/
 		/* try to continue */
 		/* try to continue */
 	}
 	}
@@ -203,7 +316,7 @@ inline static struct dest_info *uri2dst(struct dest_info* dst,
 }
 }
 
 
 
 
-
+#if 0
 /*
 /*
  * Convert a URI into the corresponding sockaddr_union (address to send to) and
  * Convert a URI into the corresponding sockaddr_union (address to send to) and
  *  send socket_info (socket/address from which to send)
  *  send socket_info (socket/address from which to send)
@@ -233,6 +346,6 @@ static inline struct socket_info *uri2sock(struct sip_msg* msg, str *uri,
 	}
 	}
 	return dst.send_sock;
 	return dst.send_sock;
 }
 }
-
+#endif
 
 
 #endif /* _TM_UT_H */
 #endif /* _TM_UT_H */

+ 1 - 1
msg_translator.h

@@ -46,7 +46,7 @@
 #define WARNING_PHRASE " \"Noisy feedback tells: "
 #define WARNING_PHRASE " \"Noisy feedback tells: "
 #define WARNING_PHRASE_LEN (sizeof(WARNING_PHRASE)-1)
 #define WARNING_PHRASE_LEN (sizeof(WARNING_PHRASE)-1)
 
 
-//#define MAX_CONTENT_LEN_BUF INT2STR_MAX_LEN /* see ut.h/int2str() */
+/*#define MAX_CONTENT_LEN_BUF INT2STR_MAX_LEN *//* see ut.h/int2str() */
 
 
 #include "parser/msg_parser.h"
 #include "parser/msg_parser.h"
 #include "ip_addr.h"
 #include "ip_addr.h"

+ 75 - 32
resolve.c

@@ -32,6 +32,9 @@
  *  2005-07-11  added resolv_init (timeouts a.s.o) (andrei)
  *  2005-07-11  added resolv_init (timeouts a.s.o) (andrei)
  *  2006-04-13  added sip_hostport2su()  (andrei)
  *  2006-04-13  added sip_hostport2su()  (andrei)
  *  2006-07-13  rdata structures put on diet (andrei)
  *  2006-07-13  rdata structures put on diet (andrei)
+ *  2006-07-17  rdata contains now also the record name (andrei)
+ *  2006-08-18  get_record can append also the additional records to the
+ *               returned list (andrei)
  */ 
  */ 
 
 
 
 
@@ -47,13 +50,21 @@
 #include "ip_addr.h"
 #include "ip_addr.h"
 #include "error.h"
 #include "error.h"
 
 
+#ifdef USE_DNS_CACHE
+#include "dns_cache.h"
+#endif
+
 
 
 
 
 /* mallocs for local stuff */
 /* mallocs for local stuff */
 #define local_malloc pkg_malloc
 #define local_malloc pkg_malloc
 #define local_free   pkg_free
 #define local_free   pkg_free
 
 
+#ifdef USE_IPV6
 int dns_try_ipv6=1; /* default on */
 int dns_try_ipv6=1; /* default on */
+#else
+int dns_try_ipv6=0; /* off, if no ipv6 support */
+#endif
 /* declared in globals.h */
 /* declared in globals.h */
 int dns_retr_time=-1;
 int dns_retr_time=-1;
 int dns_retr_no=-1;
 int dns_retr_no=-1;
@@ -118,7 +129,7 @@ unsigned char* dns_skipname(unsigned char* p, unsigned char* end)
 		/* normal label */
 		/* normal label */
 		p+=*p+1;
 		p+=*p+1;
 	}
 	}
-	return (p>=end)?0:p;
+	return (p>end)?0:p;
 }
 }
 
 
 
 
@@ -154,7 +165,7 @@ struct srv_rdata* dns_srv_parser( unsigned char* msg, unsigned char* end,
 	char name[MAX_DNS_NAME];
 	char name[MAX_DNS_NAME];
 	
 	
 	srv=0;
 	srv=0;
-	if ((rdata+6)>=end) goto error;
+	if ((rdata+6+1)>end) goto error;
 	
 	
 	memcpy((void*)&priority, rdata, 2);
 	memcpy((void*)&priority, rdata, 2);
 	memcpy((void*)&weight,   rdata+2, 2);
 	memcpy((void*)&weight,   rdata+2, 2);
@@ -227,20 +238,20 @@ struct naptr_rdata* dns_naptr_parser( unsigned char* msg, unsigned char* end,
 	char repl[MAX_DNS_NAME];
 	char repl[MAX_DNS_NAME];
 	
 	
 	naptr = 0;
 	naptr = 0;
-	if ((rdata + 7) >= end) goto error;
+	if ((rdata + 7 + 1)>end) goto error;
 	
 	
 	memcpy((void*)&order, rdata, 2);
 	memcpy((void*)&order, rdata, 2);
 	memcpy((void*)&pref, rdata + 2, 2);
 	memcpy((void*)&pref, rdata + 2, 2);
 	flags_len = rdata[4];
 	flags_len = rdata[4];
-	if ((rdata + 7 +  flags_len) >= end)
+	if ((rdata + 7 + 1 +  flags_len) > end)
 		goto error;
 		goto error;
 	flags=rdata+5;
 	flags=rdata+5;
 	services_len = rdata[5 + flags_len];
 	services_len = rdata[5 + flags_len];
-	if ((rdata + 7 + flags_len + services_len) >= end)
+	if ((rdata + 7 + 1 + flags_len + services_len) > end)
 		goto error;
 		goto error;
 	services=rdata + 6 + flags_len;
 	services=rdata + 6 + flags_len;
 	regexp_len = rdata[6 + flags_len + services_len];
 	regexp_len = rdata[6 + flags_len + services_len];
-	if ((rdata + 7 + flags_len + services_len + regexp_len) >= end)
+	if ((rdata + 7 +1 + flags_len + services_len + regexp_len) > end)
 		goto error;
 		goto error;
 	regexp=rdata + 7 + flags_len + services_len;
 	regexp=rdata + 7 + flags_len + services_len;
 	rdata = rdata + 7 + flags_len + services_len + regexp_len;
 	rdata = rdata + 7 + flags_len + services_len + regexp_len;
@@ -318,7 +329,7 @@ struct a_rdata* dns_a_parser(unsigned char* rdata, unsigned char* end)
 {
 {
 	struct a_rdata* a;
 	struct a_rdata* a;
 	
 	
-	if (rdata+4>=end) goto error;
+	if (rdata+4>end) goto error;
 	a=(struct a_rdata*)local_malloc(sizeof(struct a_rdata));
 	a=(struct a_rdata*)local_malloc(sizeof(struct a_rdata));
 	if (a==0){
 	if (a==0){
 		LOG(L_ERR, "ERROR: dns_a_parser: out of memory\n");
 		LOG(L_ERR, "ERROR: dns_a_parser: out of memory\n");
@@ -338,7 +349,7 @@ struct aaaa_rdata* dns_aaaa_parser(unsigned char* rdata, unsigned char* end)
 {
 {
 	struct aaaa_rdata* aaaa;
 	struct aaaa_rdata* aaaa;
 	
 	
-	if (rdata+16>=end) goto error;
+	if (rdata+16>end) goto error;
 	aaaa=(struct aaaa_rdata*)local_malloc(sizeof(struct aaaa_rdata));
 	aaaa=(struct aaaa_rdata*)local_malloc(sizeof(struct aaaa_rdata));
 	if (aaaa==0){
 	if (aaaa==0){
 		LOG(L_ERR, "ERROR: dns_aaaa_parser: out of memory\n");
 		LOG(L_ERR, "ERROR: dns_aaaa_parser: out of memory\n");
@@ -369,17 +380,17 @@ void free_rdata_list(struct rdata* head)
  * returns a dyn. alloc'ed struct rdata linked list with the parsed responses
  * returns a dyn. alloc'ed struct rdata linked list with the parsed responses
  * or 0 on error
  * or 0 on error
  * see rfc1035 for the query/response format */
  * see rfc1035 for the query/response format */
-struct rdata* get_record(char* name, int type)
+struct rdata* get_record(char* name, int type, int flags)
 {
 {
 	int size;
 	int size;
+	int skip;
 	int qno, answers_no;
 	int qno, answers_no;
 	int r;
 	int r;
-	int ans_len;
 	static union dns_query buff;
 	static union dns_query buff;
 	unsigned char* p;
 	unsigned char* p;
-	unsigned char* t;
 	unsigned char* end;
 	unsigned char* end;
-	static unsigned char answer[ANS_SIZE];
+	static char rec_name[MAX_DNS_NAME]; /* placeholder for the record name */
+	int rec_name_len;
 	unsigned short rtype, class, rdlength;
 	unsigned short rtype, class, rdlength;
 	unsigned int ttl;
 	unsigned int ttl;
 	struct rdata* head;
 	struct rdata* head;
@@ -414,26 +425,35 @@ struct rdata* get_record(char* name, int type)
 		for (;(p<end && (*p)); p++);
 		for (;(p<end && (*p)); p++);
 		p+=1+2+2; /* skip the ending  '\0, QCODE and QCLASS */
 		p+=1+2+2; /* skip the ending  '\0, QCODE and QCLASS */
 	#endif
 	#endif
-		if (p>=end) {
+		if (p>end) {
 			LOG(L_ERR, "ERROR: get_record: p>=end\n");
 			LOG(L_ERR, "ERROR: get_record: p>=end\n");
 			goto error;
 			goto error;
 		}
 		}
 	};
 	};
 	answers_no=ntohs((unsigned short)buff.hdr.ancount);
 	answers_no=ntohs((unsigned short)buff.hdr.ancount);
-	ans_len=ANS_SIZE;
-	t=answer;
+again:
 	for (r=0; (r<answers_no) && (p<end); r++){
 	for (r=0; (r<answers_no) && (p<end); r++){
+#if 0
 		/*  ignore it the default domain name */
 		/*  ignore it the default domain name */
 		if ((p=dns_skipname(p, end))==0) {
 		if ((p=dns_skipname(p, end))==0) {
 			LOG(L_ERR, "ERROR: get_record: skip_name=0 (#2)\n");
 			LOG(L_ERR, "ERROR: get_record: skip_name=0 (#2)\n");
 			goto error;
 			goto error;
 		}
 		}
-		/*
-		skip=dn_expand(buff.buff, end, p, t, ans_len);
+#else
+		if ((skip=dn_expand(buff.buff, end, p, rec_name, MAX_DNS_NAME-1))==-1){
+			LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name) failed\n");
+			goto error;
+		}
+#endif
 		p+=skip;
 		p+=skip;
-		*/
+		rec_name_len=strlen(rec_name);
+		if (rec_name_len>255){
+			LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name): name too"
+					" long  (%d)\n", rec_name_len);
+			goto error;
+		}
 		/* check if enough space is left for type, class, ttl & size */
 		/* check if enough space is left for type, class, ttl & size */
-		if ((p+2+2+4+2)>=end) goto error_boundary;
+		if ((p+2+2+4+2)>end) goto error_boundary;
 		/* get type */
 		/* get type */
 		memcpy((void*) &rtype, (void*)p, 2);
 		memcpy((void*) &rtype, (void*)p, 2);
 		rtype=ntohs(rtype);
 		rtype=ntohs(rtype);
@@ -450,18 +470,14 @@ struct rdata* get_record(char* name, int type)
 		memcpy((void*)&rdlength, (void*)p, 2);
 		memcpy((void*)&rdlength, (void*)p, 2);
 		rdlength=ntohs(rdlength);
 		rdlength=ntohs(rdlength);
 		p+=2;
 		p+=2;
-		/* check for type */
-		/*
-		if (rtype!=type){
-			LOG(L_ERR, "WARNING: get_record: wrong type in answer (%d!=%d)\n",
-					rtype, type);
+		if ((flags & RES_ONLY_TYPE) && (rtype!=type)){
+			/* skip */
 			p+=rdlength;
 			p+=rdlength;
 			continue;
 			continue;
 		}
 		}
-		*/
 		/* expand the "type" record  (rdata)*/
 		/* expand the "type" record  (rdata)*/
 		
 		
-		rd=(struct rdata*) local_malloc(sizeof(struct rdata));
+		rd=(struct rdata*) local_malloc(sizeof(struct rdata)+rec_name_len+1-1);
 		if (rd==0){
 		if (rd==0){
 			LOG(L_ERR, "ERROR: get_record: out of memory\n");
 			LOG(L_ERR, "ERROR: get_record: out of memory\n");
 			goto error;
 			goto error;
@@ -470,6 +486,9 @@ struct rdata* get_record(char* name, int type)
 		rd->class=class;
 		rd->class=class;
 		rd->ttl=ttl;
 		rd->ttl=ttl;
 		rd->next=0;
 		rd->next=0;
+		memcpy(rd->name, rec_name, rec_name_len);
+		rd->name[rec_name_len]=0;
+		rd->name_len=rec_name_len;
 		switch(rtype){
 		switch(rtype){
 			case T_SRV:
 			case T_SRV:
 				srv_rd= dns_srv_parser(buff.buff, end, p);
 				srv_rd= dns_srv_parser(buff.buff, end, p);
@@ -529,13 +548,36 @@ struct rdata* get_record(char* name, int type)
 		p+=rdlength;
 		p+=rdlength;
 		
 		
 	}
 	}
+	if (flags & RES_AR){
+		flags&=~RES_AR;
+		answers_no=ntohs((unsigned short)buff.hdr.nscount);
+		DBG("get_record: skipping %d NS (p=%p, end=%p)\n", answers_no, p, end);
+		for (r=0; (r<answers_no) && (p<end); r++){
+			/* skip over the ns records */
+			if ((p=dns_skipname(p, end))==0) {
+				LOG(L_ERR, "ERROR: get_record: skip_name=0 (#3)\n");
+				goto error;
+			}
+			/* check if enough space is left for type, class, ttl & size */
+			if ((p+2+2+4+2)>end) goto error_boundary;
+			memcpy((void*)&rdlength, (void*)p+2+2+4, 2);
+			p+=2+2+4+2+ntohs(rdlength);
+		}
+		answers_no=ntohs((unsigned short)buff.hdr.arcount);
+		DBG("get_record: parsing %d ARs (p=%p, end=%p)\n", answers_no, p, end);
+		goto again; /* add also the additional records */
+	}
+			
 	return head;
 	return head;
 error_boundary:
 error_boundary:
 		LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
 		LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
 		if (head) free_rdata_list(head);
 		if (head) free_rdata_list(head);
 		return 0;
 		return 0;
 error_parse:
 error_parse:
-		LOG(L_ERR, "ERROR: get_record: rdata parse error \n");
+		LOG(L_ERR, "ERROR: get_record: rdata parse error (%s, %d), %p-%p"
+						" rtype=%d, class=%d, ttl=%d, rdlength=%d \n",
+				name, type,
+				p, end, rtype, class, ttl, rdlength);
 		if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
 		if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
 								   the list */
 								   the list */
 error:
 error:
@@ -547,6 +589,8 @@ not_found:
 
 
 
 
 
 
+
+
 /* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
 /* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
  * if *port!=0.
  * if *port!=0.
  * when performing SRV lookup (*port==0) it will use proto to look for
  * when performing SRV lookup (*port==0) it will use proto to look for
@@ -554,14 +598,14 @@ not_found:
  * returns: hostent struct & *port filled with the port from the SRV record;
  * returns: hostent struct & *port filled with the port from the SRV record;
  *  0 on error
  *  0 on error
  */
  */
-struct hostent* sip_resolvehost(str* name, unsigned short* port, int proto)
+struct hostent* _sip_resolvehost(str* name, unsigned short* port, int proto)
 {
 {
 	struct hostent* he;
 	struct hostent* he;
+	struct ip_addr* ip;
+	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
 	struct rdata* head;
 	struct rdata* head;
 	struct rdata* l;
 	struct rdata* l;
 	struct srv_rdata* srv;
 	struct srv_rdata* srv;
-	struct ip_addr* ip;
-	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
 
 
 	/* try SRV if no port specified (draft-ietf-sip-srv-06) */
 	/* try SRV if no port specified (draft-ietf-sip-srv-06) */
 	if ((port)&&(*port==0)){
 	if ((port)&&(*port==0)){
@@ -604,8 +648,7 @@ struct hostent* sip_resolvehost(str* name, unsigned short* port, int proto)
 							proto);
 							proto);
 					return 0;
 					return 0;
 			}
 			}
-
-			head=get_record(tmp, T_SRV);
+			head=get_record(tmp, T_SRV, RES_ONLY_TYPE);
 			for(l=head; l; l=l->next){
 			for(l=head; l; l=l->next){
 				if (l->type!=T_SRV) continue; /*should never happen*/
 				if (l->type!=T_SRV) continue; /*should never happen*/
 				srv=(struct srv_rdata*) l->rdata;
 				srv=(struct srv_rdata*) l->rdata;

+ 32 - 9
resolve.h

@@ -31,6 +31,8 @@
  *  2003-04-12  support for resolving ipv6 address references added (andrei)
  *  2003-04-12  support for resolving ipv6 address references added (andrei)
  *  2004-07-28  darwin needs nameser_compat.h (andrei)
  *  2004-07-28  darwin needs nameser_compat.h (andrei)
  *  2006-07-13  rdata structures put on diet (andrei)
  *  2006-07-13  rdata structures put on diet (andrei)
+ *  2006-07-17  rdata contains now also the record name (andrei)
+ *  2006-08-18  get_record uses flags (andrei)
  */
  */
 
 
 
 
@@ -48,6 +50,9 @@
 #endif
 #endif
 
 
 #include "ip_addr.h"
 #include "ip_addr.h"
+#ifdef USE_DNS_CACHE
+#include "dns_wrappers.h"
+#endif
 
 
 
 
 #define MAX_QUERY_SIZE 8192
 #define MAX_QUERY_SIZE 8192
@@ -57,6 +62,9 @@
 #define MAX_DNS_STRING 255
 #define MAX_DNS_STRING 255
 
 
 
 
+/* get_record flags */
+#define RES_ONLY_TYPE 1   /* return only the specified type records */
+#define RES_AR		  2   /* return also the additional records */
 
 
 /* query union*/
 /* query union*/
 union dns_query{
 union dns_query{
@@ -72,7 +80,11 @@ struct rdata {
 	unsigned int   ttl;
 	unsigned int   ttl;
 	void* rdata;
 	void* rdata;
 	struct rdata* next;
 	struct rdata* next;
+	unsigned char name_len; /* name length w/o the terminating 0 */
+	char name[1]; /* null terminated name (len=name_len+1) */
 };
 };
+/* real size of the structure */
+#define RDATA_SIZE(s) (sizeof(struct rdata)+(s).name_len) /* +1-1 */
 
 
 
 
 /* srv rec. struct*/
 /* srv rec. struct*/
@@ -86,7 +98,7 @@ struct srv_rdata {
 
 
 
 
 /* real size of the structure */
 /* real size of the structure */
-#define SRV_RDATA_SIZE (s) (sizeof(struct srv_rdata)+(s).name_len)
+#define SRV_RDATA_SIZE(s) (sizeof(struct srv_rdata)+(s).name_len)
 
 
 /* naptr rec. struct*/
 /* naptr rec. struct*/
 struct naptr_rdata {
 struct naptr_rdata {
@@ -107,7 +119,7 @@ struct naptr_rdata {
 };
 };
 
 
 /* real size of the structure */
 /* real size of the structure */
-#define NAPTR_RDATA_SIZE (s) (sizeof(struct naptr_rdata) \
+#define NAPTR_RDATA_SIZE(s) (sizeof(struct naptr_rdata) \
 								+ (s).flags_len \
 								+ (s).flags_len \
 								+ (s).services_len \
 								+ (s).services_len \
 								+ (s).regexp_len \
 								+ (s).regexp_len \
@@ -130,11 +142,11 @@ struct cname_rdata {
 };
 };
 
 
 /* real size of the structure */
 /* real size of the structure */
-#define CNAME_RDATA_SIZE (s) (sizeof(struct cname_rdata)+(s).name_len)
+#define CNAME_RDATA_SIZE(s) (sizeof(struct cname_rdata)+(s).name_len)
 
 
 
 
 
 
-struct rdata* get_record(char* name, int type);
+struct rdata* get_record(char* name, int type, int flags);
 void free_rdata_list(struct rdata* head);
 void free_rdata_list(struct rdata* head);
 
 
 
 
@@ -299,14 +311,12 @@ error_char:
 
 
 
 
 
 
-struct hostent* sip_resolvehost(str* name, unsigned short* port, int proto);
-
+struct hostent* _sip_resolvehost(str* name, unsigned short* port, int proto);
 
 
 
 
-/* gethostbyname wrappers
- * use this, someday they will use a local cache */
 
 
-static inline struct hostent* resolvehost(char* name)
+/* gethostbyname wrapper, handles ip/ipv6 automatically */
+static inline struct hostent* _resolvehost(char* name)
 {
 {
 	static struct hostent* he=0;
 	static struct hostent* he=0;
 #ifdef HAVE_GETIPNODEBYNAME 
 #ifdef HAVE_GETIPNODEBYNAME 
@@ -381,4 +391,17 @@ int resolv_init();
 
 
 int sip_hostport2su(union sockaddr_union* su, str* host, unsigned short port,
 int sip_hostport2su(union sockaddr_union* su, str* host, unsigned short port,
 						int proto);
 						int proto);
+
+
+
+/* wrappers */
+#ifdef USE_DNS_CACHE
+#define resolvehost dns_resolvehost
+#define sip_resolvehost dns_sip_resolvehost
+#else
+#define resolvehost _resolvehost
+#define sip_resolvehost _sip_resolvehost
+#endif
+
+
 #endif
 #endif

+ 26 - 8
socket_info.c

@@ -517,8 +517,9 @@ error:
 
 
 
 
 /* fixes a socket list => resolve addresses, 
 /* fixes a socket list => resolve addresses, 
- * interface names, fills missing members, remove duplicates */
-static int fix_socket_list(struct socket_info **list)
+ * interface names, fills missing members, remove duplicates
+ * fills type_flags if not null with SOCKET_T_IPV4 and/or SOCKET_T_IPV6*/
+static int fix_socket_list(struct socket_info **list, int* type_flags)
 {
 {
 	struct socket_info* si;
 	struct socket_info* si;
 	struct socket_info* l;
 	struct socket_info* l;
@@ -528,9 +529,10 @@ static int fix_socket_list(struct socket_info **list)
 	struct hostent* he;
 	struct hostent* he;
 	char** h;
 	char** h;
 	
 	
+	if (type_flags)
+		*type_flags=0;
 	/* try to change all the interface names into addresses
 	/* try to change all the interface names into addresses
 	 *  --ugly hack */
 	 *  --ugly hack */
-	
 	for (si=*list;si;){
 	for (si=*list;si;){
 		next=si->next;
 		next=si->next;
 		if (add_interfaces(si->name.s, AF_INET, si->port_no,
 		if (add_interfaces(si->name.s, AF_INET, si->port_no,
@@ -598,6 +600,9 @@ static int fix_socket_list(struct socket_info **list)
 			}
 			}
 		hostent2ip_addr(&si->address, he, 0); /*convert to ip_addr 
 		hostent2ip_addr(&si->address, he, 0); /*convert to ip_addr 
 														 format*/
 														 format*/
+		if (type_flags){
+			*type_flags|=(si->address.af==AF_INET)?SOCKET_T_IPV4:SOCKET_T_IPV6;
+		}
 		if ((tmp=ip_addr2a(&si->address))==0) goto error;
 		if ((tmp=ip_addr2a(&si->address))==0) goto error;
 		si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1);
 		si->address_str.s=(char*)pkg_malloc(strlen(tmp)+1);
 		if (si->address_str.s==0){
 		if (si->address_str.s==0){
@@ -707,11 +712,15 @@ error:
 
 
 
 
 
 
-/* fix all 3 socket lists
+/* fix all 3 socket lists, fills socket_types if non-null
  * return 0 on success, -1 on error */
  * return 0 on success, -1 on error */
-int fix_all_socket_lists()
+int fix_all_socket_lists(int* socket_types)
 {
 {
 	struct utsname myname;
 	struct utsname myname;
+	int flags;
+	
+	if (socket_types)
+		*socket_types=0;
 	
 	
 	if ((udp_listen==0)
 	if ((udp_listen==0)
 #ifdef USE_TCP
 #ifdef USE_TCP
@@ -752,23 +761,32 @@ int fix_all_socket_lists()
 			}
 			}
 		}
 		}
 	}
 	}
-	if (fix_socket_list(&udp_listen)!=0){
+	if (fix_socket_list(&udp_listen, &flags)!=0){
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 				" udp failed\n");
 				" udp failed\n");
 		goto error;
 		goto error;
 	}
 	}
+	if (flags && socket_types){
+		*socket_types|=flags|SOCKET_T_UDP;
+	}
 #ifdef USE_TCP
 #ifdef USE_TCP
-	if (!tcp_disable && (fix_socket_list(&tcp_listen)!=0)){
+	if (!tcp_disable && (fix_socket_list(&tcp_listen, &flags)!=0)){
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 				" tcp failed\n");
 				" tcp failed\n");
 		goto error;
 		goto error;
 	}
 	}
+	if (flags && socket_types){
+		*socket_types|=flags|SOCKET_T_TCP;
+	}
 #ifdef USE_TLS
 #ifdef USE_TLS
-	if (!tls_disable && (fix_socket_list(&tls_listen)!=0)){
+	if (!tls_disable && (fix_socket_list(&tls_listen, &flags)!=0)){
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 		LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list"
 				" tls failed\n");
 				" tls failed\n");
 		goto error;
 		goto error;
 	}
 	}
+	if (flags && socket_types){
+		*socket_types|=flags|SOCKET_T_TLS;
+	}
 #endif
 #endif
 #endif
 #endif
 	if ((udp_listen==0)
 	if ((udp_listen==0)

+ 9 - 1
socket_info.h

@@ -52,9 +52,17 @@ struct socket_info* tls_listen;
 #endif
 #endif
 
 
 
 
+/* flags for finding out the address types */
+#define SOCKET_T_IPV4 1
+#define SOCKET_T_IPV6 2
+#define SOCKET_T_UDP  4
+#define SOCKET_T_TCP  8
+#define SOCKET_T_TLS 16
+
+
 int add_listen_iface(char* name, unsigned short port, unsigned short proto,
 int add_listen_iface(char* name, unsigned short port, unsigned short proto,
 							enum si_flags flags);
 							enum si_flags flags);
-int fix_all_socket_lists();
+int fix_all_socket_lists(int* socket_types);
 void print_all_socket_lists();
 void print_all_socket_lists();
 void print_aliases();
 void print_aliases();
 
 

+ 23 - 0
ut.h

@@ -94,6 +94,29 @@
 		((_via)->hdr.s+(_via)->hdr.len)))
 		((_via)->hdr.s+(_via)->hdr.len)))
 
 
 
 
+
+/* rounds to sizeof(type), but type must have a 2^k size (e.g. short, int,
+ * long, void*) */
+#define ROUND2TYPE(s, type) \
+	(((s)+(sizeof(type)-1))&(~(sizeof(type)-1)))
+
+
+/* rounds to sizeof(char*) - the first 4 byte multiple on 32 bit archs
+ * and the first 8 byte multiple on 64 bit archs */
+#define ROUND_POINTER(s) ROUND2TYPE(s, char*)
+
+/* rounds to sizeof(long) - the first 4 byte multiple on 32 bit archs
+ * and the first 8 byte multiple on 64 bit archs  (equiv. to ROUND_POINTER)*/
+#define ROUND_LONG(s)  ROUND2TYPE(s, long)
+
+/* rounds to sizeof(int) - the first t byte multiple on 32 and 64  bit archs */
+#define ROUND_INT(s) ROUND2TYPE(s, int)
+
+/* rounds to sizeof(short) - the first 2 byte multiple */
+#define ROUND_SHORT(s) ROUND2TYPE(s, short)
+
+
+
 /* links a value to a msgid */
 /* links a value to a msgid */
 struct msgid_var{
 struct msgid_var{
 	union{
 	union{

+ 9 - 0
utils/sercmd/sercmd.c

@@ -213,6 +213,15 @@ static struct cmd_alias cmd_aliases[]={
 	{	"serversion",	"core.version",			0			},
 	{	"serversion",	"core.version",			0			},
 	{	"who",			"ctl.who",				"[%v] %v: %v %v -> %v %v\n"},
 	{	"who",			"ctl.who",				"[%v] %v: %v %v -> %v %v\n"},
 	{	"listen",		"ctl.listen",			"[%v] %v: %v %v\n"},
 	{	"listen",		"ctl.listen",			"[%v] %v: %v %v\n"},
+	{	"dns_mem_info",		"dns.mem_info",			"%v / %v\n"},
+	{	"dns_debug",	"dns.debug",			
+					"%v (%v): size=%v ref=%v expire=%vs last=%vs ago f=%v\n"},
+	{	"dns_debug_all",	"dns.debug_all",			
+			"%v (%v) [%v]: size=%v ref=%v expire=%vs last=%vs ago f=%v\n"
+			"\t\t%v:%v expire=%vs f=%v\n"},
+	{	"dst_blacklist_mem_info",	"dst_blacklist.mem_info",	"%v / %v\n"},
+	{	"dst_blacklist_debug",		"dst_blacklist.debug",	
+		"%v:%v:%v expire:%v flags: %v\n"},
 	{0,0,0}
 	{0,0,0}
 };
 };
 
 

+ 20 - 1
version.h

@@ -199,6 +199,24 @@
 #endif
 #endif
 
 
 
 
+#ifdef USE_DNS_CACHE
+#define USE_DNS_CACHE_STR ", USE_DNS_CACHE"
+#else
+#define USE_DNS_CACHE_STR ""
+#endif
+
+#ifdef USE_DNS_FAILOVER
+#define USE_DNS_FAILOVER_STR ", USE_DNS_FAILOVER"
+#else
+#define USE_DNS_FAILOVER_STR ""
+#endif
+
+#ifdef USE_DST_BLACKLIST
+#define USE_DST_BLACKLIST_STR ", USE_DST_BLACKLIST"
+#else
+#define USE_DST_BLACKLIST_STR ""
+#endif
+
 #define SER_COMPILE_FLAGS \
 #define SER_COMPILE_FLAGS \
 	STATS_STR EXTRA_DEBUG_STR USE_IPV6_STR USE_TCP_STR USE_TLS_STR \
 	STATS_STR EXTRA_DEBUG_STR USE_IPV6_STR USE_TCP_STR USE_TLS_STR \
 	DISABLE_NAGLE_STR USE_MCAST_STR NO_DEBUG_STR NO_LOG_STR DNS_IP_HACK_STR \
 	DISABLE_NAGLE_STR USE_MCAST_STR NO_DEBUG_STR NO_LOG_STR DNS_IP_HACK_STR \
@@ -206,7 +224,8 @@
 	USE_SHM_MEM_STR DBG_QM_MALLOC_STR DBG_F_MALLOC_STR DEBUG_DMALLOC_STR \
 	USE_SHM_MEM_STR DBG_QM_MALLOC_STR DBG_F_MALLOC_STR DEBUG_DMALLOC_STR \
 	TIMER_DEBUG_STR \
 	TIMER_DEBUG_STR \
 	FAST_LOCK_STR NOSMP_STR USE_PTHREAD_MUTEX_STR USE_POSIX_SEM_STR \
 	FAST_LOCK_STR NOSMP_STR USE_PTHREAD_MUTEX_STR USE_POSIX_SEM_STR \
-	USE_SYSV_SEM_STR USE_COMP_STR
+	USE_SYSV_SEM_STR USE_COMP_STR USE_DNS_CACHE_STR USE_DNS_FAILOVER_STR \
+	USE_DST_BLACKLIST_STR
 
 
 
 
 #endif
 #endif