Module: sip-router
Branch: pd/websocket
Commit: f457ec98c2208d181bb94ace50b82faed6d707e0
URL:    
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=f457ec98c2208d181bb94ace50b82faed6d707e0

Author: Peter Dunkley <[email protected]>
Committer: Peter Dunkley <[email protected]>
Date:   Sun Jun 17 21:31:29 2012 +0100

modules/websocket: Filled in MI commands to dump WebSocket connection details 
and Close a WebSocket

---

 modules/websocket/ws_frame.c |  177 +++++++++++++++++++++++++++++++++++++++---
 modules/websocket/ws_mod.c   |   48 +++++++++++-
 2 files changed, 212 insertions(+), 13 deletions(-)

diff --git a/modules/websocket/ws_frame.c b/modules/websocket/ws_frame.c
index 0b0b474..fba01f5 100644
--- a/modules/websocket/ws_frame.c
+++ b/modules/websocket/ws_frame.c
@@ -21,7 +21,9 @@
  *
  */
 
+#include <limits.h>
 #include "../../tcp_conn.h"
+#include "../../tcp_server.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../lib/kmi/tree.h"
 #include "ws_frame.h"
@@ -83,6 +85,8 @@ static int decode_and_validate_ws_frame(ws_frame_t *frame)
        int mask_start, j;
        char *buf = frame->tcpinfo->buf;
 
+       LM_INFO("decoding WebSocket frame\n");
+
        /* Decode and validate first 9 bits */
        if (len < 2)
        {
@@ -200,11 +204,104 @@ static int decode_and_validate_ws_frame(ws_frame_t 
*frame)
 
 static int encode_and_send_ws_frame(ws_frame_t *frame)
 {
-       /* TODO: convert ws_frame_t into a binary WebSocket frame and send over
-          TCP/TLS */
+       int pos = 0, extended_length;
+       unsigned int frame_length;
+       char *send_buf;
+       struct dest_info dst;
+
+       LM_INFO("encoding WebSocket frame\n");
+
+       /* Validate the first byte */
+       if (!frame->fin)
+       {
+               LM_ERR("WebSocket fragmentation not supported in the sip "
+                       "sub-protocol\n");
+               return -1;
+       }
+
+       if (frame->rsv1 || frame->rsv2 || frame->rsv3)
+       {
+               LM_ERR("WebSocket reserved fields with non-zero values\n");
+               return -1;
+       }
 
+       switch(frame->opcode)
+       {
+       case OPCODE_TEXT_FRAME:
+       case OPCODE_BINARY_FRAME:
+               LM_INFO("supported non-control frame: 0x%x\n",
+                       (unsigned char) frame->opcode);
+               break;
+       case OPCODE_CLOSE:
+       case OPCODE_PING:
+       case OPCODE_PONG:
+               LM_INFO("supported control frame: 0x%x\n",
+                       (unsigned char) frame->opcode);
+               break;
+       default:
+               LM_ERR("unsupported opcode: 0x%x\n",
+                       (unsigned char) frame->opcode);
+               return -1;
+       }
+
+       /* validate the second byte */
+       if (frame->mask)
+       {
+               LM_ERR("this is a server - all messages sent will be "
+                       "unmasked\n");
+               return -1;
+       }
+
+       if (frame->payload_len < 126) extended_length = 0;
+       else if (frame->payload_len <= USHRT_MAX ) extended_length = 2;
+       else if (frame->payload_len <= UINT_MAX) extended_length = 4;
+       else
+       {
+               LM_ERR("Kamailio only supports WebSocket frames with payload "
+                       "<= %u\n", UINT_MAX);
+               return -1;
+       }
+
+       /* Allocate send buffer and build frame */
+       frame_length = frame->payload_len + extended_length + 2;
+       if ((send_buf = pkg_malloc(sizeof(unsigned char) * frame_length))
+                       == NULL)
+       {
+               LM_ERR("allocating send buffer from pkg memory\n");
+               return -1;
+       }
+       memset(send_buf, 0, frame_length);
+       send_buf[pos++] = 0x80 | (frame->opcode & 0xff);
+       if (extended_length == 0)
+               send_buf[pos++] = (frame->payload_len & 0xff);
+       else if (extended_length == 2)
+       {
+               send_buf[pos++] = 126;
+               send_buf[pos++] = (frame->payload_len & 0xff00) >> 8;
+               send_buf[pos++] = (frame->payload_len & 0x00ff) >> 0;
+       }
+       else
+       {
+               send_buf[pos++] = 127;
+               send_buf[pos++] = (frame->payload_len & 0xff000000) >> 24;
+               send_buf[pos++] = (frame->payload_len & 0x00ff0000) >> 16;
+               send_buf[pos++] = (frame->payload_len & 0x0000ff00) >> 8;
+               send_buf[pos++] = (frame->payload_len & 0x000000ff) >> 0;
+       }
+       memcpy(&send_buf[pos], frame->payload_data, frame->payload_len);
+
+       init_dst_from_rcv(&dst, &frame->tcpinfo->con->rcv);
+       if (tcp_send(&dst, NULL, send_buf, frame_length) < 0)
+       {
+               LM_ERR("sending WebSocket frame\n");
+               pkg_free(send_buf);
+               update_stat(ws_failed_connections, 1);
+               return -1;
+       }
+       
        update_stat(ws_transmitted_frames, 1);
 
+       pkg_free(send_buf);
        return 0;
 }
 
@@ -245,18 +342,12 @@ static int handle_close(ws_frame_t *frame)
 
 static int handle_ping(ws_frame_t *frame)
 {
-       ws_frame_t ws_frame;
-
        LM_INFO("Received Ping\n");
 
-       memset(&ws_frame, 0, sizeof(ws_frame_t));
-       ws_frame.fin = 1;
-       ws_frame.opcode = OPCODE_PONG;
-       ws_frame.payload_len = frame->payload_len;
-       ws_frame.payload_data =  frame->payload_data;
-       ws_frame.tcpinfo = frame->tcpinfo;
+       frame->opcode = OPCODE_PONG;
+       frame->mask = 0;
 
-       encode_and_send_ws_frame(&ws_frame);
+       encode_and_send_ws_frame(frame);
 
        return 0;
 }
@@ -329,7 +420,69 @@ int ws_frame_received(void *data)
 
 struct mi_root *ws_mi_close(struct mi_root *cmd, void *param)
 {
-       /* TODO Close specified or all connections */
+       unsigned int id;
+       struct mi_node *node = NULL;
+       ws_frame_t frame;
+       tcp_event_info_t tcpinfo;
+       short int code = 1000;
+       str reason = str_init("Normal Closure");
+       char *data;
+
+       node = cmd->node.kids;
+       if (node == NULL)
+               return 0;
+       if (node->value.s == NULL || node->value.len == 0)
+       {
+               LM_ERR("empty connection ID parameter\n");
+               return init_mi_tree(400, "Empty connection ID parameter", 29);
+       }
+       if (str2int(&node->value, &id) < 0)
+       {
+               LM_ERR("converting string to int\n");
+               return 0;
+       }
+       if (node->next != NULL)
+       {
+               LM_ERR("too many parameters\n");
+               return init_mi_tree(400, "Too many parameters", 19);
+       }
+
+       if ((tcpinfo.con = tcpconn_get(id, 0, 0, 0, 0)) == NULL)
+       {
+               LM_ERR("bad connection ID parameter\n");
+               return init_mi_tree(400, "Bad connection ID parameter", 27);
+       }
+
+       if ((data = pkg_malloc(sizeof(char) * (reason.len + 2))) == NULL)
+       {
+               LM_ERR("allocating pkg memory\n");
+               return 0;
+       }
+
+       data[0] = (code & 0xff00) >> 8;
+       data[1] = (code & 0x00ff) >> 0;
+       memcpy(&data[2], reason.s, reason.len);
+
+       memset(&frame, 0, sizeof(frame));
+       frame.fin = 1;
+       frame.opcode = OPCODE_CLOSE;
+       frame.payload_len = reason.len + 2;
+       frame.payload_data = data;
+       frame.tcpinfo = &tcpinfo;
+
+       if (encode_and_send_ws_frame(&frame) < 0)
+       {
+               LM_ERR("sending WebSocket close\n");
+               pkg_free(data);
+               return init_mi_tree(500,"Sending WebSocket close", 23);
+       }
+
+       /* TODO: cleanly close TCP/TLS connection */
+
+       update_stat(ws_local_closed_connections, 1);
+       update_stat(ws_current_connections, -1);
+
+       pkg_free(data);
        return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
 }
 
diff --git a/modules/websocket/ws_mod.c b/modules/websocket/ws_mod.c
index 302e0db..1bf1dc0 100644
--- a/modules/websocket/ws_mod.c
+++ b/modules/websocket/ws_mod.c
@@ -23,8 +23,10 @@
 
 #include "../../dprint.h"
 #include "../../events.h"
+#include "../../ip_addr.h"
 #include "../../locking.h"
 #include "../../sr_module.h"
+#include "../../tcp_conn.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../lib/kmi/mi.h"
 #include "../../lib/kmi/tree.h"
@@ -35,6 +37,9 @@
 
 MODULE_VERSION
 
+extern gen_lock_t *tcpconn_lock;
+extern struct tcp_connection **tcpconn_id_hash;
+
 static int mod_init(void);
 static void destroy(void);
 
@@ -172,6 +177,47 @@ static void destroy(void)
 
 static struct mi_root *mi_dump(struct mi_root *cmd, void *param)
 {
-       /* TODO: output all open websocket connections */
+       int h, connections = 0;
+       char *src_proto, *dst_proto;
+       char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1];
+       struct tcp_connection *c;
+
+       TCPCONN_LOCK;
+       for (h = 0; h < TCP_ID_HASH_SIZE; h++)
+       {
+               c = tcpconn_id_hash[h];
+               while(c)
+               {
+                       if (c->flags & F_CONN_WS)
+                       {
+                               src_proto = (c->rcv.proto== PROTO_TCP)
+                                               ? "tcp" : "tls";
+                               memset(src_ip, 0, IP6_MAX_STR_SIZE + 1);
+                               ip_addr2sbuf(&c->rcv.src_ip, src_ip,
+                                               IP6_MAX_STR_SIZE);
+
+                               dst_proto = (c->rcv.proto == PROTO_TCP)
+                                               ? "tcp" : "tls";
+                               memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1);
+                               ip_addr2sbuf(&c->rcv.dst_ip, src_ip,
+                                               IP6_MAX_STR_SIZE);
+
+                               LM_ERR("id - %d, "
+                                       "src - %s:%s:%hu, "
+                                       "dst - %s:%s:%hu\n",
+                                       c->id,
+                                       src_proto, src_ip, c->rcv.src_port,
+                                       dst_proto, dst_ip, c->rcv.dst_port);
+
+                               connections++;
+                       }
+
+                       c = c->id_next;
+               }
+       }
+       TCPCONN_UNLOCK;
+
+       LM_ERR("%d WebSocket connections found\n", connections);
+
        return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
 }


_______________________________________________
sr-dev mailing list
[email protected]
http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev

Reply via email to