1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-03 08:13:14 +02:00

relay: fix websocket frame decoding, answer a PONG to PING (closes #675)

This commit is contained in:
Sébastien Helleu
2016-02-06 14:08:20 +01:00
parent 060d68bb8d
commit 9263634d2e
9 changed files with 191 additions and 40 deletions
+2
View File
@@ -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)
+2 -1
View File
@@ -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++;
+113 -24
View File
@@ -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:
@@ -441,6 +450,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;
+16 -1
View File
@@ -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,
+27 -6
View File
@@ -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);
}
/*
+4 -1
View File
@@ -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,
+15 -4
View File
@@ -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)
{
+8 -1
View File
@@ -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);
@@ -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);
}
/*