Fix websocket unmasking issues. Autotest works with C httpd, now.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4699 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2023-12-29 17:45:07 +00:00
parent 8ab8335baa
commit 7964524e0a
7 changed files with 45 additions and 32 deletions

View File

@ -8,6 +8,7 @@
#include "uv.h"
#include <assert.h>
#include <stdalign.h>
#include <stdlib.h>
#include <string.h>
@ -45,6 +46,7 @@ typedef struct _tf_http_connection_t
void* user_data;
bool is_websocket;
int websocket_message_index;
void* body;
size_t body_length;
size_t content_length;
@ -134,7 +136,6 @@ static void _http_connection_destroy(tf_http_connection_t* connection, const cha
{
if (connection->tcp.data)
{
tf_printf("CLOSE %p: %s\n", connection, reason);
uv_close((uv_handle_t*)&connection->tcp, _http_connection_on_close);
}
else if (connection->ref_count == 0)
@ -171,6 +172,25 @@ static void _http_reset_connection(tf_http_connection_t* connection)
connection->parsed_length = 0;
}
static void _http_websocket_mask_in_place(uint8_t* p, uint32_t mask, size_t size)
{
int i = 0;
for (; ((intptr_t)(p + i)) % alignof(uint32_t); i++)
{
p[i] ^= (mask & 0xff);
mask = ((mask >> 8) & 0xffffff) | ((mask & 0xff) << 24);
}
int aligned_start = i;
for (; i + 4 < (int)size; i += 4)
{
*(uint32_t*)(p + i) ^= mask;
}
for (; i < (int)size; i++)
{
p[i] ^= ((mask >> (8 * ((i - aligned_start) % 4))) & 0xff);
}
}
static void _http_add_body_bytes(tf_http_connection_t* connection, const void* data, size_t size)
{
if (connection->is_websocket)
@ -179,9 +199,9 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
memcpy((char*)connection->body + connection->body_length, data, size);
connection->body_length += size;
uint8_t* p = connection->body;
while (connection->body_length >= 2)
{
uint8_t* p = connection->body;
uint8_t bits0 = p[0];
uint8_t bits1 = p[1];
if ((bits1 & (1 << 7)) == 0)
@ -190,7 +210,7 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
_http_connection_destroy(connection, "websocket server received unmasked bytes");
return;
}
uint8_t opcode = bits0 & 0xf;
uint8_t op_code = bits0 & 0xf;
bool fin = (bits0 & (1 << 7)) != 0;
size_t length = bits1 & 0x7f;
int mask_start = 2;
@ -217,33 +237,24 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
if (connection->body_length >= mask_start + length + 4)
{
uint32_t mask =
p[mask_start + 0] |
p[mask_start + 1] << 8 |
p[mask_start + 2] << 16 |
p[mask_start + 3] << 24;
size_t i = 0;
size_t start = mask_start + 4;
size_t end = mask_start + 4 + length;
for (i = start; i < end; i += 4)
{
*(uint32_t*)(p + i) ^= mask;
}
for (; i < end; i++)
{
p[i] ^= ((mask >> (8 * (i % 4))) & 0xff);
}
(uint32_t)p[mask_start + 0] |
(uint32_t)p[mask_start + 1] << 8 |
(uint32_t)p[mask_start + 2] << 16 |
(uint32_t)p[mask_start + 3] << 24;
_http_websocket_mask_in_place(p + mask_start + 4, mask, length);
if (fin)
{
if (connection->request->on_message)
{
connection->request->on_message(connection->request, p + mask_start + 4, length);
connection->request->on_message(connection->request, op_code, p + mask_start + 4, length);
}
else
{
tf_printf("MESSAGE %d [%.*s]\n", opcode, (int)length, p + mask_start + 4);
tf_printf("MESSAGE %d [%.*s]\n", op_code, (int)length, p + mask_start + 4);
}
}
size_t total_length = end;
connection->websocket_message_index++;
size_t total_length = mask_start + 4 + length;
memmove(connection->body, (char*)connection->body + total_length, connection->body_length - total_length);
connection->body_length -= total_length;
}
@ -382,7 +393,7 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t
connection->buffer_length = 0;
}
}
else
else if (read_size < 0)
{
_http_connection_destroy(connection, uv_strerror(read_size));
}