- dns naptr support (off by default)
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 18 Jun 2007 21:20:58 +0000 (21:20 +0000)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 18 Jun 2007 21:20:58 +0000 (21:20 +0000)
- 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.

17 files changed:
Makefile.defs
NEWS
action.c
cfg.lex
cfg.y
dns_cache.c
dns_cache.h
dns_wrappers.h
doc/dns.txt
forward.c
globals.h
ip_addr.h
main.c
proxy.c
resolve.c
resolve.h
version.h

index 07a63e1..a14c124 100644 (file)
@@ -75,7 +75,7 @@ MAIN_NAME=ser
 VERSION = 2
 PATCHLEVEL = 1
 SUBLEVEL =  0
-EXTRAVERSION = -dev8-sf_malloc
+EXTRAVERSION = -dev9-dns
 
 SER_VER = $(shell expr $(VERSION) \* 1000000 + $(PATCHLEVEL) \* 1000 + \
                        $(SUBLEVEL) )
@@ -401,6 +401,8 @@ endif
 # -DNO_SIG_DEBUG
 #        turns off debugging messages in signal handlers (which might be 
 #         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
 
@@ -420,11 +422,12 @@ DEFS+= $(extra_defs) \
         -DUSE_DNS_CACHE \
         -DUSE_DNS_FAILOVER \
         -DUSE_DST_BLACKLIST \
-        -DDBG_QM_MALLOC \
+        -DUSE_NAPTR \
         #-DLL_MALLOC \
         #-DSF_MALLOC \
         #-DDL_MALLOC \
         #-DF_MALLOC \
+        #-DDBG_QM_MALLOC \
         #-DDBG_F_MALLOC \
         #-DNO_DEBUG \
         #-DEXTRA_DEBUG \
diff --git a/NEWS b/NEWS
index 88d0537..84b7f76 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -71,6 +71,8 @@ modules:
                         - t_set_retr(t1, t2) - changes the retransmissions
                            intervals on the fly, on a per transaction basis.
 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
                all the shared memory on startup (fill it with 0)
              - real time options
@@ -79,6 +81,17 @@ core:
                long held locks, almost no performance impact otherwise)
 
 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 
     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 
index 22d028d..54d0e58 100644 (file)
--- a/action.c
+++ b/action.c
@@ -176,7 +176,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                                                                         from the uri */
                                        switch(u->proto){
                                                case PROTO_NONE:
-                                                       dst.proto=PROTO_UDP;
+                                                       /*dst.proto=PROTO_UDP; */
+                                                       /* no proto, try to get it from the dns */
                                                        break;
                                                case PROTO_UDP:
 #ifdef USE_TCP
diff --git a/cfg.lex b/cfg.lex
index 058a31a..3a64c05 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -67,6 +67,8 @@
  *  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_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
 REV_DNS         rev_dns
 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_NO            dns_retr_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>{DNS_TRY_IPV6}        { count(); yylval.strval=yytext;
                                                                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;
                                                                return DNS_RETR_TIME; }
 <INITIAL>{DNS_RETR_NO} { count(); yylval.strval=yytext;
diff --git a/cfg.y b/cfg.y
index 062a179..ec121cd 100644 (file)
--- a/cfg.y
+++ b/cfg.y
  *              (vlada)
  * 2007-02-09  separated command needed for tls-in-core and for tls in general
  *              (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_TIMER2_POLICY (andrei)
+ * 2007-06-16  added DDNS_SRV_LB, DNS_TRY_NAPTR (andrei)
  */
 
 %{
        #define IF_DNS_FAILOVER(x) warn("dns failover support not compiled in")
 #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
        #define IF_DST_BLACKLIST(x) x
 #else
@@ -265,6 +272,11 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token DNS
 %token REV_DNS
 %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_NO
 %token DNS_SERVERS_NO
@@ -570,6 +582,16 @@ assign_stm:
        | REV_DNS EQUAL error { yyerror("boolean value expected"); }
        | DNS_TRY_IPV6 EQUAL NUMBER   { dns_try_ipv6=$3; }
        | 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 error { yyerror("number expected"); }
        | DNS_RETR_NO EQUAL NUMBER   { dns_retr_no=$3; }
index 34eebb1..2279092 100644 (file)
@@ -32,6 +32,7 @@
  *  2006-10-06  port 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-16  naptr support (andrei)
  */
 
 #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 */
 int dns_flags=0; /* default flags used for the  dns_*resolvehost 
                     (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 UNLOCK_DNS_HASH()      lock_release(dns_hash_lock)
@@ -126,6 +127,7 @@ static const char* dns_str_errors[]={
        "blacklisted ip",
        "name too long ", /* try again with a shorter name */
        "ip AF mismatch", /* address family mismatch */
+       "unresolvable NAPTR request", 
        "bug - critical error"
 };
 
@@ -293,6 +295,13 @@ int init_dns_cache()
                                        " support for it is not compiled -- ignoring\n");
 #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();
        if (dns_timer_h==0){
                ret=E_OUT_OF_MEM;
@@ -411,8 +420,10 @@ inline static struct dns_hash_entry* _dns_hash_find(str* name, int type,
        *err=0;
 again:
        *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,
                                                                                                name->len, type, *h);
+#endif
        clist_foreach_safe(&dns_hash[*h], e, tmp, next){
                /* automatically remove expired elements */
                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);
        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",
                        e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
+#endif
        LOCK_DNS_HASH();
                *dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
                                                                                 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);
        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",
                        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
                                                                                 only from within a lock */
        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;
        ticks_t now;
        
+#ifdef DNS_CACHE_DEBUG
        DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
                                                                        type, ttl, flags);
+#endif
        size=sizeof(struct dns_hash_entry)+name->len-1+1;
        e=shm_malloc(size);
        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 */
        if (size==0){
+#ifdef DNS_CACHE_DEBUG
                DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
                                name->len, name->s, type);
+#endif
                return 0;
        }
        /* compute size */
@@ -949,6 +968,7 @@ inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
                return 0;
        }
        memset(e, 0, size); /* init with 0 */
+       clist_init(e, next, prev);
        e->total_size=size;
        e->name_len=name->len;
        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);
                                rr->rdata=(void*)((char*)rr+
                                                                ROUND_POINTER(sizeof(struct dns_rr)));
-                               /* copy the whole srv_rdata block*/
+                               /* copy the whole naptr_rdata block*/
                                memcpy(rr->rdata, l->rdata, 
                                                NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
                                /* 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(
                                                                                        *(struct naptr_rdata*)l->rdata)));
                                tail_rr=&(rr->next);
+                               rr=rr->next;
                        }
                        break;
                case T_CNAME:
@@ -1267,6 +1288,7 @@ found:
                                                                                NAPTR_RDATA_SIZE(
                                                                                        *(struct naptr_rdata*)l->rdata)));
                                rec[r].tail_rr=&(rec[r].rr->next);
+                               rec[r].rr=rec[r].rr->next;
                                break;
                        case T_CNAME:
                                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;
        l=e;
+#ifdef DNS_CACHE_DEBUG
        DBG("dns_get_related(%p (%.*s, %d), %d, *%p) (%d)\n", e,
                        e->name_len, e->name, e->type, type, *records, cname_chain_len);
+#endif
        clist_init(l, next, prev);
        if (type==e->type){
                ret=e;
@@ -1347,7 +1371,8 @@ inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
                                                if (t){
                                                        if ((t->type==T_CNAME) && *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)){
@@ -1355,11 +1380,34 @@ inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
                                                if (t){
                                                        if ((t->type==T_CNAME) && *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;
+#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:
                                /* nothing extra */
                                break;
@@ -1593,7 +1641,7 @@ error:
  *               now - current time/ticks value
  * returns pointer to the rr on success and sets no to the rr number
  *         0 on error and fills the error flags
- *
      *
  * Example usage:
  * list all non-expired non-bad-marked ips for name:
  * e=dns_get_entry(name, T_A);
@@ -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
  * if *port!=0.
  * 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;
  *  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 ip_addr* ip;
        static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
        int len;
        str srv_name;
+       char srv_proto;
 
        if ((use_dns_cache==0) || (dns_hash==0)){ 
                /* not init or off => use normal, non-cached version */
                return _sip_resolvehost(name, port, proto);
        }
        len=0;
+       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) */
        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){
                        LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
                                                " (%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);
                        }
                        
-                       switch(proto){
+                       switch(srv_proto){
                                case PROTO_NONE: /* no proto specified, use udp */
-                                       goto skip_srv;
+                                       if (proto)
+                                               *proto=PROTO_UDP;
+                                       /* no break */
                                case PROTO_UDP:
                                        memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_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;
                                default:
                                        LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
-                                                       proto);
+                                                       (int)srv_proto);
                                        return 0;
                        }
 
@@ -2051,7 +2141,7 @@ struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
                                return he;
                }
        }
-skip_srv:
+/*skip_srv:*/
        if (name->len >= MAX_DNS_NAME) {
                LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
                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.
  *  (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 
@@ -2359,8 +2613,8 @@ 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, 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)
 {
        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;
        int ret;
        struct hostent* he;
+       char srv_proto;
 
        if (dns_hash==0){ /* not init => use normal, non-cached version */
                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;
        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 */
+               h->proto=srv_proto; /* store initial protocol */
                if (port){
                        if (*port==0){
                                /* try SRV if initial call & no port specified
@@ -2409,12 +2673,15 @@ int dns_sip_resolve(struct dns_srv_handle* h,  str* name,
 #endif
                                                *ip=*tmp_ip;
                                                *port=h->port;
+                                               /* proto already set */
                                                return 0;
                                        }
                                        
-                                       switch(proto){
+                                       switch(srv_proto){
                                                case PROTO_NONE: /* no proto specified, use udp */
-                                                       goto skip_srv;
+                                                       if (proto)
+                                                               *proto=PROTO_UDP;
+                                                       /* no break */
                                                case PROTO_UDP:
                                                        memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_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;
                                                default:
                                                        LOG(L_CRIT, "BUG: sip_resolvehost: "
-                                                                       "unknown proto %d\n", proto);
+                                                                       "unknown proto %d\n", (int)srv_proto);
                                                        return -E_DNS_CRITICAL;
                                        }
                                        srv_name.s=tmp;
                                        srv_name.len=len;
-                                       
                                        if ((ret=dns_srv_resolve_ip(h, &srv_name, ip,
                                                                                                                        port, flags))>=0)
                                        {
@@ -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", 
                                                        name->len, name->s, h->srv_no, h->ip_no, ret);
 #endif
+                                               /* proto already set */
                                                return ret;
                                        }
                                }
                        }else{ /* if (*port==0) */
                                h->port=*port; /* store initial port */
+                               /* proto already set */
                        }
                } /* if (port) */
        }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;
                        /* continue srv resolving */
                        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", 
                                        name->len, name->s, h->srv_no, h->ip_no, ret);
                        return ret;
        }
-skip_srv:
+/*skip_srv:*/
        if (name->len >= MAX_DNS_NAME) {
                LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
                return -E_DNS_NAME_TOO_LONG;
@@ -2472,13 +2742,133 @@ skip_srv:
        ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
        if (port)
                *port=h->port;
+       if (proto)
+               *proto=h->proto;
+#ifdef DNS_CACHE_DEBUG
        DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n", 
                        name->len, name->s, h->srv_no, h->ip_no, ret);
+#endif
        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
  * 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){
                                for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
                                        rpc->add(ctx, "sddddddd", 
-                                                               e->name, e->type, i, e->total_size,
-                                                               e->refcnt.val,
-                                                               (s_ticks_t)(e->expire-now)<0?-1:
+                                                               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(now-e->last_used),
-                                                               e->err_flags);
+                                                               (int)TICKS_TO_S(now-e->last_used),
+                                                               (int)e->err_flags);
                                        switch(e->type){
                                                case T_A:
                                                case T_AAAA:
@@ -2616,8 +3006,8 @@ void dns_cache_debug_all(rpc_t* rpc, void* ctx)
                                                                        ((struct srv_rdata*)(rr->rdata))->name);
                                                        break;
                                                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;
                                                case T_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, "dd",
-                                                               (s_ticks_t)(rr->expire-now)<0?-1:
+                                                               (int)(s_ticks_t)(rr->expire-now)<0?-1:
                                                                        TICKS_TO_S(rr->expire-now),
-                                                       rr->err_flags);
+                                                               (int)rr->err_flags);
                                }
                        }
                }
index 848c048..f53dec0 100644 (file)
@@ -29,6 +29,7 @@
 /* History:
  * --------
  *  2006-07-13  created by andrei
+ *  2007-06-16  naptr support (andrei)
  */
 
 
@@ -36,6 +37,7 @@
 #define __dns_cache_h
 
 #include "str.h"
+#include "config.h" /* MAX_BRANCHES */
 #include "timer.h"
 #include "ip_addr.h"
 #include "atomic_ops.h"
@@ -73,13 +75,12 @@ enum dns_errors{
                                        E_DNS_AF_MISMATCH /* ipv4 or ipv6 only requested, but 
                                                                                 name contains an ip addr. of the
                                                                                 opossite type */ ,
+                                       E_DNS_NO_NAPTR /* unresolvable naptr record */,
                                        E_DNS_CRITICAL /* critical error, marks the end
                                                                          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) */
 const char* dns_strerror(int err);
@@ -92,7 +93,8 @@ const char* dns_strerror(int err);
 #define DNS_IPV4_ONLY  1
 #define DNS_IPV6_ONLY  2
 #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 */
@@ -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;
+#else
+typedef unsigned long long srv_flags_t;
+#endif
 
 struct dns_srv_handle{
        struct dns_hash_entry* srv; /* srv entry */
@@ -151,7 +160,8 @@ struct dns_srv_handle{
 #endif
        unsigned short port; /* current port */
        unsigned char srv_no; /* current record no. in the srv entry */
-       unsigned char ip_no;   /* current record no. in the a/aaaa entry */
+       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_no=h->ip_no=0;
+       h->port=0;
+       h->proto=0;
 #ifdef DNS_SRV_LB
        h->srv_tried_rrs=0;
 #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
  */
 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 */ 
 inline static int dns_sip_resolve2su(struct dns_srv_handle* h,
                                                                         union sockaddr_union* su,
                                                                         str* name, unsigned short port,
-                                                                        int proto, int flags)
+                                                                        char* proto, int flags)
 {
        struct ip_addr ip;
        int ret;
index c8c57ee..9ceac1d 100644 (file)
@@ -36,6 +36,6 @@
 
 struct hostent* dns_resolvehost(char* name);
 struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
-                                                                               int proto);
+                                                                               char* proto);
 
 #endif
index bbc618e..472e0e6 100644 (file)
@@ -3,6 +3,7 @@
 # History:
 # --------
 # 2006-09-08  created by andrei
+# 2007-06-18  added naptr & friends, dns_srv_lb, more compile options (andrei)
 #
 
 Overview
@@ -18,6 +19,8 @@ Overview
   destination host doesn't send any reply to a forwarded invite within the
   sip timeout interval (whose value can be configured using the tm fr_timer
    parameter).
+ 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
@@ -42,6 +45,19 @@ DNS Cache and Failover Drawbacks
      Workaround: compile without dns failover support (-DUSE_DNS_FAILOVER).
   Turning it off from the config file is not enough in this case (the extra
    memory will still be used).
+ 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
@@ -59,6 +75,41 @@ DNS Resolver Options
       If off only ipv4 (A) lookups will be used.
       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.
       Default: system specific, depends also on the/etc/resolv.conf content
       (usually 5 s).
@@ -98,6 +149,19 @@ DNS Resolver Options
  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
 
    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
    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
      the default behaviour (low level).
       Possible values:
@@ -165,9 +255,38 @@ DNS Cache Compile Options
    USE_DNS_FAILOVER - if defined the dns failover support will be compiled in.
       (default). Compiling the dns failover support has a few disadvantages,
       see the "Drawbacks" section.
+
+   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 
    form DEFS list. To add a compile options add it to the make command line,
      e.g.: make proper; make all extra_defs=-DUSE_DNS_FAILOVER
    or for a permanent solution, edit Makefile.defs and add it to DEFS 
-   (don't foget to prefix it with -D).
+   (don't foget to prefix it with -D). Some options require editing 
+   dns_cache.c or resolve.[ch] (just grep after them).
index c6c3b5f..0df5324 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -315,7 +315,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                if (use_dns_failover){
                        dns_srv_handle_init(&dns_srv_h);
                        err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-                                                                       send_info->proto, dns_flags);
+                                                                       &send_info->proto, dns_flags);
                        if (err!=0){
                                LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
                                                " failed: %s [%d]\n", dst->len, ZSW(dst->s),
@@ -325,7 +325,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                        }
                }else
 #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,"
                                                " dropping packet\n", dst->len, ZSW(dst->s));
                        ret=E_BAD_ADDRESS;
@@ -449,7 +449,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 #ifdef USE_DNS_FAILOVER
        }while(dst && use_dns_failover && dns_srv_handle_next(&dns_srv_h, err) && 
                        ((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
-                                                                 send_info->proto, dns_flags))==0));
+                                                                 &send_info->proto, dns_flags))==0));
        if ((err!=0) && (err!=-E_DNS_EOR)){
                LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
                                                        " failed: %s [%d] (dropping packet)\n",
@@ -483,6 +483,7 @@ int update_sock_struct_from_via( union sockaddr_union* to,
        str* name;
        int err;
        unsigned short port;
+       char proto;
 
        port=0;
        if(via==msg->via1){ 
@@ -526,7 +527,8 @@ int update_sock_struct_from_via( union sockaddr_union* to,
            sip_resolvehost now accepts str -janakj
        */
        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){
                LOG(L_NOTICE, "ERROR:forward_reply:resolve_host(%.*s) failure\n",
index d445786..48bb25e 100644 (file)
--- a/globals.h
+++ b/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 int dns_flags; /* default flags used for the  dns_*resolvehost 
                     (compatibility wrappers) */
+extern int dns_srv_lb; /* default SRV LB support value */
+
 #endif
 #ifdef USE_DST_BLACKLIST
 extern int use_dst_blacklist; /* 1 if the blacklist is enabled */
index d743b4d..a7f4cba 100644 (file)
--- a/ip_addr.h
+++ b/ip_addr.h
@@ -89,13 +89,13 @@ struct socket_info{
        str name; /* name - eg.: foo.bar or 10.0.0.1 */
        struct ip_addr address; /* ip address */
        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*/
        enum si_flags flags; /* SI_IS_IP | SI_IS_LO | SI_IS_MCAST */
        union sockaddr_union su; 
-       int proto; /* tcp or udp*/
        struct socket_info* next;
        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*/
        struct socket_info* bind_address; /* sock_info structure on which 
                                                                          the msg was received*/
-       short proto;
+       char proto;
 #ifdef USE_COMP
        short comp; /* compression */
 #endif
@@ -121,7 +121,7 @@ struct dest_info{
        struct socket_info* send_sock;
        union sockaddr_union to;
        int id; /* tcp stores the connection id here */ 
-       short proto;
+       char proto;
 #ifdef USE_COMP
        short comp;
 #endif
diff --git a/main.c b/main.c
index ab47123..bcc402e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1579,7 +1579,7 @@ try_again:
                goto error;
        }
 #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");
                goto error;
        }
diff --git a/proxy.c b/proxy.c
index e20aa41..a53ca08 100644 (file)
--- a/proxy.c
+++ b/proxy.c
@@ -200,10 +200,11 @@ error:
 /* same as add_proxy, but it doesn't add the proxy to the list
  * 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 hostent* he;
+       char proto;
 
        p=(struct proxy_l*) pkg_malloc(sizeof(struct proxy_l));
        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));
        p->name=*name;
        p->port=port;
-       p->proto=proto;
 
        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){
                ser_error=E_BAD_ADDRESS;
                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);
                goto error;
        }
+       p->proto=proto;
        p->ok=1;
        return p;
 error:
index a1b73c4..d8b0449 100644 (file)
--- a/resolve.c
+++ b/resolve.c
@@ -35,6 +35,7 @@
  *  2006-07-17  rdata contains now also the record name (andrei)
  *  2006-08-18  get_record can append also the additional records to the
  *               returned list (andrei)
+ *  2007-06-15  naptr support (andrei)
  */ 
 
 
 #include <string.h>
 
 #include "resolve.h"
+#include "compiler_opt.h"
 #include "dprint.h"
 #include "mem/mem.h"
 #include "ip_addr.h"
 #include "error.h"
+#include "globals.h" /* tcp_disable, tls_disable a.s.o */
 
 #ifdef USE_DNS_CACHE
 #include "dns_cache.h"
@@ -65,6 +68,17 @@ int dns_try_ipv6=1; /* default on */
 #else
 int dns_try_ipv6=0; /* off, if no ipv6 support */
 #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 */
 int dns_retr_time=-1;
 int dns_retr_no=-1;
@@ -72,6 +86,23 @@ int dns_servers_no=-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
  * params: retr_time  - time before retransmitting (must be >0)
  *         retr_no    - retransmissions number
@@ -102,6 +133,9 @@ int resolv_init()
 #warning "no resolv timeout support"
        LOG(L_WARN, "WARNING: resolv_init: no resolv options support - resolv"
                        " options will be ignored\n");
+#endif
+#ifdef USE_NAPTR
+       init_naptr_proto_prefs();
 #endif
        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");
                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_len=flags_len;
@@ -555,7 +589,9 @@ again:
        if (flags & RES_AR){
                flags&=~RES_AR;
                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);
+#endif
                for (r=0; (r<answers_no) && (p<end); r++){
                        /* skip over the ns records */
                        if ((p=dns_skipname(p, end))==0) {
@@ -568,7 +604,9 @@ again:
                        p+=2+2+4+2+ntohs(rdlength);
                }
                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);
+#endif
                goto again; /* add also the additional records */
        }
                        
@@ -591,47 +629,242 @@ not_found:
        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
+ * 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;
  *  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 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 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) */
        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){
                        LOG(L_WARN, "WARNING: sip_resolvehost: domain name too long (%d),"
                                                " unable to perform SRV lookup\n", name->len);
                }else{
-                       /* check if it's an ip address */
-                       if ( ((ip=str2ip(name))!=0)
-#ifdef USE_IPV6
-                                 || ((ip=str2ip6(name))!=0)
-#endif
-                               ){
-                               /* we are lucky, this is an ip address */
-                               return ip_addr2he(name,ip);
-                       }
                        
-                       switch(proto){
+                       switch(srv_proto){
                                case PROTO_NONE: /* no proto specified, use udp */
-                                       goto skip_srv;
+                                       if (proto)
+                                               *proto=PROTO_UDP;
+                                       /* no break */
                                case PROTO_UDP:
                                        memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_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;
                                default:
                                        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*/
                                srv=(struct srv_rdata*) l->rdata;
                                if (srv==0){
                                        LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n");
-                                       free_rdata_list(head);
+                                       /* cleanup on exit only */
                                        break;
                                }
                                he=resolvehost(srv->name);
                                if (he!=0){
                                        /* we found it*/
+#ifdef RESOLVE_DBG
                                        DBG("sip_resolvehost: SRV(%s) = %s:%d\n",
-                                                       tmp, srv->name, srv->port);
+                                                       srv_target, srv->name, srv->port);
+#endif
                                        *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," 
                                        " 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) {
-               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;
 }
+#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)
  *  and write the result in the sockaddr_union to
  *  returns -1 on error (resolve failed), 0 on success */
 int sip_hostport2su(union sockaddr_union* su, str* name, unsigned short port,
-                                               int proto)
+                                               char* proto)
 {
        struct hostent* he;
        
-       
        he=sip_resolvehost(name, &port, proto);
        if (he==0){
                ser_error=E_BAD_ADDRESS;
index ff3b42d..5e78275 100644 (file)
--- a/resolve.h
+++ b/resolve.h
@@ -33,6 +33,7 @@
  *  2006-07-13  rdata structures put on diet (andrei)
  *  2006-07-17  rdata contains now also the record name (andrei)
  *  2006-08-18  get_record uses flags (andrei)
+ *  2006-06-16  naptr support (andrei)
  */
 
 
 #include "dns_wrappers.h"
 #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 ANS_SIZE       8192
@@ -151,6 +157,10 @@ void free_rdata_list(struct rdata* head);
 
 
 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)\
@@ -200,8 +210,10 @@ static inline struct ip_addr* str2ip(str* st)
        
        return &ip;
 error_dots:
+#ifdef RESOLVE_DBG
        DBG("str2ip: ERROR: too %s dots in [%.*s]\n", (i>3)?"many":"few", 
                        st->len, st->s);
+#endif
        return 0;
  error_char:
        /*
@@ -291,15 +303,21 @@ static inline struct ip_addr* str2ip6(str* st)
        return &ip;
 
 error_too_many_colons:
+#ifdef RESOLVE_DBG
        DBG("str2ip6: ERROR: too many colons in [%.*s]\n", st->len, st->s);
+#endif
        return 0;
 
 error_too_few_colons:
+#ifdef RESOLVE_DBG
        DBG("str2ip6: ERROR: too few colons in [%.*s]\n", st->len, st->s);
+#endif
        return 0;
 
 error_colons:
+#ifdef RESOLVE_DBG
        DBG("str2ip6: ERROR: too many double colons in [%.*s]\n", st->len, st->s);
+#endif
        return 0;
 
 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 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
 
 
+
+#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
index 7ced62b..d82cb22 100644 (file)
--- a/version.h
+++ b/version.h
 #define USE_DNS_FAILOVER_STR ""
 #endif
 
+#ifdef USE_NAPTR
+#define USE_NAPTR_STR ", USE_NAPTR"
+#else
+#define USE_NAPTR_STR ""
+#endif
+
 #ifdef USE_DST_BLACKLIST
 #define USE_DST_BLACKLIST_STR ", USE_DST_BLACKLIST"
 #else
        USE_FUTEX_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_DST_BLACKLIST_STR
+       USE_NAPTR_STR USE_DST_BLACKLIST_STR
 
 
 #endif