core: Fixes for WSS (secure WebSocket) transport and Via:s
[sip-router] / msg_translator.c
index ae52878..0447bfa 100644 (file)
  *--andrei
 */
 
+/*!
+ * \file
+ * \brief SIP-router core :: 
+ * \ingroup core
+ * Module: \ref core
+ */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
 #include "ut.h"
 #include "pt.h"
 #include "cfg/cfg.h"
+#include "parser/parse_to.h"
 #include "forward.h"
 
 
-#define append_str(_dest,_src,_len) \
-       do{\
-               memcpy( (_dest) , (_src) , (_len) );\
-               (_dest) += (_len) ;\
-       }while(0);
-
 #define append_str_trans(_dest,_src,_len,_msg) \
        append_str( (_dest), (_src), (_len) );
 
 extern char version[];
 extern int version_len;
 
-/* global flags for build_req_from_sip_req */
-static unsigned int global_req_flags=0;
-
 
 
 /** per process fixup function for global_req_flags.
   * It should be called from the configuration framework.
   */
-void fix_global_req_flags( str* name)
+void fix_global_req_flags(str* gname, str* name)
 {
        global_req_flags=0;
        switch(cfg_get(core, core_cfg, udp_mtu_try_proto)){
@@ -196,16 +195,20 @@ static int check_via_address(struct ip_addr* ip, str *name,
        struct hostent* he;
        int i;
        char* s;
+       #ifdef USE_IPV6
        int len;
+       #endif
 
        /* maybe we are lucky and name it's an ip */
        s=ip_addr2a(ip);
        if (s){
                DBG("check_via_address(%s, %.*s, %d)\n",
                        s, name->len, name->s, resolver);
-               len=strlen(s);
 
        #ifdef USE_IPV6
+
+               len=strlen(s);
+
                /* check if name->s is an ipv6 address or an ipv6 address ref. */
                if ((ip->af==AF_INET6) &&
                                (       ((len==name->len)&&(strncasecmp(name->s, s, name->len)==0))
@@ -486,7 +489,7 @@ char* clen_builder( struct sip_msg* msg, int *clen_len, int diff,
 
 /* checks if a lump opt condition
  * returns 1 if cond is true, 0 if false */
-static inline int lump_check_opt(      enum lump_conditions cond,
+static inline int lump_check_opt(      struct lump *l,
                                                                        struct sip_msg* msg,
                                                                        struct dest_info* snd_i
                                                                        )
@@ -510,10 +513,11 @@ static inline int lump_check_opt( enum lump_conditions cond,
                                proto=msg->rcv.proto; \
                        } \
 
-       switch(cond){
+       switch(l->u.cond){
                case COND_FALSE:
                        return 0;
                case COND_TRUE:
+                       LUMP_SET_COND_TRUE(l);
                        return 1;
                case COND_IF_DIFF_REALMS:
                        get_ip_port_proto;
@@ -525,28 +529,43 @@ static inline int lump_check_opt( enum lump_conditions cond,
 #endif
                                        (ip_addr_cmp(ip, &snd_i->send_sock->address)))
                                return 0;
-                       else return 1;
+                       else {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       }
                case COND_IF_DIFF_AF:
                        get_ip_port_proto;
-                       if (ip->af!=snd_i->send_sock->address.af) return 1;
-                       else return 0;
+                       if (ip->af!=snd_i->send_sock->address.af) {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       } else return 0;
                case COND_IF_DIFF_PROTO:
                        get_ip_port_proto;
-                       if (proto!=snd_i->send_sock->proto) return 1;
-                       else return 0;
+                       if (proto!=snd_i->send_sock->proto) {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       } else return 0;
                case COND_IF_DIFF_PORT:
                        get_ip_port_proto;
-                       if (port!=snd_i->send_sock->port_no) return 1;
-                       else return 0;
+                       if (port!=snd_i->send_sock->port_no) {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       } else return 0;
                case COND_IF_DIFF_IP:
                        get_ip_port_proto;
                        if (ip_addr_cmp(ip, &snd_i->send_sock->address)) return 0;
-                       else return 1;
+                       else {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       }
                case COND_IF_RAND:
-                       return (rand()>=RAND_MAX/2);
+                       if(rand()>=RAND_MAX/2) {
+                               LUMP_SET_COND_TRUE(l);
+                               return 1;
+                       } else return 0;
                default:
                        LOG(L_CRIT, "BUG: lump:w_check_opt: unknown lump condition %d\n",
-                                       cond);
+                                       l->u.cond);
        }
        return 0; /* false */
 }
@@ -564,6 +583,9 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
        struct lump* r;
        str* send_address_str;
        str* send_port_str;
+       str* recv_address_str = NULL;
+       str* recv_port_str = NULL;
+       int  recv_port_no = 0;
        struct socket_info* send_sock;
        
 
@@ -608,7 +630,7 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                switch((subst_l)->u.subst){ \
                        case SUBST_RCV_IP: \
                                if (msg->rcv.bind_address){ \
-                                       new_len+=msg->rcv.bind_address->address_str.len; \
+                                       new_len+=recv_address_str->len; \
                                        if (msg->rcv.bind_address->address.af!=AF_INET) \
                                                new_len+=2; \
                                }else{ \
@@ -618,7 +640,7 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                break; \
                        case SUBST_RCV_PORT: \
                                if (msg->rcv.bind_address){ \
-                                       new_len+=msg->rcv.bind_address->port_no_str.len; \
+                                       new_len+=recv_port_str->len; \
                                }else{ \
                                        /* FIXME */ \
                                        LOG(L_CRIT, "FIXME: null bind_address\n"); \
@@ -629,13 +651,23 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                        switch(msg->rcv.bind_address->proto){ \
                                                case PROTO_NONE: \
                                                case PROTO_UDP: \
+                                                       new_len+=3; \
+                                                       break; \
                                                case PROTO_TCP: \
                                                case PROTO_TLS: \
-                                                               new_len+=3; \
-                                                               break; \
+                                                       switch(msg->rcv.proto){ \
+                                                               case PROTO_WS: \
+                                                               case PROTO_WSS: \
+                                                                       new_len+=2; \
+                                                                       break; \
+                                                               default: \
+                                                                       new_len+=3; \
+                                                                       break; \
+                                                       } \
+                                                       break; \
                                                case PROTO_SCTP: \
-                                                               new_len+=4; \
-                                                               break; \
+                                                       new_len+=4; \
+                                                       break; \
                                                default: \
                                                LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
                                                                msg->rcv.bind_address->proto); \
@@ -647,25 +679,33 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                break; \
                        case SUBST_RCV_ALL: \
                                if (msg->rcv.bind_address){ \
-                                       new_len+=msg->rcv.bind_address->address_str.len; \
+                                       new_len+=recv_address_str->len; \
                                        if (msg->rcv.bind_address->address.af!=AF_INET) \
                                                new_len+=2; \
-                                       if (msg->rcv.bind_address->port_no!=SIP_PORT){ \
+                                       if (recv_port_no!=SIP_PORT){ \
                                                /* add :port_no */ \
-                                               new_len+=1+msg->rcv.bind_address->port_no_str.len; \
+                                               new_len+=1+recv_port_str->len; \
                                        }\
                                                /*add;transport=xxx*/ \
                                        switch(msg->rcv.bind_address->proto){ \
                                                case PROTO_NONE: \
                                                case PROTO_UDP: \
-                                                               break; /* udp is the default */ \
+                                                       break; /* udp is the default */ \
                                                case PROTO_TCP: \
                                                case PROTO_TLS: \
-                                                               new_len+=TRANSPORT_PARAM_LEN+3; \
-                                                               break; \
+                                                       switch(msg->rcv.proto){ \
+                                                               case PROTO_WS: \
+                                                               case PROTO_WSS: \
+                                                                       new_len+=TRANSPORT_PARAM_LEN+2; \
+                                                                       break; \
+                                                               default: \
+                                                                       new_len+=TRANSPORT_PARAM_LEN+3; \
+                                                                       break; \
+                                                       } \
+                                                       break; \
                                                case PROTO_SCTP: \
-                                                               new_len+=TRANSPORT_PARAM_LEN+4; \
-                                                               break; \
+                                                       new_len+=TRANSPORT_PARAM_LEN+4; \
+                                                       break; \
                                                default: \
                                                LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
                                                                msg->rcv.bind_address->proto); \
@@ -700,13 +740,23 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                        switch(send_sock->proto){ \
                                                case PROTO_NONE: \
                                                case PROTO_UDP: \
+                                                       new_len+=3; \
+                                                       break; \
                                                case PROTO_TCP: \
                                                case PROTO_TLS: \
-                                                               new_len+=3; \
-                                                               break; \
+                                                       switch(send_info->proto){ \
+                                                               case PROTO_WS: \
+                                                               case PROTO_WSS: \
+                                                                       new_len+=2; \
+                                                                       break; \
+                                                               default: \
+                                                                       new_len+=3; \
+                                                                       break; \
+                                                       } \
+                                                       break; \
                                                case PROTO_SCTP: \
-                                                               new_len+=4; \
-                                                               break; \
+                                                       new_len+=4; \
+                                                       break; \
                                                default: \
                                                LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
                                                                send_sock->proto); \
@@ -731,14 +781,22 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                        switch(send_sock->proto){ \
                                                case PROTO_NONE: \
                                                case PROTO_UDP: \
-                                                               break; /* udp is the default */ \
+                                                       break; /* udp is the default */ \
                                                case PROTO_TCP: \
                                                case PROTO_TLS: \
-                                                               new_len+=TRANSPORT_PARAM_LEN+3; \
-                                                               break; \
+                                                       switch(send_info->proto){ \
+                                                               case PROTO_WS: \
+                                                               case PROTO_WSS: \
+                                                                       new_len+=TRANSPORT_PARAM_LEN+2; \
+                                                                       break; \
+                                                               default: \
+                                                                       new_len+=TRANSPORT_PARAM_LEN+3; \
+                                                                       break; \
+                                                       } \
+                                                       break; \
                                                case PROTO_SCTP: \
-                                                               new_len+=TRANSPORT_PARAM_LEN+4; \
-                                                               break; \
+                                                       new_len+=TRANSPORT_PARAM_LEN+4; \
+                                                       break; \
                                                default: \
                                                LOG(L_CRIT, "BUG: lumps_len: unknown proto %d\n", \
                                                                send_sock->proto); \
@@ -765,19 +823,36 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
        s_offset=0;
        new_len=0;
        /* init send_address_str & send_port_str */
-       if (msg->set_global_address.len)
+       if(send_sock && send_sock->useinfo.name.len>0)
+               send_address_str=&(send_sock->useinfo.name);
+       else if (msg->set_global_address.len)
                send_address_str=&(msg->set_global_address);
        else
                send_address_str=&(send_sock->address_str);
-       if (msg->set_global_port.len)
+       if(send_sock && send_sock->useinfo.port_no>0)
+               send_port_str=&(send_sock->useinfo.port_no_str);
+       else if (msg->set_global_port.len)
                send_port_str=&(msg->set_global_port);
        else
                send_port_str=&(send_sock->port_no_str);
-
+       /* init recv_address_str, recv_port_str & recv_port_no */
+       if(msg->rcv.bind_address) {
+               if(msg->rcv.bind_address->useinfo.name.len>0)
+                       recv_address_str=&(msg->rcv.bind_address->useinfo.name);
+               else
+                       recv_address_str=&(msg->rcv.bind_address->address_str);
+               if(msg->rcv.bind_address->useinfo.port_no>0) {
+                       recv_port_str=&(msg->rcv.bind_address->useinfo.port_no_str);
+                       recv_port_no = msg->rcv.bind_address->useinfo.port_no;
+               } else {
+                       recv_port_str=&(msg->rcv.bind_address->port_no_str);
+                       recv_port_no = msg->rcv.bind_address->port_no;
+               }
+       }
 
        for(t=lumps;t;t=t->next){
                /* skip if this is an OPT lump and the condition is not satisfied */
-               if ((t->op==LUMP_ADD_OPT)&& !lump_check_opt(t->u.cond, msg, send_info))
+               if ((t->op==LUMP_ADD_OPT)&& !lump_check_opt(t, msg, send_info))
                        continue;
                for(r=t->before;r;r=r->before){
                        switch(r->op){
@@ -790,7 +865,7 @@ static inline int lumps_len(struct sip_msg* msg, struct lump* lumps,
                                case LUMP_ADD_OPT:
                                        /* skip if this is an OPT lump and the condition is
                                         * not satisfied */
-                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                       if (!lump_check_opt(r, msg, send_info))
                                                goto skip_before;
                                        break;
                                default:
@@ -846,7 +921,7 @@ skip_before:
                                case LUMP_ADD_OPT:
                                        /* skip if this is an OPT lump and the condition is
                                         * not satisfied */
-                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                       if (!lump_check_opt(r, msg, send_info))
                                                goto skip_after;
                                        break;
                                default:
@@ -883,6 +958,9 @@ static inline void process_lumps(   struct sip_msg* msg,
        int s_offset;
        str* send_address_str;
        str* send_port_str;
+       str* recv_address_str = NULL;
+       str* recv_port_str = NULL;
+       int  recv_port_no = 0;
        struct socket_info* send_sock;
 
 #ifdef USE_COMP
@@ -943,9 +1021,9 @@ static inline void process_lumps(  struct sip_msg* msg,
                                if (msg->rcv.bind_address->address.af!=AF_INET){\
                                        new_buf[offset]='['; offset++; \
                                }\
-                               memcpy(new_buf+offset, msg->rcv.bind_address->address_str.s, \
-                                               msg->rcv.bind_address->address_str.len); \
-                               offset+=msg->rcv.bind_address->address_str.len; \
+                               memcpy(new_buf+offset, recv_address_str->s, \
+                                               recv_address_str->len); \
+                               offset+=recv_address_str->len; \
                                if (msg->rcv.bind_address->address.af!=AF_INET){\
                                        new_buf[offset]=']'; offset++; \
                                }\
@@ -956,9 +1034,9 @@ static inline void process_lumps(  struct sip_msg* msg,
                        break; \
                case SUBST_RCV_PORT: \
                        if (msg->rcv.bind_address){  \
-                               memcpy(new_buf+offset, msg->rcv.bind_address->port_no_str.s, \
-                                               msg->rcv.bind_address->port_no_str.len); \
-                               offset+=msg->rcv.bind_address->port_no_str.len; \
+                               memcpy(new_buf+offset, recv_port_str->s, \
+                                               recv_port_str->len); \
+                               offset+=recv_port_str->len; \
                        }else{  \
                                /*FIXME*/ \
                                LOG(L_CRIT, "FIXME: process_lumps: null bind_address\n"); \
@@ -970,19 +1048,19 @@ static inline void process_lumps(        struct sip_msg* msg,
                                if (msg->rcv.bind_address->address.af!=AF_INET){\
                                        new_buf[offset]='['; offset++; \
                                }\
-                               memcpy(new_buf+offset, msg->rcv.bind_address->address_str.s, \
-                                               msg->rcv.bind_address->address_str.len); \
-                               offset+=msg->rcv.bind_address->address_str.len; \
+                               memcpy(new_buf+offset, recv_address_str->s, \
+                                               recv_address_str->len); \
+                               offset+=recv_address_str->len; \
                                if (msg->rcv.bind_address->address.af!=AF_INET){\
                                        new_buf[offset]=']'; offset++; \
                                }\
                                /* :port */ \
-                               if (msg->rcv.bind_address->port_no!=SIP_PORT){ \
+                               if (recv_port_no!=SIP_PORT){ \
                                        new_buf[offset]=':'; offset++; \
                                        memcpy(new_buf+offset, \
-                                                       msg->rcv.bind_address->port_no_str.s, \
-                                                       msg->rcv.bind_address->port_no_str.len); \
-                                       offset+=msg->rcv.bind_address->port_no_str.len; \
+                                                       recv_port_str->s, \
+                                                       recv_port_str->len); \
+                                       offset+=recv_port_str->len; \
                                }\
                                switch(msg->rcv.bind_address->proto){ \
                                        case PROTO_NONE: \
@@ -992,15 +1070,25 @@ static inline void process_lumps(        struct sip_msg* msg,
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
                                                                TRANSPORT_PARAM_LEN); \
                                                offset+=TRANSPORT_PARAM_LEN; \
-                                               memcpy(new_buf+offset, "tcp", 3); \
-                                               offset+=3; \
+                                               if (msg->rcv.proto == PROTO_WS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tcp", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_TLS: \
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
                                                                TRANSPORT_PARAM_LEN); \
                                                offset+=TRANSPORT_PARAM_LEN; \
-                                               memcpy(new_buf+offset, "tls", 3); \
-                                               offset+=3; \
+                                               if (msg->rcv.proto == PROTO_WS || msg->rcv.proto == PROTO_WSS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tls", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_SCTP: \
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
@@ -1079,15 +1167,25 @@ static inline void process_lumps(       struct sip_msg* msg,
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
                                                                TRANSPORT_PARAM_LEN); \
                                                offset+=TRANSPORT_PARAM_LEN; \
-                                               memcpy(new_buf+offset, "tcp", 3); \
-                                               offset+=3; \
+                                               if (send_info->proto == PROTO_WS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tcp", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_TLS: \
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
                                                                TRANSPORT_PARAM_LEN); \
                                                offset+=TRANSPORT_PARAM_LEN; \
-                                               memcpy(new_buf+offset, "tls", 3); \
-                                               offset+=3; \
+                                               if (send_info->proto == PROTO_WS || send_info->proto == PROTO_WSS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tls", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_SCTP: \
                                                memcpy(new_buf+offset, TRANSPORT_PARAM, \
@@ -1115,12 +1213,22 @@ static inline void process_lumps(       struct sip_msg* msg,
                                                offset+=3; \
                                                break; \
                                        case PROTO_TCP: \
-                                               memcpy(new_buf+offset, "tcp", 3); \
-                                               offset+=3; \
+                                               if (msg->rcv.proto == PROTO_WS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tcp", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_TLS: \
-                                               memcpy(new_buf+offset, "tls", 3); \
-                                               offset+=3; \
+                                               if (msg->rcv.proto == PROTO_WS || msg->rcv.proto == PROTO_WSS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tls", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_SCTP: \
                                                memcpy(new_buf+offset, "sctp", 4); \
@@ -1145,12 +1253,22 @@ static inline void process_lumps(       struct sip_msg* msg,
                                                offset+=3; \
                                                break; \
                                        case PROTO_TCP: \
-                                               memcpy(new_buf+offset, "tcp", 3); \
-                                               offset+=3; \
+                                               if (send_info->proto == PROTO_WS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tcp", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_TLS: \
-                                               memcpy(new_buf+offset, "tls", 3); \
-                                               offset+=3; \
+                                               if (send_info->proto == PROTO_WS || send_info->proto == PROTO_WSS) { \
+                                                       memcpy(new_buf+offset, "ws", 2); \
+                                                       offset+=2; \
+                                               } else { \
+                                                       memcpy(new_buf+offset, "tls", 3); \
+                                                       offset+=3; \
+                                               } \
                                                break; \
                                        case PROTO_SCTP: \
                                                memcpy(new_buf+offset, "sctp", 4); \
@@ -1185,7 +1303,33 @@ static inline void process_lumps(        struct sip_msg* msg,
                send_port_str=&(msg->set_global_port);
        else
                send_port_str=&(send_sock->port_no_str);
-
+       /* init send_address_str & send_port_str */
+       if(send_sock && send_sock->useinfo.name.len>0)
+               send_address_str=&(send_sock->useinfo.name);
+       else if (msg->set_global_address.len)
+               send_address_str=&(msg->set_global_address);
+       else
+               send_address_str=&(send_sock->address_str);
+       if(send_sock && send_sock->useinfo.port_no>0)
+               send_port_str=&(send_sock->useinfo.port_no_str);
+       else if (msg->set_global_port.len)
+               send_port_str=&(msg->set_global_port);
+       else
+               send_port_str=&(send_sock->port_no_str);
+       /* init recv_address_str, recv_port_str & recv_port_no */
+       if(msg->rcv.bind_address) {
+               if(msg->rcv.bind_address->useinfo.name.len>0)
+                       recv_address_str=&(msg->rcv.bind_address->useinfo.name);
+               else
+                       recv_address_str=&(msg->rcv.bind_address->address_str);
+               if(msg->rcv.bind_address->useinfo.port_no>0) {
+                       recv_port_str=&(msg->rcv.bind_address->useinfo.port_no_str);
+                       recv_port_no = msg->rcv.bind_address->useinfo.port_no;
+               } else {
+                       recv_port_str=&(msg->rcv.bind_address->port_no_str);
+                       recv_port_no = msg->rcv.bind_address->port_no;
+               }
+       }
 
        orig=msg->buf;
        offset=*new_buf_offs;
@@ -1199,7 +1343,7 @@ static inline void process_lumps( struct sip_msg* msg,
                                /* skip if this is an OPT lump and the condition is
                                 * not satisfied */
                                if ((t->op==LUMP_ADD_OPT) &&
-                                               (!lump_check_opt(t->u.cond, msg, send_info)))
+                                               (!lump_check_opt(t, msg, send_info)))
                                        continue;
                                /* just add it here! */
                                /* process before  */
@@ -1216,7 +1360,7 @@ static inline void process_lumps( struct sip_msg* msg,
                                                case LUMP_ADD_OPT:
                                                        /* skip if this is an OPT lump and the condition is
                                                        * not satisfied */
-                                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                                       if (!lump_check_opt(r, msg, send_info))
                                                                goto skip_before;
                                                        break;
                                                default:
@@ -1257,7 +1401,7 @@ skip_before:
                                                case LUMP_ADD_OPT:
                                                        /* skip if this is an OPT lump and the condition is
                                                        * not satisfied */
-                                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                                       if (!lump_check_opt(r, msg, send_info))
                                                                goto skip_after;
                                                        break;
                                                default:
@@ -1298,7 +1442,7 @@ skip_after:
                                                case LUMP_ADD_OPT:
                                                        /* skip if this is an OPT lump and the condition is
                                                        * not satisfied */
-                                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                                       if (!lump_check_opt(r, msg, send_info))
                                                                goto skip_nop_before;
                                                        break;
                                                default:
@@ -1327,7 +1471,7 @@ skip_nop_before:
                                                case LUMP_ADD_OPT:
                                                        /* skip if this is an OPT lump and the condition is
                                                        * not satisfied */
-                                                       if (!lump_check_opt(r->u.cond, msg, send_info))
+                                                       if (!lump_check_opt(r, msg, send_info))
                                                                goto skip_nop_after;
                                                        break;
                                                default:
@@ -1358,6 +1502,10 @@ static inline int adjust_clen(struct sip_msg* msg, int body_delta, int proto)
        struct lump* anchor;
        char* clen_buf;
        int clen_len, body_only;
+#ifdef USE_TCP
+       char* body;
+       int comp_clen;
+#endif /* USE_TCP */
 
        /* Calculate message length difference caused by lumps modifying message
         * body, from this point on the message body must not be modified. Zero
@@ -1379,7 +1527,7 @@ static inline int adjust_clen(struct sip_msg* msg, int body_delta, int proto)
                        LOG(L_ERR, "adjust_clen: error parsing content-length\n");
                        goto error;
                }
-               if (msg->content_length==0){
+               if (unlikely(msg->content_length==0)){
                        /* not present, we need to add it */
                        /* msg->unparsed should point just before the final crlf
                         * - whole message was parsed by the above parse_headers
@@ -1391,12 +1539,37 @@ static inline int adjust_clen(struct sip_msg* msg, int body_delta, int proto)
                                goto error;
                        }
                        body_only=0;
+               }else{
+                       /* compute current content length and compare it with the
+                          one in the message */
+                       body=get_body(msg);
+                       if (unlikely(body==0)){
+                               ser_error=E_BAD_REQ;
+                               LOG(L_ERR, "adjust_clen: no message body found"
+                                               " (missing crlf?)");
+                               goto error;
+                       }
+                       comp_clen=msg->len-(int)(body-msg->buf)+body_delta;
+                       if (comp_clen!=(int)(long)msg->content_length->parsed){
+                               /* note: we don't distinguish here between received with
+                                  wrong content-length and content-length changed, we just
+                                  fix it automatically in both cases (the reason being
+                                  that an error message telling we have received a msg-
+                                  with wrong content-length is of very little use) */
+                               anchor = del_lump(msg, msg->content_length->body.s-msg->buf,
+                                                                       msg->content_length->body.len,
+                                                                       HDR_CONTENTLENGTH_T);
+                               if (anchor==0) {
+                                       LOG(L_ERR, "adjust_clen: Can't remove original"
+                                                               " Content-Length\n");
+                                       goto error;
+                               }
+                               body_only=1;
+                       }
                }
-       }
-#endif
-
-
-       if ((anchor==0) && body_delta){
+       }else
+#endif /* USE_TCP */
+       if (body_delta){
                if (parse_headers(msg, HDR_CONTENTLENGTH_F, 0) == -1) {
                        LOG(L_ERR, "adjust_clen: Error parsing Content-Length\n");
                        goto error;
@@ -1420,13 +1593,14 @@ static inline int adjust_clen(struct sip_msg* msg, int body_delta, int proto)
                                        goto error;
                                }
                                body_only=0;
-                       }else{
-                               DBG("adjust_clen: UDP packet with no clen => not adding one \n");
-                       }
+                       } /* else
+                               DBG("adjust_clen: UDP packet with no clen => "
+                                               "not adding one \n"); */
                }else{
                        /* Content-Length has been found, remove it */
                        anchor = del_lump(      msg, msg->content_length->body.s - msg->buf,
-                                                               msg->content_length->body.len, HDR_CONTENTLENGTH_T);
+                                                               msg->content_length->body.len,
+                                                               HDR_CONTENTLENGTH_T);
                        if (anchor==0) {
                                LOG(L_ERR, "adjust_clen: Can't remove original"
                                                        " Content-Length\n");
@@ -1442,7 +1616,7 @@ static inline int adjust_clen(struct sip_msg* msg, int body_delta, int proto)
                                        HDR_CONTENTLENGTH_T) == 0)
                        goto error;
        }
-
+       
        return 0;
 error:
        if (clen_buf) pkg_free(clen_buf);
@@ -1454,7 +1628,9 @@ error:
 /** builds a request in memory from another sip request.
   *
   * Side-effects: - it adds lumps to the msg which are _not_ cleaned.
-  * All the added lumps are HDR_VIA_T.
+  * The added lumps are HDR_VIA_T (almost always added), HDR_CONTENLENGTH_T
+  * and HDR_ROUTE_T (when a Route: header is added as a result of a non-null
+  * msg->path_vec).
   *               - it might change send_info->proto and send_info->send_socket
   *                 if proto fallback is enabled (see below).
   *
@@ -1475,14 +1651,22 @@ error:
   *                       message > mtu and send_info->proto==PROTO_UDP. 
   *                       It will also update send_info->proto.
   *                     - FL_FORCE_RPORT: add rport to via
+  * @param mode - flags for building the message, can be a combination of:
+  *                 * BUILD_NO_LOCAL_VIA - don't add a local via
+  *                 * BUILD_NO_VIA1_UPDATE - don't update first via (rport,
+  *                    received a.s.o)
+  *                 * BUILD_NO_PATH - don't add a Route: header with the 
+  *                   msg->path_vec content.
+  *                 * BUILD_IN_SHM - build the result in shm memory
   *
-  * @return pointer to the new request (pkg_malloc'ed, needs freeing when
+  * @return pointer to the new request (pkg_malloc'ed or shm_malloc'ed,
+  * depending on the presence of the BUILD_IN_SHM flag, needs freeing when
   *   done) and sets returned_len or 0 on error.
   */
 char * build_req_buf_from_sip_req( struct sip_msg* msg,
                                                                unsigned int *returned_len,
-                                                               struct dest_info* send_info
-                                                               )
+                                                               struct dest_info* send_info,
+                                                               unsigned int mode)
 {
        unsigned int len, new_len, received_len, rport_len, uri_len, via_len,
                                 body_delta;
@@ -1491,10 +1675,13 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
        char* rport_buf;
        char* new_buf;
        char* buf;
+       str path_buf;
        unsigned int offset, s_offset, size;
        struct lump* via_anchor;
        struct lump* via_lump;
        struct lump* via_insert_param;
+       struct lump* path_anchor;
+       struct lump* path_lump;
        str branch;
        unsigned int flags;
        unsigned int udp_mtu;
@@ -1509,7 +1696,11 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
        new_buf=0;
        received_buf=0;
        rport_buf=0;
+       via_anchor=0;
        line_buf=0;
+       via_len=0;
+       path_buf.s=0;
+       path_buf.len=0;
 
        flags=msg->msg_flags|global_req_flags;
        /* Calculate message body difference and adjust Content-Length */
@@ -1520,16 +1711,24 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
                goto error00;
        }
 
+       if(unlikely(mode&BUILD_NO_LOCAL_VIA))
+               goto after_local_via;
+
        /* create the via header */
        branch.s=msg->add_to_branch_s;
        branch.len=msg->add_to_branch_len;
 
+       via_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
+       if (unlikely(via_anchor==0)) goto error00;
        line_buf = create_via_hf( &via_len, msg, send_info, &branch);
-       if (!line_buf){
+       if (unlikely(!line_buf)){
                LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
                                        "memory allocation failure\n");
                goto error00;
        }
+after_local_via:
+       if(unlikely(mode&BUILD_NO_VIA1_UPDATE))
+               goto after_update_via1;
        /* check if received needs to be added */
        if ( received_test(msg) ) {
                if ((received_buf=received_builder(msg,&received_len))==0){
@@ -1604,6 +1803,46 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
 
        }
 
+after_update_via1:
+       /* add route with path content */
+       if(unlikely(!(mode&BUILD_NO_PATH) && msg->path_vec.s &&
+                                       msg->path_vec.len)){
+               path_buf.len=ROUTE_PREFIX_LEN+msg->path_vec.len+CRLF_LEN;
+               path_buf.s=pkg_malloc(path_buf.len+1);
+               if (unlikely(path_buf.s==0)){
+                       LOG(L_ERR, "out of memory\n");
+                       ser_error=E_OUT_OF_MEM;
+                       goto error00;
+               }
+               memcpy(path_buf.s, ROUTE_PREFIX, ROUTE_PREFIX_LEN);
+               memcpy(path_buf.s+ROUTE_PREFIX_LEN, msg->path_vec.s,
+                                       msg->path_vec.len);
+               memcpy(path_buf.s+ROUTE_PREFIX_LEN+msg->path_vec.len, CRLF, CRLF_LEN);
+               path_buf.s[path_buf.len]=0;
+               /* insert Route header either before the other routes
+                  (if present & parsed), after the local via or after in front of
+                   the first via if we don't add a local via*/
+               if (msg->route){
+                       path_anchor=anchor_lump(msg, msg->route->name.s-buf, 0, 
+                                                                       HDR_ROUTE_T);
+               }else if (likely(via_anchor)){
+                       path_anchor=via_anchor;
+               }else if (likely(msg->via1)){
+                       path_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, 
+                                                                       HDR_ROUTE_T);
+               }else{
+                       /* if no via1 (theoretically possible for non-sip messages,
+                          e.g. http xmlrpc) */
+                       path_anchor=anchor_lump(msg, msg->headers->name.s-buf, 0, 
+                                                                       HDR_ROUTE_T);
+               }
+               if (unlikely(path_anchor==0))
+                       goto error05;
+               if (unlikely((path_lump=insert_new_lump_after(path_anchor, path_buf.s,
+                                                                                                               path_buf.len,
+                                                                                                               HDR_ROUTE_T))==0))
+                       goto error05;
+       }
        /* compute new msg len and fix overlapping zones*/
        new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info)+via_len;
 #ifdef XL_DEBUG
@@ -1612,7 +1851,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
        udp_mtu=cfg_get(core, core_cfg, udp_mtu);
        di.proto=PROTO_NONE;
        if (unlikely((send_info->proto==PROTO_UDP) && udp_mtu && 
-                                       (flags & FL_MTU_FB_MASK) && (new_len>udp_mtu))){
+                                       (flags & FL_MTU_FB_MASK) && (new_len>udp_mtu)
+                                       && (!(mode&BUILD_NO_LOCAL_VIA)))){
 
                di=*send_info; /* copy whole struct - will be used in the Via builder */
                di.proto=PROTO_NONE; /* except the proto */
@@ -1640,7 +1880,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
                
                if (di.proto!=PROTO_NONE){
                        new_len-=via_len;
-                       pkg_free(line_buf);
+                       if(likely(line_buf)) pkg_free(line_buf);
                        line_buf = create_via_hf( &via_len, msg, &di, &branch);
                        if (!line_buf){
                                LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
@@ -1653,17 +1893,19 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
        /* add via header to the list */
        /* try to add it before msg. 1st via */
        /* add first via, as an anchor for second via*/
-       via_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
-       if (via_anchor==0) goto error04;
-       if ((via_lump=insert_new_lump_before(via_anchor, line_buf, via_len,
+       if(likely(line_buf)) {
+               if ((via_lump=insert_new_lump_before(via_anchor, line_buf, via_len,
                                                                                        HDR_VIA_T))==0)
-               goto error04;
-
+                       goto error04;
+       }
        if (msg->new_uri.s){
                uri_len=msg->new_uri.len;
                new_len=new_len-msg->first_line.u.request.uri.len+uri_len;
        }
-       new_buf=(char*)pkg_malloc(new_len+1);
+       if(unlikely(mode&BUILD_IN_SHM))
+               new_buf=(char*)shm_malloc(new_len+1);
+       else
+               new_buf=(char*)pkg_malloc(new_len+1);
        if (new_buf==0){
                ser_error=E_OUT_OF_MEM;
                LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: out of memory\n");
@@ -1713,6 +1955,8 @@ error03:
        if (rport_buf) pkg_free(rport_buf);
 error04:
        if (line_buf) pkg_free(line_buf);
+error05:
+       if (path_buf.s) pkg_free(path_buf.s);
 error00:
        *returned_len=0;
        return 0;
@@ -1790,7 +2034,7 @@ error:
 }
 
 
-char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
+char * build_res_buf_from_sip_req( unsigned int code, str *text ,str *new_tag,
                struct sip_msg* msg, unsigned int *returned_len, struct bookmark *bmark)
 {
        char              *buf, *p;
@@ -1807,7 +2051,6 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
        unsigned int      warning_len;
        char*             content_len_buf;
        unsigned int      content_len_len;
-       unsigned int      text_len;
        char *after_body;
        str  to_tag;
        char *totags;
@@ -1817,8 +2060,6 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
        received_buf=rport_buf=warning_buf=content_len_buf=0;
        received_len=rport_len=warning_len=content_len_len=0;
 
-       text_len=strlen(text);
-
        to_tag.s=0;  /* fixes gcc 4.0 warning */
        to_tag.len=0;
 
@@ -1857,7 +2098,7 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
 
        /* first line */
        len += msg->first_line.u.request.version.len + 1/*space*/ + 3/*code*/ + 1/*space*/ +
-               text_len + CRLF_LEN/*new line*/;
+               text->len + CRLF_LEN/*new line*/;
        /*headers that will be copied (TO, FROM, CSEQ,CALLID,VIA)*/
        for ( hdr=msg->headers ; hdr ; hdr=hdr->next ) {
                switch (hdr->type) {
@@ -1899,7 +2140,7 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
        }
        /* server header */
        if (server_signature)
-               len += SERVER_HDR_LEN + CRLF_LEN;
+               len += server_hdr.len + CRLF_LEN;
        /* warning hdr */
        if (sip_warning) {
                warning_buf = warning_builder(msg,&warning_len);
@@ -1937,8 +2178,8 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
                *(p+i) = '0' + foo - ( foo/10 )*10;
        p += 3;
        *(p++) = ' ' ;
-       memcpy( p , text , text_len );
-       p += text_len;
+       memcpy( p , text->s , text->len );
+       p += text->len;
        memcpy( p, CRLF, CRLF_LEN );
        p+=CRLF_LEN;
        /* headers*/
@@ -2038,8 +2279,8 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
                }
        /* server header */
        if (server_signature) {
-               memcpy( p, SERVER_HDR , SERVER_HDR_LEN );
-               p+=SERVER_HDR_LEN;
+               memcpy( p, server_hdr.s, server_hdr.len );
+               p+=server_hdr.len;
                memcpy( p, CRLF, CRLF_LEN );
                p+=CRLF_LEN;
        }
@@ -2159,22 +2400,33 @@ char* via_builder( unsigned int *len,
        str* port_str; /* port no displayed in via */
        struct socket_info* send_sock;
        int comp_len, comp_name_len;
+#ifdef USE_COMP
        char* comp_name;
+#endif /* USE_COMP */
+       int port;
+       struct ip_addr ip;
+       union sockaddr_union *from = NULL;
+       union sockaddr_union local_addr;
+       struct tcp_connection *con = NULL;
 
        send_sock=send_info->send_sock;
-       /* use pre-set address in via or the outbound socket one */
-       if ( hp && hp->host->len)
+       /* use pre-set address in via, the outbound socket alias or address one */
+       if (hp && hp->host->len)
                address_str=hp->host;
+       else if(send_sock->useinfo.name.len>0)
+               address_str=&(send_sock->useinfo.name);
        else
                address_str=&(send_sock->address_str);
        if (hp && hp->port->len)
                port_str=hp->port;
+       else if(send_sock->useinfo.port_no>0)
+               port_str=&(send_sock->useinfo.port_no_str);
        else
                port_str=&(send_sock->port_no_str);
        
        comp_len=comp_name_len=0;
-       comp_name=0;
 #ifdef USE_COMP
+       comp_name=0;
        switch(send_info->comp){
                case COMP_NONE:
                        break;
@@ -2223,6 +2475,41 @@ char* via_builder( unsigned int *len,
                memcpy(line_buf+MY_VIA_LEN-4, "TLS ", 4);
        }else if (send_info->proto==PROTO_SCTP){
                memcpy(line_buf+MY_VIA_LEN-4, "SCTP ", 5);
+       }else if (send_info->proto==PROTO_WS){
+                if (unlikely(send_info->send_flags.f & SND_F_FORCE_SOCKET
+                                && send_info->send_sock)) {
+                        local_addr = send_info->send_sock->su;
+                        su_setport(&local_addr, 0); /* any local port will do */
+                        from = &local_addr;
+                }
+
+                port = su_getport(&send_info->to);
+                if (likely(port)) {
+                        su2ip_addr(&ip, &send_info->to);
+                        con = tcpconn_get(send_info->id, &ip, port, from, 0);
+                }
+                else if (likely(send_info->id))
+                        con = tcpconn_get(send_info->id, 0, 0, 0, 0);
+                else {
+                        LM_CRIT("BUG: via_builder called with null_id & to\n");
+                        return 0;
+                }
+
+                if (con == NULL) {
+                        LM_WARN("TCP/TLS connection for WebSocket could not be found\n");
+                        return 0;
+                }
+
+               if (con->rcv.proto==PROTO_WS) {
+                       memcpy(line_buf+MY_VIA_LEN-4, "WS ", 3);
+               } else if (con->rcv.proto==PROTO_WSS) {
+                       memcpy(line_buf+MY_VIA_LEN-4, "WSS ", 4);
+               } else {
+                       LOG(L_CRIT, "BUG: via_builder: unknown proto %d\n", con->rcv.proto);
+                       return 0;
+               }
+       }else if (send_info->proto==PROTO_WSS){
+               memcpy(line_buf+MY_VIA_LEN-4, "WSS ", 4);
        }else{
                LOG(L_CRIT, "BUG: via_builder: unknown proto %d\n", send_info->proto);
                return 0;
@@ -2287,7 +2574,7 @@ char* create_via_hf( unsigned int *len,
        char* via;
        str extra_params;
        struct hostport hp;
-#ifdef USE_TCP
+#if defined USE_TCP || defined USE_SCTP
        char* id_buf;
        unsigned int id_len;
 
@@ -2299,13 +2586,21 @@ char* create_via_hf( unsigned int *len,
        extra_params.s=0;
 
 
-#ifdef USE_TCP
+#if defined USE_TCP || defined USE_SCTP
        /* add id if tcp */
-       if (msg
-       && ((msg->rcv.proto==PROTO_TCP)
+       if (msg && (
+#ifdef USE_TCP
+               (msg->rcv.proto==PROTO_TCP)
 #ifdef USE_TLS
                        || (msg->rcv.proto==PROTO_TLS)
 #endif
+#ifdef USE_SCTP
+                       ||
+#endif /* USE_SCTP */
+#endif /* USE_TCP */
+#ifdef USE_SCTP
+                       (msg->rcv.proto==PROTO_SCTP)
+#endif /* USE_SCTP */
                        )){
                if  ((id_buf=id_builder(msg, &id_len))==0){
                        LOG(L_ERR, "ERROR: create_via_hf:"
@@ -2318,16 +2613,33 @@ char* create_via_hf( unsigned int *len,
                extra_params.s=id_buf;
                extra_params.len=id_len;
        }
-#endif
+#endif /* USE_TCP || USE_SCTP */
+
+       /* test and add rport parameter to local via - rfc3581 */
+       if(msg->msg_flags&FL_ADD_LOCAL_RPORT) {
+               /* params so far + ';rport' + '\0' */
+               via = (char*)pkg_malloc(extra_params.len+RPORT_LEN);
+               if(via==0) {
+                       LM_ERR("building local rport via param failed\n");
+                       if (extra_params.s) pkg_free(extra_params.s);
+                       return 0;
+               }
+               if(extra_params.len!=0) {
+                       memcpy(via, extra_params.s, extra_params.len);
+                       pkg_free(extra_params.s);
+               }
+               memcpy(via + extra_params.len, RPORT, RPORT_LEN-1);
+               extra_params.s = via;
+               extra_params.len += RPORT_LEN-1;
+               extra_params.s[extra_params.len]='\0';
+       }
 
        set_hostport(&hp, msg);
        via = via_builder( len, send_info, branch,
                                                        extra_params.len?&extra_params:0, &hp);
 
-#ifdef USE_TCP
-       /* we do not need id_buf any more, the id is already in the new via header */
-       if (id_buf) pkg_free(id_buf);
-#endif
+       /* we do not need extra_params any more, already in the new via header */
+       if (extra_params.s) pkg_free(extra_params.s);
        return via;
 }
 
@@ -2494,3 +2806,28 @@ char * build_all( struct sip_msg* msg, int touch_clen,
        *returned_len = offset;
        return new_buf; 
 }
+
+
+
+/**
+ * parse buf in msg and fill several fields
+ */
+int build_sip_msg_from_buf(struct sip_msg *msg, char *buf, int len,
+               unsigned int id)
+{
+       if(msg==0 || buf==0)
+               return -1;
+
+       memset(msg, 0, sizeof(sip_msg_t));
+       msg->id = id;
+       msg->buf = buf;
+       msg->len = len;
+       if (parse_msg(buf, len, msg)!=0) {
+               LM_ERR("parsing failed");
+               return -1;
+       }
+       msg->set_global_address=default_global_address;
+       msg->set_global_port=default_global_port;
+       return 0;
+}
+