Skip to content

Instantly share code, notes, and snippets.

@king1600
Last active August 17, 2017 05:11
Show Gist options
  • Save king1600/e0dd3654d925be4c515051fbfb7eb588 to your computer and use it in GitHub Desktop.
Save king1600/e0dd3654d925be4c515051fbfb7eb588 to your computer and use it in GitHub Desktop.
WebSocket frame parsing and dumping
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Frame {
unsigned fin : 1; // 0 or 1 (bool) if frame fin
unsigned masked : 1; // 0 or 1 (bool) if frame masked
unsigned char opcode; // 0 - 10 (int) frame opcode
// reserved bit values
unsigned rsv1 : 1;
unsigned rsv2 : 1;
unsigned rsv3 : 1;
char *data; // frame payload data
size_t len; // frame payload length
} Frame;
/**
* Generate a random byte
* (change function if neccesary)
* @return {unsigned char} a random byte
*/
inline unsigned char randByte() {
return (unsigned char)(rand() % 0xff + 1);
}
/**
* Calculate the dumped size of a frame
* @param {Frame*} frame the frame to calculate
* @return {size_t} the size of the dumped frame
*/
inline size_t FrameSize(Frame *frame) {
size_t i = 2 + (frame->masked ? 4 : 0) + frame->len;
if (frame->len >= 126 && frame->len < 65536) i += 2;
else if (frame->len >= 0x10000) i += 8;
return i;
}
/**
* Parse data into websocket frame
* @param {Frame*} frame the frame to fill with parsed info
* @param {const char*} data the data to parse
* @param {size_t} len the size of the data to parse
*/
void Parse(Frame *frame, const char *data, size_t len) {
// create variables and empty out current
int i, count = 0;
size_t size, offset = 0;
memset(frame, 0, sizeof(*frame));
// get frame first byte header info
frame->fin = (data[offset] >> 7) & 1;
frame->rsv1 = (data[offset] >> 6) & 1;
frame->rsv2 = (data[offset] >> 5) & 1;
frame->rsv3 = (data[offset] >> 4) & 1;
frame->opcode = data[offset++] & 0x0f;
// get frame masked state and payload length
frame->masked = (data[offset] >> 7) & 1;
size = data[offset++] & (~0x80);
if (size == 0x7f) count = 8;
else if (size == 0x7e) count = 2;
if (count > 0) size = 0;
while (count-- > 0)
size |= (data[offset++] & 0xff) << (8 * count);
frame->len = size;
// if masked, get mask from frame
unsigned char mask[4];
if (frame->masked)
for (i = 0; i < 4; i++)
mask[i] = data[offset++] & 0xff;
// get payload and mask it if neccessary
char payload[size + 1] = {0};
for (i = 0; i < size; i++)
payload[i] = !frame->masked ? data[offset + i]
: data[offset + i] ^ mask[i % 4];
// set frame data
free(frame->data);
frame->data = malloc(sizeof(char*));
memcpy(frame->data, payload, size);
}
/**
* Dump websocket frame into data to send out
* @param {Frame*} frame the frame to dump from
* @param {char*} out the output of the dumped frame
*/
void Dump(Frame *frame, char *out) {
size_t i, offset = 0; // calculation variables
memset(out, 0, sizeof(char*)); // empty output
// set first header byte
out[offset] = frame->rsv1 << 7;
out[offset] |= frame->rsv1 << 6;
out[offset] |= frame->rsv1 << 5;
out[offset] |= frame->rsv1 << 4;
out[offset++] |= (char)frame->opcode;
// set second header byte
char masked = frame->rsv1 << 7;
if (frame->len <= 125) {
out[offset++] = (char)(masked | frame->len);
} else if (frame->len >= 126 && frame->len < 65536) {
out[offset++] = (char)(masked | 0x7e);
for (i = 8; i > -1; i -= 8)
out[offset++] = (char)((frame->len >> i) & 0xff);
} else {
out[offset++] = (char)(masked | 0x7f);
for (i = 56; i > -1; i -= 8)
out[offset++] = (char)((frame->len >> i) & 0xff);
}
// if frame masked, create mask
unsigned char mask[4] = { 0 };
for (i = 0; i < 4; i++) {
mask[i] = randByte();
out[offset++] = mask[i];
}
// add payload data
for (i = 0; i < frame->len; i++)
out[offset + i] = (char)(!frame->masked ?
frame->data[i] : (frame->data[i] ^ mask[i % 4]));
}
int main() {
// for rand()
srand(time(NULL));
// using Dump example
Frame frame = {0};
frame.fin = 1;
frame.masked = 2;
frame.data = (char*)"Hello world";
frame.len = strlen(frame.data);
char out[FrameSize(&frame)];
Dump(&frame, out);
// using parse example
char *buffer = /* socket read or something */
size_t length = /* from socket read */
Parse(&frame, buffer, length);
printf("Payload: %s\n", frame->data);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment