C websockify: support for binary websocket protocol with HyBi/RFC 6455.

The server prefers binary over base64 encoding, given a choice. This is required as noVNC no longer supports base64 encoding.
This commit is contained in:
Samuel Brian 2016-11-07 15:43:31 +10:00
parent 8a43ea7dbe
commit 53a49c2bbd
3 changed files with 54 additions and 18 deletions

View File

@ -271,22 +271,30 @@ int encode_hybi(u_char const *src, size_t srclength,
char *target, size_t targsize, unsigned int opcode) char *target, size_t targsize, unsigned int opcode)
{ {
unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0; unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0;
if ((int)srclength <= 0)
{
return 0;
}
b64_sz = ((srclength - 1) / 3) * 4 + 4; if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) {
handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n");
return -1;
}
target[0] = (char)(opcode & 0x0F | 0x80); target[0] = (char)(opcode & 0x0F | 0x80);
if (b64_sz <= 125) { if ((int)srclength <= 0) {
target[1] = (char) b64_sz; return 0;
}
if (opcode & OPCODE_TEXT) {
len = ((srclength - 1) / 3) * 4 + 4;
} else {
len = srclength;
}
if (len <= 125) {
target[1] = (char) len;
payload_offset = 2; payload_offset = 2;
} else if ((b64_sz > 125) && (b64_sz < 65536)) { } else if ((len > 125) && (len < 65536)) {
target[1] = (char) 126; target[1] = (char) 126;
*(u_short*)&(target[2]) = htons(b64_sz); *(u_short*)&(target[2]) = htons(len);
payload_offset = 4; payload_offset = 4;
} else { } else {
handler_emsg("Sending frames larger than 65535 bytes not supported\n"); handler_emsg("Sending frames larger than 65535 bytes not supported\n");
@ -296,8 +304,13 @@ int encode_hybi(u_char const *src, size_t srclength,
//payload_offset = 10; //payload_offset = 10;
} }
len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset); if (opcode & OPCODE_TEXT) {
len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset);
} else {
memcpy(target+payload_offset, src, srclength);
len = srclength;
}
if (len < 0) { if (len < 0) {
return len; return len;
} }
@ -366,7 +379,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
//printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining); //printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining);
payload = frame + hdr_length + 4*masked; payload = frame + hdr_length + 4*masked;
if (*opcode != 1 && *opcode != 2) { if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) {
handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode); handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
continue; continue;
} }
@ -391,8 +404,13 @@ int decode_hybi(unsigned char *src, size_t srclength,
payload[i] ^= mask[i%4]; payload[i] ^= mask[i%4];
} }
// base64 decode the data if (*opcode & OPCODE_TEXT) {
len = ws_b64_pton((const char*)payload, target+target_offset, targsize); // base64 decode the data
len = ws_b64_pton((const char*)payload, target+target_offset, targsize);
} else {
memcpy(target+target_offset, payload, payload_length);
len = payload_length;
}
// Restore the first character of the next frame // Restore the first character of the next frame
payload[payload_length] = save_char; payload[payload_length] = save_char;
@ -573,6 +591,7 @@ ws_ctx_t *do_handshake(int sock) {
headers_t *headers; headers_t *headers;
int len, ret, i, offset; int len, ret, i, offset;
ws_ctx_t * ws_ctx; ws_ctx_t * ws_ctx;
char *response_protocol;
// Peek, but don't read the data // Peek, but don't read the data
len = recv(sock, handshake, 1024, MSG_PEEK); len = recv(sock, handshake, 1024, MSG_PEEK);
@ -648,10 +667,23 @@ ws_ctx_t *do_handshake(int sock) {
} }
headers = ws_ctx->headers; headers = ws_ctx->headers;
if (strstr(headers->protocols, "binary")) {
ws_ctx->opcode = OPCODE_BINARY;
response_protocol = "binary";
} else if (strstr(headers->protocols, "base64")) {
ws_ctx->opcode = OPCODE_TEXT;
response_protocol = "base64";
} else {
handler_emsg("Invalid protocol '%s', expecting 'binary' or 'base64'\n",
headers->protocols);
return NULL;
}
if (ws_ctx->hybi > 0) { if (ws_ctx->hybi > 0) {
handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi); handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
gen_sha1(headers, sha1); gen_sha1(headers, sha1);
sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, "base64"); sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, response_protocol);
} else { } else {
if (ws_ctx->hixie == 76) { if (ws_ctx->hixie == 76) {
handler_msg("using protocol Hixie 76\n"); handler_msg("using protocol Hixie 76\n");

View File

@ -26,6 +26,9 @@ Sec-WebSocket-Protocol: %s\r\n\
#define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n" #define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
#define OPCODE_TEXT 0x01
#define OPCODE_BINARY 0x02
typedef struct { typedef struct {
char path[1024+1]; char path[1024+1];
char host[1024+1]; char host[1024+1];
@ -44,6 +47,7 @@ typedef struct {
SSL *ssl; SSL *ssl;
int hixie; int hixie;
int hybi; int hybi;
int opcode;
headers_t *headers; headers_t *headers;
char *cin_buf; char *cin_buf;
char *cout_buf; char *cout_buf;

View File

@ -155,7 +155,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
cout_start = 0; cout_start = 0;
if (ws_ctx->hybi) { if (ws_ctx->hybi) {
cout_end = encode_hybi(ws_ctx->cin_buf, bytes, cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
ws_ctx->cout_buf, BUFSIZE, 1); ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode);
} else { } else {
cout_end = encode_hixie(ws_ctx->cin_buf, bytes, cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
ws_ctx->cout_buf, BUFSIZE); ws_ctx->cout_buf, BUFSIZE);
@ -202,7 +202,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
} }
if (opcode == 8) { if (opcode == 8) {
handler_emsg("client sent orderly close frame\n"); handler_msg("client sent orderly close frame\n");
break; break;
} }