1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

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 <andy@warmcat.com>
This commit is contained in:
Andy Green 2011-02-09 08:49:14 +00:00 committed by Andy Green
parent f3d3b40364
commit bfb051f3a3
9 changed files with 152 additions and 45 deletions

View file

@ -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 */

View file

@ -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_04(wsi))
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_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:

View file

@ -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.

View file

@ -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);

View file

@ -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:

View file

@ -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);

View file

@ -161,6 +161,8 @@ else ignored
<dd>group id to change to after setting listen socket, or -1.
<dt><b>uid</b>
<dd>user id to change to after setting listen socket, or -1.
<dt><b>options</b>
<dd>0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
</dl>
<h3>Description</h3>
<blockquote>
@ -340,7 +342,8 @@ Many protocols won't care becuse their packets are always small.
<i>const char *</i> <b>path</b>,
<i>const char *</i> <b>host</b>,
<i>const char *</i> <b>origin</b>,
<i>const char *</i> <b>protocol</b>)
<i>const char *</i> <b>protocol</b>,
<i>int</i> <b>ietf_version_or_minus_one</b>)
<h3>Arguments</h3>
<dl>
<dt><b>this</b>
@ -362,6 +365,9 @@ signed certs
<dd>Comma-separated list of protocols being asked for from
the server, or just one. The server will pick the one it
likes best.
<dt><b>ietf_version_or_minus_one</b>
<dd>-1 to ask to connect using the default, latest
protocol supported, or the specific protocol ordinal
</dl>
<h3>Description</h3>
<blockquote>

View file

@ -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 <andy@warmcat.com> "
@ -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");

View file

@ -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);