static size_t ws_receive_frame(unsigned char *frame, size_t *length, int *type)
{
unsigned char masks[4];
unsigned char mask;
unsigned char flength;
unsigned char idx_first_mask;
unsigned char idx_first_data;
size_t data_length, consumed;
int i;
int j;
*type = frame[0] & 0x0F;
#ifdef DEBUG
printf("[C] ws_receive_frame type %d, processing %zu bytes.\n", *type, *length);
#endif
if (frame[0] == (WS_FIN | WS_OPCODE_CON_CLOSE_FRAME))
return 0;
consumed = 0; // assume no data is consumed
if (frame[0] == (WS_FIN | WS_OPCODE_TEXT_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_BINARY_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_PING_FRAME) || frame[0] == (WS_FIN | WS_OPCODE_PONG_FRAME))
{
idx_first_mask = 2;
mask = frame[1];
flength = mask & 0x7F;
if (flength == 126)
{
idx_first_mask = 4;
// the following 2 bytes interpreted as a 16-bit unsigned integer are the data length
data_length = ((size_t)frame[2] << 8) | (size_t)frame[3];
}
else if (flength == 127)
{
idx_first_mask = 10;
// the following 8 bytes interpreted as a 64-bit unsigned integer
// (the most significant bit MUST be 0) are the data length
data_length = ((size_t)frame[2] << 56) | ((size_t)frame[3] << 48) | ((size_t)frame[4] << 40) | ((size_t)frame[5] << 32) | ((size_t)frame[6] << 24) | ((size_t)frame[7] << 16) | ((size_t)frame[8] << 8) | (size_t)frame[9];
}
else
{
data_length = (size_t)flength;
}
#ifdef DEBUG
printf("[C] ws_receive_frame: flength: %d, data_length: %zu\n", flength, data_length);
#endif
idx_first_data = (unsigned char)(idx_first_mask + 4);
masks[0] = frame[idx_first_mask + 0];
masks[1] = frame[idx_first_mask + 1];
masks[2] = frame[idx_first_mask + 2];
masks[3] = frame[idx_first_mask + 3];
// return if there is insufficient data to complete the frame
if (idx_first_data + data_length > *length)
{
// blank out the type
*type = 0;
return 0;
}
// decode the message
for (i = idx_first_data, j = 0; j < (int)data_length; i++, j++)
frame[j] = frame[i] ^ masks[j % 4]; // neat, overwrite the incoming frame buffer
// the entire WebSocket frame has been processed
consumed = i; // the number of bytes consumed (equal to idx_first_data + data_length)
*length = data_length;
if (*type == WS_OPCODE_TEXT_FRAME)
frame[j] = '\0';
}
else
printf("[C] ws_receive_frame: received an unknown frame %02X (%d).\n", frame[0], *type);
return consumed;
}
size_t preamble_ws_frame(char **frame_data, size_t length, unsigned char type)
{
unsigned char *frame;
unsigned char idx_first_data;
if (length <= 125)
{
idx_first_data = 2;
}
else if (0xFFFF < length)
{
idx_first_data = 10;
}
else
{
idx_first_data = 4;
}
frame = malloc(idx_first_data + length);
if (frame == NULL)
return 0;
frame[0] = type;
if (length <= 125)
{
frame[1] = length & 0x7F;
idx_first_data = 2;
}
else if (0xFFFF < length)
{
frame[1] = 127;
frame[2] = (unsigned char)((length >> 56) & 0xFF);
frame[3] = (unsigned char)((length >> 48) & 0xFF);
frame[4] = (unsigned char)((length >> 40) & 0xFF);
frame[5] = (unsigned char)((length >> 32) & 0xFF);
frame[6] = (unsigned char)((length >> 24) & 0xFF);
frame[7] = (unsigned char)((length >> 16) & 0xFF);
frame[8] = (unsigned char)((length >> 8) & 0xFF);
frame[9] = (unsigned char)(length & 0xFF);
idx_first_data = 10;
}
else
{
frame[1] = 126;
frame[2] = (length >> 8) & 0xFF;
frame[3] = length & 0xFF;
idx_first_data = 4;
}
*frame_data = (char *)frame;
return (size_t)idx_first_data;
}