Skip to content

Commit

Permalink
Fix websocket client to support multiple clients at the same time.
Browse files Browse the repository at this point in the history
The generated key for the handshake validation has moved to the header.

IoT.js-DCO-1.0-Signed-off-by: Istvan Miklos imiklos2@inf.u-szeged.hu
  • Loading branch information
Istvan Miklos authored and yichoi committed Sep 7, 2018
1 parent 452f642 commit bbdd157
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/js/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ Websocket.prototype.connect = function(url, port, path, callback) {

this._socket.on('data', function(data) {
if (self._firstMessage) {
var remaining_data = native.parseHandshakeData(data);
var remaining_data = native.parseHandshakeData(data, self._handle);
self._handle.onhandshakedone(remaining_data);
} else {
self._handle.ondata(data);
Expand Down
107 changes: 64 additions & 43 deletions src/modules/iotjs_module_websocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
static void iotjs_wsclient_destroy(iotjs_wsclient_t *wsclient) {
IOTJS_RELEASE(wsclient->tcp_buff.buffer);
IOTJS_RELEASE(wsclient->ws_buff.data);
IOTJS_RELEASE(wsclient->generated_key);
IOTJS_RELEASE(wsclient);
}

Expand All @@ -44,7 +45,6 @@ iotjs_wsclient_t *iotjs_wsclient_create(const jerry_value_t jobject) {


static const char WS_GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static unsigned char *generated_key = NULL;

/**
* The protocol is as follows:
Expand Down Expand Up @@ -78,15 +78,15 @@ static void iotjs_websocket_create_callback(jerry_value_t jsref,
}


static unsigned char *ws_generate_key(jerry_value_t jsref) {
static unsigned char *ws_generate_key(jerry_value_t jsref, size_t *key_len) {
unsigned char *key = IOTJS_CALLOC(16, unsigned char);
for (int i = 0; i < 16; i++) {
key[i] = rand() % 256;
}

unsigned char *ret_val = NULL;

if (!iotjs_base64_encode(&ret_val, key, 16)) {
if (!(*key_len = iotjs_base64_encode(&ret_val, key, 16))) {
jerry_value_t ret_str =
jerry_create_string((jerry_char_t *)"mbedtls base64 encode failed");
iotjs_websocket_create_callback(jsref, ret_str, IOTJS_MAGIC_STRING_ONERROR);
Expand All @@ -110,15 +110,24 @@ static char *iotjs_ws_write_data(char *buff, void *data, size_t size) {
}


static bool iotjs_check_handshake_key(char *server_key) {
unsigned char *out_buff = NULL;
static bool iotjs_check_handshake_key(char *server_key, jerry_value_t jsref) {
void *native_p;
JNativeInfoType *out_native_info;
bool has_p =
jerry_get_object_native_pointer(jsref, &native_p, &out_native_info);
if (!has_p || out_native_info != &wsclient_native_info) {
return false;
}
iotjs_wsclient_t *wsclient = (iotjs_wsclient_t *)native_p;

size_t concatenated_size = strlen(WS_GUID) + strlen((char *)generated_key);
unsigned char *out_buff = NULL;
size_t ws_guid_size = strlen(WS_GUID);
size_t generated_key_size = strnlen((char *)wsclient->generated_key, 24);
size_t concatenated_size = ws_guid_size + generated_key_size;
unsigned char concatenated[concatenated_size + 1];

memcpy(concatenated, generated_key, strlen((char *)generated_key));
memcpy(concatenated + strlen((char *)generated_key), WS_GUID,
strlen(WS_GUID));
memcpy(concatenated, wsclient->generated_key, generated_key_size);
memcpy(concatenated + generated_key_size, WS_GUID, ws_guid_size);
concatenated[concatenated_size] = '\0';

size_t out_buff_size =
Expand All @@ -130,7 +139,7 @@ static bool iotjs_check_handshake_key(char *server_key) {
ret_val = false;
}

IOTJS_RELEASE(generated_key);
IOTJS_RELEASE(wsclient->generated_key);
IOTJS_RELEASE(key_out);
IOTJS_RELEASE(out_buff);

Expand Down Expand Up @@ -229,6 +238,33 @@ static void iotjs_websocket_create_buffer_and_cb(char **buff_ptr,
}


static jerry_value_t iotjs_websocket_check_error(uint8_t code) {
switch (code) {
case WS_ERR_INVALID_UTF8: {
return JS_CREATE_ERROR(COMMON, "Invalid UTF8 string in UTF8 message");
}

case WS_ERR_INVALID_TERMINATE_CODE: {
return JS_CREATE_ERROR(COMMON, "Invalid terminate code received");
}

case WS_ERR_UNKNOWN_OPCODE: {
return JS_CREATE_ERROR(COMMON, "Uknown opcode received");
}

case WS_ERR_NATIVE_POINTER_ERR: {
return JS_CREATE_ERROR(COMMON, "WebSocket native pointer unavailable");
}

case WS_ERR_FRAME_SIZE_LIMIT: {
return JS_CREATE_ERROR(COMMON, "Frame size received exceeds limit");
}

default: { return jerry_create_undefined(); };
}
}


JS_FUNCTION(PrepareHandshakeRequest) {
DJS_CHECK_THIS();

Expand All @@ -243,8 +279,17 @@ JS_FUNCTION(PrepareHandshakeRequest) {
return JS_CREATE_ERROR(COMMON, "Invalid host and/or path arguments!");
};

generated_key = ws_generate_key(jsref);
size_t generated_key_len = strlen((char *)generated_key);
void *native_p;
JNativeInfoType *out_native_info;
bool has_p =
jerry_get_object_native_pointer(jsref, &native_p, &out_native_info);
if (!has_p || out_native_info != &wsclient_native_info) {
return iotjs_websocket_check_error(WS_ERR_NATIVE_POINTER_ERR);
}
iotjs_wsclient_t *wsclient = (iotjs_wsclient_t *)native_p;

size_t generated_key_len = 0;
wsclient->generated_key = ws_generate_key(jsref, &generated_key_len);

jerry_value_t jfinal = iotjs_bufferwrap_create_buffer(
header_fixed_size + iotjs_string_size(&l_endpoint) +
Expand All @@ -265,7 +310,7 @@ JS_FUNCTION(PrepareHandshakeRequest) {
buff_ptr = iotjs_ws_write_header(buff_ptr, upgrade);
buff_ptr = iotjs_ws_write_header(buff_ptr, connection);
buff_ptr = iotjs_ws_write_header(buff_ptr, sec_websocket_key);
memcpy(buff_ptr, generated_key, generated_key_len);
memcpy(buff_ptr, wsclient->generated_key, generated_key_len);
buff_ptr += generated_key_len;
buff_ptr = iotjs_ws_write_header(buff_ptr, line_end);
buff_ptr = iotjs_ws_write_header(buff_ptr, sec_websocket_ver);
Expand All @@ -279,14 +324,15 @@ JS_FUNCTION(PrepareHandshakeRequest) {

JS_FUNCTION(ParseHandshakeData) {
DJS_CHECK_THIS();
DJS_CHECK_ARGS(1, object);
DJS_CHECK_ARGS(2, object, object);

jerry_value_t jbuffer = JS_GET_ARG(0, object);
iotjs_bufferwrap_t *buff_wrap = iotjs_bufferwrap_from_jbuffer(jbuffer);
if (buff_wrap->length < 12 || strncmp(buff_wrap->buffer + 9, "101", 3)) {
return JS_CREATE_ERROR(COMMON, "WebSocket connection failed");
}

jerry_value_t jsref = JS_GET_ARG(1, object);
char ws_accept[] = "Sec-WebSocket-Accept: ";

char *frame_end = strstr(buff_wrap->buffer, "\r\n\r\n");
Expand All @@ -296,7 +342,7 @@ JS_FUNCTION(ParseHandshakeData) {

frame_end += 4; // \r\n\r\n

if (!iotjs_check_handshake_key(key)) {
if (!iotjs_check_handshake_key(key, jsref)) {
return JS_CREATE_ERROR(COMMON, "WebSocket handshake key comparison failed");
}

Expand Down Expand Up @@ -330,33 +376,6 @@ static void iotjs_websocket_concat_tcp_buffers(iotjs_wsclient_t *wsclient,
}


static jerry_value_t iotjs_websocket_check_error(uint8_t code) {
switch (code) {
case WS_ERR_INVALID_UTF8: {
return JS_CREATE_ERROR(COMMON, "Invalid UTF8 string in UTF8 message");
}

case WS_ERR_INVALID_TERMINATE_CODE: {
return JS_CREATE_ERROR(COMMON, "Invalid terminate code received");
}

case WS_ERR_UNKNOWN_OPCODE: {
return JS_CREATE_ERROR(COMMON, "Uknown opcode received");
}

case WS_ERR_NATIVE_POINTER_ERR: {
return JS_CREATE_ERROR(COMMON, "WebSocket native pointer unavailable");
}

case WS_ERR_FRAME_SIZE_LIMIT: {
return JS_CREATE_ERROR(COMMON, "Frame size received exceeds limit");
}

default: { return jerry_create_undefined(); };
}
}


static uint8_t iotjs_websocket_decode_frame(iotjs_wsclient_t *wsclient,
char *first_byte, char *buff_ptr,
uint32_t payload_len,
Expand Down Expand Up @@ -488,7 +507,7 @@ JS_FUNCTION(WsReceive) {
bool has_p =
jerry_get_object_native_pointer(jsref, &native_p, &out_native_info);
if (!has_p || out_native_info != &wsclient_native_info) {
return WS_ERR_NATIVE_POINTER_ERR;
return iotjs_websocket_check_error(WS_ERR_NATIVE_POINTER_ERR);
}
iotjs_wsclient_t *wsclient = (iotjs_wsclient_t *)native_p;

Expand Down Expand Up @@ -600,6 +619,8 @@ JS_FUNCTION(WsInit) {
wsclient->ws_buff.data = NULL;
wsclient->ws_buff.length = 0;

wsclient->generated_key = NULL;

return jerry_create_undefined();
}

Expand Down
1 change: 1 addition & 0 deletions src/modules/iotjs_module_websocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ typedef struct {
char first_byte;
bool masked;
} ws_buff;
unsigned char *generated_key;
} iotjs_wsclient_t;

#endif /* IOTJS_MODULE_WEBSOCKET_H */

0 comments on commit bbdd157

Please sign in to comment.