diff options
-rw-r--r-- | ChangeLog.asciidoc | 2 | ||||
-rw-r--r-- | src/plugins/relay/irc/relay-irc.c | 3 | ||||
-rw-r--r-- | src/plugins/relay/relay-client.c | 137 | ||||
-rw-r--r-- | src/plugins/relay/relay-client.h | 17 | ||||
-rw-r--r-- | src/plugins/relay/relay-raw.c | 33 | ||||
-rw-r--r-- | src/plugins/relay/relay-raw.h | 5 | ||||
-rw-r--r-- | src/plugins/relay/relay-websocket.c | 19 | ||||
-rw-r--r-- | src/plugins/relay/relay-websocket.h | 9 | ||||
-rw-r--r-- | src/plugins/relay/weechat/relay-weechat-msg.c | 6 |
9 files changed, 191 insertions, 40 deletions
diff --git a/ChangeLog.asciidoc b/ChangeLog.asciidoc index 7c02213fd..af4d58436 100644 --- a/ChangeLog.asciidoc +++ b/ChangeLog.asciidoc @@ -33,6 +33,8 @@ https://weechat.org/files/releasenotes/ReleaseNotes-devel.html[release notes] keep_eol is 2 and the string ends with separators * irc: add missing completion "*" for target in command /msg * irc: fix /msg command with multiple targets including "*" +* relay: fix decoding of multiple frames in a single websocket message, + send PONG on PING received in a websocket frame (issue #675) * relay: fix command "input" received from clients with only spaces in content of message (weechat protocol) (issue #663) diff --git a/src/plugins/relay/irc/relay-irc.c b/src/plugins/relay/irc/relay-irc.c index 5731edd1a..00754f45e 100644 --- a/src/plugins/relay/irc/relay-irc.c +++ b/src/plugins/relay/irc/relay-irc.c @@ -236,7 +236,8 @@ relay_irc_sendf (struct t_relay_client *client, const char *format, ...) if (message) { snprintf (message, length, "%s\r\n", str_message); - relay_client_send (client, message, strlen (message), NULL); + relay_client_send (client, RELAY_CLIENT_MSG_STANDARD, + message, strlen (message), NULL); free (message); } number++; diff --git a/src/plugins/relay/relay-client.c b/src/plugins/relay/relay-client.c index fbb00cebb..1e67c6aef 100644 --- a/src/plugins/relay/relay-client.c +++ b/src/plugins/relay/relay-client.c @@ -58,6 +58,9 @@ char *relay_client_status_name[] = /* name of status (for signal/info) */ char *relay_client_data_type_string[] = /* strings for data types */ { "text", "binary" }; +char *relay_client_msg_type_string[] = /* prefix in raw buffer for message */ +{ "", "[PING]\n", "[PONG]\n" }; + struct t_relay_client *relay_clients = NULL; struct t_relay_client *last_relay_client = NULL; int relay_client_count = 0; /* number of clients */ @@ -295,7 +298,8 @@ relay_client_recv_text (struct t_relay_client *client, const char *data) pos - client->partial_message + 1); if (raw_msg) { - relay_raw_print (client, RELAY_RAW_FLAG_RECV, + relay_raw_print (client, RELAY_CLIENT_MSG_STANDARD, + RELAY_RAW_FLAG_RECV, raw_msg, strlen (raw_msg) + 1); free (raw_msg); } @@ -336,7 +340,9 @@ relay_client_recv_text (struct t_relay_client *client, const char *data) handshake = relay_websocket_build_handshake (client); if (handshake) { - relay_client_send (client, handshake, + relay_client_send (client, + RELAY_CLIENT_MSG_STANDARD, + handshake, strlen (handshake), NULL); free (handshake); client->websocket = 2; @@ -411,7 +417,10 @@ relay_client_recv_text (struct t_relay_client *client, const char *data) } else { - /* receive text from client */ + /* + * interpret text from client, according to the relay + * protocol used + */ switch (client->protocol) { case RELAY_PROTOCOL_WEECHAT: @@ -442,6 +451,54 @@ relay_client_recv_text (struct t_relay_client *client, const char *data) } /* + * Reads text buffer from a client. + */ + +void +relay_client_recv_text_buffer (struct t_relay_client *client, + const char *buffer, + unsigned long long length_buffer) +{ + unsigned long long index; + unsigned char msg_type; + + index = 0; + while (index < length_buffer) + { + msg_type = RELAY_CLIENT_MSG_STANDARD; + + /* + * in case of websocket, we can receive PING from client: + * trace this PING in raw buffer and answer with a PONG + */ + if (client->websocket == 2) + { + msg_type = (unsigned char)buffer[index]; + if (msg_type == RELAY_CLIENT_MSG_PING) + { + /* print message in raw buffer */ + relay_raw_print (client, RELAY_CLIENT_MSG_PING, + RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_BINARY, + buffer + index + 1, + strlen (buffer + index + 1) + 1); + /* answer with a PONG */ + relay_client_send (client, + RELAY_CLIENT_MSG_PONG, + buffer + index + 1, + strlen (buffer + index + 1), + NULL); + } + index++; + } + + if (msg_type == RELAY_CLIENT_MSG_STANDARD) + relay_client_recv_text (client, buffer + index); + + index += strlen (buffer + index) + 1; + } +} + +/* * Reads data from a client. */ @@ -449,10 +506,10 @@ int relay_client_recv_cb (void *arg_client, int fd) { struct t_relay_client *client; - static char buffer[4096], decoded[4096 + 1]; + static char buffer[4096], decoded[8192 + 1]; const char *ptr_buffer; int num_read, rc; - unsigned long long decoded_length; + unsigned long long decoded_length, length_buffer; /* make C compiler happy */ (void) fd; @@ -474,6 +531,7 @@ relay_client_recv_cb (void *arg_client, int fd) { buffer[num_read] = '\0'; ptr_buffer = buffer; + length_buffer = num_read; /* * if we are receiving the first message from client, check if it looks @@ -533,13 +591,14 @@ relay_client_recv_cb (void *arg_client, int fd) return WEECHAT_RC_OK; } ptr_buffer = decoded; + length_buffer = decoded_length; } if ((client->websocket == 1) || (client->recv_data_type == RELAY_CLIENT_DATA_TEXT)) { /* websocket initializing or text data for this client */ - relay_client_recv_text (client, ptr_buffer); + relay_client_recv_text_buffer (client, ptr_buffer, length_buffer); } else { @@ -600,7 +659,9 @@ relay_client_recv_cb (void *arg_client, int fd) void relay_client_outqueue_add (struct t_relay_client *client, const char *data, int data_size, - int raw_flags[2], const char *raw_message[2], + enum t_relay_client_msg_type raw_msg_type[2], + int raw_flags[2], + const char *raw_message[2], int raw_size[2]) { struct t_relay_client_outqueue *new_outqueue; @@ -622,6 +683,7 @@ relay_client_outqueue_add (struct t_relay_client *client, new_outqueue->data_size = data_size; for (i = 0; i < 2; i++) { + new_outqueue->raw_msg_type[i] = RELAY_CLIENT_MSG_STANDARD; new_outqueue->raw_flags[i] = 0; new_outqueue->raw_message[i] = NULL; new_outqueue->raw_size[i] = 0; @@ -630,6 +692,7 @@ relay_client_outqueue_add (struct t_relay_client *client, new_outqueue->raw_message[i] = malloc (raw_size[i]); if (new_outqueue->raw_message[i]) { + new_outqueue->raw_msg_type[i] = raw_msg_type[i]; new_outqueue->raw_flags[i] = raw_flags[i]; memcpy (new_outqueue->raw_message[i], raw_message[i], raw_size[i]); @@ -708,10 +771,13 @@ relay_client_outqueue_free_all (struct t_relay_client *client) */ int -relay_client_send (struct t_relay_client *client, const char *data, +relay_client_send (struct t_relay_client *client, + enum t_relay_client_msg_type msg_type, + const char *data, int data_size, const char *message_raw_buffer) { - int num_sent, raw_size[2], raw_flags[2], i; + int num_sent, raw_size[2], raw_flags[2], opcode, i; + enum t_relay_client_msg_type raw_msg_type[2]; char *websocket_frame; unsigned long long length_frame; const char *ptr_data, *raw_msg[2]; @@ -725,6 +791,7 @@ relay_client_send (struct t_relay_client *client, const char *data, /* set raw messages */ for (i = 0; i < 2; i++) { + raw_msg_type[i] = msg_type; raw_flags[i] = RELAY_RAW_FLAG_SEND; raw_msg[i] = NULL; raw_size[i] = 0; @@ -754,8 +821,10 @@ relay_client_send (struct t_relay_client *client, const char *data, { raw_msg[0] = data; raw_size[0] = data_size; - if ((client->websocket != 1) - && (client->send_data_type == RELAY_CLIENT_DATA_BINARY)) + if ((msg_type == RELAY_CLIENT_MSG_PING) + || (msg_type == RELAY_CLIENT_MSG_PONG) + || ((client->websocket != 1) + && (client->send_data_type == RELAY_CLIENT_DATA_BINARY))) { /* * set binary flag if we send binary to client @@ -774,7 +843,21 @@ relay_client_send (struct t_relay_client *client, const char *data, /* if websocket is initialized, encode data in a websocket frame */ if (client->websocket == 2) { - websocket_frame = relay_websocket_encode_frame (client, data, data_size, + switch (msg_type) + { + case RELAY_CLIENT_MSG_PING: + opcode = WEBSOCKET_FRAME_OPCODE_PING; + break; + case RELAY_CLIENT_MSG_PONG: + opcode = WEBSOCKET_FRAME_OPCODE_PONG; + break; + default: + opcode = (client->send_data_type == RELAY_CLIENT_DATA_TEXT) ? + WEBSOCKET_FRAME_OPCODE_TEXT : WEBSOCKET_FRAME_OPCODE_BINARY; + break; + } + websocket_frame = relay_websocket_encode_frame (opcode, data, + data_size, &length_frame); if (websocket_frame) { @@ -792,7 +875,7 @@ relay_client_send (struct t_relay_client *client, const char *data, if (client->outqueue) { relay_client_outqueue_add (client, ptr_data, data_size, - raw_flags, raw_msg, raw_size); + raw_msg_type, raw_flags, raw_msg, raw_size); } else { @@ -809,8 +892,8 @@ relay_client_send (struct t_relay_client *client, const char *data, { if (raw_msg[i]) { - relay_raw_print (client, - raw_flags[i], raw_msg[i], raw_size[i]); + relay_raw_print (client, raw_msg_type[i], raw_flags[i], + raw_msg[i], raw_size[i]); } } if (num_sent > 0) @@ -821,9 +904,10 @@ relay_client_send (struct t_relay_client *client, const char *data, if (num_sent < data_size) { /* some data was not sent, add it to outqueue */ - relay_client_outqueue_add (client, ptr_data + num_sent, + relay_client_outqueue_add (client, + ptr_data + num_sent, data_size - num_sent, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); } } else if (num_sent < 0) @@ -835,8 +919,10 @@ relay_client_send (struct t_relay_client *client, const char *data, || (num_sent == GNUTLS_E_INTERRUPTED)) { /* add message to queue (will be sent later) */ - relay_client_outqueue_add (client, ptr_data, data_size, - raw_flags, raw_msg, raw_size); + relay_client_outqueue_add (client, + ptr_data, data_size, + raw_msg_type, raw_flags, + raw_msg, raw_size); } else { @@ -860,7 +946,8 @@ relay_client_send (struct t_relay_client *client, const char *data, { /* add message to queue (will be sent later) */ relay_client_outqueue_add (client, ptr_data, data_size, - raw_flags, raw_msg, raw_size); + raw_msg_type, raw_flags, + raw_msg, raw_size); } else { @@ -949,10 +1036,12 @@ relay_client_timer_cb (void *data, int remaining_calls) * (so that it is displayed only one time, even if * message is sent in many chunks) */ - relay_raw_print (ptr_client, - ptr_client->outqueue->raw_flags[i], - ptr_client->outqueue->raw_message[i], - ptr_client->outqueue->raw_size[i]); + relay_raw_print ( + ptr_client, + ptr_client->outqueue->raw_msg_type[i], + ptr_client->outqueue->raw_flags[i], + ptr_client->outqueue->raw_message[i], + ptr_client->outqueue->raw_size[i]); ptr_client->outqueue->raw_flags[i] = 0; free (ptr_client->outqueue->raw_message[i]); ptr_client->outqueue->raw_message[i] = NULL; diff --git a/src/plugins/relay/relay-client.h b/src/plugins/relay/relay-client.h index db2366aed..670c001d0 100644 --- a/src/plugins/relay/relay-client.h +++ b/src/plugins/relay/relay-client.h @@ -49,6 +49,17 @@ enum t_relay_client_data_type RELAY_NUM_CLIENT_DATA_TYPES, }; +/* type of message exchanged with the client (used for websockets) */ + +enum t_relay_client_msg_type +{ + RELAY_CLIENT_MSG_STANDARD, + RELAY_CLIENT_MSG_PING, + RELAY_CLIENT_MSG_PONG, + /* number of message types */ + RELAY_NUM_CLIENT_MSG_TYPES, +}; + /* macros for status */ #define RELAY_CLIENT_HAS_ENDED(client) \ @@ -61,6 +72,7 @@ struct t_relay_client_outqueue { char *data; /* data to send */ int data_size; /* number of bytes */ + int raw_msg_type[2]; /* msgs types */ int raw_flags[2]; /* flags for raw messages */ char *raw_message[2]; /* msgs for raw buffer (can be NULL)*/ int raw_size[2]; /* size (in bytes) of raw messages */ @@ -107,6 +119,7 @@ struct t_relay_client }; extern char *relay_client_status_string[]; +extern char *relay_client_msg_type_string[]; extern struct t_relay_client *relay_clients; extern struct t_relay_client *last_relay_client; extern int relay_client_count; @@ -117,7 +130,9 @@ extern struct t_relay_client *relay_client_search_by_id (int id); extern int relay_client_status_search (const char *name); extern void relay_client_set_desc (struct t_relay_client *client); extern int relay_client_recv_cb (void *arg_client, int fd); -extern int relay_client_send (struct t_relay_client *client, const char *data, +extern int relay_client_send (struct t_relay_client *client, + enum t_relay_client_msg_type msg_type, + const char *data, int data_size, const char *message_raw_buffer); extern int relay_client_timer_cb (void *data, int remaining_calls); extern struct t_relay_client *relay_client_new (int sock, const char *address, diff --git a/src/plugins/relay/relay-raw.c b/src/plugins/relay/relay-raw.c index 28a1b55b6..5b8d32209 100644 --- a/src/plugins/relay/relay-raw.c +++ b/src/plugins/relay/relay-raw.c @@ -218,17 +218,20 @@ relay_raw_message_add_to_list (time_t date, const char *prefix, */ void -relay_raw_message_add (struct t_relay_client *client, int flags, +relay_raw_message_add (struct t_relay_client *client, + enum t_relay_client_msg_type msg_type, + int flags, const char *data, int data_size) { - char *buf, *buf2, prefix[256], prefix_arrow[16]; + char *buf, *buf2, *buf3, prefix[256], prefix_arrow[16]; const unsigned char *ptr_buf; const char *hexa = "0123456789ABCDEF"; - int pos_buf, pos_buf2, char_size, i; + int pos_buf, pos_buf2, char_size, i, length; struct t_relay_raw_message *new_raw_message; buf = NULL; buf2 = NULL; + buf3 = NULL; if (flags & RELAY_RAW_FLAG_BINARY) { @@ -274,7 +277,12 @@ relay_raw_message_add (struct t_relay_client *client, int flags, } buf2[pos_buf2] = '\0'; } + } + if (!(flags & RELAY_RAW_FLAG_BINARY) + || (msg_type == RELAY_CLIENT_MSG_PING) + || (msg_type == RELAY_CLIENT_MSG_PONG)) + { /* build prefix with arrow */ prefix_arrow[0] = '\0'; switch (flags & (RELAY_RAW_FLAG_RECV | RELAY_RAW_FLAG_SEND)) @@ -319,10 +327,20 @@ relay_raw_message_add (struct t_relay_client *client, int flags, } } + length = strlen (relay_client_msg_type_string[msg_type]) + + strlen ((buf2) ? buf2 : ((buf) ? buf : data)) + 1; + buf3 = malloc (length); + if (buf3) + { + snprintf (buf3, length, "%s%s", + relay_client_msg_type_string[msg_type], + (buf2) ? buf2 : ((buf) ? buf : data)); + } + new_raw_message = relay_raw_message_add_to_list ( time (NULL), prefix, - (buf2) ? buf2 : ((buf) ? buf : data)); + (buf3) ? buf3 : ((buf2) ? buf2 : ((buf) ? buf : data))); if (new_raw_message) { @@ -336,6 +354,8 @@ relay_raw_message_add (struct t_relay_client *client, int flags, free (buf); if (buf2) free (buf2); + if (buf3) + free (buf3); } /* @@ -343,14 +363,15 @@ relay_raw_message_add (struct t_relay_client *client, int flags, */ void -relay_raw_print (struct t_relay_client *client, int flags, +relay_raw_print (struct t_relay_client *client, + enum t_relay_client_msg_type msg_type, int flags, const char *data, int data_size) { /* auto-open Relay raw buffer if debug for irc plugin is >= 1 */ if (!relay_raw_buffer && (weechat_relay_plugin->debug >= 1)) relay_raw_open (0); - relay_raw_message_add (client, flags, data, data_size); + relay_raw_message_add (client, msg_type, flags, data, data_size); } /* diff --git a/src/plugins/relay/relay-raw.h b/src/plugins/relay/relay-raw.h index a4d8d4f02..4546a2b5d 100644 --- a/src/plugins/relay/relay-raw.h +++ b/src/plugins/relay/relay-raw.h @@ -20,6 +20,8 @@ #ifndef WEECHAT_RELAY_RAW_H #define WEECHAT_RELAY_RAW_H 1 +#include "relay-client.h" + #define RELAY_RAW_BUFFER_NAME "relay_raw" #define RELAY_RAW_PREFIX_RECV "-->" #define RELAY_RAW_PREFIX_SEND "<--" @@ -47,7 +49,8 @@ extern void relay_raw_open (int switch_to_buffer); extern struct t_relay_raw_message *relay_raw_message_add_to_list (time_t date, const char *prefix, const char *message); -extern void relay_raw_print (struct t_relay_client *client, int flags, +extern void relay_raw_print (struct t_relay_client *client, + enum t_relay_client_msg_type msg_type, int flags, const char *data, int data_size); extern void relay_raw_message_free_all (); extern int relay_raw_add_to_infolist (struct t_infolist *infolist, diff --git a/src/plugins/relay/relay-websocket.c b/src/plugins/relay/relay-websocket.c index 95a59f4b4..e9c1fd410 100644 --- a/src/plugins/relay/relay-websocket.c +++ b/src/plugins/relay/relay-websocket.c @@ -29,6 +29,7 @@ #include "relay.h" #include "relay-client.h" #include "relay-config.h" +#include "relay-websocket.h" /* @@ -249,7 +250,8 @@ relay_websocket_send_http (struct t_relay_client *client, if (message) { snprintf (message, length, "HTTP/1.1 %s\r\n\r\n", http); - relay_client_send (client, message, strlen (message), NULL); + relay_client_send (client, RELAY_CLIENT_MSG_STANDARD, + message, strlen (message), NULL); free (message); } } @@ -269,6 +271,7 @@ relay_websocket_decode_frame (const unsigned char *buffer, unsigned long long *decoded_length) { unsigned long long i, index_buffer, length_frame_size, length_frame; + unsigned char opcode; *decoded_length = 0; index_buffer = 0; @@ -276,6 +279,8 @@ relay_websocket_decode_frame (const unsigned char *buffer, /* loop to decode all frames in message */ while (index_buffer + 2 <= buffer_length) { + opcode = buffer[index_buffer] & 15; + /* * check if frame is masked: client MUST send a masked frame; if frame is * not masked, we MUST reject it and close the connection (see RFC 6455) @@ -311,13 +316,18 @@ relay_websocket_decode_frame (const unsigned char *buffer, } index_buffer += 4; + /* copy opcode in decoded data */ + decoded[*decoded_length] = (opcode == WEBSOCKET_FRAME_OPCODE_PING) ? + RELAY_CLIENT_MSG_PING : RELAY_CLIENT_MSG_STANDARD; + *decoded_length += 1; + /* decode data using masks */ for (i = 0; i < length_frame; i++) { decoded[*decoded_length + i] = (int)((unsigned char)buffer[index_buffer + i]) ^ masks[i % 4]; } decoded[*decoded_length + length_frame] = '\0'; - *decoded_length += length_frame; + *decoded_length += length_frame + 1; index_buffer += length_frame; } @@ -334,7 +344,7 @@ relay_websocket_decode_frame (const unsigned char *buffer, */ char * -relay_websocket_encode_frame (struct t_relay_client *client, +relay_websocket_encode_frame (int opcode, const char *buffer, unsigned long long length, unsigned long long *length_frame) @@ -348,7 +358,8 @@ relay_websocket_encode_frame (struct t_relay_client *client, if (!frame) return NULL; - frame[0] = (client->send_data_type == RELAY_CLIENT_DATA_TEXT) ? 0x81 : 0x82; + frame[0] = 0x80; + frame[0] |= opcode; if (length <= 125) { diff --git a/src/plugins/relay/relay-websocket.h b/src/plugins/relay/relay-websocket.h index 8d6e565b9..54eab904a 100644 --- a/src/plugins/relay/relay-websocket.h +++ b/src/plugins/relay/relay-websocket.h @@ -20,6 +20,13 @@ #ifndef WEECHAT_RELAY_WEBSOCKET_H #define WEECHAT_RELAY_WEBSOCKET_H 1 +#define WEBSOCKET_FRAME_OPCODE_CONTINUATION 0x00 +#define WEBSOCKET_FRAME_OPCODE_TEXT 0x01 +#define WEBSOCKET_FRAME_OPCODE_BINARY 0x02 +#define WEBSOCKET_FRAME_OPCODE_CLOSE 0x08 +#define WEBSOCKET_FRAME_OPCODE_PING 0x09 +#define WEBSOCKET_FRAME_OPCODE_PONG 0x0A + extern int relay_websocket_is_http_get_weechat (const char *message); extern void relay_websocket_save_header (struct t_relay_client *client, const char *message); @@ -31,7 +38,7 @@ extern int relay_websocket_decode_frame (const unsigned char *buffer, unsigned long long length, unsigned char *decoded, unsigned long long *decoded_length); -extern char *relay_websocket_encode_frame (struct t_relay_client *client, +extern char *relay_websocket_encode_frame (int opcode, const char *buffer, unsigned long long length, unsigned long long *length_frame); diff --git a/src/plugins/relay/weechat/relay-weechat-msg.c b/src/plugins/relay/weechat/relay-weechat-msg.c index 8ddc1fc1f..b84ec94d4 100644 --- a/src/plugins/relay/weechat/relay-weechat-msg.c +++ b/src/plugins/relay/weechat/relay-weechat-msg.c @@ -1057,7 +1057,8 @@ relay_weechat_msg_send (struct t_relay_client *client, msg->id); /* send compressed data */ - relay_client_send (client, (const char *)dest, dest_size + 5, + relay_client_send (client, RELAY_CLIENT_MSG_STANDARD, + (const char *)dest, dest_size + 5, raw_message); free (dest); @@ -1082,7 +1083,8 @@ relay_weechat_msg_send (struct t_relay_client *client, /* send uncompressed data */ snprintf (raw_message, sizeof (raw_message), "obj: %d bytes, id: %s", msg->data_size, msg->id); - relay_client_send (client, msg->data, msg->data_size, raw_message); + relay_client_send (client, RELAY_CLIENT_MSG_STANDARD, + msg->data, msg->data_size, raw_message); } /* |