* @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
*/
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;
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;
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 */
*/
static int
-handshake_04(struct libwebsocket *wsi)
+handshake_0405(struct libwebsocket *wsi)
{
static const char *websocket_magic_guid_04 =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
wsi->ietf_spec_revision =
atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);
-
/*
* Perform the handshake according to the protocol version the
* client announced
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:
* 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.
#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,
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);
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;
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)
{
}
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:
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
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
* FIN (b7)
*/
- c = xor_mask(wsi, c);
+ c = wsi->xor_mask(wsi, c);
if (c & 0x70) {
fprintf(stderr,
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 "
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 =
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 */
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;
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;
}
break;
case 4:
+ case 5:
/*
* 04 logical framing from the spec (all this is masked when
* incoming and has to be unmasked)
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;
static int
-libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi)
+libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
{
int fd;
char buf[4 + 20];
}
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;
}
break;
case 4:
-
+ case 5:
switch (protocol & 0xf) {
case LWS_WRITE_TEXT:
n = LWS_WS_OPCODE_04__TEXT_FRAME;
#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
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;
*/
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 */
/* copy the frame nonce into place */
memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4);
+
}
send_raw:
unsigned char final;
int pings_vs_pongs;
+ unsigned char (*xor_mask)(struct libwebsocket *, unsigned char);
/* client support */
char initial_handshake_hash_base64[30];
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);
<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>
<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>
<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>
{ "port", required_argument, NULL, 'p' },
{ "ssl", no_argument, NULL, 's' },
{ "killmask", no_argument, NULL, 'k' },
+ { "version", required_argument, NULL, 'v' },
{ NULL, 0, 0, 0 }
};
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> "
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) {
case 'k':
opts = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
break;
+ case 'v':
+ ietf_version = atoi(optarg);
+ break;
case 'h':
goto usage;
}
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");
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");
{ "mirror", no_argument, NULL, 'm' },
{ "replicate", required_argument, NULL, 'r' },
{ "killmask", no_argument, NULL, 'k' },
+ { "version", required_argument, NULL, 'v' },
{ NULL, 0, 0, 0 }
};
struct winsize w;
unsigned long oldus = 0;
unsigned long l;
+ int ietf_version = -1;
if (argc < 2)
goto usage;
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) {
case 'k':
write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
break;
+ case 'v':
+ ietf_version = atoi(optarg);
+ break;
case 'h':
goto usage;
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);