Browse Source

- dns naptr support (off by default)
- dns naptr related config options: dns_try_naptr (off by default),
dns_udp_pref, dns_tcp_pref. dns_tls_pref (protocol preferences for naptr
record selection)
- dns srv load balancing config options: dns_srv_lb (off by default)
- dns resolver & cache api change (to support getting the protocol via naptr)
- fix: dns iteration through A & AAAA records was not correct

For more info see doc/dns.txt.

Andrei Pelinescu-Onciul 18 years ago
parent
commit
dd4ffbb76d
17 changed files with 1186 additions and 94 deletions
  1. 5 2
      Makefile.defs
  2. 13 0
      NEWS
  3. 2 1
      action.c
  4. 17 0
      cfg.lex
  5. 23 1
      cfg.y
  6. 419 29
      dns_cache.c
  7. 18 6
      dns_cache.h
  8. 1 1
      dns_wrappers.h
  9. 120 1
      doc/dns.txt
  10. 6 4
      forward.c
  11. 2 0
      globals.h
  12. 4 4
      ip_addr.h
  13. 1 1
      main.c
  14. 5 3
      proxy.c
  15. 493 38
      resolve.c
  16. 50 2
      resolve.h
  17. 7 1
      version.h

+ 5 - 2
Makefile.defs

@@ -75,7 +75,7 @@ MAIN_NAME=ser
 VERSION = 2
 VERSION = 2
 PATCHLEVEL = 1
 PATCHLEVEL = 1
 SUBLEVEL =  0
 SUBLEVEL =  0
-EXTRAVERSION = -dev8-sf_malloc
+EXTRAVERSION = -dev9-dns
 
 
 SER_VER = $(shell expr $(VERSION) \* 1000000 + $(PATCHLEVEL) \* 1000 + \
 SER_VER = $(shell expr $(VERSION) \* 1000000 + $(PATCHLEVEL) \* 1000 + \
 			$(SUBLEVEL) )
 			$(SUBLEVEL) )
@@ -401,6 +401,8 @@ endif
 # -DNO_SIG_DEBUG
 # -DNO_SIG_DEBUG
 #        turns off debugging messages in signal handlers (which might be 
 #        turns off debugging messages in signal handlers (which might be 
 #         unsafe)
 #         unsafe)
+# -DUSE_NAPTR
+#		 turns on naptr support (but must be also enabled from the config)
 
 
 # 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
 
 
@@ -420,11 +422,12 @@ DEFS+= $(extra_defs) \
 	 -DUSE_DNS_CACHE \
 	 -DUSE_DNS_CACHE \
 	 -DUSE_DNS_FAILOVER \
 	 -DUSE_DNS_FAILOVER \
 	 -DUSE_DST_BLACKLIST \
 	 -DUSE_DST_BLACKLIST \
-	 -DDBG_QM_MALLOC \
+	 -DUSE_NAPTR \
 	 #-DLL_MALLOC \
 	 #-DLL_MALLOC \
 	 #-DSF_MALLOC \
 	 #-DSF_MALLOC \
 	 #-DDL_MALLOC \
 	 #-DDL_MALLOC \
 	 #-DF_MALLOC \
 	 #-DF_MALLOC \
+	 #-DDBG_QM_MALLOC \
 	 #-DDBG_F_MALLOC \
 	 #-DDBG_F_MALLOC \
 	 #-DNO_DEBUG \
 	 #-DNO_DEBUG \
 	 #-DEXTRA_DEBUG \
 	 #-DEXTRA_DEBUG \

+ 13 - 0
NEWS

@@ -71,6 +71,8 @@ modules:
                         - t_set_retr(t1, t2) - changes the retransmissions
                         - t_set_retr(t1, t2) - changes the retransmissions
                            intervals on the fly, on a per transaction basis.
                            intervals on the fly, on a per transaction basis.
 core:
 core:
+             - dns naptr support (see dns_try_naptr and dns_<proto>_pref)
+             - dns srv based load balancing support (see dns_srv_lb)
              - support for locking ser's pages in memory, pre-mapping
              - support for locking ser's pages in memory, pre-mapping
                all the shared memory on startup (fill it with 0)
                all the shared memory on startup (fill it with 0)
              - real time options
              - real time options
@@ -79,6 +81,17 @@ core:
                long held locks, almost no performance impact otherwise)
                long held locks, almost no performance impact otherwise)
 
 
 new config variables:
 new config variables:
+  dns_srv_lb = yes | no (default no) - enable dns srv weight based load 
+    balancing (see doc/dns.txt)
+  dns_try_naptr = yes | no (default no) - enable naptr support 
+    (see doc/dns.txt for more info)
+  dns_{udp,tcp,tls}_pref = number - ser preference for each protocol when
+    doing naptr lookups. By default dns_udp_pref=3, dns_tcp_pref=2 and 
+    dns_tls_pref=1. To use the remote site preferences set all dns_*_pref to 
+    the same positive value (e.g. dns_udp_pref=1, dns_tcp_pref=1, 
+    dns_udp_pref=1). To completely ignore NAPTR records for a specific 
+    protocol, set the corresponding protocol preference to -1 (or any other 
+    negative number).  (see doc/dns.txt for more info)
   mlock_pages = yes |no (default no) - locks all ser pages into memory making 
   mlock_pages = yes |no (default no) - locks all ser pages into memory making 
     it unswappable (in general one doesn't want his sip proxy swapped out :-))
     it unswappable (in general one doesn't want his sip proxy swapped out :-))
   shm_force_alloc = yes | no (default no) - tries to pre-fault all the 
   shm_force_alloc = yes | no (default no) - tries to pre-fault all the 

+ 2 - 1
action.c

@@ -176,7 +176,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 											 from the uri */
 											 from the uri */
 					switch(u->proto){
 					switch(u->proto){
 						case PROTO_NONE:
 						case PROTO_NONE:
-							dst.proto=PROTO_UDP;
+							/*dst.proto=PROTO_UDP; */
+							/* no proto, try to get it from the dns */
 							break;
 							break;
 						case PROTO_UDP:
 						case PROTO_UDP:
 #ifdef USE_TCP
 #ifdef USE_TCP

+ 17 - 0
cfg.lex

@@ -67,6 +67,8 @@
  *  2007-06-07  added SHM_FORCE_ALLOC, MLOCK_PAGES, REAL_TIME, RT_PRIO,
  *  2007-06-07  added SHM_FORCE_ALLOC, MLOCK_PAGES, REAL_TIME, RT_PRIO,
  *              RT_POLICY, RT_TIMER1_PRIO, RT_TIMER1_POLICY, RT_TIMER2_PRIO,
  *              RT_POLICY, RT_TIMER1_PRIO, RT_TIMER1_POLICY, RT_TIMER2_PRIO,
  *              RT_TIMER2_POLICY (andrei)
  *              RT_TIMER2_POLICY (andrei)
+ *  2007-06-16  added DNS_SRV_LB, DNS_TRY_NAPTR (andrei)
+ *  2007-06-18  added DNS_{UDP,TCP,TLS}_PREF (andrei)
  */
  */
 
 
 
 
@@ -231,6 +233,11 @@ ALIAS		alias
 DNS		 dns
 DNS		 dns
 REV_DNS	 rev_dns
 REV_DNS	 rev_dns
 DNS_TRY_IPV6	dns_try_ipv6
 DNS_TRY_IPV6	dns_try_ipv6
+DNS_TRY_NAPTR	dns_try_naptr
+DNS_SRV_LB		dns_srv_lb|dns_srv_loadbalancing
+DNS_UDP_PREF	dns_udp_pref|dns_udp_preference
+DNS_TCP_PREF	dns_tcp_pref|dns_tcp_preference
+DNS_TLS_PREF	dns_tls_pref|dns_tls_preference
 DNS_RETR_TIME	dns_retr_time
 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
@@ -455,6 +462,16 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{REV_DNS}	{ count(); yylval.strval=yytext; return REV_DNS; }
 <INITIAL>{REV_DNS}	{ count(); yylval.strval=yytext; return REV_DNS; }
 <INITIAL>{DNS_TRY_IPV6}	{ count(); yylval.strval=yytext;
 <INITIAL>{DNS_TRY_IPV6}	{ count(); yylval.strval=yytext;
 								return DNS_TRY_IPV6; }
 								return DNS_TRY_IPV6; }
+<INITIAL>{DNS_TRY_NAPTR}	{ count(); yylval.strval=yytext;
+								return DNS_TRY_NAPTR; }
+<INITIAL>{DNS_SRV_LB}	{ count(); yylval.strval=yytext;
+								return DNS_SRV_LB; }
+<INITIAL>{DNS_UDP_PREF}	{ count(); yylval.strval=yytext;
+								return DNS_UDP_PREF; }
+<INITIAL>{DNS_TCP_PREF}	{ count(); yylval.strval=yytext;
+								return DNS_TCP_PREF; }
+<INITIAL>{DNS_TLS_PREF}	{ count(); yylval.strval=yytext;
+								return DNS_TLS_PREF; }
 <INITIAL>{DNS_RETR_TIME}	{ count(); yylval.strval=yytext;
 <INITIAL>{DNS_RETR_TIME}	{ count(); yylval.strval=yytext;
 								return DNS_RETR_TIME; }
 								return DNS_RETR_TIME; }
 <INITIAL>{DNS_RETR_NO}	{ count(); yylval.strval=yytext;
 <INITIAL>{DNS_RETR_NO}	{ count(); yylval.strval=yytext;

+ 23 - 1
cfg.y

@@ -78,9 +78,10 @@
  *              (vlada)
  *              (vlada)
  * 2007-02-09  separated command needed for tls-in-core and for tls in general
  * 2007-02-09  separated command needed for tls-in-core and for tls in general
  *              (andrei)
  *              (andrei)
- *  2007-06-07  added SHM_FORCE_ALLOC, MLOCK_PAGES, REAL_TIME, RT_PRIO,
+ * 2007-06-07  added SHM_FORCE_ALLOC, MLOCK_PAGES, REAL_TIME, RT_PRIO,
  *              RT_POLICY, RT_TIMER1_PRIO, RT_TIMER1_POLICY, RT_TIMER2_PRIO,
  *              RT_POLICY, RT_TIMER1_PRIO, RT_TIMER1_POLICY, RT_TIMER2_PRIO,
  *              RT_TIMER2_POLICY (andrei)
  *              RT_TIMER2_POLICY (andrei)
+ * 2007-06-16  added DDNS_SRV_LB, DNS_TRY_NAPTR (andrei)
  */
  */
 
 
 %{
 %{
@@ -139,6 +140,12 @@
 	#define IF_DNS_FAILOVER(x) warn("dns failover support not compiled in")
 	#define IF_DNS_FAILOVER(x) warn("dns failover support not compiled in")
 #endif
 #endif
 
 
+#ifdef USE_NAPTR
+	#define IF_NAPTR(x) x
+#else
+	#define IF_NAPTR(x) warn("dns naptr support not compiled in")
+#endif
+
 #ifdef USE_DST_BLACKLIST
 #ifdef USE_DST_BLACKLIST
 	#define IF_DST_BLACKLIST(x) x
 	#define IF_DST_BLACKLIST(x) x
 #else
 #else
@@ -265,6 +272,11 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token DNS
 %token DNS
 %token REV_DNS
 %token REV_DNS
 %token DNS_TRY_IPV6
 %token DNS_TRY_IPV6
+%token DNS_TRY_NAPTR
+%token DNS_SRV_LB
+%token DNS_UDP_PREF
+%token DNS_TCP_PREF
+%token DNS_TLS_PREF
 %token DNS_RETR_TIME
 %token DNS_RETR_TIME
 %token DNS_RETR_NO
 %token DNS_RETR_NO
 %token DNS_SERVERS_NO
 %token DNS_SERVERS_NO
@@ -570,6 +582,16 @@ assign_stm:
 	| REV_DNS EQUAL error { yyerror("boolean value expected"); }
 	| REV_DNS EQUAL error { yyerror("boolean value expected"); }
 	| DNS_TRY_IPV6 EQUAL NUMBER   { dns_try_ipv6=$3; }
 	| DNS_TRY_IPV6 EQUAL NUMBER   { dns_try_ipv6=$3; }
 	| DNS_TRY_IPV6 error { yyerror("boolean value expected"); }
 	| DNS_TRY_IPV6 error { yyerror("boolean value expected"); }
+	| DNS_TRY_NAPTR EQUAL NUMBER   { IF_NAPTR(dns_try_naptr=$3); }
+	| DNS_TRY_NAPTR error { yyerror("boolean value expected"); }
+	| DNS_SRV_LB EQUAL NUMBER   { IF_DNS_FAILOVER(dns_srv_lb=$3); }
+	| DNS_SRV_LB error { yyerror("boolean value expected"); }
+	| DNS_UDP_PREF EQUAL NUMBER   { IF_NAPTR(dns_udp_pref=$3); }
+	| DNS_UDP_PREF error { yyerror("number expected"); }
+	| DNS_TCP_PREF EQUAL NUMBER   { IF_NAPTR(dns_tcp_pref=$3); }
+	| DNS_TCP_PREF error { yyerror("number expected"); }
+	| DNS_TLS_PREF EQUAL NUMBER   { IF_NAPTR(dns_tls_pref=$3); }
+	| DNS_TLS_PREF error { yyerror("number expected"); }
 	| DNS_RETR_TIME EQUAL NUMBER   { dns_retr_time=$3; }
 	| DNS_RETR_TIME EQUAL NUMBER   { dns_retr_time=$3; }
 	| DNS_RETR_TIME error { yyerror("number expected"); }
 	| DNS_RETR_TIME error { yyerror("number expected"); }
 	| DNS_RETR_NO EQUAL NUMBER   { dns_retr_no=$3; }
 	| DNS_RETR_NO EQUAL NUMBER   { dns_retr_no=$3; }

+ 419 - 29
dns_cache.c

@@ -32,6 +32,7 @@
  *  2006-10-06  port fix (andrei)
  *  2006-10-06  port fix (andrei)
  *  2007-06-14  dns iterate through A & AAAA records fix (andrei)
  *  2007-06-14  dns iterate through A & AAAA records fix (andrei)
  *  2007-06-15  srv rr weight based load balancing support (andrei)
  *  2007-06-15  srv rr weight based load balancing support (andrei)
+ *  2007-06-16  naptr support (andrei)
  */
  */
 
 
 #ifdef USE_DNS_CACHE
 #ifdef USE_DNS_CACHE
@@ -88,7 +89,7 @@ unsigned int dns_cache_min_ttl=DEFAULT_DNS_CACHE_MIN_TTL; /* minimum ttl */
 unsigned int dns_timer_interval=DEFAULT_DNS_TIMER_INTERVAL; /* in s */
 unsigned int dns_timer_interval=DEFAULT_DNS_TIMER_INTERVAL; /* in s */
 int dns_flags=0; /* default flags used for the  dns_*resolvehost 
 int dns_flags=0; /* default flags used for the  dns_*resolvehost 
                     (compatibility wrappers) */
                     (compatibility wrappers) */
-int dns_srv_lb=1; /* off by default */
+int dns_srv_lb=0; /* off by default */
 
 
 #define LOCK_DNS_HASH()		lock_get(dns_hash_lock)
 #define LOCK_DNS_HASH()		lock_get(dns_hash_lock)
 #define UNLOCK_DNS_HASH()	lock_release(dns_hash_lock)
 #define UNLOCK_DNS_HASH()	lock_release(dns_hash_lock)
@@ -126,6 +127,7 @@ static const char* dns_str_errors[]={
 	"blacklisted ip",
 	"blacklisted ip",
 	"name too long ", /* try again with a shorter name */
 	"name too long ", /* try again with a shorter name */
 	"ip AF mismatch", /* address family mismatch */
 	"ip AF mismatch", /* address family mismatch */
+	"unresolvable NAPTR request", 
 	"bug - critical error"
 	"bug - critical error"
 };
 };
 
 
@@ -293,6 +295,13 @@ int init_dns_cache()
 					" support for it is not compiled -- ignoring\n");
 					" support for it is not compiled -- ignoring\n");
 #endif
 #endif
 	}
 	}
+	if (dns_try_naptr){
+#ifndef USE_NAPTR
+	LOG(L_WARN, "WARING: dns_cache_init: NAPTR support is enabled, but"
+				" support for it is not compiled -- ignoring\n");
+#endif
+		dns_flags|=DNS_TRY_NAPTR;
+	}
 	dns_timer_h=timer_alloc();
 	dns_timer_h=timer_alloc();
 	if (dns_timer_h==0){
 	if (dns_timer_h==0){
 		ret=E_OUT_OF_MEM;
 		ret=E_OUT_OF_MEM;
@@ -411,8 +420,10 @@ inline static struct dns_hash_entry* _dns_hash_find(str* name, int type,
 	*err=0;
 	*err=0;
 again:
 again:
 	*h=dns_hash_no(name->s, name->len, type);
 	*h=dns_hash_no(name->s, name->len, type);
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_hash_find(%.*s(%d), %d), h=%d\n", name->len, name->s,
 	DBG("dns_hash_find(%.*s(%d), %d), h=%d\n", name->len, name->s,
 												name->len, type, *h);
 												name->len, type, *h);
+#endif
 	clist_foreach_safe(&dns_hash[*h], e, tmp, next){
 	clist_foreach_safe(&dns_hash[*h], e, tmp, next){
 		/* automatically remove expired elements */
 		/* automatically remove expired elements */
 		if ((s_ticks_t)(now-e->expire)>=0){
 		if ((s_ticks_t)(now-e->expire)>=0){
@@ -645,8 +656,10 @@ inline static int dns_cache_add(struct dns_hash_entry* e)
 	}
 	}
 	atomic_inc(&e->refcnt);
 	atomic_inc(&e->refcnt);
 	h=dns_hash_no(e->name, e->name_len, e->type);
 	h=dns_hash_no(e->name, e->name_len, e->type);
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
 	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);
 			e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
+#endif
 	LOCK_DNS_HASH();
 	LOCK_DNS_HASH();
 		*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
 		*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
 										 only from within a lock */
 										 only from within a lock */
@@ -681,8 +694,10 @@ inline static int dns_cache_add_unsafe(struct dns_hash_entry* e)
 	}
 	}
 	atomic_inc(&e->refcnt);
 	atomic_inc(&e->refcnt);
 	h=dns_hash_no(e->name, e->name_len, e->type);
 	h=dns_hash_no(e->name, e->name_len, e->type);
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
 	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);
 			e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
+#endif
 	*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
 	*dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
 										 only from within a lock */
 										 only from within a lock */
 	clist_append(&dns_hash[h], e, next, prev);
 	clist_append(&dns_hash[h], e, next, prev);
@@ -704,8 +719,10 @@ inline static struct dns_hash_entry* dns_cache_mk_bad_entry(str* name,
 	int size;
 	int size;
 	ticks_t now;
 	ticks_t now;
 	
 	
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
 	DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
 									type, ttl, flags);
 									type, ttl, flags);
+#endif
 	size=sizeof(struct dns_hash_entry)+name->len-1+1;
 	size=sizeof(struct dns_hash_entry)+name->len-1+1;
 	e=shm_malloc(size);
 	e=shm_malloc(size);
 	if (e==0){
 	if (e==0){
@@ -937,8 +954,10 @@ inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
 	}
 	}
 	*tail=0; /* mark the end of our tmp_lst */
 	*tail=0; /* mark the end of our tmp_lst */
 	if (size==0){
 	if (size==0){
+#ifdef DNS_CACHE_DEBUG
 		DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
 		DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
 				name->len, name->s, type);
 				name->len, name->s, type);
+#endif
 		return 0;
 		return 0;
 	}
 	}
 	/* compute size */
 	/* compute size */
@@ -949,6 +968,7 @@ inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
 		return 0;
 		return 0;
 	}
 	}
 	memset(e, 0, size); /* init with 0 */
 	memset(e, 0, size); /* init with 0 */
+	clist_init(e, next, prev);
 	e->total_size=size;
 	e->total_size=size;
 	e->name_len=name->len;
 	e->name_len=name->len;
 	e->type=type;
 	e->type=type;
@@ -1013,7 +1033,7 @@ inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
 				max_ttl=MAX(max_ttl, ttl);
 				max_ttl=MAX(max_ttl, ttl);
 				rr->rdata=(void*)((char*)rr+
 				rr->rdata=(void*)((char*)rr+
 								ROUND_POINTER(sizeof(struct dns_rr)));
 								ROUND_POINTER(sizeof(struct dns_rr)));
-				/* copy the whole srv_rdata block*/
+				/* copy the whole naptr_rdata block*/
 				memcpy(rr->rdata, l->rdata, 
 				memcpy(rr->rdata, l->rdata, 
 						NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
 						NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
 				/* adjust the string pointer */
 				/* adjust the string pointer */
@@ -1034,6 +1054,7 @@ inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
 										NAPTR_RDATA_SIZE(
 										NAPTR_RDATA_SIZE(
 											*(struct naptr_rdata*)l->rdata)));
 											*(struct naptr_rdata*)l->rdata)));
 				tail_rr=&(rr->next);
 				tail_rr=&(rr->next);
+				rr=rr->next;
 			}
 			}
 			break;
 			break;
 		case T_CNAME:
 		case T_CNAME:
@@ -1267,6 +1288,7 @@ found:
 										NAPTR_RDATA_SIZE(
 										NAPTR_RDATA_SIZE(
 											*(struct naptr_rdata*)l->rdata)));
 											*(struct naptr_rdata*)l->rdata)));
 				rec[r].tail_rr=&(rec[r].rr->next);
 				rec[r].tail_rr=&(rec[r].rr->next);
+				rec[r].rr=rec[r].rr->next;
 				break;
 				break;
 			case T_CNAME:
 			case T_CNAME:
 				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
 				rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
@@ -1332,8 +1354,10 @@ inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
 	
 	
 	ret=0;
 	ret=0;
 	l=e;
 	l=e;
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_get_related(%p (%.*s, %d), %d, *%p) (%d)\n", 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);
 			e->name_len, e->name, e->type, type, *records, cname_chain_len);
+#endif
 	clist_init(l, next, prev);
 	clist_init(l, next, prev);
 	if (type==e->type){
 	if (type==e->type){
 		ret=e;
 		ret=e;
@@ -1347,7 +1371,8 @@ inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
 						if (t){
 						if (t){
 							if ((t->type==T_CNAME) && *records)
 							if ((t->type==T_CNAME) && *records)
 								dns_get_related(t, T_A, records);
 								dns_get_related(t, T_A, records);
-							clist_append(l, t, next, prev);
+							lst_end=t->prev; /* needed for clist_append*/
+							clist_append_sublist(l, t, lst_end, next, prev);
 						}
 						}
 					}
 					}
 					if (!(dns_flags&DNS_IPV4_ONLY)){
 					if (!(dns_flags&DNS_IPV4_ONLY)){
@@ -1355,11 +1380,34 @@ inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
 						if (t){
 						if (t){
 							if ((t->type==T_CNAME) && *records)
 							if ((t->type==T_CNAME) && *records)
 								dns_get_related(t, T_AAAA, records);
 								dns_get_related(t, T_AAAA, records);
-							clist_append(l, t, next, prev);
+							lst_end=t->prev; /* needed for clist_append*/
+							clist_append_sublist(l, t, lst_end, next, prev);
 						}
 						}
 					}
 					}
 				}
 				}
 				break;
 				break;
+#ifdef USE_NAPTR
+			case T_NAPTR:
+#ifdef NAPTR_CACHE_ALL_ARS
+				if (*records)
+						dns_cache_mk_rd_entry2(*records);
+#else
+				for (rr=e->rr_lst; rr && *records; rr=rr->next){
+					if (naptr_get_sip_proto((struct naptr_rdata*)rr->rdata)>0){
+						tmp.s=((struct naptr_rdata*)rr->rdata)->repl;
+						tmp.len=((struct naptr_rdata*)rr->rdata)->repl_len;
+						t=dns_cache_mk_rd_entry(&tmp, T_SRV, records);
+						if (t){
+							if (*records)
+								dns_get_related(t, T_SRV, records);
+							lst_end=t->prev; /* needed for clist_append*/
+							clist_append_sublist(l, t, lst_end, next, prev);
+						}
+					}
+				}
+#endif /* NAPTR_CACHE_ALL_ARS */
+#endif /* USE_NAPTR */
+				break;
 			default:
 			default:
 				/* nothing extra */
 				/* nothing extra */
 				break;
 				break;
@@ -1593,7 +1641,7 @@ error:
  *               now - current time/ticks value
  *               now - current time/ticks value
  * returns pointer to the rr on success and sets no to the rr number
  * returns pointer to the rr on success and sets no to the rr number
  *         0 on error and fills the error flags
  *         0 on error and fills the error flags
- *
+	*
  * Example usage:
  * Example usage:
  * list all non-expired non-bad-marked ips for name:
  * list all non-expired non-bad-marked ips for name:
  * e=dns_get_entry(name, T_A);
  * e=dns_get_entry(name, T_A);
@@ -1980,6 +2028,36 @@ struct hostent* dns_resolvehost(char* name)
 
 
 
 
 
 
+
+#if 0
+/* resolves a host name trying  NAPTR,  SRV, A & AAAA lookups, for details
+ *  see dns_sip_resolve()
+ *  FIXME: this version will return only the first ip
+ * 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, 
+										char* proto)
+{
+	struct dns_srv_handle h;
+	struct ip_addr ip;
+	int ret;
+	
+	if ((use_dns_cache==0) || (dns_hash==0)){ 
+		/* not init or off => use normal, non-cached version */
+		return _sip_resolvehost(name, port, proto);
+	}
+	dns_srv_handle_init(&h);
+	ret=dns_sip_resolve(&h, name, &ip, port, proto, dns_flags);
+	dns_srv_handle_put(&h);
+	if (ret>=0)
+		return ip_addr2he(name, &ip);
+	return 0;
+}
+#endif
+
+
+
 /* 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
@@ -1987,23 +2065,33 @@ struct hostent* dns_resolvehost(char* name)
  * 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* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
+struct hostent* dns_srv_sip_resolvehost(str* name, unsigned short* port, 
+										char* proto)
 {
 {
 	struct hostent* he;
 	struct hostent* he;
 	struct ip_addr* ip;
 	struct ip_addr* ip;
 	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
 	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
 	int len;
 	int len;
 	str srv_name;
 	str srv_name;
+	char srv_proto;
 
 
 	if ((use_dns_cache==0) || (dns_hash==0)){ 
 	if ((use_dns_cache==0) || (dns_hash==0)){ 
 		/* not init or off => use normal, non-cached version */
 		/* not init or off => use normal, non-cached version */
 		return _sip_resolvehost(name, port, proto);
 		return _sip_resolvehost(name, port, proto);
 	}
 	}
 	len=0;
 	len=0;
+	if (proto){ /* makes sure we have a protocol set*/
+		if (*proto==0)
+			*proto=srv_proto=PROTO_UDP; /* default */
+		else
+			srv_proto=*proto;
+	}else{
+		srv_proto=PROTO_UDP;
+	}
 	/* 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)){
-		*port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we don't
-														find another */
+		*port=(srv_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){
 		if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
 			LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
 			LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
 						" (%d), unable to perform SRV lookup\n", name->len);
 						" (%d), unable to perform SRV lookup\n", name->len);
@@ -2018,9 +2106,11 @@ struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
 				return ip_addr2he(name,ip);
 				return ip_addr2he(name,ip);
 			}
 			}
 			
 			
-			switch(proto){
+			switch(srv_proto){
 				case PROTO_NONE: /* no proto specified, use udp */
 				case PROTO_NONE: /* no proto specified, use udp */
-					goto skip_srv;
+					if (proto)
+						*proto=PROTO_UDP;
+					/* no break */
 				case PROTO_UDP:
 				case PROTO_UDP:
 					memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 					memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 					memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
 					memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
@@ -2041,7 +2131,7 @@ struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
 					break;
 					break;
 				default:
 				default:
 					LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
 					LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
-							proto);
+							(int)srv_proto);
 					return 0;
 					return 0;
 			}
 			}
 
 
@@ -2051,7 +2141,7 @@ struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
 				return he;
 				return he;
 		}
 		}
 	}
 	}
-skip_srv:
+/*skip_srv:*/
 	if (name->len >= MAX_DNS_NAME) {
 	if (name->len >= MAX_DNS_NAME) {
 		LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
 		LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
 		return 0;
 		return 0;
@@ -2062,6 +2152,170 @@ skip_srv:
 
 
 
 
 
 
+#ifdef USE_NAPTR
+/* iterates over a naptr rr list, returning each time a "good" naptr record
+ * is found.( srv type, no regex and a supported protocol)
+ * params:
+ *         naptr_head - naptr dns_rr list head
+ *         tried      - bitmap used to keep track of the already tried records
+ *                      (no more then sizeof(tried)*8 valid records are 
+ *                      ever walked
+ *         srv_name   - if succesfull, it will be set to the selected record
+ *                      srv name (naptr repl.)
+ *         proto      - if succesfull it will be set to the selected record
+ *                      protocol
+ * returns  0 if no more records found or a pointer to the selected record
+ *  and sets  protocol and srv_name
+ * WARNING: when calling first time make sure you run first 
+ *           naptr_iterate_init(&tried)
+ */
+struct naptr_rdata* dns_naptr_sip_iterate(struct dns_rr* naptr_head, 
+											naptr_bmp_t* tried,
+											str* srv_name, char* proto)
+{
+	int i, idx;
+	struct dns_rr* l;
+	struct naptr_rdata* naptr;
+	struct naptr_rdata* naptr_saved;
+	char saved_proto;
+	char naptr_proto;
+
+	idx=0;
+	naptr_proto=PROTO_NONE;
+	naptr_saved=0;
+	saved_proto=0;
+	i=0;
+	for(l=naptr_head; l && (i<MAX_NAPTR_RRS); l=l->next){
+		naptr=(struct naptr_rdata*) l->rdata;
+		if (naptr==0){
+				LOG(L_CRIT, "naptr_iterate: BUG: null rdata\n");
+			goto end;
+		}
+		/* check if valid and get proto */
+		if ((naptr_proto=naptr_get_sip_proto(naptr))<=0) continue;
+		if (*tried& (1<<i)){
+			i++;
+			continue; /* already tried */
+		}
+#ifdef DNS_CACHE_DEBUG
+		DBG("naptr_iterate: found a valid sip NAPTR rr %.*s,"
+					" proto %d\n", naptr->repl_len, naptr->repl, 
+					(int)naptr_proto);
+#endif
+		if ((naptr_proto_supported(naptr_proto))){
+			if (naptr_choose(&naptr_saved, &saved_proto,
+								naptr, naptr_proto))
+				idx=i;
+			}
+		i++;
+	}
+	if (naptr_saved){
+		/* found something */
+#ifdef DNS_CACHE_DEBUG
+		DBG("naptr_iterate: choosed NAPTR rr %.*s, proto %d"
+					" tried: 0x%x\n", naptr_saved->repl_len, 
+					naptr_saved->repl, (int)saved_proto, *tried);
+#endif
+		*tried|=1<<idx;
+		*proto=saved_proto;
+		srv_name->s=naptr_saved->repl;
+		srv_name->len=naptr_saved->repl_len;
+		return naptr_saved;
+	}
+end:
+	return 0;
+}
+
+
+
+/* resolves a host name trying NAPTR lookup if *proto==0 and *port==0, 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; if proto==0 => no SRV lookup
+ * returns: hostent struct & *port filled with the port from the SRV record;
+ *  0 on error
+ */
+struct hostent* dns_naptr_sip_resolvehost(str* name, unsigned short* port, 
+										char* proto)
+{
+	struct hostent* he;
+	struct ip_addr* tmp_ip;
+	naptr_bmp_t tried_bmp;
+	struct dns_hash_entry* e;
+	char n_proto;
+	str srv_name;
+	
+	he=0;
+	if (dns_hash==0){ /* not init => use normal, non-cached version */
+		LOG(L_WARN, "WARNING: dns_sip_resolvehost: called before dns cache"
+					" initialization\n");
+		return _sip_resolvehost(name, port, proto);
+	}
+	if (proto && port && (*proto==0) && (*port==0)){
+		*proto=PROTO_UDP; /* just in case we don't find another */
+		/* 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 (((dns_flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6))||
+				((dns_flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
+				return 0;
+			}
+#endif
+			*port=SIP_PORT;
+			return ip_addr2he(name, tmp_ip);
+		}
+		/* do naptr lookup */
+		if ((e=dns_get_entry(name, T_NAPTR))==0)
+			goto naptr_not_found;
+		naptr_iterate_init(&tried_bmp);
+		while(dns_naptr_sip_iterate(e->rr_lst, &tried_bmp,
+												&srv_name, &n_proto)){
+			if ((he=dns_srv_get_he(&srv_name, port, dns_flags))!=0){
+#ifdef DNS_CACHE_DEBUG
+				DBG("dns_naptr_sip_resolvehost(%.*s, %d, %d) srv, ret=%p\n", 
+							name->len, name->s, (int)*port, (int)*proto, he);
+#endif
+				dns_hash_put(e);
+				*proto=n_proto;
+				return he;
+			}
+		}
+		/* no acceptable naptr record found, fallback to srv */
+		dns_hash_put(e);
+	}
+naptr_not_found:
+	return dns_srv_sip_resolvehost(name, port, proto);
+}
+#endif /* USE_NAPTR */
+
+
+
+/* resolves a host name trying NAPTR lookup if *proto==0 and *port==0, 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; 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, 
+										char* proto)
+{
+#ifdef USE_NAPTR
+	if (dns_flags&DNS_TRY_NAPTR)
+		return dns_naptr_sip_resolvehost(name, port, proto);
+#endif
+	return dns_srv_sip_resolvehost(name, port, proto);
+}
+
+
+
 /* performs an a lookup, fills the dns_entry pointer and the ip addr.
 /* 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
  *  (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 
  *   to the result, if not it uses the current value and tries to use 
@@ -2359,8 +2613,8 @@ error:
  *            0 on success and it fills *ip, *port, dns_sip_resolve_h
  *            0 on success and it fills *ip, *port, dns_sip_resolve_h
  * WARNING: when finished, dns_sip_resolve_put(h) must be called!
  * WARNING: when finished, dns_sip_resolve_put(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 dns_srv_sip_resolve(struct dns_srv_handle* h,  str* name,
+						struct ip_addr* ip, unsigned short* port, char* proto,
 						int flags)
 						int flags)
 {
 {
 	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
 	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
@@ -2369,6 +2623,7 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 	struct ip_addr* tmp_ip;
 	struct ip_addr* tmp_ip;
 	int ret;
 	int ret;
 	struct hostent* he;
 	struct hostent* he;
+	char srv_proto;
 
 
 	if (dns_hash==0){ /* not init => use normal, non-cached version */
 	if (dns_hash==0){ /* not init => use normal, non-cached version */
 		LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
 		LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
@@ -2383,8 +2638,17 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 	}
 	}
 	len=0;
 	len=0;
 	if ((h->srv==0) && (h->a==0)){ /* first call */
 	if ((h->srv==0) && (h->a==0)){ /* first call */
-		h->port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
+		if (proto){ /* makes sure we have a protocol set*/
+			if (*proto==0)
+				*proto=srv_proto=PROTO_UDP; /* default */
+			else
+				srv_proto=*proto;
+		}else{
+			srv_proto=PROTO_UDP;
+		}
+		h->port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
 														don't find another */
 														don't find another */
+		h->proto=srv_proto; /* store initial protocol */
 		if (port){
 		if (port){
 			if (*port==0){
 			if (*port==0){
 				/* try SRV if initial call & no port specified
 				/* try SRV if initial call & no port specified
@@ -2409,12 +2673,15 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 #endif
 #endif
 						*ip=*tmp_ip;
 						*ip=*tmp_ip;
 						*port=h->port;
 						*port=h->port;
+						/* proto already set */
 						return 0;
 						return 0;
 					}
 					}
 					
 					
-					switch(proto){
+					switch(srv_proto){
 						case PROTO_NONE: /* no proto specified, use udp */
 						case PROTO_NONE: /* no proto specified, use udp */
-							goto skip_srv;
+							if (proto)
+								*proto=PROTO_UDP;
+							/* no break */
 						case PROTO_UDP:
 						case PROTO_UDP:
 							memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 							memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 							memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
 							memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
@@ -2435,12 +2702,11 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 							break;
 							break;
 						default:
 						default:
 							LOG(L_CRIT, "BUG: sip_resolvehost: "
 							LOG(L_CRIT, "BUG: sip_resolvehost: "
-									"unknown proto %d\n", proto);
+									"unknown proto %d\n", (int)srv_proto);
 							return -E_DNS_CRITICAL;
 							return -E_DNS_CRITICAL;
 					}
 					}
 					srv_name.s=tmp;
 					srv_name.s=tmp;
 					srv_name.len=len;
 					srv_name.len=len;
-					
 					if ((ret=dns_srv_resolve_ip(h, &srv_name, ip,
 					if ((ret=dns_srv_resolve_ip(h, &srv_name, ip,
 															port, flags))>=0)
 															port, flags))>=0)
 					{
 					{
@@ -2448,11 +2714,13 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 						DBG("dns_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n", 
 						DBG("dns_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n", 
 							name->len, name->s, h->srv_no, h->ip_no, ret);
 							name->len, name->s, h->srv_no, h->ip_no, ret);
 #endif
 #endif
+						/* proto already set */
 						return ret;
 						return ret;
 					}
 					}
 				}
 				}
 			}else{ /* if (*port==0) */
 			}else{ /* if (*port==0) */
 				h->port=*port; /* store initial port */
 				h->port=*port; /* store initial port */
+				/* proto already set */
 			}
 			}
 		} /* if (port) */
 		} /* if (port) */
 	}else if (h->srv){
 	}else if (h->srv){
@@ -2460,11 +2728,13 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 			srv_name.len=h->srv->name_len;
 			srv_name.len=h->srv->name_len;
 			/* continue srv resolving */
 			/* continue srv resolving */
 			ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags);
 			ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags);
+			if (proto)
+				*proto=h->proto;
 			DBG("dns_sip_resolve(%.*s, %d, %d), srv, ret=%d\n", 
 			DBG("dns_sip_resolve(%.*s, %d, %d), srv, ret=%d\n", 
 					name->len, name->s, h->srv_no, h->ip_no, ret);
 					name->len, name->s, h->srv_no, h->ip_no, ret);
 			return ret;
 			return ret;
 	}
 	}
-skip_srv:
+/*skip_srv:*/
 	if (name->len >= MAX_DNS_NAME) {
 	if (name->len >= MAX_DNS_NAME) {
 		LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
 		LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
 		return -E_DNS_NAME_TOO_LONG;
 		return -E_DNS_NAME_TOO_LONG;
@@ -2472,13 +2742,133 @@ skip_srv:
 	ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
 	ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
 	if (port)
 	if (port)
 		*port=h->port;
 		*port=h->port;
+	if (proto)
+		*proto=h->proto;
+#ifdef DNS_CACHE_DEBUG
 	DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n", 
 	DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n", 
 			name->len, name->s, h->srv_no, h->ip_no, ret);
 			name->len, name->s, h->srv_no, h->ip_no, ret);
+#endif
 	return ret;
 	return ret;
 }
 }
 
 
 
 
 
 
+#ifdef USE_NAPTR
+/* resolves a host name trying:
+ * - NAPTR lookup if the address is not an ip and proto!=0, port!=0
+ *    *port==0 and *proto=0 and if flags allow NAPTR lookups
+ * -SRV lookup if  port!=0 and *port==0
+ * - normal A/AAAA lookup if *port!=0, or 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
+ * 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(h) must be called!
+ */
+int dns_naptr_sip_resolve(struct dns_srv_handle* h,  str* name,
+						struct ip_addr* ip, unsigned short* port, char* proto,
+						int flags)
+{
+	struct hostent* he;
+	struct ip_addr* tmp_ip;
+	naptr_bmp_t tried_bmp;
+	struct dns_hash_entry* e;
+	char n_proto;
+	str srv_name;
+	int ret;
+	
+	ret=-E_DNS_NO_NAPTR;
+	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_NAPTR;
+	}
+	if (((h->srv==0) && (h->a==0)) && /* first call */
+			 proto && port && (*proto==0) && (*port==0)){
+		*proto=PROTO_UDP; /* just in case we don't find another */
+		
+		/* 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;
+			h->port=SIP_PORT;
+			h->proto=*proto;
+			*port=h->port;
+			return 0;
+		}
+		/* do naptr lookup */
+		if ((e=dns_get_entry(name, T_NAPTR))==0)
+			goto naptr_not_found;
+		naptr_iterate_init(&tried_bmp);
+		while(dns_naptr_sip_iterate(e->rr_lst, &tried_bmp,
+												&srv_name, &n_proto)){
+			dns_srv_handle_init(h); /* make sure h does not contain garbage
+									from previous dns_srv_sip_resolve calls */
+			if ((ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags))>=0){
+#ifdef DNS_CACHE_DEBUG
+				DBG("dns_naptr_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n", 
+								name->len, name->s, h->srv_no, h->ip_no, ret);
+#endif
+				dns_hash_put(e);
+				*proto=n_proto;
+				h->proto=*proto;
+				return ret;
+			}
+		}
+		/* no acceptable naptr record found, fallback to srv */
+		dns_hash_put(e);
+		dns_srv_handle_init(h); /* make sure h does not contain garbage
+								from previous dns_srv_sip_resolve calls */
+	}
+naptr_not_found:
+	return dns_srv_sip_resolve(h, name, ip, port, proto, flags);
+}
+#endif /* USE_NAPTR */
+
+
+
+/* resolves a host name trying:
+ * - NAPTR lookup if the address is not an ip and proto!=0, port!=0
+ *    *port==0 and *proto=0 and if flags allow NAPTR lookups
+ * -SRV lookup if  port!=0 and *port==0
+ * - normal A/AAAA lookup if *port!=0, or 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
+ * 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(h) must be called!
+ */
+int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
+						struct ip_addr* ip, unsigned short* port, char* proto,
+						int flags)
+{
+#ifdef USE_NAPTR
+	if (flags&DNS_TRY_NAPTR)
+		return dns_naptr_sip_resolve(h, name, ip, port, proto, flags);
+#endif
+	return dns_srv_sip_resolve(h, name, ip, port, proto, flags);
+}
+
 /* performs an a lookup and fills ip with the first good ip address
 /* performs an a lookup and fills ip with the first good ip address
  * returns 0 on success, <0 on error (see the error codes)
  * returns 0 on success, <0 on error (see the error codes)
  */
  */
@@ -2596,12 +2986,12 @@ void dns_cache_debug_all(rpc_t* rpc, void* ctx)
 			clist_foreach(&dns_hash[h], e, next){
 			clist_foreach(&dns_hash[h], e, next){
 				for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
 				for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
 					rpc->add(ctx, "sddddddd", 
 					rpc->add(ctx, "sddddddd", 
-								e->name, e->type, i, e->total_size,
-								e->refcnt.val,
-								(s_ticks_t)(e->expire-now)<0?-1:
+								e->name, (int)e->type, i, (int)e->total_size,
+								(int)e->refcnt.val,
+								(int)(s_ticks_t)(e->expire-now)<0?-1:
 									TICKS_TO_S(e->expire-now),
 									TICKS_TO_S(e->expire-now),
-								TICKS_TO_S(now-e->last_used),
-								e->err_flags);
+								(int)TICKS_TO_S(now-e->last_used),
+								(int)e->err_flags);
 					switch(e->type){
 					switch(e->type){
 						case T_A:
 						case T_A:
 						case T_AAAA:
 						case T_AAAA:
@@ -2616,8 +3006,8 @@ void dns_cache_debug_all(rpc_t* rpc, void* ctx)
 									((struct srv_rdata*)(rr->rdata))->name);
 									((struct srv_rdata*)(rr->rdata))->name);
 							break;
 							break;
 						case T_NAPTR:
 						case T_NAPTR:
-							rpc->add(ctx, "ss", "naptr", 
-									((struct naptr_rdata*)(rr->rdata))->flags);
+							rpc->add(ctx, "ss", "naptr ",
+								((struct naptr_rdata*)(rr->rdata))->flags);
 							break;
 							break;
 						case T_CNAME:
 						case T_CNAME:
 							rpc->add(ctx, "ss", "cname", 
 							rpc->add(ctx, "ss", "cname", 
@@ -2627,9 +3017,9 @@ void dns_cache_debug_all(rpc_t* rpc, void* ctx)
 							rpc->add(ctx, "ss", "unknown", "?");
 							rpc->add(ctx, "ss", "unknown", "?");
 					}
 					}
 					rpc->add(ctx, "dd",
 					rpc->add(ctx, "dd",
-								(s_ticks_t)(rr->expire-now)<0?-1:
+								(int)(s_ticks_t)(rr->expire-now)<0?-1:
 									TICKS_TO_S(rr->expire-now),
 									TICKS_TO_S(rr->expire-now),
-							rr->err_flags);
+								(int)rr->err_flags);
 				}
 				}
 			}
 			}
 		}
 		}

+ 18 - 6
dns_cache.h

@@ -29,6 +29,7 @@
 /* History:
 /* History:
  * --------
  * --------
  *  2006-07-13  created by andrei
  *  2006-07-13  created by andrei
+ *  2007-06-16  naptr support (andrei)
  */
  */
 
 
 
 
@@ -36,6 +37,7 @@
 #define __dns_cache_h
 #define __dns_cache_h
 
 
 #include "str.h"
 #include "str.h"
+#include "config.h" /* MAX_BRANCHES */
 #include "timer.h"
 #include "timer.h"
 #include "ip_addr.h"
 #include "ip_addr.h"
 #include "atomic_ops.h"
 #include "atomic_ops.h"
@@ -73,13 +75,12 @@ enum dns_errors{
 					E_DNS_AF_MISMATCH /* ipv4 or ipv6 only requested, but 
 					E_DNS_AF_MISMATCH /* ipv4 or ipv6 only requested, but 
 										 name contains an ip addr. of the
 										 name contains an ip addr. of the
 										 opossite type */ ,
 										 opossite type */ ,
+					E_DNS_NO_NAPTR /* unresolvable naptr record */,
 					E_DNS_CRITICAL /* critical error, marks the end
 					E_DNS_CRITICAL /* critical error, marks the end
 									  of the error table (always last) */
 									  of the error table (always last) */
 };
 };
 
 
 
 
-extern int dns_flags; /* default flags used for dns lookup */
-extern int dns_srv_lb; /* default SRV LB support value */
 
 
 /* return a short string, printable error description (err <=0) */
 /* return a short string, printable error description (err <=0) */
 const char* dns_strerror(int err);
 const char* dns_strerror(int err);
@@ -92,7 +93,8 @@ const char* dns_strerror(int err);
 #define DNS_IPV4_ONLY	1
 #define DNS_IPV4_ONLY	1
 #define DNS_IPV6_ONLY	2
 #define DNS_IPV6_ONLY	2
 #define DNS_IPV6_FIRST	4
 #define DNS_IPV6_FIRST	4
-#define DNS_SRV_RR_LB		8  /* SRV RR weight based load balancing */
+#define DNS_SRV_RR_LB	8  /* SRV RR weight based load balancing */
+#define DNS_TRY_NAPTR	16 /* enable naptr lookup */
 
 
 
 
 /* ip blacklist error flags */
 /* ip blacklist error flags */
@@ -141,7 +143,14 @@ struct dns_hash_entry{
 };
 };
 
 
 
 
+#if MAX_BRANCHES < 16
+/* forking is limited by tm to 12 by default */
+typedef unsigned short srv_flags_t;
+#elif MAX_BRANCHES < 32
 typedef unsigned int srv_flags_t;
 typedef unsigned int srv_flags_t;
+#else
+typedef unsigned long long srv_flags_t;
+#endif
 
 
 struct dns_srv_handle{
 struct dns_srv_handle{
 	struct dns_hash_entry* srv; /* srv entry */
 	struct dns_hash_entry* srv; /* srv entry */
@@ -151,7 +160,8 @@ struct dns_srv_handle{
 #endif
 #endif
 	unsigned short port; /* current port */
 	unsigned short port; /* current port */
 	unsigned char srv_no; /* current record no. in the srv entry */
 	unsigned char srv_no; /* current record no. in the srv entry */
-	unsigned char ip_no;   /* current record no. in the a/aaaa entry */
+	unsigned char ip_no;  /* current record no. in the a/aaaa entry */
+	unsigned char proto;  /* protocol number */
 };
 };
 
 
 
 
@@ -241,6 +251,8 @@ inline static void dns_srv_handle_init(struct dns_srv_handle* h)
 {
 {
 	h->srv=h->a=0;
 	h->srv=h->a=0;
 	h->srv_no=h->ip_no=0;
 	h->srv_no=h->ip_no=0;
+	h->port=0;
+	h->proto=0;
 #ifdef DNS_SRV_LB
 #ifdef DNS_SRV_LB
 	h->srv_tried_rrs=0;
 	h->srv_tried_rrs=0;
 #endif
 #endif
@@ -280,13 +292,13 @@ struct hostent* dns_get_he(str* name, int flags);
  *  dns_srv_handle_put(h) must be called when h is no longer needed
  *  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,
 int dns_sip_resolve(struct dns_srv_handle* h,  str* name, struct ip_addr* ip,
-					unsigned short* port, int proto, int flags);
+					unsigned short* port, char* proto, int flags);
 
 
 /* same as above, but fills su intead of changing port and filling an ip */ 
 /* 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,
 inline static int dns_sip_resolve2su(struct dns_srv_handle* h,
 									 union sockaddr_union* su,
 									 union sockaddr_union* su,
 									 str* name, unsigned short port,
 									 str* name, unsigned short port,
-									 int proto, int flags)
+									 char* proto, int flags)
 {
 {
 	struct ip_addr ip;
 	struct ip_addr ip;
 	int ret;
 	int ret;

+ 1 - 1
dns_wrappers.h

@@ -36,6 +36,6 @@
 
 
 struct hostent* dns_resolvehost(char* name);
 struct hostent* dns_resolvehost(char* name);
 struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
 struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
-										int proto);
+										char* proto);
 
 
 #endif
 #endif

+ 120 - 1
doc/dns.txt

@@ -3,6 +3,7 @@
 # History:
 # History:
 # --------
 # --------
 # 2006-09-08  created by andrei
 # 2006-09-08  created by andrei
+# 2007-06-18  added naptr & friends, dns_srv_lb, more compile options (andrei)
 #
 #
 
 
 Overview
 Overview
@@ -18,6 +19,8 @@ Overview
   destination host doesn't send any reply to a forwarded invite within 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
   sip timeout interval (whose value can be configured using the tm fr_timer
    parameter).
    parameter).
+ When SRV based load balancing is enabled ser can even do DNS based load 
+ balancing (see RFC2782 and the dns_srv_lb option below).
 
 
 
 
 DNS Cache and Failover Drawbacks
 DNS Cache and Failover Drawbacks
@@ -42,6 +45,19 @@ DNS Cache and Failover Drawbacks
      Workaround: compile without dns failover support (-DUSE_DNS_FAILOVER).
      Workaround: compile without dns failover support (-DUSE_DNS_FAILOVER).
   Turning it off from the config file is not enough in this case (the extra
   Turning it off from the config file is not enough in this case (the extra
    memory will still be used).
    memory will still be used).
+ 
+ On the other hand using the dns cache saves lots of DNS queries and makes
+ DNS based failover and DNS based load balancing possible. If the destination
+ blacklist is enabled, ser can do failover even if forwarding in stateless 
+ mode.
+ In the ideal case with dns cache enabled ser will do only one query for
+ a NAPTR (if enabled) or SRV lookup and then it will use the results for the
+ record's TTL (for example if all the resulting records have 1 minute TTL,
+  ser won't make another query for this domain for 1 minute). Even negative
+ answers will be cached.
+ Without the dns cache, each NAPTR or SRV lookup will result in at least 2 
+ queries. These queries will happen every time, for each message (even if 
+ all of them go to the same domain).
 
 
 
 
 DNS Resolver Options
 DNS Resolver Options
@@ -59,6 +75,41 @@ DNS Resolver Options
       If off only ipv4 (A) lookups will be used.
       If off only ipv4 (A) lookups will be used.
       Default: on if ser is compiled with ipv6 support.
       Default: on if ser is compiled with ipv6 support.
 
 
+   dns_try_naptr = on | off - if on ser will first try a NAPTR lookup for
+      destinations that don't have the protocol or port specified and 
+      are not simple ip addresses (as described in RFC 3263). This will 
+      introduce a slight performance penalty and will probably cause extra
+      DNS lookups. For example a lookup for a non-existing domain will
+      produce one extra query: NAPTR(domain), SRV(_sip._udp.domain) 
+      and A/AAAA(domain).
+      If the result of a query contains several NAPTR records, ser will select
+      among them according to the RFC2915 and ser preference towards a
+      specific protocol (see dns_udp_pref, dns_tcp_pref and dns_tls_pref 
+      below). For an RFC3263 compliant configuration (choose the remote side
+      preferred protocol if supported), set dns_udp_pref, dns_tcp_pref and
+      dns_tls_pref to the same value (>=0), e.g. 0.
+      Default: off
+
+   dns_udp_pref = number - udp protocol preference when doing NAPTR lookups.
+      This option works together with dns_tcp_pref and dns_tls_pref. If all
+      this options have the same positive value and more NAPTR records are
+      available, ser will select the NAPTR record preferred by the remote side
+      (according to RFC2915). If the values are positive but different, ser
+      will select the NAPTR record whose protocol it prefers the most
+      (the protocol with the highest dns_<proto>_pref number). If there are 
+       several NAPTR records with the same preferred protocol, ser will select        among them based on their order and preference (see RFC2915).
+      To completely disable selecting a specific protocol, use  a negative
+       number. For example dns_tcp_pref=-1 will completely disable selection
+       of tcp NAPTR records, even if this will result in the NAPTR lookup
+       failure.
+       Default: dns_udp_pref=3, dns_tcp_pref=2 and dns_tls_pref=1
+       (prefer udp, but if no udp NAPTR record found or no SRV-resolvable 
+        udp NAPTR record found use tcp records and if this fails too use tls)
+
+   dns_tcp_pref = number  (see dns_udp_pref above)
+
+   dns_tls_pref = number (see dns_udp_pref above)
+
    dns_retr_time = time - time in s before retrying a dns request.
    dns_retr_time = time - time in s before retrying a dns request.
       Default: system specific, depends also on the/etc/resolv.conf content
       Default: system specific, depends also on the/etc/resolv.conf content
       (usually 5 s).
       (usually 5 s).
@@ -98,6 +149,19 @@ DNS Resolver Options
  dns server (to avoid unnecessary extra lookups).
  dns server (to avoid unnecessary extra lookups).
 
 
 
 
+DNS Resolver Compile Options
+
+   USE_NAPTR - if defined the naptr lookup support will be compiled in.
+      NAPTR support still has to be enabled from ser's config file (it's
+      off by default).
+
+   RESOLVE_DBG - if defined, the resolver will be very verbose: it will log
+      a lot of debugging information at L_DBG level.
+
+   NAPTR_DBG - if defined the NAPTR related resolver functions will be very
+       verbose.
+
+
 DNS Cache and Failover Config Variables
 DNS Cache and Failover Config Variables
 
 
    use_dns_cache = on | off - if off the dns cache won't be used (all dns
    use_dns_cache = on | off - if off the dns cache won't be used (all dns
@@ -118,6 +182,32 @@ DNS Cache and Failover Config Variables
    Depends on use_dns_cache being on. If tm is used along with dns failover is
    Depends on use_dns_cache being on. If tm is used along with dns failover is
    recommended to also turn on dst_blacklist.
    recommended to also turn on dst_blacklist.
 
 
+   dns_srv_lb = on | off or
+   dns_srv_loadbalancing = on | off - if on instead of doing simple dns 
+        failover (like above), ser will load balance requests to different srv
+        records of the same priority based on the srv records weights (like 
+        described in RFC2782). For a destination which has different priorities
+        for all its srv records, this option will be equivalent with simple
+        dns failover.
+        Note: this option requires having dns failover enabled (see 
+        use_dns_failover above).
+        Default: off.
+
+   dns_try_ipv6 = on | off - shared with the resolver (see resolver 
+        description).
+
+   dns_try_naptr = on | off - shared with the resolver (see resolver 
+        description).
+
+   dns_udp_pref =  number - shared with the resolver (see resolver 
+        description).
+
+   dns_tcp_pref =  number - shared with the resolver (see resolver 
+        description).
+
+   dns_tls_pref =  number - shared with the resolver (see resolver 
+        description).
+
    dns_cache_flags = dns cache specific resolver flags, used for overriding
    dns_cache_flags = dns cache specific resolver flags, used for overriding
      the default behaviour (low level).
      the default behaviour (low level).
       Possible values:
       Possible values:
@@ -165,9 +255,38 @@ DNS Cache Compile Options
    USE_DNS_FAILOVER - if defined the dns failover support will be compiled in.
    USE_DNS_FAILOVER - if defined the dns failover support will be compiled in.
       (default). Compiling the dns failover support has a few disadvantages,
       (default). Compiling the dns failover support has a few disadvantages,
       see the "Drawbacks" section.
       see the "Drawbacks" section.
+
+   DNS_SRV_LB  - if defined (default) support for load balancing using 
+       srv records weights (as described in RFC2782) will be compiled in.
+       Note however that it still must be enabled from the ser config, it's
+       disabled by default (see the dns_srv_lb config option).
+
+   USE_NAPTR  - (shared with the resolver)  if defined NAPTR support will
+       be compiled in (default). Note that even if compiled, NAPTR support
+       must be enabled also from the ser config (see the dns_try_naptr option).
+
+   NAPTR_CACHE_ALL_ARS - if defined all the additional records in a NAPTR
+       answer will be cached. Normally ser would cache only "related" records
+       (records that are directly referred), but for answers with lots of 
+        A/AAAA records it might happen that not all of the SRV records will fit
+       in the AR section. In this case, without this compile option ser will 
+       not cache the un-referred A/AAAA records. BY default this option is
+       disabled.
+
+   CACHE_RELEVANT_RECS_ONLY - if defined (default), records in the AR section
+       of an answer will be cached only if they are "related" to the query.
+       For example if the query is for a SRV record, A & AAAA records in the
+       AR section will be cached only if there are SRV records pointing to 
+       them. This avoids adding possible garbage to the cache.
+       If this option is not defined (experimental), everything in the AR
+       section will be added to the cache.
+
+   DNS_CACHE_DEBUG - if defined the dns cache will be very verbose (it will
+       log lots of messages at the L_DBG levell).
  
  
  Note: To remove a compile options,  edit ser's Makefile.defs and remove it 
  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,
    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
      e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER
    or for a permanent solution, edit Makefile.defs and add it to DEFS 
    or for a permanent solution, edit Makefile.defs and add it to DEFS 
-   (don't foget to prefix it with -D).
+   (don't foget to prefix it with -D). Some options require editing 
+   dns_cache.c or resolve.[ch] (just grep after them).

+ 6 - 4
forward.c

@@ -315,7 +315,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 		if (use_dns_failover){
 		if (use_dns_failover){
 			dns_srv_handle_init(&dns_srv_h);
 			dns_srv_handle_init(&dns_srv_h);
 			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
 			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-									send_info->proto, dns_flags);
+									&send_info->proto, dns_flags);
 			if (err!=0){
 			if (err!=0){
 				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
 				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
 						" failed: %s [%d]\n", dst->len, ZSW(dst->s),
 						" failed: %s [%d]\n", dst->len, ZSW(dst->s),
@@ -325,7 +325,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			}
 			}
 		}else
 		}else
 #endif
 #endif
-		if (sip_hostport2su(&send_info->to, dst, port, send_info->proto)<0){
+		if (sip_hostport2su(&send_info->to, dst, port, &send_info->proto)<0){
 			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
 			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
 						" dropping packet\n", dst->len, ZSW(dst->s));
 						" dropping packet\n", dst->len, ZSW(dst->s));
 			ret=E_BAD_ADDRESS;
 			ret=E_BAD_ADDRESS;
@@ -449,7 +449,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 #ifdef USE_DNS_FAILOVER
 #ifdef USE_DNS_FAILOVER
 	}while(dst && use_dns_failover && dns_srv_handle_next(&dns_srv_h, err) && 
 	}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,
 			((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-								  send_info->proto, dns_flags))==0));
+								  &send_info->proto, dns_flags))==0));
 	if ((err!=0) && (err!=-E_DNS_EOR)){
 	if ((err!=0) && (err!=-E_DNS_EOR)){
 		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
 		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
 							" failed: %s [%d] (dropping packet)\n",
 							" failed: %s [%d] (dropping packet)\n",
@@ -483,6 +483,7 @@ int update_sock_struct_from_via( union sockaddr_union* to,
 	str* name;
 	str* name;
 	int err;
 	int err;
 	unsigned short port;
 	unsigned short port;
+	char proto;
 
 
 	port=0;
 	port=0;
 	if(via==msg->via1){ 
 	if(via==msg->via1){ 
@@ -526,7 +527,8 @@ int update_sock_struct_from_via( union sockaddr_union* to,
 	    sip_resolvehost now accepts str -janakj
 	    sip_resolvehost now accepts str -janakj
 	*/
 	*/
 	DBG("update_sock_struct_from_via: trying SRV lookup\n");
 	DBG("update_sock_struct_from_via: trying SRV lookup\n");
-	he=sip_resolvehost(name, &port, via->proto);
+	proto=via->proto;
+	he=sip_resolvehost(name, &port, &proto);
 	
 	
 	if (he==0){
 	if (he==0){
 		LOG(L_NOTICE, "ERROR:forward_reply:resolve_host(%.*s) failure\n",
 		LOG(L_NOTICE, "ERROR:forward_reply:resolve_host(%.*s) failure\n",

+ 2 - 0
globals.h

@@ -212,6 +212,8 @@ extern unsigned int dns_cache_min_ttl; /* minimum ttl */
 extern unsigned int dns_timer_interval; /* gc timer interval in s */
 extern unsigned int dns_timer_interval; /* gc timer interval in s */
 extern int dns_flags; /* default flags used for the  dns_*resolvehost 
 extern int dns_flags; /* default flags used for the  dns_*resolvehost 
                     (compatibility wrappers) */
                     (compatibility wrappers) */
+extern int dns_srv_lb; /* default SRV LB support value */
+
 #endif
 #endif
 #ifdef USE_DST_BLACKLIST
 #ifdef USE_DST_BLACKLIST
 extern int use_dst_blacklist; /* 1 if the blacklist is enabled */
 extern int use_dst_blacklist; /* 1 if the blacklist is enabled */

+ 4 - 4
ip_addr.h

@@ -89,13 +89,13 @@ struct socket_info{
 	str name; /* name - eg.: foo.bar or 10.0.0.1 */
 	str name; /* name - eg.: foo.bar or 10.0.0.1 */
 	struct ip_addr address; /* ip address */
 	struct ip_addr address; /* ip address */
 	str address_str;        /* ip address converted to string -- optimization*/
 	str address_str;        /* ip address converted to string -- optimization*/
-	unsigned short port_no;  /* port number */
 	str port_no_str; /* port number converted to string -- optimization*/
 	str port_no_str; /* port number converted to string -- optimization*/
 	enum si_flags flags; /* SI_IS_IP | SI_IS_LO | SI_IS_MCAST */
 	enum si_flags flags; /* SI_IS_IP | SI_IS_LO | SI_IS_MCAST */
 	union sockaddr_union su; 
 	union sockaddr_union su; 
-	int proto; /* tcp or udp*/
 	struct socket_info* next;
 	struct socket_info* next;
 	struct socket_info* prev;
 	struct socket_info* prev;
+	unsigned short port_no;  /* port number */
+	char proto; /* tcp or udp*/
 };
 };
 
 
 
 
@@ -109,7 +109,7 @@ struct receive_info{
 	union sockaddr_union src_su; /* useful for replies*/
 	union sockaddr_union src_su; /* useful for replies*/
 	struct socket_info* bind_address; /* sock_info structure on which 
 	struct socket_info* bind_address; /* sock_info structure on which 
 									  the msg was received*/
 									  the msg was received*/
-	short proto;
+	char proto;
 #ifdef USE_COMP
 #ifdef USE_COMP
 	short comp; /* compression */
 	short comp; /* compression */
 #endif
 #endif
@@ -121,7 +121,7 @@ struct dest_info{
 	struct socket_info* send_sock;
 	struct socket_info* send_sock;
 	union sockaddr_union to;
 	union sockaddr_union to;
 	int id; /* tcp stores the connection id here */ 
 	int id; /* tcp stores the connection id here */ 
-	short proto;
+	char proto;
 #ifdef USE_COMP
 #ifdef USE_COMP
 	short comp;
 	short comp;
 #endif
 #endif

+ 1 - 1
main.c

@@ -1579,7 +1579,7 @@ try_again:
 		goto error;
 		goto error;
 	}
 	}
 #ifdef USE_DNS_CACHE
 #ifdef USE_DNS_CACHE
-	if (init_dns_cache()<0){
+	if (use_dns_cache && init_dns_cache()<0){
 		LOG(L_CRIT, "could not initialize the dns cache, exiting...\n");
 		LOG(L_CRIT, "could not initialize the dns cache, exiting...\n");
 		goto error;
 		goto error;
 	}
 	}

+ 5 - 3
proxy.c

@@ -200,10 +200,11 @@ error:
 /* same as add_proxy, but it doesn't add the proxy to the list
 /* same as add_proxy, but it doesn't add the proxy to the list
  * uses also SRV if possible & port==0 (quick hack) */
  * uses also SRV if possible & port==0 (quick hack) */
 
 
-struct proxy_l* mk_proxy(str* name, unsigned short port, int proto)
+struct proxy_l* mk_proxy(str* name, unsigned short port, int protocol)
 {
 {
 	struct proxy_l* p;
 	struct proxy_l* p;
 	struct hostent* he;
 	struct hostent* he;
+	char proto;
 
 
 	p=(struct proxy_l*) pkg_malloc(sizeof(struct proxy_l));
 	p=(struct proxy_l*) pkg_malloc(sizeof(struct proxy_l));
 	if (p==0){
 	if (p==0){
@@ -214,10 +215,10 @@ struct proxy_l* mk_proxy(str* name, unsigned short port, int proto)
 	memset(p,0,sizeof(struct proxy_l));
 	memset(p,0,sizeof(struct proxy_l));
 	p->name=*name;
 	p->name=*name;
 	p->port=port;
 	p->port=port;
-	p->proto=proto;
 
 
 	DBG("DEBUG: mk_proxy: doing DNS lookup...\n");
 	DBG("DEBUG: mk_proxy: doing DNS lookup...\n");
-	he=sip_resolvehost(name, &(p->port), proto);
+	proto=protocol;
+	he=sip_resolvehost(name, &(p->port), &proto);
 	if (he==0){
 	if (he==0){
 		ser_error=E_BAD_ADDRESS;
 		ser_error=E_BAD_ADDRESS;
 		LOG(L_CRIT, "ERROR: mk_proxy: could not resolve hostname:"
 		LOG(L_CRIT, "ERROR: mk_proxy: could not resolve hostname:"
@@ -229,6 +230,7 @@ struct proxy_l* mk_proxy(str* name, unsigned short port, int proto)
 		pkg_free(p);
 		pkg_free(p);
 		goto error;
 		goto error;
 	}
 	}
+	p->proto=proto;
 	p->ok=1;
 	p->ok=1;
 	return p;
 	return p;
 error:
 error:

+ 493 - 38
resolve.c

@@ -35,6 +35,7 @@
  *  2006-07-17  rdata contains now also the record name (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
  *  2006-08-18  get_record can append also the additional records to the
  *               returned list (andrei)
  *               returned list (andrei)
+ *  2007-06-15  naptr support (andrei)
  */ 
  */ 
 
 
 
 
@@ -45,10 +46,12 @@
 #include <string.h>
 #include <string.h>
 
 
 #include "resolve.h"
 #include "resolve.h"
+#include "compiler_opt.h"
 #include "dprint.h"
 #include "dprint.h"
 #include "mem/mem.h"
 #include "mem/mem.h"
 #include "ip_addr.h"
 #include "ip_addr.h"
 #include "error.h"
 #include "error.h"
+#include "globals.h" /* tcp_disable, tls_disable a.s.o */
 
 
 #ifdef USE_DNS_CACHE
 #ifdef USE_DNS_CACHE
 #include "dns_cache.h"
 #include "dns_cache.h"
@@ -65,6 +68,17 @@ int dns_try_ipv6=1; /* default on */
 #else
 #else
 int dns_try_ipv6=0; /* off, if no ipv6 support */
 int dns_try_ipv6=0; /* off, if no ipv6 support */
 #endif
 #endif
+int dns_try_naptr=0;  /* off by default */
+
+int dns_udp_pref=3;  /* udp transport preference (for naptr) */
+int dns_tcp_pref=2;  /* tcp transport preference (for naptr) */
+int dns_tls_pref=1;  /* tls transport preference (for naptr) */
+
+#ifdef USE_NAPTR
+#define PROTO_LAST  PROTO_SCTP
+static int naptr_proto_pref[PROTO_LAST];
+#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;
@@ -72,6 +86,23 @@ int dns_servers_no=-1;
 int dns_search_list=-1;
 int dns_search_list=-1;
 
 
 
 
+#ifdef USE_NAPTR
+void init_naptr_proto_prefs()
+{
+	if ((PROTO_UDP >= PROTO_LAST) || (PROTO_TCP >= PROTO_LAST) ||
+		(PROTO_TLS >= PROTO_LAST)){
+		BUG("init_naptr_proto_prefs: array too small \n");
+		return;
+	}
+	naptr_proto_pref[PROTO_UDP]=dns_udp_pref;
+	naptr_proto_pref[PROTO_TCP]=dns_tcp_pref;
+	naptr_proto_pref[PROTO_TLS]=dns_tls_pref;
+}
+
+#endif /* USE_NAPTR */
+
+
+
 /* init. the resolver
 /* init. the resolver
  * params: retr_time  - time before retransmitting (must be >0)
  * params: retr_time  - time before retransmitting (must be >0)
  *         retr_no    - retransmissions number
  *         retr_no    - retransmissions number
@@ -102,6 +133,9 @@ int resolv_init()
 #warning "no resolv timeout support"
 #warning "no resolv timeout support"
 	LOG(L_WARN, "WARNING: resolv_init: no resolv options support - resolv"
 	LOG(L_WARN, "WARNING: resolv_init: no resolv options support - resolv"
 			" options will be ignored\n");
 			" options will be ignored\n");
+#endif
+#ifdef USE_NAPTR
+	init_naptr_proto_prefs();
 #endif
 #endif
 	return 0;
 	return 0;
 }
 }
@@ -266,8 +300,8 @@ struct naptr_rdata* dns_naptr_parser( unsigned char* msg, unsigned char* end,
 		LOG(L_ERR, "ERROR: dns_naptr_parser: out of memory\n");
 		LOG(L_ERR, "ERROR: dns_naptr_parser: out of memory\n");
 		goto error;
 		goto error;
 	}
 	}
-	naptr->order=ntohs(naptr->order);
-	naptr->pref=ntohs(naptr->pref);
+	naptr->order=ntohs(order);
+	naptr->pref=ntohs(pref);
 	
 	
 	naptr->flags=&naptr->str_table[0];
 	naptr->flags=&naptr->str_table[0];
 	naptr->flags_len=flags_len;
 	naptr->flags_len=flags_len;
@@ -555,7 +589,9 @@ again:
 	if (flags & RES_AR){
 	if (flags & RES_AR){
 		flags&=~RES_AR;
 		flags&=~RES_AR;
 		answers_no=ntohs((unsigned short)buff.hdr.nscount);
 		answers_no=ntohs((unsigned short)buff.hdr.nscount);
+#ifdef RESOLVE_DBG
 		DBG("get_record: skipping %d NS (p=%p, end=%p)\n", answers_no, p, end);
 		DBG("get_record: skipping %d NS (p=%p, end=%p)\n", answers_no, p, end);
+#endif
 		for (r=0; (r<answers_no) && (p<end); r++){
 		for (r=0; (r<answers_no) && (p<end); r++){
 			/* skip over the ns records */
 			/* skip over the ns records */
 			if ((p=dns_skipname(p, end))==0) {
 			if ((p=dns_skipname(p, end))==0) {
@@ -568,7 +604,9 @@ again:
 			p+=2+2+4+2+ntohs(rdlength);
 			p+=2+2+4+2+ntohs(rdlength);
 		}
 		}
 		answers_no=ntohs((unsigned short)buff.hdr.arcount);
 		answers_no=ntohs((unsigned short)buff.hdr.arcount);
+#ifdef RESOLVE_DBG
 		DBG("get_record: parsing %d ARs (p=%p, end=%p)\n", answers_no, p, end);
 		DBG("get_record: parsing %d ARs (p=%p, end=%p)\n", answers_no, p, end);
+#endif
 		goto again; /* add also the additional records */
 		goto again; /* add also the additional records */
 	}
 	}
 			
 			
@@ -591,47 +629,242 @@ not_found:
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef USE_NAPTR
 
 
+/* service matching constants, lowercase */
+#define SIP_SCH		0x2b706973
+#define SIPS_SCH	0x73706973
+#define SIP_D2U		0x00753264
+#define SIP_D2T		0x00743264
+#define SIP_D2S		0x00733264
+#define SIPS_D2T	0x7432642b
 
 
 
 
+/* get protocol from a naptr rdata and check for validity
+ * returns > 0 (PROTO_UDP, PROTO_TCP, PROTO_SCTP or PROTO_TLS)
+ *         <=0  on error 
+ */
+char naptr_get_sip_proto(struct naptr_rdata* n)
+{
+	unsigned int s;
+	char proto;
 
 
-/* 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
+	proto=-1;
+	
+	if ((n->flags_len!=1) || (*n->flags!='s'))
+		return -1;
+	if (n->regexp_len!=0)
+		return -1;
+	/* SIP+D2U, SIP+D2T, SIP+D2S, SIPS+D2T */
+	if (n->services_len==7){ /* SIP+D2X */
+		s=n->services[0]+(n->services[1]<<8)+(n->services[2]<<16)+
+				(n->services[3]<<24);
+		s|=0x20202020;
+		if (s==SIP_SCH){
+			s=n->services[4]+(n->services[5]<<8)+(n->services[6]<<16);
+			s|=0x00202020;
+			switch(s){
+				case SIP_D2U:
+					proto=PROTO_UDP;
+					break;
+				case SIP_D2T:
+					proto=PROTO_TCP;
+					break;
+				case SIP_D2S:
+					proto=PROTO_SCTP;
+					break;
+				default:
+					return -1;
+			}
+		}else{
+			return -1;
+		}
+	}else if  (n->services_len==8){ /*SIPS+D2T */
+		s=n->services[0]+(n->services[1]<<8)+(n->services[2]<<16)+
+				(n->services[3]<<24);
+		s|=0x20202020;
+		if (s==SIPS_SCH){
+			s=n->services[4]+(n->services[5]<<8)+(n->services[6]<<16)+
+					(n->services[7]<<24);
+			s|=0x20202020;
+			if (s==SIPS_D2T){
+				proto=PROTO_TLS;
+			}
+		}else{
+			return -1;
+		}
+	}else{
+		return -1;
+	}
+	return proto;
+}
+
+
+
+inline static int proto_pref_score(char proto)
+{
+	if ((proto>=PROTO_UDP) && (proto<= PROTO_TLS))
+		return naptr_proto_pref[(int)proto];
+	return 0;
+}
+
+
+
+/* returns true if we support the protocol */
+int naptr_proto_supported(char proto)
+{
+	if (proto_pref_score(proto)<0)
+		return 0;
+	switch(proto){
+		case PROTO_UDP:
+			return 1;
+#ifdef USE_TCP
+		case PROTO_TCP:
+			return !tcp_disable;
+#ifdef USE_TLS
+		case PROTO_TLS:
+			return !tls_disable;
+#endif /* USE_TLS */
+#endif /* USE_TCP */
+		case PROTO_SCTP:
+			return 0; /* not supported */
+	}
+	return 0;
+}
+
+
+
+
+/* returns true if new_proto is preferred over old_proto */
+int naptr_proto_preferred(char new_proto, char old_proto)
+{
+	return proto_pref_score(new_proto)>proto_pref_score(old_proto);
+}
+
+
+/* choose between 2 naptr records, should take into account local
+ * preferences too
+ * returns 1 if the new record was selected, 0 otherwise */
+int naptr_choose (struct naptr_rdata** crt, char* crt_proto,
+									struct naptr_rdata* n , char n_proto)
+{
+#ifdef NAPTR_DBG
+	DBG("naptr_choose(o: %d w: %d p:%d , o: %d w:%d p:%d)\n",
+			*crt?(int)(*crt)->order:-1, *crt?(int)(*crt)->pref:-1,
+			(int)*crt_proto,
+			(int)n->order, (int)n->pref, (int)n_proto);
+#endif
+	if ((*crt==0) || ((*crt_proto!=n_proto) && 
+						( naptr_proto_preferred(n_proto, *crt_proto))) )
+			goto change;
+	if ((n->order<(*crt)->order) || ((n->order== (*crt)->order) &&
+									(n->pref < (*crt)->pref))){
+			goto change;
+	}
+#ifdef NAPTR_DBG
+	DBG("naptr_choose: no change\n");
+#endif
+	return 0;
+change:
+#ifdef NAPTR_DBG
+	DBG("naptr_choose: changed\n");
+#endif
+	*crt_proto=n_proto;
+	*crt=n;
+	return 1;
+}
+#endif /* USE_NAPTR */
+
+
+
+/* internal sip srv resolver: resolves a host name trying:
+ * - SRV lookup if the address is not an ip *port==0. The result of the SRV
+ *   query will be used for an A/AAAA lookup.
+ *  - normal A/AAAA lookup (either fallback from the above or if *port!=0
+ *   and *proto!=0 or port==0 && proto==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
  * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
+ * If zt is set, name will be assumed to be 0 terminated and some copy 
+ * operations will be avoided.
+ * If is_srv is set it will assume name has the srv prefixes for sip already
+ *  appended and it's already 0-term'ed; if not it will append them internally.
+ * If ars !=0, it will first try to look through them and only if the SRV
+ *   record is not found it will try doing a DNS query  (ars will not be
+ *   freed, the caller should take care of them)
  * 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* srv_sip_resolvehost(str* name, int zt, unsigned short* port,
+									char* proto, int is_srv, struct rdata* ars)
 {
 {
 	struct hostent* he;
 	struct hostent* he;
 	struct ip_addr* ip;
 	struct ip_addr* ip;
-	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
-	struct rdata* head;
+	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups and
+	                                  null. term  strings */
 	struct rdata* l;
 	struct rdata* l;
 	struct srv_rdata* srv;
 	struct srv_rdata* srv;
+	struct rdata* srv_head;
+	char* srv_target;
+	char srv_proto;
 
 
+	/* init */
+	srv_head=0;
+	srv_target=0;
+	if (name->len >= MAX_DNS_NAME) {
+		LOG(L_ERR, "sip_resolvehost: domain name too long\n");
+		he=0;
+		goto end;
+	}
+#ifdef RESOLVE_DBG
+	DBG("srv_sip_resolvehost: %.*s:%d proto=%d\n", name->len, name->s,
+			port?(int)*port:-1, proto?(int)*proto:-1);
+#endif
+	if (is_srv){
+		/* skip directly to srv resolving */
+		srv_proto=(proto)?*proto:0;
+		*port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT;
+		if (zt){
+			srv_target=name->s; /* name.s must be 0 terminated in
+								  this case */
+		}else{
+			memcpy(tmp, name->s, name->len);
+			tmp[name->len] = '\0';
+			srv_target=tmp;
+		}
+		goto do_srv; /* skip to the actual srv query */
+	}
+	if (proto){ /* makes sure we have a protocol set*/
+		if (*proto==0)
+			*proto=srv_proto=PROTO_UDP; /* default */
+		else
+			srv_proto=*proto;
+	}else{
+		srv_proto=PROTO_UDP;
+	}
 	/* 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)){
-		*port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we don't
-														find another */
+		*port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
+														  don't find another */
+		/* 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 */
+			he=ip_addr2he(name, ip);
+			goto end;
+		}
 		if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
 		if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
 			LOG(L_WARN, "WARNING: sip_resolvehost: domain name too long (%d),"
 			LOG(L_WARN, "WARNING: sip_resolvehost: domain name too long (%d),"
 						" unable to perform SRV lookup\n", name->len);
 						" unable to perform SRV lookup\n", name->len);
 		}else{
 		}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){
+			switch(srv_proto){
 				case PROTO_NONE: /* no proto specified, use udp */
 				case PROTO_NONE: /* no proto specified, use udp */
-					goto skip_srv;
+					if (proto)
+						*proto=PROTO_UDP;
+					/* no break */
 				case PROTO_UDP:
 				case PROTO_UDP:
 					memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 					memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
 					memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
 					memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
@@ -649,55 +882,277 @@ struct hostent* _sip_resolvehost(str* name, unsigned short* port, int proto)
 					break;
 					break;
 				default:
 				default:
 					LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
 					LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
-							proto);
-					return 0;
+							srv_proto);
+					he=0;
+					goto end;
 			}
 			}
-			head=get_record(tmp, T_SRV, RES_ONLY_TYPE);
-			for(l=head; l; l=l->next){
+			srv_target=tmp;
+do_srv:
+			/* try to find the SRV records inside previous ARs  first*/
+			for (l=ars; l; l=l->next){
+				if (l->type!=T_SRV) continue; 
+				srv=(struct srv_rdata*) l->rdata;
+				if (srv==0){
+					LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n");
+					/* cleanup on exit only */
+					break;
+				}
+				he=resolvehost(srv->name);
+				if (he!=0){
+					/* we found it*/
+#ifdef RESOLVE_DBG
+					DBG("sip_resolvehost: found SRV(%s) = %s:%d in AR\n",
+							srv_target, srv->name, srv->port);
+#endif
+					*port=srv->port;
+					/* cleanup on exit */
+					goto end;
+				}
+			}
+			srv_head=get_record(srv_target, T_SRV, RES_ONLY_TYPE);
+			for(l=srv_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;
 				if (srv==0){
 				if (srv==0){
 					LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n");
 					LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n");
-					free_rdata_list(head);
+					/* cleanup on exit only */
 					break;
 					break;
 				}
 				}
 				he=resolvehost(srv->name);
 				he=resolvehost(srv->name);
 				if (he!=0){
 				if (he!=0){
 					/* we found it*/
 					/* we found it*/
+#ifdef RESOLVE_DBG
 					DBG("sip_resolvehost: SRV(%s) = %s:%d\n",
 					DBG("sip_resolvehost: SRV(%s) = %s:%d\n",
-							tmp, srv->name, srv->port);
+							srv_target, srv->name, srv->port);
+#endif
 					*port=srv->port;
 					*port=srv->port;
-					free_rdata_list(head); /*clean up*/
-					return he;
+					/* cleanup on exit */
+					goto end;
 				}
 				}
 			}
 			}
-			if (head) free_rdata_list(head); /*clean up*/
+			if (is_srv){
+				/* if the name was already into SRV format it doesn't make
+				 * any sense to fall back to A/AAAA */
+				he=0;
+				goto end;
+			}
+			/* cleanup on exit */
+#ifdef RESOLVE_DBG
 			DBG("sip_resolvehost: no SRV record found for %.*s," 
 			DBG("sip_resolvehost: no SRV record found for %.*s," 
 					" trying 'normal' lookup...\n", name->len, name->s);
 					" trying 'normal' lookup...\n", name->len, name->s);
+#endif
+		}
+	}
+/*skip_srv:*/
+	if (likely(!zt)){
+		memcpy(tmp, name->s, name->len);
+		tmp[name->len] = '\0';
+		he=resolvehost(tmp);
+	}else{
+		he=resolvehost(name->s);
+	}
+end:
+#ifdef RESOLVE_DBG
+	DBG("srv_sip_resolvehost: returning %p (%.*s:%d proto=%d)\n",
+			he, name->len, name->s,
+			port?(int)*port:-1, proto?(int)*proto:-1);
+#endif
+	if (srv_head)
+		free_rdata_list(srv_head);
+	return he;
+}
+
+
+
+#ifdef USE_NAPTR 
+
+
+/* iterates over a naptr rr list, returning each time a "good" naptr record
+ * is found.( srv type, no regex and a supported protocol)
+ * params:
+ *         naptr_head - naptr rr list head
+ *         tried      - bitmap used to keep track of the already tried records
+ *                      (no more then sizeof(tried)*8 valid records are 
+ *                      ever walked
+ *         srv_name   - if succesfull, it will be set to the selected record
+ *                      srv name (naptr repl.)
+ *         proto      - if succesfull it will be set to the selected record
+ *                      protocol
+ * returns  0 if no more records found or a pointer to the selected record
+ *  and sets  protocol and srv_name
+ * WARNING: when calling first time make sure you run first 
+ *           naptr_iterate_init(&tried)
+ */
+struct rdata* naptr_sip_iterate(struct rdata* naptr_head, 
+										naptr_bmp_t* tried,
+										str* srv_name, char* proto)
+{
+	int i, idx;
+	struct rdata* l;
+	struct rdata* l_saved;
+	struct naptr_rdata* naptr;
+	struct naptr_rdata* naptr_saved;
+	char saved_proto;
+	char naptr_proto;
+
+	idx=0;
+	naptr_proto=PROTO_NONE;
+	naptr_saved=0;
+	l_saved=0;
+	saved_proto=0;
+	i=0;
+	for(l=naptr_head; l && (i<MAX_NAPTR_RRS); l=l->next){
+		if (l->type!=T_NAPTR) continue; 
+		naptr=(struct naptr_rdata*) l->rdata;
+		if (naptr==0){
+				LOG(L_CRIT, "naptr_iterate: BUG: null rdata\n");
+			goto end;
 		}
 		}
+		/* check if valid and get proto */
+		if ((naptr_proto=naptr_get_sip_proto(naptr))<=0) continue;
+		if (*tried& (1<<i)){
+			i++;
+			continue; /* already tried */
+		}
+#ifdef NAPTR_DBG
+		DBG("naptr_iterate: found a valid sip NAPTR rr %.*s,"
+					" proto %d\n", naptr->repl_len, naptr->repl, 
+					(int)naptr_proto);
+#endif
+		if ((naptr_proto_supported(naptr_proto))){
+			if (naptr_choose(&naptr_saved, &saved_proto,
+								naptr, naptr_proto))
+				idx=i;
+				l_saved=l;
+			}
+		i++;
+	}
+	if (naptr_saved){
+		/* found something */
+#ifdef NAPTR_DBG
+		DBG("naptr_iterate: choosed NAPTR rr %.*s, proto %d"
+					" tried: 0x%x\n", naptr_saved->repl_len, 
+					naptr_saved->repl, (int)saved_proto, *tried);
+#endif
+		*tried|=1<<idx;
+		*proto=saved_proto;
+		srv_name->s=naptr_saved->repl;
+		srv_name->len=naptr_saved->repl_len;
+		return l_saved;
 	}
 	}
-skip_srv:
+end:
+	return 0;
+}
+
+
+
+
+/* internal sip naptr resolver function: resolves a host name trying:
+ * - NAPTR lookup if the address is not an ip and *proto==0 and *port==0.
+ *   The result of the NAPTR query will be used for a SRV lookup
+ * - SRV lookup if the address is not an ip *port==0. The result of the SRV
+ *   query will be used for an A/AAAA lookup.
+ *  - normal A/AAAA lookup (either fallback from the above or if *port!=0
+ *   and *proto!=0 or port==0 && proto==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* naptr_sip_resolvehost(str* name,  unsigned short* port,
+										char* proto)
+{
+	struct hostent* he;
+	struct ip_addr* ip;
+	static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups and
+	                                  null. term  strings */
+	struct rdata* l;
+	struct rdata* naptr_head;
+	char n_proto;
+	str srv_name;
+	naptr_bmp_t tried_bmp; /* tried bitmap */
+
+
+
+	naptr_head=0;
+	he=0;
 	if (name->len >= MAX_DNS_NAME) {
 	if (name->len >= MAX_DNS_NAME) {
-		LOG(L_ERR, "sip_resolvehost: domain name too long\n");
-		return 0;
+		LOG(L_ERR, "naptr_sip_resolvehost: domain name too long\n");
+		goto end;
 	}
 	}
-	memcpy(tmp, name->s, name->len);
-	tmp[name->len] = '\0';
-	he=resolvehost(tmp);
+	/* try NAPTR if no port or protocol is specified and NAPTR lookup is
+	 * enabled */
+	if (port && proto && (*proto==0) && (*port==0)){
+		*proto=PROTO_UDP; /* just in case we don't find another */
+		if ( ((ip=str2ip(name))!=0)
+#ifdef	USE_IPV6
+			  || ((ip=str2ip6(name))!=0)
+#endif
+		){
+			/* we are lucky, this is an ip address */
+			he=ip_addr2he(name,ip);
+			*port=SIP_PORT;
+			goto end;
+		}
+		memcpy(tmp, name->s, name->len);
+		tmp[name->len] = '\0';
+		naptr_head=get_record(tmp, T_NAPTR, RES_AR);
+		naptr_iterate_init(&tried_bmp);
+		while((l=naptr_sip_iterate(naptr_head, &tried_bmp,
+										&srv_name, &n_proto))!=0){
+			if ((he=srv_sip_resolvehost(&srv_name, 1, port, proto, 1, l))!=0){
+				*proto=n_proto;
+				return he;
+			}
+		}
+		/*clean up on exit*/
+#ifdef RESOLVE_DBG
+		DBG("naptr_sip_resolvehost: no NAPTR record found for %.*s," 
+				" trying SRV lookup...\n", name->len, name->s);
+#endif
+	}
+	/* fallback to normal srv lookup */
+	he=srv_sip_resolvehost(name, 0, port, proto, 0, 0);
+end:
+	if (naptr_head)
+		free_rdata_list(naptr_head);
 	return he;
 	return he;
 }
 }
+#endif /* USE_NAPTR */
+
+
 
 
+/* resolves a host name trying:
+ * - NAPTR lookup if enabled, the address is not an ip and *proto==0 and 
+ *   *port==0. The result of the NAPTR query will be used for a SRV lookup
+ * - SRV lookup if the address is not an ip *port==0. The result of the SRV
+ *   query will be used for an A/AAAA lookup.
+ *  - normal A/AAAA lookup (either fallback from the above or if *port!=0
+ *   and *proto!=0 or port==0 && proto==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* _sip_resolvehost(str* name, unsigned short* port, char* proto)
+{
+#ifdef USE_NAPTR
+	if (dns_try_naptr)
+		return naptr_sip_resolvehost(name, port, proto);
+#endif
+	return srv_sip_resolvehost(name, 0, port, proto, 0, 0);
+}
 
 
 
 
 /* resolve host, port, proto using sip rules (e.g. use SRV if port=0 a.s.o)
 /* resolve host, port, proto using sip rules (e.g. use SRV if port=0 a.s.o)
  *  and write the result in the sockaddr_union to
  *  and write the result in the sockaddr_union to
  *  returns -1 on error (resolve failed), 0 on success */
  *  returns -1 on error (resolve failed), 0 on success */
 int sip_hostport2su(union sockaddr_union* su, str* name, unsigned short port,
 int sip_hostport2su(union sockaddr_union* su, str* name, unsigned short port,
-						int proto)
+						char* proto)
 {
 {
 	struct hostent* he;
 	struct hostent* he;
 	
 	
-	
 	he=sip_resolvehost(name, &port, proto);
 	he=sip_resolvehost(name, &port, proto);
 	if (he==0){
 	if (he==0){
 		ser_error=E_BAD_ADDRESS;
 		ser_error=E_BAD_ADDRESS;

+ 50 - 2
resolve.h

@@ -33,6 +33,7 @@
  *  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-07-17  rdata contains now also the record name (andrei)
  *  2006-08-18  get_record uses flags (andrei)
  *  2006-08-18  get_record uses flags (andrei)
+ *  2006-06-16  naptr support (andrei)
  */
  */
 
 
 
 
@@ -54,6 +55,11 @@
 #include "dns_wrappers.h"
 #include "dns_wrappers.h"
 #endif
 #endif
 
 
+/* define RESOLVE_DBG for debugging info (very noisy) */
+#define RESOLVE_DBG
+/* define NAPTR_DBG for naptr related debugging info (very noisy) */
+#define NAPTR_DBG
+
 
 
 #define MAX_QUERY_SIZE 8192
 #define MAX_QUERY_SIZE 8192
 #define ANS_SIZE       8192
 #define ANS_SIZE       8192
@@ -151,6 +157,10 @@ void free_rdata_list(struct rdata* head);
 
 
 
 
 extern int dns_try_ipv6;
 extern int dns_try_ipv6;
+extern int dns_try_naptr;
+extern int dns_udp_pref;  /* udp transport preference (for naptr) */
+extern int dns_tcp_pref;  /* tcp transport preference (for naptr) */
+extern int dns_tls_pref;  /* tls transport preference (for naptr) */
 
 
 
 
 #define rev_resolvehost(ip)\
 #define rev_resolvehost(ip)\
@@ -200,8 +210,10 @@ static inline struct ip_addr* str2ip(str* st)
 	
 	
 	return &ip;
 	return &ip;
 error_dots:
 error_dots:
+#ifdef RESOLVE_DBG
 	DBG("str2ip: ERROR: too %s dots in [%.*s]\n", (i>3)?"many":"few", 
 	DBG("str2ip: ERROR: too %s dots in [%.*s]\n", (i>3)?"many":"few", 
 			st->len, st->s);
 			st->len, st->s);
+#endif
 	return 0;
 	return 0;
  error_char:
  error_char:
 	/*
 	/*
@@ -291,15 +303,21 @@ static inline struct ip_addr* str2ip6(str* st)
 	return &ip;
 	return &ip;
 
 
 error_too_many_colons:
 error_too_many_colons:
+#ifdef RESOLVE_DBG
 	DBG("str2ip6: ERROR: too many colons in [%.*s]\n", st->len, st->s);
 	DBG("str2ip6: ERROR: too many colons in [%.*s]\n", st->len, st->s);
+#endif
 	return 0;
 	return 0;
 
 
 error_too_few_colons:
 error_too_few_colons:
+#ifdef RESOLVE_DBG
 	DBG("str2ip6: ERROR: too few colons in [%.*s]\n", st->len, st->s);
 	DBG("str2ip6: ERROR: too few colons in [%.*s]\n", st->len, st->s);
+#endif
 	return 0;
 	return 0;
 
 
 error_colons:
 error_colons:
+#ifdef RESOLVE_DBG
 	DBG("str2ip6: ERROR: too many double colons in [%.*s]\n", st->len, st->s);
 	DBG("str2ip6: ERROR: too many double colons in [%.*s]\n", st->len, st->s);
+#endif
 	return 0;
 	return 0;
 
 
 error_char:
 error_char:
@@ -311,7 +329,7 @@ error_char:
 
 
 
 
 
 
-struct hostent* _sip_resolvehost(str* name, unsigned short* port, int proto);
+struct hostent* _sip_resolvehost(str* name, unsigned short* port, char* proto);
 
 
 
 
 
 
@@ -390,7 +408,7 @@ skip_ipv4:
 int resolv_init();
 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);
+						char* proto);
 
 
 
 
 
 
@@ -404,4 +422,34 @@ int sip_hostport2su(union sockaddr_union* su, str* host, unsigned short port,
 #endif
 #endif
 
 
 
 
+
+#ifdef USE_NAPTR
+/* NAPTR helper functions */
+typedef unsigned int naptr_bmp_t; /* type used for keeping track of tried
+									 naptr records*/
+#define MAX_NAPTR_RRS (sizeof(naptr_bmp_t)*8)
+
+/* use before first call to naptr_sip_iterate */
+#define naptr_iterate_init(bmp) \
+	do{ \
+		*(bmp)=0; \
+	}while(0) \
+
+struct rdata* naptr_sip_iterate(struct rdata* naptr_head, 
+										naptr_bmp_t* tried,
+										str* srv_name, char* proto);
+/* returns sip proto if valis sip naptr record, .-1 otherwise */
+char naptr_get_sip_proto(struct naptr_rdata* n);
+/* returns true if new_proto is preferred over old_proto */
+int naptr_proto_preferred(char new_proto, char old_proto);
+/* returns true if we support the protocol */
+int naptr_proto_supported(char proto);
+/* choose between 2 naptr records, should take into account local
+ * preferences too
+ * returns 1 if the new record was selected, 0 otherwise */
+int naptr_choose (struct naptr_rdata** crt, char* crt_proto,
+									struct naptr_rdata* n , char n_proto);
+
+#endif/* USE_NAPTR */
+
 #endif
 #endif

+ 7 - 1
version.h

@@ -260,6 +260,12 @@
 #define USE_DNS_FAILOVER_STR ""
 #define USE_DNS_FAILOVER_STR ""
 #endif
 #endif
 
 
+#ifdef USE_NAPTR
+#define USE_NAPTR_STR ", USE_NAPTR"
+#else
+#define USE_NAPTR_STR ""
+#endif
+
 #ifdef USE_DST_BLACKLIST
 #ifdef USE_DST_BLACKLIST
 #define USE_DST_BLACKLIST_STR ", USE_DST_BLACKLIST"
 #define USE_DST_BLACKLIST_STR ", USE_DST_BLACKLIST"
 #else
 #else
@@ -291,7 +297,7 @@
 	USE_FUTEX_STR \
 	USE_FUTEX_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_DNS_CACHE_STR USE_DNS_FAILOVER_STR \
 	USE_SYSV_SEM_STR USE_COMP_STR USE_DNS_CACHE_STR USE_DNS_FAILOVER_STR \
-	USE_DST_BLACKLIST_STR
+	USE_NAPTR_STR USE_DST_BLACKLIST_STR
 
 
 
 
 #endif
 #endif