[sr-dev] git:pd/websocket: modules/websocket: Filled in MI commands to dump WebSocket connection details and Close a WebSocket

Peter Dunkley peter.dunkley at crocodile-rcs.com
Sun Jun 17 22:32:29 CEST 2012


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 <peter.dunkley at crocodile-rcs.com>
Committer: Peter Dunkley <peter.dunkley at crocodile-rcs.com>
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);
 }




More information about the sr-dev mailing list