diff --git a/lib/client-handshake.c b/lib/client-handshake.c index 183277ef..da585a69 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -99,6 +99,8 @@ libwebsocket_client_connect(struct libwebsocket_context *this, wsi->xor_mask = xor_no_mask; switch (wsi->ietf_spec_revision) { + case 0: + break; case 4: wsi->xor_mask = xor_mask_04; break; diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index a857beac..ba596bf7 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -304,6 +304,47 @@ libwebsockets_get_peer_addresses(int fd, char *name, int name_len, } } +void libwebsockets_00_spaceout(char *key, int spaces, int seed) +{ + char *p; + + key++; + while (spaces--) { + if (*key && (seed & 1)) + key++; + seed >>= 1; + + p = key + strlen(key); + while (p >= key) { + p[1] = p[0]; + p--; + } + *key++ = ' '; + } +} + +void libwebsockets_00_spam(char *key, int count, int seed) +{ + char *p; + + key++; + while (count--) { + + if (*key && (seed & 1)) + key++; + seed >>= 1; + + p = key + strlen(key); + while (p >= key) { + p[1] = p[0]; + p--; + } + *key++ = 0x21 + ((seed & 0xffff) % 15); + /* 4 would use it up too fast.. not like it matters */ + seed >>= 1; + } +} + /** * libwebsocket_service_fd() - Service polled socket with something waiting * @this: Websocket context @@ -760,6 +801,19 @@ libwebsocket_service_fd(struct libwebsocket_context *this, sizeof wsi->key_b64); /* + * 00 example client handshake + * + * GET /socket.io/websocket HTTP/1.1 + * Upgrade: WebSocket + * Connection: Upgrade + * Host: 127.0.0.1:9999 + * Origin: http://127.0.0.1 + * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^ + * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1 + * Cookie: socketio=websocket + * + * (Á®Ä0¶†≥ + * * 04 example client handshake * * GET /chat HTTP/1.1 @@ -773,6 +827,93 @@ libwebsocket_service_fd(struct libwebsocket_context *this, */ p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", wsi->c_path); + + if (wsi->ietf_spec_revision == 0) { + unsigned char spaces_1, spaces_2; + unsigned int max_1, max_2; + unsigned int num_1, num_2; + unsigned long product_1, product_2; + char key_1[40]; + char key_2[40]; + unsigned int seed; + unsigned int count; + char challenge[16]; + + read(this->fd_random, &spaces_1, sizeof(char)); + read(this->fd_random, &spaces_2, sizeof(char)); + + spaces_1 = (spaces_1 % 12) + 1; + spaces_2 = (spaces_2 % 12) + 1; + + max_1 = 4294967295 / spaces_1; + max_2 = 4294967295 / spaces_2; + + read(this->fd_random, &num_1, sizeof(int)); + read(this->fd_random, &num_2, sizeof(int)); + + num_1 = (num_1 % max_1); + num_2 = (num_2 % max_2); + + challenge[0] = num_1 >> 24; + challenge[1] = num_1 >> 16; + challenge[2] = num_1 >> 8; + challenge[3] = num_1; + challenge[4] = num_2 >> 24; + challenge[5] = num_2 >> 16; + challenge[6] = num_2 >> 8; + challenge[7] = num_2; + + product_1 = num_1 * spaces_1; + product_2 = num_2 * spaces_2; + + sprintf(key_1, "%lu", product_1); + sprintf(key_2, "%lu", product_2); + + read(this->fd_random, &seed, sizeof(int)); + read(this->fd_random, &count, sizeof(int)); + + libwebsockets_00_spam(key_1, (count % 12) + 1, seed); + + read(this->fd_random, &seed, sizeof(int)); + read(this->fd_random, &count, sizeof(int)); + + libwebsockets_00_spam(key_2, (count % 12) + 1, seed); + + read(this->fd_random, &seed, sizeof(int)); + + libwebsockets_00_spaceout(key_1, spaces_1, seed); + libwebsockets_00_spaceout(key_2, spaces_2, seed >> 16); + + p += sprintf(p, "Upgrade: websocket\x0d\x0a" + "Connection: Upgrade\x0d\x0aHost: %s\x0d\x0a", + wsi->c_host); + if (wsi->c_origin) + p += sprintf(p, "Origin: %s\x0d\x0a", + wsi->c_origin); + + if (wsi->c_protocol) + p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", + wsi->c_protocol); + + p += sprintf(p, "Sec-WebSocket-Key1: %s\x0d\x0a", + key_1); + p += sprintf(p, "Sec-WebSocket-Key2: %s\x0d\x0a", + key_2); + + p += sprintf(p, "\x0d\x0a"); + + read(this->fd_random, p, 8); + memcpy(&challenge[8], p, 8); + p += 8; + + /* precompute what we want to see from the server */ + + MD5((unsigned char *)challenge, 16, + (unsigned char *)wsi->initial_handshake_hash_base64); + + goto issue_hdr; + } + p += sprintf(p, "Host: %s\x0d\x0a", wsi->c_host); p += sprintf(p, "Upgrade: websocket\x0d\x0a"); p += sprintf(p, "Connection: Upgrade\x0d\x0a" @@ -789,13 +930,6 @@ libwebsocket_service_fd(struct libwebsocket_context *this, p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a\x0d\x0a", wsi->ietf_spec_revision); - /* done with these now */ - - free(wsi->c_path); - free(wsi->c_host); - if (wsi->c_origin) - free(wsi->c_origin); - /* prepare the expected server accept response */ strcpy((char *)buf, wsi->key_b64); @@ -806,6 +940,15 @@ libwebsocket_service_fd(struct libwebsocket_context *this, lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64, sizeof wsi->initial_handshake_hash_base64); +issue_hdr: + + /* done with these now */ + + free(wsi->c_path); + free(wsi->c_host); + if (wsi->c_origin) + free(wsi->c_origin); + /* send our request to the server */ @@ -877,6 +1020,54 @@ libwebsocket_service_fd(struct libwebsocket_context *this, goto bail3; } + /* + * 00 / 76 --> + * + * HTTP/1.1 101 WebSocket Protocol Handshake + * Upgrade: WebSocket + * Connection: Upgrade + * Sec-WebSocket-Origin: http://127.0.0.1 + * Sec-WebSocket-Location: ws://127.0.0.1:9999/socket.io/websocket + * + * xxxxxxxxxxxxxxxx + */ + + if (wsi->ietf_spec_revision == 0) { + if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len || + !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || + !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len || + !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len || + (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len && + wsi->c_protocol != NULL)) { + fprintf(stderr, "libwebsocket_client_handshake " + "missing required header(s)\n"); + pkt[len] = '\0'; + fprintf(stderr, "%s", pkt); + goto bail3; + } + + strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token); + if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token, + "101 websocket protocol handshake")) { + fprintf(stderr, "libwebsocket_client_handshake " + "server sent bad HTTP response '%s'\n", + wsi->utf8_token[WSI_TOKEN_HTTP].token); + goto bail3; + } + + if (wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len < 16) { + fprintf(stderr, "libwebsocket_client_handshake " + "challenge reply too short %d\n", + wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len); + pkt[len] = '\0'; + fprintf(stderr, "%s", pkt); + goto bail3; + + } + + goto select_protocol; + } + /* * well, what the server sent looked reasonable for syntax. * Now let's confirm it sent all the necessary headers @@ -929,6 +1120,7 @@ libwebsocket_service_fd(struct libwebsocket_context *this, goto bail3; } +select_protocol: pc = wsi->c_protocol; @@ -999,6 +1191,22 @@ libwebsocket_service_fd(struct libwebsocket_context *this, } check_accept: + + if (wsi->ietf_spec_revision == 0) { + + if (memcmp(wsi->initial_handshake_hash_base64, + wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 16)) { + fprintf(stderr, "libwebsocket_client_handshake " + "failed 00 challenge compare\n"); + + pkt[len] = '\0'; + fprintf(stderr, "%s", pkt); + goto bail2; + } + + goto accept_ok; + } + /* * Confirm his accept token is the one we precomputed */ @@ -1026,6 +1234,8 @@ libwebsocket_service_fd(struct libwebsocket_context *this, SHA1(buf, strlen((char *)buf), wsi->masking_key_04); } +accept_ok: + /* allocate the per-connection user memory (if any) */ if (wsi->protocol->per_session_data_size) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index fe7238fc..a97de07c 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -124,7 +124,7 @@ enum lws_token_indexes { }; /* - * From 06 sped + * From 06 spec 1000 1000 indicates a normal closure, meaning whatever purpose the diff --git a/lib/parsers.c b/lib/parsers.c index 8a0f564d..60a9096e 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -114,11 +114,19 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) if (wsi->parser_state != WSI_TOKEN_CHALLENGE) break; - /* -76 has no version header */ + /* -76 has no version header ... server */ if (!wsi->utf8_token[WSI_TOKEN_VERSION].token_len && + wsi->mode != LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY && wsi->utf8_token[wsi->parser_state].token_len != 8) break; + /* -76 has no version header ... client */ + if (!wsi->utf8_token[WSI_TOKEN_VERSION].token_len && + wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY && + wsi->utf8_token[wsi->parser_state].token_len != 16) + break; + + /* <= 03 has old handshake with version header needs 8 bytes */ if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len && atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token) < 4 &&