Module: sip-router
Branch: pd/websocket
Commit: 68c60fd4156bda792463bd202b82afb2c967dcdd
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=68c60fd…
Author: Peter Dunkley <peter.dunkley(a)crocodile-rcs.com>
Committer: Peter Dunkley <peter.dunkley(a)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;
}