Last active
August 17, 2017 05:11
-
-
Save king1600/e0dd3654d925be4c515051fbfb7eb588 to your computer and use it in GitHub Desktop.
WebSocket frame parsing and dumping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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