Handle partial HyBi client frames. Cleanup buffer names.

Significant refactor of decode_hybi() and use of it in do_proxy().
This commit is contained in:
Joel Martin 2012-02-02 16:04:21 -06:00
parent bc1ea59867
commit bea32aebed
3 changed files with 123 additions and 87 deletions

View File

@ -105,14 +105,14 @@ ws_ctx_t *alloc_ws_ctx() {
if (! (ctx = malloc(sizeof(ws_ctx_t))) )
{ fatal("malloc()"); }
if (! (ctx->tbuf = malloc(BUFSIZE)) )
{ fatal("malloc of tbuf"); }
if (! (ctx->cbuf = malloc(BUFSIZE)) )
{ fatal("malloc of cbuf"); }
if (! (ctx->tbuf_tmp = malloc(BUFSIZE)) )
{ fatal("malloc of tbuf_tmp"); }
if (! (ctx->cbuf_tmp = malloc(BUFSIZE)) )
{ fatal("malloc of cbuf_tmp"); }
if (! (ctx->cin_buf = malloc(BUFSIZE)) )
{ fatal("malloc of cin_buf"); }
if (! (ctx->cout_buf = malloc(BUFSIZE)) )
{ fatal("malloc of cout_buf"); }
if (! (ctx->tin_buf = malloc(BUFSIZE)) )
{ fatal("malloc of tin_buf"); }
if (! (ctx->tout_buf = malloc(BUFSIZE)) )
{ fatal("malloc of tout_buf"); }
ctx->headers = malloc(sizeof(headers_t));
ctx->ssl = NULL;
@ -121,10 +121,10 @@ ws_ctx_t *alloc_ws_ctx() {
}
int free_ws_ctx(ws_ctx_t *ctx) {
free(ctx->tbuf);
free(ctx->cbuf);
free(ctx->tbuf_tmp);
free(ctx->cbuf_tmp);
free(ctx->cin_buf);
free(ctx->cout_buf);
free(ctx->tin_buf);
free(ctx->tout_buf);
free(ctx);
}
@ -224,7 +224,8 @@ int encode_hixie(u_char const *src, size_t srclength,
}
int decode_hixie(char *src, size_t srclength,
u_char *target, size_t targsize, unsigned int *opcode) {
u_char *target, size_t targsize,
unsigned int *opcode, unsigned int *left) {
char *start, *end, cntstr[4];
int i, len, framecount = 0, retlen = 0;
unsigned char chr;
@ -232,6 +233,8 @@ int decode_hixie(char *src, size_t srclength,
handler_emsg("WebSocket framing error\n");
return -1;
}
*left = srclength;
if (srclength == 2 &&
(src[0] == '\xff') &&
(src[1] == '\x00')) {
@ -258,6 +261,7 @@ int decode_hixie(char *src, size_t srclength,
snprintf(cntstr, 3, "%d", framecount);
traffic(cntstr);
}
*left = 0;
return retlen;
}
@ -300,43 +304,51 @@ int encode_hybi(u_char const *src, size_t srclength,
}
int decode_hybi(unsigned char *src, size_t srclength,
u_char *target, size_t targsize, unsigned int *opcode)
u_char *target, size_t targsize,
unsigned int *opcode, unsigned int *left)
{
unsigned char *frame, *mask, *payload, save_char, cntstr[4];;
int masked = 0;
int i = 0, len, framecount = 0;
size_t remaining;
unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
if ((unsigned int) srclength <= 0)
{
return 0;
}
*left = srclength;
frame = src;
//printf("Deocde new frame\n");
while (frame - src < srclength) {
while (1) {
// Need at least two bytes of the header
// Find beginning of next frame. First time hdr_length, masked and
// payload_length are zero
frame += hdr_length + 4*masked + payload_length;
//printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n",
// (unsigned char) frame[0],
// (unsigned char) frame[1],
// (unsigned char) frame[2],
// (unsigned char) frame[3], srclength);
if (frame > src + srclength) {
//printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) );
break;
}
remaining = (src + srclength) - frame;
if (remaining < 2) {
//printf("Truncated frame header from client\n");
break;
}
framecount ++;
*opcode = frame[0] & 0x0f;
masked = (frame[1] & 0x80) >> 7;
if (*opcode == 0x8) {
// client sent orderly close frame
break;
}
//printf("opcode %d, frame[0..3]: 0x%x 0x%x 0x%x 0x%x\n", *opcode,
// (unsigned char) frame[0],
// (unsigned char) frame[1],
// (unsigned char) frame[2],
// (unsigned char) frame[3]);
payload_length = frame[1] & 0x7f;
if (payload_length == 0) {
// TODO: next frame
handler_msg("empty frame\n");
frame += 6;
continue;
} else if (payload_length < 126) {
if (payload_length < 126) {
hdr_length = 2;
//frame += 2 * sizeof(char);
} else if (payload_length == 126) {
@ -346,23 +358,24 @@ int decode_hybi(unsigned char *src, size_t srclength,
handler_emsg("Receiving frames larger than 65535 bytes not supported\n");
return -1;
}
//printf(" payload_length: %u, raw remaining: %u\n", payload_length, (unsigned int) srclength - target_offset);
payload = frame + hdr_length + 4;
if ((hdr_length + 4*masked + payload_length) > remaining) {
continue;
}
//printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining);
payload = frame + hdr_length + 4*masked;
if (*opcode != 1 && *opcode != 2) {
handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
frame = frame + hdr_length + 4 + payload_length;
continue;
}
if ((payload_length > 0) && (!(frame[1] & 0x80))) {
handler_emsg("Received unmasked payload from client\n");
return -1;
if (payload_length == 0) {
handler_msg("Ignoring empty frame\n");
continue;
}
if ((hdr_length + 4 + payload_length) > (srclength - target_offset)) {
handler_emsg("Truncated frame received from client\n");
if ((payload_length > 0) && (!masked)) {
handler_emsg("Received unmasked payload from client\n");
return -1;
}
@ -385,11 +398,9 @@ int decode_hybi(unsigned char *src, size_t srclength,
handler_emsg("Base64 decode error code %d", len);
return len;
}
target_offset += len;
//printf(" len %d, raw %s\n", len, frame);
target_offset += len;
frame = frame + hdr_length + 4 + payload_length;
}
if (framecount > 1) {
@ -397,6 +408,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
traffic(cntstr);
}
*left = remaining;
return target_offset;
}
@ -557,7 +569,7 @@ ws_ctx_t *do_handshake(int sock) {
char handshake[4096], response[4096], sha1[29], trailer[17];
char *scheme, *pre;
headers_t *headers;
int len, ret;
int len, ret, i, offset;
ws_ctx_t * ws_ctx;
// Peek, but don't read the data
@ -598,12 +610,20 @@ ws_ctx_t *do_handshake(int sock) {
scheme = "ws";
handler_msg("using plain (not SSL) socket\n");
}
len = ws_recv(ws_ctx, handshake, 4096);
if (len == 0) {
handler_emsg("Client closed during handshake\n");
return NULL;
offset = 0;
for (i = 0; i < 10; i++) {
len = ws_recv(ws_ctx, handshake+offset, 4096);
if (len == 0) {
handler_emsg("Client closed during handshake\n");
return NULL;
}
offset += len;
handshake[offset] = 0;
if (strstr(handshake, "\r\n\r\n")) {
break;
}
usleep(10);
}
handshake[len] = 0;
//handler_msg("handshake: %s\n", handshake);
if (!parse_handshake(ws_ctx, handshake)) {

View File

@ -45,10 +45,10 @@ typedef struct {
int hixie;
int hybi;
headers_t *headers;
char *tbuf;
char *cbuf;
char *tbuf_tmp;
char *cbuf_tmp;
char *cin_buf;
char *cout_buf;
char *tin_buf;
char *tout_buf;
} ws_ctx_t;
typedef struct {

View File

@ -54,10 +54,13 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
fd_set rlist, wlist, elist;
struct timeval tv;
int i, maxfd, client = ws_ctx->sockfd;
unsigned int opcode, tstart, tend, cstart, cend, ret;
unsigned int opcode, left, ret;
unsigned int tout_start, tout_end, cout_start, cout_end;
unsigned int tin_start, tin_end;
ssize_t len, bytes;
tstart = tend = cstart = cend = 0;
tout_start = tout_end = cout_start = cout_end;
tin_start = tin_end = 0;
maxfd = client > target ? client+1 : target+1;
while (1) {
@ -71,14 +74,14 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
FD_SET(client, &elist);
FD_SET(target, &elist);
if (tend == tstart) {
if (tout_end == tout_start) {
// Nothing queued for target, so read from client
FD_SET(client, &rlist);
} else {
// Data queued for target, so write to it
FD_SET(target, &wlist);
}
if (cend == cstart) {
if (cout_end == cout_start) {
// Nothing queued for client, so read from target
FD_SET(target, &rlist);
} else {
@ -107,17 +110,17 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
}
if (FD_ISSET(target, &wlist)) {
len = tend-tstart;
bytes = send(target, ws_ctx->tbuf + tstart, len, 0);
len = tout_end-tout_start;
bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0);
if (pipe_error) { break; }
if (bytes < 0) {
handler_emsg("target connection error: %s\n",
strerror(errno));
break;
}
tstart += bytes;
if (tstart >= tend) {
tstart = tend = 0;
tout_start += bytes;
if (tout_start >= tout_end) {
tout_start = tout_end = 0;
traffic(">");
} else {
traffic(">.");
@ -125,17 +128,17 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
}
if (FD_ISSET(client, &wlist)) {
len = cend-cstart;
bytes = ws_send(ws_ctx, ws_ctx->cbuf + cstart, len);
len = cout_end-cout_start;
bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len);
if (pipe_error) { break; }
if (len < 3) {
handler_emsg("len: %d, bytes: %d: %d\n",
(int) len, (int) bytes,
(int) *(ws_ctx->cbuf + cstart));
(int) *(ws_ctx->cout_buf + cout_start));
}
cstart += bytes;
if (cstart >= cend) {
cstart = cend = 0;
cout_start += bytes;
if (cout_start >= cout_end) {
cout_start = cout_end = 0;
traffic("<");
} else {
traffic("<.");
@ -143,28 +146,28 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
}
if (FD_ISSET(target, &rlist)) {
bytes = recv(target, ws_ctx->cbuf_tmp, DBUFSIZE , 0);
bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0);
if (pipe_error) { break; }
if (bytes <= 0) {
handler_emsg("target closed connection\n");
break;
}
cstart = 0;
cout_start = 0;
if (ws_ctx->hybi) {
cend = encode_hybi(ws_ctx->cbuf_tmp, bytes,
ws_ctx->cbuf, BUFSIZE, 1);
cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
ws_ctx->cout_buf, BUFSIZE, 1);
} else {
cend = encode_hixie(ws_ctx->cbuf_tmp, bytes,
ws_ctx->cbuf, BUFSIZE);
cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
ws_ctx->cout_buf, BUFSIZE);
}
/*
printf("encoded: ");
for (i=0; i< cend; i++) {
printf("%u,", (unsigned char) *(ws_ctx->cbuf+i));
for (i=0; i< cout_end; i++) {
printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i));
}
printf("\n");
*/
if (cend < 0) {
if (cout_end < 0) {
handler_emsg("encoding error\n");
break;
}
@ -172,25 +175,30 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
}
if (FD_ISSET(client, &rlist)) {
bytes = ws_recv(ws_ctx, ws_ctx->tbuf_tmp, BUFSIZE-1);
bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1);
if (pipe_error) { break; }
if (bytes <= 0) {
handler_emsg("client closed connection\n");
break;
}
tin_end += bytes;
/*
printf("before decode: ");
for (i=0; i< bytes; i++) {
printf("%u,", (unsigned char) *(ws_ctx->tbuf_tmp+i));
printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i));
}
printf("\n");
*/
if (ws_ctx->hybi) {
len = decode_hybi(ws_ctx->tbuf_tmp, bytes,
ws_ctx->tbuf, BUFSIZE-1, &opcode);
len = decode_hybi(ws_ctx->tin_buf + tin_start,
tin_end-tin_start,
ws_ctx->tout_buf, BUFSIZE-1,
&opcode, &left);
} else {
len = decode_hixie(ws_ctx->tbuf_tmp, bytes,
ws_ctx->tbuf, BUFSIZE-1, &opcode);
len = decode_hixie(ws_ctx->tin_buf + tin_start,
tin_end-tin_start,
ws_ctx->tout_buf, BUFSIZE-1,
&opcode, &left);
}
if (opcode == 8) {
@ -201,7 +209,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
/*
printf("decoded: ");
for (i=0; i< len; i++) {
printf("%u,", (unsigned char) *(ws_ctx->tbuf+i));
printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i));
}
printf("\n");
*/
@ -209,9 +217,17 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
handler_emsg("decoding error\n");
break;
}
if (left) {
tin_start = tin_end - left;
//printf("partial frame from client");
} else {
tin_start = 0;
tin_end = 0;
}
traffic("}");
tstart = 0;
tend = len;
tout_start = 0;
tout_end = len;
}
}
}