core: Make sure that responses to requests received on a WebSocket are sent on existi...
[sip-router] / forward.c
index eb1b7c9..89fe18f 100644 (file)
--- a/forward.c
+++ b/forward.c
  *               socket is multicast (andrei)
  */
 
+/*!
+ * \file
+ * \brief SIP-router core :: 
+ * \ingroup core
+ * Module: \ref core
+ */
+
 
 
 #include <string.h>
@@ -71,7 +78,9 @@
 #include "hash_func.h"
 #include "config.h"
 #include "parser/msg_parser.h"
+#include "char_msg_val.h"
 #include "route.h"
+#include "events.h"
 #include "dprint.h"
 #include "globals.h"
 #include "cfg_core.h"
 #include "dst_blacklist.h"
 #endif
 #include "compiler_opt.h"
+#include "core_stats.h"
 
 #ifdef DEBUG_DMALLOC
 #include <dmalloc.h>
 /* return a socket_info_pointer to the sending socket; as opposed to
  * get_send_socket, which returns process's default socket, get_out_socket
  * attempts to determine the outbound interface which will be used;
- * it creates a temporary connected socket to determine it; it will
+ * it uses a temporary connected socket to determine it; it will
  * be very likely noticeably slower, but it can deal better with
  * multihomed hosts
  */
+
+static int mhomed_sock_cache_disabled = 0;
+static int sock_inet = -1;
+#ifdef USE_IPV6
+static int sock_inet6 = -1;
+#endif /* USE_IPV6 */
+
+static void apply_force_send_socket(struct dest_info* dst, struct sip_msg* msg);
+
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
 {
-       int temp_sock;
+       int* temp_sock;
        socklen_t len;
        union sockaddr_union from; 
        struct socket_info* si;
        struct ip_addr ip;
+       union sockaddr_union uncon;
 
-       if (proto!=PROTO_UDP) {
+       memset(&uncon, 0, sizeof(union sockaddr_union));
+       uncon.sin.sin_family = AF_UNSPEC;
+
+       if (unlikely(proto!=PROTO_UDP)) {
                LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
                return 0;
        }
-       
-       temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 );
-       if (temp_sock==-1) {
-               LOG(L_ERR, "ERROR: get_out_socket: socket() failed: %s\n",
-                               strerror(errno));
+retry:
+       switch(to->s.sa_family){
+       case AF_INET : {
+               if(unlikely(sock_inet < 0)){
+                       sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
+                       if (sock_inet==-1) {
+                               LM_ERR("socket() failed: %s\n", strerror(errno));
+                               return 0;
+                       }
+               }
+               temp_sock = &sock_inet;
+               break;
+       }
+#ifdef USE_IPV6
+       case AF_INET6 : {
+               if(unlikely(sock_inet6 < 0)){
+                       sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+                       if (sock_inet6==-1) {
+                               LM_ERR("socket() failed: %s\n", strerror(errno));
+                               return 0;
+                       }
+               }
+               temp_sock = &sock_inet6;
+               break;
+       }
+#endif /* USE_IPV6 */
+       default: {
+               LM_ERR("Unknown protocol family \n");
                return 0;
        }
-       if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
+       }
+
+       if( !mhomed_sock_cache_disabled ){
+               /* some Linux kernel versions (all?) along with other UNIXes don't re-bound the sock if already bound */
+               /* to un-bound a socket set sin_family to AF_UNSPEC and zero out the rest*/
+               if (unlikely(connect(*temp_sock, &uncon.s, sockaddru_len(uncon)) < 0))
+                               mhomed_sock_cache_disabled = 1;
+       }
+
+       if (unlikely(connect(*temp_sock, &to->s, sockaddru_len(*to))==-1)) {
+               if (unlikely(errno==EISCONN && !mhomed_sock_cache_disabled)){
+                       /*  no multiple connects support on the same socket */
+                       mhomed_sock_cache_disabled=1;
+                       if (sock_inet>=0){
+                               close(sock_inet);
+                               sock_inet=-1;
+                       }
+#ifdef USE_IPV6
+                       if (sock_inet6>=0){
+                               close(sock_inet6);
+                               sock_inet6=-1;
+                       }
+#endif /* USE_IPV6 */
+                       goto retry;
+               }
                LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
                                strerror(errno));
                goto error;
        }
        len=sizeof(from);
-       if (getsockname(temp_sock, &from.s, &len)==-1) {
+       if (unlikely(getsockname(*temp_sock, &from.s, &len)==-1)) {
                LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
                                strerror(errno));
                goto error;
@@ -140,54 +210,94 @@ struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
        su2ip_addr(&ip, &from);
        si=find_si(&ip, 0, proto);
        if (si==0) goto error;
-       close(temp_sock);
        DBG("DEBUG: get_out_socket: socket determined: %p\n", si );
+       if (unlikely(mhomed_sock_cache_disabled)){
+               close(*temp_sock);
+               *temp_sock=-1;
+       }
        return si;
 error:
        LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
-       close(temp_sock);
+       if (unlikely(mhomed_sock_cache_disabled && *temp_sock >=0)){
+               close(*temp_sock);
+               *temp_sock=-1;
+       }
        return 0;
 }
 
 
 
-/* returns a socket_info pointer to the sending socket or 0 on error
- * params: sip msg (can be null), destination socket_union pointer, protocol
- * if msg!=null and msg->force_send_socket, the force_send_socket will be
- * used
+/** get the sending socket for a corresponding destination.
+ * @param force_send_socket - if !=0 and the protocol and af correspond
+ *                            with the destination, it will be returned.
+ *                            If the protocol or af check fail, a look-alike
+ *                            socket will be searched for and mismatch will be
+ *                            set. If no look-alike socket is found it will
+ *                            fallback to normal resolution.
+ * @param to - destination
+ * @param proto - protocol
+ * @param mismatch - result parameter, set if a force_send_socket was used, but
+ *                   there was an error matching it exactly to the destination.
+ *                   Possible values: 0 ok, SS_MISMATCH_PROTO,
+ *                   SS_MISMATCH_ADDR, SS_MISMATCH_AF, SS_MISMATCH_MCAST.
+ * @return a socket_info pointer to the sending socket on success (and possibly
+ *         sets mismatch) or 0 on error.
  */
-struct socket_info* get_send_socket(struct sip_msg *msg, 
-                                                                               union sockaddr_union* to, int proto)
+struct socket_info* get_send_socket2(struct socket_info* force_send_socket,
+                                                                               union sockaddr_union* to, int proto,
+                                                                               enum ss_mismatch* mismatch)
 {
        struct socket_info* send_sock;
+       struct socket_info* orig;
        
+       if (likely(mismatch)) *mismatch=0;
        /* check if send interface is not forced */
-       if (unlikely(msg && msg->force_send_socket)){
-               if (unlikely(msg->force_send_socket->proto!=proto)){
-                       DBG("get_send_socket: force_send_socket of different proto"
-                                       " (%d)!\n", proto);
-                       msg->force_send_socket=find_si(&(msg->force_send_socket->address),
-                                                                                       msg->force_send_socket->port_no,
+       if (unlikely(force_send_socket)){
+               if (unlikely(force_send_socket->proto!=proto)){
+                       orig=force_send_socket;
+                       force_send_socket=find_si(&(force_send_socket->address),
+                                                                                       force_send_socket->port_no,
                                                                                        proto);
-                       if (unlikely(msg->force_send_socket == 0)){
+                       if (unlikely(force_send_socket == 0)){
+                               if (likely(mismatch)) *mismatch=SS_MISMATCH_ADDR;
                                LOG(L_WARN, "WARNING: get_send_socket: "
-                                               "protocol/port mismatch\n");
+                                               "protocol/port mismatch (forced %s:%s:%d,"
+                                               " to %s:%s)\n",
+                                               proto2a(orig->proto), ip_addr2a(&orig->address),
+                                               orig->port_no,
+                                               proto2a(proto), su2a(to, sizeof(*to)));
                                goto not_forced;
                        }
+                       if (likely(mismatch)) *mismatch=SS_MISMATCH_PROTO;
                }
-               if (unlikely(msg->force_send_socket->address.af!=to->s.sa_family)){
-                       DBG("get_send_socket: force_send_socket of different af (dst %d,"
-                                       " forced %d)\n",
-                                       to->s.sa_family, msg->force_send_socket->address.af);
+               if (unlikely(force_send_socket->address.af!=to->s.sa_family)){
+                       DBG("get_send_socket: force_send_socket of different af"
+                                       " (dst %d - %s:%s forced %d -%s:%s:%d)\n",
+                                       to->s.sa_family, proto2a(proto), su2a(to, sizeof(*to)),
+                                       force_send_socket->address.af,
+                                       proto2a(force_send_socket->proto),
+                                       ip_addr2a(&force_send_socket->address),
+                                       force_send_socket->port_no);
+                       if (likely(mismatch)) *mismatch=SS_MISMATCH_AF;
                        goto not_forced;
                }
-               if (likely((msg->force_send_socket->socket!=-1) &&
-                                       !(msg->force_send_socket->flags & SI_IS_MCAST)))
-                               return msg->force_send_socket;
+               /* check if listening on the socket (the check does not work
+                  for TCP and TLS, for them socket==-1 on all the processes
+                  except tcp_main(), see close_extra_socks() */
+               if (likely((force_send_socket->socket!=-1 ||
+                                               force_send_socket->proto==PROTO_TCP ||
+                                               force_send_socket->proto==PROTO_TLS) &&
+                                       !(force_send_socket->flags & SI_IS_MCAST)))
+                               return force_send_socket;
                else{
-                       if (!(msg->force_send_socket->flags & SI_IS_MCAST))
+                       if (!(force_send_socket->flags & SI_IS_MCAST))
                                LOG(L_WARN, "WARNING: get_send_socket: not listening"
-                                                        " on the requested socket, no fork mode?\n");
+                                                        " on the requested socket (%s:%s:%d),"
+                                                        " no fork mode?\n",
+                                                       proto2a(force_send_socket->proto),
+                                                       ip_addr2a(&force_send_socket->address),
+                                                       force_send_socket->port_no);
+                       else if (likely(mismatch)) *mismatch=SS_MISMATCH_MCAST;
                }
        };
 not_forced:
@@ -197,7 +307,9 @@ not_forced:
                        return send_sock; /* found or error*/
                else if (send_sock->socket==-1){
                        LOG(L_WARN, "WARNING: get_send_socket: not listening on the"
-                                       " requested socket, no fork mode?\n");
+                                       " requested socket (%s:%s:%d), no fork mode?\n",
+                                       proto2a(send_sock->proto), ip_addr2a(&send_sock->address),
+                                       send_sock->port_no);
                        /* continue: try to use some socket */
                }
        }
@@ -207,6 +319,7 @@ not_forced:
         * eg: ipv4 -> ipv6 or ipv6 -> ipv4) */
        switch(proto){
 #ifdef USE_TCP
+               case PROTO_WS:
                case PROTO_TCP:
                /* on tcp just use the "main address", we don't really now the
                 * sending address (we can find it out, but we'll need also to see
@@ -276,12 +389,58 @@ not_forced:
                        }else send_sock=bind_address;
                        break;
                default:
-                       LOG(L_CRIT, "BUG: get_send_socket: unknown proto %d\n", proto);
+                       LOG(L_CRIT, "BUG: get_send_socket: unsupported proto %d (%s)\n",
+                                       proto, proto2a(proto));
        }
        return send_sock;
 }
 
+static struct _check_self_func {
+       check_self_f fself;
+       struct _check_self_func *next;
+} *_check_self_func_list = NULL;
 
+/* check if _check_self_func_list is set
+ * - return 1 if yes, 0 if no
+ */
+int is_check_self_func_list_set(void)
+{
+       return (_check_self_func_list)?1:0;
+}
+
+/* register a function to be called when matching for myself
+ * - return 0 on success, -1 on error
+ * - f must have same prototype as check_self() and return same kind of values
+ */
+int register_check_self_func(check_self_f f)
+{
+       struct _check_self_func *nf = 0;
+       nf=(struct _check_self_func*)pkg_malloc(sizeof(struct _check_self_func));
+       if(nf==0)
+       {
+               LM_ERR("no more pkg\n");
+               return -1;
+       }
+       nf->fself = f;
+       nf->next = _check_self_func_list;
+       _check_self_func_list = nf;
+       return 0;
+}
+
+/* run registered check self functions
+ * returns 1 if true, 0 if false
+ */
+int run_check_self_func(str* host, unsigned short port, unsigned short proto)
+{
+       struct _check_self_func *sf = 0;
+
+       if(_check_self_func_list==NULL)
+               return 0;
+       for(sf=_check_self_func_list; sf; sf=sf->next)
+               if(sf->fself(host, port, proto)==1)
+                       return 1;
+       return 0;
+}
 
 /* checks if the proto: host:port is one of the address we listen on;
  * if port==0, the  port number is ignored
@@ -296,7 +455,8 @@ int check_self(str* host, unsigned short port, unsigned short proto)
        /* try to look into the aliases*/
        if (grep_aliases(host->s, host->len, port, proto)==0){
                DBG("check_self: host != me\n");
-               return 0;
+               return (_check_self_func_list==NULL)?0:run_check_self_func(host,
+                                                                                                               port, proto);
        }
 found:
        return 1;
@@ -326,9 +486,12 @@ int check_self_port(unsigned short port, unsigned short proto)
  *               default port or non srv. lookup is desired, the port must
  *               be !=0 
  *   port      - used only if dst!=0 (else the port in send_info->to is used)
- *   send_info - filled dest_info structure:
- *               if the send_socket member is null, a send_socket will be 
- *               chosen automatically
+ *   send_info - value/result partially filled dest_info structure:
+ *                 - send_info->proto and comp are used
+ *                 - send_info->to will be filled (dns)
+ *                 - send_info->send_flags is filled from the message
+ *                 - if the send_socket member is null, a send_socket will be 
+ *                   chosen automatically
  * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
  * random id along with proto==PROTO_TCP can have bad consequences, same for
  *   a bogus send_socket value)
@@ -380,18 +543,19 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                        goto error;
                }
        }/* dst */
+       send_info->send_flags=msg->fwd_send_flags;
        /* calculate branch for outbound request;  if syn_branch is turned off,
           calculate is from transaction key, i.e., as an md5 of From/To/CallID/
           CSeq exactly the same way as TM does; good for reboot -- than messages
           belonging to transaction lost due to reboot will still be forwarded
           with the same branch parameter and will be match-able downstream
-
-       if it is turned on, we don't care about reboot; we simply put a simple
+       
+          if it is turned on, we don't care about reboot; we simply put a simple
           value in there; better for performance
        */
        if (syn_branch ) {
-               *msg->add_to_branch_s='0';
-               msg->add_to_branch_len=1;
+               memcpy(msg->add_to_branch_s, "z9hG4bKcydzigwkX", 16);
+               msg->add_to_branch_len=16;
        } else {
                if (!char_msg_val( msg, md5 ))  { /* parses transaction key */
                        LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
@@ -407,7 +571,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                }
        }
        /* try to send the message until success or all the ips are exhausted
-        *  (if dns lookup is peformed && the dns cache used ) */
+        *  (if dns lookup is performed && the dns cache used ) */
 #ifdef USE_DNS_FAILOVER
        do{
 #endif
@@ -433,7 +597,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 #endif
                        if (buf) pkg_free(buf);
                        send_info->proto=proto;
-                       buf = build_req_buf_from_sip_req(msg, &len, send_info);
+                       buf = build_req_buf_from_sip_req(msg, &len, send_info, 0);
                        if (!buf){
                                LOG(L_ERR, "ERROR: forward_request: building failed\n");
                                ret=E_OUT_OF_MEM; /* most probable */
@@ -479,8 +643,7 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
                if (msg_send(send_info, buf, len)<0){
                        ret=ser_error=E_SEND;
 #ifdef USE_DST_BLACKLIST
-                       if (cfg_get(core, core_cfg, use_dst_blacklist))
-                               dst_blacklist_add(BLST_ERR_SEND, send_info, msg);
+                       dst_blacklist_add(BLST_ERR_SEND, send_info, msg);
 #endif
 #ifdef USE_DNS_FAILOVER
                        continue; /* try another ip */
@@ -519,6 +682,12 @@ end:
 #endif
        if (buf) pkg_free(buf);
        /* received_buf & line_buf will be freed in receive_msg by free_lump_list*/
+#if defined STATS_REQ_FWD_OK || defined STATS_REQ_FWD_DROP
+       if(ret==0)
+               STATS_REQ_FWD_OK();
+       else
+               STATS_REQ_FWD_DROP();
+#endif /* STATS_REQ_FWD_* */
        return ret;
 }
 
@@ -625,7 +794,7 @@ int forward_reply(struct sip_msg* msg)
                || (msg->via2==0) || (msg->via2->error!=PARSE_OK))
        {
                /* no second via => error */
-               LOG(L_ERR, "ERROR: forward_reply: no 2nd via found in reply\n");
+               LOG(L_DBG, "broken reply to forward - no 2nd via\n");
                goto error;
        }
 
@@ -636,16 +805,26 @@ int forward_reply(struct sip_msg* msg)
        }
 
        dst.proto=msg->via2->proto;
+       SND_FLAGS_OR(&dst.send_flags, &msg->fwd_send_flags, &msg->rpl_send_flags);
        if (update_sock_struct_from_via( &dst.to, msg, msg->via2 )==-1) goto error;
 #ifdef USE_COMP
        dst.comp=msg->via2->comp_no;
 #endif
 
+#if defined USE_TCP || defined USE_SCTP
+       if (
 #ifdef USE_TCP
-       if (dst.proto==PROTO_TCP
+                       dst.proto==PROTO_TCP
 #ifdef USE_TLS
                        || dst.proto==PROTO_TLS
 #endif
+#ifdef USE_SCTP
+                       ||
+#endif /* USE_SCTP */
+#endif /* USE_TCP */
+#ifdef USE_SCTP
+                       dst.proto==PROTO_SCTP
+#endif /* USE_SCTP */
                        ){
                /* find id in i param if it exists */
                if (msg->via1->i && msg->via1->i->value.s){
@@ -661,7 +840,14 @@ int forward_reply(struct sip_msg* msg)
                                
        } 
 #endif
-       if (msg_send(&dst, new_buf, new_len)<0) goto error;
+
+       apply_force_send_socket(&dst, msg);
+
+       if (msg_send(&dst, new_buf, new_len)<0)
+       {
+               STATS_RPL_FWD_DROP();
+               goto error;
+       }
 #ifdef STATS
        STATS_TX_RESPONSE(  (msg->first_line.u.reply.statuscode/100) );
 #endif
@@ -670,6 +856,7 @@ int forward_reply(struct sip_msg* msg)
                        msg->via2->host.len, msg->via2->host.s,
                        (unsigned short) msg->via2->port);
 
+       STATS_RPL_FWD_OK();
        pkg_free(new_buf);
 skip:
        return 0;
@@ -677,3 +864,10 @@ error:
        if (new_buf) pkg_free(new_buf);
        return -1;
 }
+
+static void apply_force_send_socket(struct dest_info* dst, struct sip_msg* msg)
+{
+       if (msg->force_send_socket != 0) {
+               dst->send_sock = get_send_socket(msg, &dst->to, dst->proto);
+       }
+}