[sr-dev] git:pd/websocket: modules/websocket: received frame decoding

Peter Dunkley peter.dunkley at crocodile-rcs.com
Sun Jun 17 01:44:46 CEST 2012


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

Author: Peter Dunkley <peter.dunkley at crocodile-rcs.com>
Committer: Peter Dunkley <peter.dunkley at crocodile-rcs.com>
Date:   Sun Jun 17 00:44:14 2012 +0100

modules/websocket: received frame decoding

---

 modules/websocket/example/websocket_test.html |   71 +++++++
 modules/websocket/ws_frame.c                  |  245 +++++++++++++++++++++++-
 2 files changed, 305 insertions(+), 11 deletions(-)

diff --git a/modules/websocket/example/websocket_test.html b/modules/websocket/example/websocket_test.html
new file mode 100644
index 0000000..308b945
--- /dev/null
+++ b/modules/websocket/example/websocket_test.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+
+<title>WebSocket Test</title>
+
+<script language="javascript" type="text/javascript">
+var wsUri = "ws://192.168.111.12/";
+var output;
+
+function init()
+{
+	output = document.getElementById("output");
+	testWebSocket();
+}
+
+function testWebSocket() 
+{
+	websocket = new WebSocket(wsUri, "sip");
+	websocket.onopen = function(evt) { onOpen(evt) };
+	websocket.onclose = function(evt) { onClose(evt) };
+	websocket.onmessage = function(evt) { onMessage(evt) };
+	websocket.onerror = function(evt) { onError(evt) };
+}
+
+function onOpen(evt)
+{
+	writeToScreen("CONNECTED");
+	doSend("WebSocket rocks");
+	doSend("WebSocket rolls");
+	doSend("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
+	websocket.close();
+}
+
+function onClose(evt)
+{
+	writeToScreen("DISCONNECTED");
+}
+
+function onMessage(evt)
+{
+	writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>'); websocket.close();
+}
+
+function onError(evt)
+{
+	writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+}
+
+function doSend(message)
+{
+	writeToScreen("SENT: " + message);
+	websocket.send(message);
+}
+
+function writeToScreen(message)
+{
+	var pre = document.createElement("p");
+	pre.style.wordWrap = "break-word";
+	pre.innerHTML = message; output.appendChild(pre);
+}
+
+window.addEventListener("load", init, false);
+
+</script>
+
+<h2>WebSocket Test</h2>
+
+<div id="output"></div>
+
+</html>
diff --git a/modules/websocket/ws_frame.c b/modules/websocket/ws_frame.c
index ed7943b..bce0a09 100644
--- a/modules/websocket/ws_frame.c
+++ b/modules/websocket/ws_frame.c
@@ -26,13 +26,204 @@
 #include "ws_frame.h"
 #include "ws_mod.h"
 
-#define FRAME_BUF_SIZE 1024
-static char frame_buf[FRAME_BUF_SIZE];
+/*   0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-------+-+-------------+-------------------------------+
+     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+     | |1|2|3|       |K|             |                               |
+     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+     |     Extended payload length continued, if payload len == 127  |
+     + - - - - - - - - - - - - - - - +-------------------------------+
+     |                               |Masking-key, if MASK set to 1  |
+     +-------------------------------+-------------------------------+
+     | Masking-key (continued)       |          Payload Data         |
+     +-------------------------------- - - - - - - - - - - - - - - - +
+     :                     Payload Data continued ...                :
+     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+     |                     Payload Data continued ...                |
+     +---------------------------------------------------------------+ */
+
+typedef struct {
+	unsigned int fin;
+	unsigned int rsv1;
+	unsigned int rsv2;
+	unsigned int rsv3;
+	unsigned int opcode;
+	unsigned int mask;
+	unsigned int payload_len;
+	unsigned char masking_key[4];
+	char *payload_data;
+	tcp_event_info_t *tcpinfo;
+} ws_frame_t;
+
+#define BYTE0_MASK_FIN		(0x80)
+#define BYTE0_MASK_RSV1		(0x40)
+#define BYTE0_MASK_RSV2		(0x20)
+#define BYTE0_MASK_RSV3 	(0x10)
+#define BYTE0_MASK_OPCODE	(0x0F)
+#define BYTE1_MASK_MASK		(0x80)
+#define BYTE1_MASK_PAYLOAD_LEN	(0x7F)
+
+#define OPCODE_CONTINUATION	(0x0)
+#define OPCODE_TEXT_FRAME	(0x1)
+#define OPCODE_BINARY_FRAME	(0x2)
+/* 0x3 - 0x7 are reserved for further non-control frames */
+#define OPCODE_CLOSE		(0x8)
+#define OPCODE_PING		(0x9)
+#define OPCODE_PONG		(0xa)
+/* 0xb - 0xf are reserved for further control frames */
+
+
+static int decode_and_validate_ws_frame(ws_frame_t *frame)
+{
+	unsigned int i, len=frame->tcpinfo->len;
+	int mask_start, j;
+	char *buf = frame->tcpinfo->buf;
+
+	/* Decode and validate first 9 bits */
+	if (len < 2)
+	{
+		LM_WARN("message is too short\n");
+		return -1;
+	}
+	frame->fin = (buf[0] & 0xff) & BYTE0_MASK_FIN;
+	frame->rsv1 = (buf[0] & 0xff) & BYTE0_MASK_RSV1;
+	frame->rsv2 = (buf[0] & 0xff) & BYTE0_MASK_RSV2;
+	frame->rsv3 = (buf[0] & 0xff) & BYTE0_MASK_RSV3;
+	frame->opcode = (buf[0] & 0xff) & BYTE0_MASK_OPCODE;
+	frame->mask = (buf[1] & 0xff) & BYTE1_MASK_MASK;
+	
+	if (!frame->fin)
+	{
+		LM_WARN("WebSocket fragmentation not supported in the sip "
+			"sub-protocol\n");
+		return -1;
+	}
+
+	if (frame->rsv1 || frame->rsv2 || frame->rsv3)
+	{
+		LM_WARN("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_WARN("unsupported opcode: 0x%x\n",
+			(unsigned char) frame->opcode);
+		return -1;
+	}
+
+	if (!frame->mask)
+	{
+		LM_WARN("this is a server - all received messages must be "
+			"masked\n");
+		return -1;
+	}
+
+	/* Decode and validate length */
+	frame->payload_len = (buf[1] & 0xff) & BYTE1_MASK_PAYLOAD_LEN;
+	if (frame->payload_len == 126)
+	{
+		if (len < 4)
+		{
+			LM_WARN("message is too short\n");
+			return -1;
+		}
+		mask_start = 4;
+
+		frame->payload_len = 	  ((buf[2] & 0xff) <<  8)
+					| ((buf[3] & 0xff) <<  0);
+	}
+	else if (frame->payload_len == 127)
+	{
+		if (len < 10)
+		{
+			LM_WARN("message is too short\n");
+			return -1;
+		}
+		mask_start = 10;
+
+		/* Only decoding the last four bytes of the length...
+		   This limits the size of WebSocket messages that can be
+		   handled to 2^32 = which should be plenty for SIP! */
+	 	frame->payload_len =	  ((buf[6] & 0xff) << 24)
+					| ((buf[7] & 0xff) << 16)
+					| ((buf[8] & 0xff) <<  8)
+					| ((buf[9] & 0xff) <<  0);
+	}
+	else
+		mask_start = 2;
+
+	/* Decode mask */
+	frame->masking_key[0] = (buf[mask_start + 0] & 0xff);
+	frame->masking_key[1] = (buf[mask_start + 1] & 0xff);
+	frame->masking_key[2] = (buf[mask_start + 2] & 0xff);
+	frame->masking_key[3] = (buf[mask_start + 3] & 0xff);
+
+	/* Decode and unmask payload */
+	if (len < frame->payload_len + mask_start)
+	{
+		LM_WARN("message not complete payload_len = %u but only "
+			"received %u\n", frame->payload_len, len);
+		return -1;
+	}
+	frame->payload_data = &buf[mask_start + 4];
+	for (i = 0; i < frame->payload_len; i++)
+	{
+		j = i % 4;
+		frame->payload_data[i]
+			= frame->payload_data[i] ^ frame->masking_key[j];
+	}
+
+	LM_INFO("Rx (decoded): %.*s\n",
+		(int) frame->payload_len, frame->payload_data);
+
+	return frame->opcode;
+}
+
+static int handle_sip_message(ws_frame_t *msg)
+{
+	LM_INFO("Received SIP message\n");
+	return 0;
+}
+
+static int handle_close(ws_frame_t *msg)
+{
+	LM_INFO("Received Close\n");
+	return 0;
+}
+
+static int handle_ping(ws_frame_t *msg)
+{
+	LM_INFO("Received Ping\n");
+	return 0;
+}
+
+static int handle_pong(ws_frame_t *msg)
+{
+	LM_INFO("Received Pong\n");
+	return 0;
+}
 
 int ws_frame_received(void *data)
 {
-	int printed;
-	str output;
+	ws_frame_t ws_frame;
 	tcp_event_info_t *tev = (tcp_event_info_t *) data;
 
 	if (tev == NULL || tev->buf == NULL || tev->len <= 0)
@@ -41,14 +232,46 @@ int ws_frame_received(void *data)
 		return -1;
 	}
 
-	output.len = 0;
-	output.s = frame_buf;
+	ws_frame.tcpinfo = tev;
+	switch(decode_and_validate_ws_frame(&ws_frame))
+	{
+	case OPCODE_TEXT_FRAME:
+	case OPCODE_BINARY_FRAME:
+		if (handle_sip_message(&ws_frame) < 0)
+		{
+			LM_ERR("handling SIP message\n");
+			return -1;
+		}
+		break;
+
+	case OPCODE_CLOSE:
+		if (handle_close(&ws_frame) < 0)
+		{
+			LM_ERR("handling Close\n");
+			return -1;
+		}
+		break;
 
-	for (printed = 0; printed < tev->len && output.len < FRAME_BUF_SIZE - 3;
-			printed++)
-		output.len += sprintf(output.s + output.len, "%02x ",
-				(unsigned char) tev->buf[printed]);
-	LM_INFO("Rx: %.*s\n", output.len, output.s);
+	case OPCODE_PING:
+		if (handle_ping(&ws_frame) < 0)
+		{
+			LM_ERR("handling Ping\n");
+			return -1;
+		}
+		break;
+
+	case OPCODE_PONG:
+		if (handle_pong(&ws_frame) < 0)
+		{
+			LM_ERR("handling Pong\n");
+			return -1;
+		}
+		break;
+		
+	default:
+		LM_WARN("received bad frame\n");
+		return -1;
+	}
 
 	return 0;
 }




More information about the sr-dev mailing list