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