From: Andy Green Date: Wed, 9 Feb 2011 08:49:14 +0000 (+0000) Subject: introduce-ietf-05-framing-and-commandline-options.patch X-Git-Tag: upstream/1.7.3~1427 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bfb051f3a35a4b83e30c9e37d7ce8b92ab564d9e;p=platform%2Fupstream%2Flibwebsockets.git introduce-ietf-05-framing-and-commandline-options.patch This adds 05 support, and -v switches on test-client and test-ping to allow setting their ietf protocol version to 4 or 5. It also optimizes the masking to us a function pointer, which takes some conditionals out of the fast path. Signed-off-by: Andy Green --- diff --git a/lib/client-handshake.c b/lib/client-handshake.c index b805cd1..0624dcb 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -87,6 +87,8 @@ libwebsocket_client_close(struct libwebsocket *wsi) * @protocol: Comma-separated list of protocols being asked for from * the server, or just one. The server will pick the one it * likes best. + * @ietf_version_or_minus_one: -1 to ask to connect using the default, latest + * protocol supported, or the specific protocol ordinal * * This function creates a connection to a remote server */ @@ -99,7 +101,8 @@ libwebsocket_client_connect(struct libwebsocket_context *this, const char *path, const char *host, const char *origin, - const char *protocol) + const char *protocol, + int ietf_version_or_minus_one) { struct hostent *server_hostent; struct sockaddr_in server_addr; @@ -137,13 +140,41 @@ libwebsocket_client_connect(struct libwebsocket_context *this, this->wsi[this->fds_count] = wsi; - wsi->ietf_spec_revision = 4; + /* -1 means just use latest supported */ + + if (ietf_version_or_minus_one == -1) + ietf_version_or_minus_one = 5; + + wsi->ietf_spec_revision = ietf_version_or_minus_one; wsi->name_buffer_pos = 0; wsi->user_space = NULL; wsi->state = WSI_STATE_CLIENT_UNCONNECTED; wsi->pings_vs_pongs = 0; wsi->protocol = NULL; + /* set up appropriate masking */ + + wsi->xor_mask = xor_no_mask; + + switch (wsi->ietf_spec_revision) { + case 4: + wsi->xor_mask = xor_mask_04; + break; + case 5: + wsi->xor_mask = xor_mask_05; + break; + default: + fprintf(stderr, + "Client ietf version %d not supported\n", + wsi->ietf_spec_revision); + return NULL; + } + + /* force no mask if he asks for that though */ + + if (this->options & LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK) + wsi->xor_mask = xor_no_mask; + for (n = 0; n < WSI_TOKEN_COUNT; n++) { wsi->utf8_token[n].token = NULL; wsi->utf8_token[n].token_len = 0; @@ -309,7 +340,8 @@ libwebsocket_client_connect(struct libwebsocket_context *this, p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin); if (protocol != NULL) p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol); - p += sprintf(p, "Sec-WebSocket-Version: 4\x0d\x0a\x0d\x0a"); + p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a\x0d\x0a", + wsi->ietf_spec_revision); /* prepare the expected server accept response */ diff --git a/lib/handshake.c b/lib/handshake.c index ab2f09e..2a4ebb6 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -222,7 +222,7 @@ bail: */ static int -handshake_04(struct libwebsocket *wsi) +handshake_0405(struct libwebsocket *wsi) { static const char *websocket_magic_guid_04 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; @@ -537,7 +537,6 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) wsi->ietf_spec_revision = atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token); - /* * Perform the handshake according to the protocol version the * client announced @@ -545,20 +544,32 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) switch (wsi->ietf_spec_revision) { case 0: /* applies to 76 and 00 */ + wsi->xor_mask = xor_no_mask; if (handshake_00(wsi)) goto bail; break; case 4: /* 04 */ + wsi->xor_mask = xor_mask_04; + debug("libwebsocket_parse calling handshake_04\n"); + if (handshake_0405(wsi)) + goto bail; + break; + case 5: /* 05 */ + wsi->xor_mask = xor_mask_05; debug("libwebsocket_parse calling handshake_04\n"); - if (handshake_04(wsi)) + if (handshake_0405(wsi)) goto bail; break; + default: fprintf(stderr, "Unknown client spec version %d\n", wsi->ietf_spec_revision); goto bail; } + fprintf(stderr, "accepted v%02d connection\n", + wsi->ietf_spec_revision); + break; case WSI_STATE_ESTABLISHED: diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 6a74a77..d4c8ac4 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -607,6 +607,7 @@ static void sigpipe_handler(int x) * else ignored * @gid: group id to change to after setting listen socket, or -1. * @uid: user id to change to after setting listen socket, or -1. + * @options: 0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK * * This function creates the listening socket and takes care * of all initialization in one step. diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index e579303..10d8584 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -24,7 +24,10 @@ #define CONTEXT_PORT_NO_LISTEN 0 -#define LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK 1 + +enum libwebsocket_context_options { + LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK = 1, +}; enum libwebsocket_callback_reasons { LWS_CALLBACK_ESTABLISHED, @@ -258,7 +261,8 @@ libwebsocket_client_connect(struct libwebsocket_context *clients, const char *path, const char *host, const char *origin, - const char *protocol); + const char *protocol, + int ietf_version_or_minus_one); extern const char * libwebsocket_canonical_hostname(struct libwebsocket_context *this); diff --git a/lib/parsers.c b/lib/parsers.c index 304c574..7551f14 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -220,13 +220,15 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) return 0; } -static inline unsigned char -xor_mask(struct libwebsocket *wsi, unsigned char c) +unsigned char +xor_no_mask(struct libwebsocket *wsi, unsigned char c) +{ + return c; +} + +unsigned char +xor_mask_04(struct libwebsocket *wsi, unsigned char c) { - if (wsi->protocol->owning_server->options & - LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK) - return c; - c ^= wsi->masking_key_04[wsi->frame_mask_index++]; if (wsi->frame_mask_index == 20) wsi->frame_mask_index = 0; @@ -234,6 +236,13 @@ xor_mask(struct libwebsocket *wsi, unsigned char c) return c; } +unsigned char +xor_mask_05(struct libwebsocket *wsi, unsigned char c) +{ + return c ^ wsi->frame_masking_nonce_04[(wsi->frame_mask_index++) & 3]; +} + + static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) { @@ -255,9 +264,14 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) } break; case 4: + case 5: wsi->frame_masking_nonce_04[0] = c; wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_1; break; + default: + fprintf(stderr, "libwebsocket_rx_sm doesn't know " + "about spec version %d\n", wsi->ietf_spec_revision); + break; } break; case LWS_RXPS_04_MASK_NONCE_1: @@ -275,6 +289,9 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK) goto post_mask; + if (wsi->ietf_spec_revision > 4) + goto post_sha1; + /* * we are able to compute the frame key now * it's a SHA1 of ( frame nonce we were just sent, concatenated @@ -297,6 +314,8 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04); +post_sha1: + /* * start from the zero'th byte in the XOR key buffer since * this is the start of a frame with a new key @@ -356,7 +375,7 @@ post_mask: * FIN (b7) */ - c = xor_mask(wsi, c); + c = wsi->xor_mask(wsi, c); if (c & 0x70) { fprintf(stderr, @@ -381,7 +400,7 @@ post_mask: break; case LWS_RXPS_04_FRAME_HDR_LEN: - c = xor_mask(wsi, c); + c = wsi->xor_mask(wsi, c); if (c & 0x80) { fprintf(stderr, "Frame has extensions " @@ -430,14 +449,14 @@ post_mask: break; case LWS_RXPS_04_FRAME_HDR_LEN16_2: - c = xor_mask(wsi, c); + c = wsi->xor_mask(wsi, c); wsi->rx_packet_length = c << 8; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; break; case LWS_RXPS_04_FRAME_HDR_LEN16_1: - c = xor_mask(wsi, c); + c = wsi->xor_mask(wsi, c); wsi->rx_packet_length |= c; wsi->lws_rx_parse_state = @@ -445,7 +464,7 @@ post_mask: break; case LWS_RXPS_04_FRAME_HDR_LEN64_8: - c = xor_mask(wsi, c); + c = wsi->xor_mask(wsi, c); if (c & 0x80) { fprintf(stderr, "b63 of length must be zero\n"); /* kill the connection */ @@ -461,42 +480,42 @@ post_mask: case LWS_RXPS_04_FRAME_HDR_LEN64_7: #if defined __LP64__ - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 48; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 48; #endif wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; break; case LWS_RXPS_04_FRAME_HDR_LEN64_6: #if defined __LP64__ - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 40; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 40; #endif wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; break; case LWS_RXPS_04_FRAME_HDR_LEN64_5: #if defined __LP64__ - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 32; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 32; #endif wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; break; case LWS_RXPS_04_FRAME_HDR_LEN64_4: - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 24; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 24; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; break; case LWS_RXPS_04_FRAME_HDR_LEN64_3: - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 16; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 16; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; break; case LWS_RXPS_04_FRAME_HDR_LEN64_2: - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 8; + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)) << 8; wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; break; case LWS_RXPS_04_FRAME_HDR_LEN64_1: - wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)); + wsi->rx_packet_length |= ((size_t)wsi->xor_mask(wsi, c)); wsi->lws_rx_parse_state = LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; break; @@ -541,7 +560,8 @@ issue: case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING + - (wsi->rx_user_buffer_head++)] = xor_mask(wsi, c); + (wsi->rx_user_buffer_head++)] = + wsi->xor_mask(wsi, c); if (--wsi->rx_packet_length == 0) { wsi->lws_rx_parse_state = LWS_RXPS_NEW; @@ -627,6 +647,7 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) } break; case 4: + case 5: /* * 04 logical framing from the spec (all this is masked when * incoming and has to be unmasked) @@ -695,6 +716,11 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; break; + default: + fprintf(stderr, "client_rx_sm doesn't know how " + "to handle spec version %02d\n", + wsi->ietf_spec_revision); + break; } break; @@ -943,7 +969,7 @@ int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, static int -libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi) +libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi) { int fd; char buf[4 + 20]; @@ -965,21 +991,27 @@ libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi) } close(fd); + /* start masking from first byte of masking key buffer */ + wsi->frame_mask_index = 0; + + if (wsi->ietf_spec_revision != 4) + return 0; + + /* 04 only does SHA-1 more complex key */ + /* * the frame key is the frame nonce (4 bytes) followed by the * connection masking key, hashed by SHA1 */ memcpy(buf, wsi->frame_masking_nonce_04, 4); + memcpy(buf + 4, wsi->masking_key_04, 20); /* concatenate the nonce with the connection key then hash it */ SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04); - /* start masking from first byte of masking key buffer */ - wsi->frame_mask_index = 0; - return 0; } @@ -1065,7 +1097,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, break; case 4: - + case 5: switch (protocol & 0xf) { case LWS_WRITE_TEXT: n = LWS_WS_OPCODE_04__TEXT_FRAME; @@ -1135,11 +1167,12 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, #endif /* - * Deal with masking if appropriate + * Deal with masking if we are in client -> server direction and + * the protocol demands it */ if (wsi->mode == LWS_CONNMODE_WS_CLIENT && - wsi->ietf_spec_revision == 4) { + wsi->ietf_spec_revision >= 4) { /* * this is only useful for security tests where it's required @@ -1148,7 +1181,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, if (!(protocol & LWS_WRITE_CLIENT_IGNORE_XOR_MASK)) { - if (libwebsocket_04_frame_mask_generate(wsi)) { + if (libwebsocket_0405_frame_mask_generate(wsi)) { fprintf(stderr, "libwebsocket_write: " "frame mask generation failed\n"); return 1; @@ -1160,7 +1193,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, */ for (n = 0; n < (len + pre + post); n++) - buf[n - pre] = xor_mask(wsi, buf[n - pre]); + buf[n - pre] = wsi->xor_mask(wsi, buf[n - pre]); } /* make space for the frame nonce in clear */ @@ -1168,6 +1201,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, /* copy the frame nonce into place */ memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4); + } send_raw: diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 8028b94..14d4fb5 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -224,6 +224,7 @@ struct libwebsocket { unsigned char final; int pings_vs_pongs; + unsigned char (*xor_mask)(struct libwebsocket *, unsigned char); /* client support */ char initial_handshake_hash_base64[30]; @@ -261,3 +262,12 @@ lws_b64_decode_string(const char *in, char *out, int out_size); extern int lws_b64_selftest(void); + +extern unsigned char +xor_no_mask(struct libwebsocket *wsi, unsigned char c); + +extern unsigned char +xor_mask_04(struct libwebsocket *wsi, unsigned char c); + +extern unsigned char +xor_mask_05(struct libwebsocket *wsi, unsigned char c); diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index b98904f..422ed7c 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -161,6 +161,8 @@ else ignored
group id to change to after setting listen socket, or -1.
uid
user id to change to after setting listen socket, or -1. +
options +
0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK

Description

@@ -340,7 +342,8 @@ Many protocols won't care becuse their packets are always small. const char * path, const char * host, const char * origin, -const char * protocol) +const char * protocol, +int ietf_version_or_minus_one)

Arguments

this @@ -362,6 +365,9 @@ signed certs
Comma-separated list of protocols being asked for from the server, or just one. The server will pick the one it likes best. +
ietf_version_or_minus_one +
-1 to ask to connect using the default, latest +protocol supported, or the specific protocol ordinal

Description

diff --git a/test-server/test-client.c b/test-server/test-client.c index 59595e0..3508a52 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -158,6 +158,7 @@ static struct option options[] = { { "port", required_argument, NULL, 'p' }, { "ssl", no_argument, NULL, 's' }, { "killmask", no_argument, NULL, 'k' }, + { "version", required_argument, NULL, 'v' }, { NULL, 0, 0, 0 } }; @@ -171,6 +172,7 @@ int main(int argc, char **argv) const char *address; struct libwebsocket *wsi_dumb; struct libwebsocket *wsi_mirror; + int ietf_version = -1; /* latest */ fprintf(stderr, "libwebsockets test client\n" "(C) Copyright 2010 Andy Green " @@ -179,10 +181,8 @@ int main(int argc, char **argv) if (argc < 2) goto usage; - optind++; - while (n >= 0) { - n = getopt_long(argc, argv, "khsp:", options, NULL); + n = getopt_long(argc, argv, "v:khsp:", options, NULL); if (n < 0) continue; switch (n) { @@ -195,6 +195,9 @@ int main(int argc, char **argv) case 'k': opts = LWS_WRITE_CLIENT_IGNORE_XOR_MASK; break; + case 'v': + ietf_version = atoi(optarg); + break; case 'h': goto usage; } @@ -225,7 +228,7 @@ int main(int argc, char **argv) wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl, "/", argv[optind], argv[optind], - protocols[PROTOCOL_DUMB_INCREMENT].name); + protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version); if (wsi_dumb == NULL) { fprintf(stderr, "libwebsocket dumb connect failed\n"); @@ -236,7 +239,7 @@ int main(int argc, char **argv) wsi_mirror = libwebsocket_client_connect(context, address, port, use_ssl, "/", argv[optind], argv[optind], - protocols[PROTOCOL_LWS_MIRROR].name); + protocols[PROTOCOL_LWS_MIRROR].name, ietf_version); if (wsi_mirror == NULL) { fprintf(stderr, "libwebsocket dumb connect failed\n"); diff --git a/test-server/test-ping.c b/test-server/test-ping.c index e1cb0ad..25a3226 100644 --- a/test-server/test-ping.c +++ b/test-server/test-ping.c @@ -277,6 +277,7 @@ static struct option options[] = { { "mirror", no_argument, NULL, 'm' }, { "replicate", required_argument, NULL, 'r' }, { "killmask", no_argument, NULL, 'k' }, + { "version", required_argument, NULL, 'v' }, { NULL, 0, 0, 0 } }; @@ -310,6 +311,7 @@ int main(int argc, char **argv) struct winsize w; unsigned long oldus = 0; unsigned long l; + int ietf_version = -1; if (argc < 2) goto usage; @@ -318,7 +320,7 @@ int main(int argc, char **argv) optind++; while (n >= 0) { - n = getopt_long(argc, argv, "kr:hmfts:n:i:p:", options, NULL); + n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:", options, NULL); if (n < 0) continue; switch (n) { @@ -356,6 +358,9 @@ int main(int argc, char **argv) case 'k': write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK; break; + case 'v': + ietf_version = atoi(optarg); + break; case 'h': goto usage; @@ -393,8 +398,9 @@ int main(int argc, char **argv) for (n = 0; n < clients; n++) { wsi[n] = libwebsocket_client_connect(context, address, port, - use_ssl, "/", libwebsocket_canonical_hostname(context), - "origin", protocols[PROTOCOL_LWS_MIRROR].name); + use_ssl, "/", address, + "origin", protocols[PROTOCOL_LWS_MIRROR].name, + ietf_version); if (wsi[n] == NULL) { fprintf(stderr, "client connnection %d failed to " "connect\n", n);