#include <netdb.h>
-/*
- * In-place str to lower case
- */
-
-void
-strtolower(char *s)
-{
- while (*s) {
- *s = tolower(*s);
- s++;
- }
-}
-
-
/**
* libwebsocket_client_connect() - Connect to another websocket server
* @this: Websocket context
{
struct hostent *server_hostent;
struct sockaddr_in server_addr;
- char buf[150];
- char key_b64[150];
- char hash[20];
+ char pkt[512];
struct pollfd pfd;
- static const char magic_websocket_guid[] =
- "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- static const char magic_websocket_04_masking_guid[] =
- "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
- char pkt[1024];
- char *p = &pkt[0];
- const char *pc;
- int len;
- int okay = 0;
struct libwebsocket *wsi;
int n;
int plen = 0;
-#ifdef LWS_OPENSSL_SUPPORT
- char ssl_err_buf[512];
-#else
+#ifndef LWS_OPENSSL_SUPPORT
if (ssl_connection) {
fprintf(stderr, "libwebsockets not configured for ssl\n");
return NULL;
#endif
wsi = malloc(sizeof(struct libwebsocket));
- if (wsi == NULL) {
- fprintf(stderr, "Out of memory allocing new connection\n");
- return NULL;
- }
+ if (wsi == NULL)
+ goto bail1;
memset(wsi, 0, sizeof *wsi);
wsi->pings_vs_pongs = 0;
wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
+#ifdef LWS_OPENSSL_SUPPORT
+ wsi->use_ssl = ssl_connection;
+#endif
+
+ /* copy parameters over so state machine has access */
+
+ wsi->c_path = malloc(strlen(path) + 1);
+ if (wsi->c_path == NULL)
+ goto bail1;
+ strcpy(wsi->c_path, path);
+ wsi->c_host = malloc(strlen(host) + 1);
+ if (wsi->c_host == NULL)
+ goto oom1;
+ strcpy(wsi->c_host, host);
+ wsi->c_origin = malloc(strlen(origin) + 1);
+ if (wsi->c_origin == NULL)
+ goto oom2;
+ strcpy(wsi->c_origin, origin);
+ if (protocol) {
+ wsi->c_protocol = malloc(strlen(protocol) + 1);
+ if (wsi->c_protocol == NULL)
+ goto oom3;
+ strcpy(wsi->c_protocol, protocol);
+ } else
+ wsi->c_protocol = NULL;
+
/* set up appropriate masking */
fprintf(stderr,
"Client ietf version %d not supported\n",
wsi->ietf_spec_revision);
- return NULL;
+ goto oom4;
}
/* force no mask if he asks for that though */
server_hostent = gethostbyname(address);
if (server_hostent == NULL) {
fprintf(stderr, "Unable to get host name from %s\n", address);
- goto bail1;
+ goto oom4;
}
wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
if (wsi->sock < 0) {
fprintf(stderr, "Unable to open socket\n");
- goto bail1;
+ goto oom4;
}
- insert_wsi(this, wsi);
-
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
if (connect(wsi->sock, (struct sockaddr *)&server_addr,
sizeof(struct sockaddr)) == -1) {
fprintf(stderr, "Connect failed\n");
- goto bail1;
+ goto oom4;
}
+ /* into fd -> wsi hashtable */
+
+ insert_wsi(this, wsi);
+
+ /* into internal poll list */
+
+ this->fds[this->fds_count].fd = wsi->sock;
+ this->fds[this->fds_count].revents = 0;
+ this->fds[this->fds_count++].events = POLLIN;
+
+ /* external POLL support via protocol 0 */
+ this->protocols[0].callback(this, wsi,
+ LWS_CALLBACK_ADD_POLL_FD,
+ (void *)(long)wsi->sock, NULL, POLLIN);
+
/* we are connected to server, or proxy */
if (this->http_proxy_port) {
goto bail1;
}
- pfd.fd = wsi->sock;
- pfd.events = POLLIN;
- pfd.revents = 0;
+ libwebsocket_set_timeout(wsi,
+ PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5);
- n = poll(&pfd, 1, 5000);
- if (n <= 0) {
- close(wsi->sock);
- fprintf(stderr, "libwebsocket_client_handshake "
- "timeout on proxy response");
- goto bail1;
- }
-
- n = recv(wsi->sock, pkt, sizeof pkt, 0);
- if (n < 0) {
- close(wsi->sock);
- fprintf(stderr, "ERROR reading from proxy socket\n");
- goto bail1;
- }
-
- pkt[13] = '\0';
- if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
- close(wsi->sock);
- fprintf(stderr, "ERROR from proxy: %s\n", pkt);
- goto bail1;
- }
-
- /* we can just start sending to proxy */
- }
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (ssl_connection) {
-
- wsi->ssl = SSL_new(this->ssl_client_ctx);
- wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
- SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
-
- if (SSL_connect(wsi->ssl) <= 0) {
- fprintf(stderr, "SSL connect error %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- goto bail1;
- }
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
- n = SSL_get_verify_result(wsi->ssl);
- if (n != X509_V_OK) {
- if (n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
- ssl_connection != 2) {
-
- fprintf(stderr, "server's cert didn't "
- "look good %d\n", n);
- goto bail2;
- }
- }
- } else {
- wsi->ssl = NULL;
-#endif
-
-
-#ifdef LWS_OPENSSL_SUPPORT
+ return wsi;
}
-#endif
/*
- * create the random key
+ * provoke service to issue the handshake directly
+ * we need to do it this way because in the proxy case, this is the
+ * next state and executed only if and when we get a good proxy
+ * response inside the state machine
*/
- n = read(this->fd_random, hash, 16);
- if (n != 16) {
- fprintf(stderr, "Unable to read from random device %s\n",
- SYSTEM_RANDOM_FILEPATH);
- goto bail2;
- }
-
- lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
-
- /*
- * 04 example client handshake
- *
- * GET /chat HTTP/1.1
- * Host: server.example.com
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- * Sec-WebSocket-Origin: http://example.com
- * Sec-WebSocket-Protocol: chat, superchat
- * Sec-WebSocket-Version: 4
- */
-
- p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", path);
- p += sprintf(p, "Host: %s\x0d\x0a", host);
- p += sprintf(p, "Upgrade: websocket\x0d\x0a");
- p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
- strcpy(p, key_b64);
- p += strlen(key_b64);
- 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: %d\x0d\x0a\x0d\x0a",
- wsi->ietf_spec_revision);
-
-
- /* prepare the expected server accept response */
-
- strcpy(buf, key_b64);
- strcpy(&buf[strlen(buf)], magic_websocket_guid);
-
- SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
-
- lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
- sizeof wsi->initial_handshake_hash_base64);
-
- /* send our request to the server */
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (ssl_connection)
- n = SSL_write(wsi->ssl, pkt, p - pkt);
- else
-#endif
- n = send(wsi->sock, pkt, p - pkt, 0);
-
- if (n < 0) {
- fprintf(stderr, "ERROR writing to client socket\n");
- goto bail2;
- }
-
- wsi->parser_state = WSI_TOKEN_NAME_PART;
-
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
pfd.fd = wsi->sock;
- pfd.events = POLLIN;
- pfd.revents = 0;
-
- n = poll(&pfd, 1, 5000);
- if (n < 0) {
- fprintf(stderr, "libwebsocket_client_handshake socket error "
- "while waiting for handshake response");
- goto bail2;
- }
- if (n == 0) {
- fprintf(stderr, "libwebsocket_client_handshake timeout "
- "while waiting for handshake response");
- goto bail2;
- }
-
- /* interpret the server response */
-
- /*
- * HTTP/1.1 101 Switching Protocols
- * Upgrade: websocket
- * Connection: Upgrade
- * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- * Sec-WebSocket-Protocol: chat
- */
-
-#ifdef LWS_OPENSSL_SUPPORT
- if (ssl_connection)
- len = SSL_read(wsi->ssl, pkt, sizeof pkt);
- else
-#endif
- len = recv(wsi->sock, pkt, sizeof pkt, 0);
-
- if (len < 0) {
- fprintf(stderr, "libwebsocket_client_handshake read error\n");
- goto bail2;
- }
-
- p = pkt;
- for (n = 0; n < len; n++)
- libwebsocket_parse(wsi, *p++);
-
- if (wsi->parser_state != WSI_PARSING_COMPLETE) {
- fprintf(stderr, "libwebsocket_client_handshake server response"
- " failed parsing\n");
- goto bail2;
- }
-
- /*
- * well, what the server sent looked reasonable for syntax.
- * Now let's confirm it sent all the necessary headers
- */
-
- if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
- !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
- !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
- !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
- !wsi->utf8_token[WSI_TOKEN_NONCE].token_len ||
- (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
- protocol != NULL)) {
- fprintf(stderr, "libwebsocket_client_handshake "
- "missing required header(s)\n");
- pkt[len] = '\0';
- fprintf(stderr, "%s", pkt);
- goto bail2;
- }
-
- /*
- * Everything seems to be there, now take a closer look at what is in
- * each header
- */
-
- strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
- "101 switching protocols")) {
- fprintf(stderr, "libwebsocket_client_handshake server sent bad"
- " HTTP response '%s'\n",
- wsi->utf8_token[WSI_TOKEN_HTTP].token);
- goto bail2;
- }
-
- strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
- fprintf(stderr, "libwebsocket_client_handshake server sent bad"
- " Upgrade header '%s'\n",
- wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
- goto bail2;
- }
-
- strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
- if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
- fprintf(stderr, "libwebsocket_client_handshake server sent bad"
- " Connection hdr '%s'\n",
- wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
- goto bail2;
- }
- /*
- * confirm the protocol the server wants to talk was in the list of
- * protocols we offered
- */
-
- if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
+ pfd.revents = POLLIN;
+ libwebsocket_service_fd(this, &pfd);
- /* no protocol name to work from, default to first protocol */
- wsi->protocol = &this->protocols[0];
-
- goto check_accept;
- }
-
- pc = protocol;
- while (*pc && !okay) {
- if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
- (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
- pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
- okay = 1;
- continue;
- }
- while (*pc && *pc != ',')
- pc++;
- while (*pc && *pc != ' ')
- pc++;
- }
- if (!okay) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "sent bad protocol '%s'\n",
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
- goto bail2;
- }
-
- /*
- * identify the selected protocol struct and set it
- */
- n = 0;
- wsi->protocol = NULL;
- while (this->protocols[n].callback) {
- if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
- this->protocols[n].name) == 0)
- wsi->protocol = &this->protocols[n];
- n++;
- }
-
- if (wsi->protocol == NULL) {
- fprintf(stderr, "libwebsocket_client_handshake server "
- "requested protocol '%s', which we "
- "said we supported but we don't!\n",
- wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
- goto bail2;
- }
-
-check_accept:
- /*
- * Confirm his accept token is the same as the one we precomputed
- */
-
- if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
- wsi->initial_handshake_hash_base64)) {
- fprintf(stderr, "libwebsocket_client_handshake server sent "
- "bad ACCEPT '%s' vs computed '%s'\n",
- wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
- wsi->initial_handshake_hash_base64);
- goto bail2;
- }
-
- /*
- * Calculate the masking key to use when sending data to server
- */
-
- strcpy(buf, key_b64);
- p = buf + strlen(key_b64);
- strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
- p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
- strcpy(p, magic_websocket_04_masking_guid);
- SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
-
- /* allocate the per-connection user memory (if any) */
-
- if (wsi->protocol->per_session_data_size) {
- wsi->user_space = malloc(
- wsi->protocol->per_session_data_size);
- if (wsi->user_space == NULL) {
- fprintf(stderr, "Out of memory for "
- "conn user space\n");
- goto bail2;
- }
- } else
- wsi->user_space = NULL;
-
- /* okay he is good to go */
-
- this->fds[this->fds_count].fd = wsi->sock;
- this->fds[this->fds_count].revents = 0;
- this->fds[this->fds_count++].events = POLLIN;
-
- /* external POLL support via protocol 0 */
- this->protocols[0].callback(this, wsi,
- LWS_CALLBACK_ADD_POLL_FD,
- (void *)(long)wsi->sock, NULL, POLLIN);
-
-
- wsi->state = WSI_STATE_ESTABLISHED;
- wsi->mode = LWS_CONNMODE_WS_CLIENT;
+ return wsi;
- fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
+oom4:
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
- /* call him back to inform him he is up */
+oom3:
+ free(wsi->c_origin);
- wsi->protocol->callback(this, wsi,
- LWS_CALLBACK_CLIENT_ESTABLISHED,
- wsi->user_space,
- NULL, 0);
- return wsi;
+oom2:
+ free(wsi->c_host);
+oom1:
+ free(wsi->c_path);
-bail2:
- libwebsocket_close_and_free_session(this, wsi);
bail1:
free(wsi);
#include "private-libwebsockets.h"
+/*
+ * In-place str to lower case
+ */
+
+static void
+strtolower(char *s)
+{
+ while (*s) {
+ *s = tolower(*s);
+ s++;
+ }
+}
+
/* file descriptor hash management */
struct libwebsocket *
unsigned int clilen;
struct sockaddr_in cli_addr;
struct timeval tv;
-
+ static const char magic_websocket_guid[] =
+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ static const char magic_websocket_04_masking_guid[] =
+ "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
+ char hash[20];
+ char pkt[1024];
+ char *p = &pkt[0];
+ const char *pc;
+ int okay = 0;
+#ifdef LWS_OPENSSL_SUPPORT
+ char ssl_err_buf[512];
+#endif
/*
* you can call us with pollfd = NULL to just allow the once-per-second
* global timeout checks; if less than a second since the last check
}
break;
+ case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
+
+ /* handle proxy hung up on us */
+
+ if (pollfd->revents & (POLLERR | POLLHUP)) {
+
+ fprintf(stderr, "Proxy connection %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+ }
+
+ n = recv(wsi->sock, pkt, sizeof pkt, 0);
+ if (n < 0) {
+ libwebsocket_close_and_free_session(this, wsi);
+ fprintf(stderr, "ERROR reading from proxy socket\n");
+ return 1;
+ }
+
+ pkt[13] = '\0';
+ if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
+ libwebsocket_close_and_free_session(this, wsi);
+ fprintf(stderr, "ERROR from proxy: %s\n", pkt);
+ return 1;
+ }
+
+ /* clear his proxy connection timeout */
+
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* fallthru */
+
+ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
+
+ #ifdef LWS_OPENSSL_SUPPORT
+ if (wsi->use_ssl) {
+
+ wsi->ssl = SSL_new(this->ssl_client_ctx);
+ wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+ SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
+
+ if (SSL_connect(wsi->ssl) <= 0) {
+ fprintf(stderr, "SSL connect error %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+ }
+
+ n = SSL_get_verify_result(wsi->ssl);
+ if (n != X509_V_OK) {
+ if (n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
+ wsi->use_ssl != 2) {
+
+ fprintf(stderr, "server's cert didn't "
+ "look good %d\n", n);
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+ }
+ }
+ } else {
+ wsi->ssl = NULL;
+ #endif
+
+
+ #ifdef LWS_OPENSSL_SUPPORT
+ }
+ #endif
+
+ /*
+ * create the random key
+ */
+
+ n = read(this->fd_random, hash, 16);
+ if (n != 16) {
+ fprintf(stderr, "Unable to read from random dev %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ free(wsi->c_path);
+ free(wsi->c_host);
+ free(wsi->c_origin);
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+ }
+
+ lws_b64_encode_string(hash, 16, wsi->key_b64,
+ sizeof wsi->key_b64);
+
+ /*
+ * 04 example client handshake
+ *
+ * GET /chat HTTP/1.1
+ * Host: server.example.com
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+ * Sec-WebSocket-Origin: http://example.com
+ * Sec-WebSocket-Protocol: chat, superchat
+ * Sec-WebSocket-Version: 4
+ */
+
+ p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", wsi->c_path);
+ p += sprintf(p, "Host: %s\x0d\x0a", wsi->c_host);
+ p += sprintf(p, "Upgrade: websocket\x0d\x0a");
+ p += sprintf(p, "Connection: Upgrade\x0d\x0a"
+ "Sec-WebSocket-Key: ");
+ strcpy(p, wsi->key_b64);
+ p += strlen(wsi->key_b64);
+ p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a",
+ wsi->c_origin);
+ if (wsi->c_protocol != NULL)
+ p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
+ wsi->c_protocol);
+ 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);
+ free(wsi->c_origin);
+
+ /* prepare the expected server accept response */
+
+ strcpy((char *)buf, wsi->key_b64);
+ strcpy((char *)&buf[strlen((char *)buf)], magic_websocket_guid);
+
+ SHA1(buf, strlen((char *)buf), (unsigned char *)hash);
+
+ lws_b64_encode_string(hash, 20,
+ wsi->initial_handshake_hash_base64,
+ sizeof wsi->initial_handshake_hash_base64);
+
+ /* send our request to the server */
+
+ #ifdef LWS_OPENSSL_SUPPORT
+ if (wsi->use_ssl)
+ n = SSL_write(wsi->ssl, pkt, p - pkt);
+ else
+ #endif
+ n = send(wsi->sock, pkt, p - pkt, 0);
+
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to client socket\n");
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+ }
+
+ wsi->parser_state = WSI_TOKEN_NAME_PART;
+ wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY;
+ libwebsocket_set_timeout(wsi,
+ PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 5);
+
+ break;
+
+ case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
+
+ /* handle server hung up on us */
+
+ if (pollfd->revents & (POLLERR | POLLHUP)) {
+
+ fprintf(stderr, "Server connection %p (fd=%d) dead\n",
+ (void *)wsi, pollfd->fd);
+
+ goto bail3;
+ }
+
+
+ /* interpret the server response */
+
+ /*
+ * HTTP/1.1 101 Switching Protocols
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
+ * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
+ * Sec-WebSocket-Protocol: chat
+ */
+
+ #ifdef LWS_OPENSSL_SUPPORT
+ if (wsi->use_ssl)
+ len = SSL_read(wsi->ssl, pkt, sizeof pkt);
+ else
+ #endif
+ len = recv(wsi->sock, pkt, sizeof pkt, 0);
+
+ if (len < 0) {
+ fprintf(stderr,
+ "libwebsocket_client_handshake read error\n");
+ goto bail3;
+ }
+
+ p = pkt;
+ for (n = 0; n < len; n++)
+ libwebsocket_parse(wsi, *p++);
+
+ if (wsi->parser_state != WSI_PARSING_COMPLETE) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "server response ailed parsing\n");
+ goto bail3;
+ }
+
+ /*
+ * well, what the server sent looked reasonable for syntax.
+ * Now let's confirm it sent all the necessary headers
+ */
+
+ if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_NONCE].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;
+ }
+
+ /*
+ * Everything seems to be there, now take a closer look at what
+ * is in each header
+ */
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
+ "101 switching protocols")) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "server sent bad HTTP response '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ goto bail3;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token,
+ "websocket")) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad Upgrade header '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ goto bail3;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token,
+ "upgrade")) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad Connection hdr '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ goto bail3;
+ }
+
+
+ pc = wsi->c_protocol;
+
+ /*
+ * confirm the protocol the server wants to talk was in the list
+ * of protocols we offered
+ */
+
+ if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
+
+ /*
+ * no protocol name to work from,
+ * default to first protocol
+ */
+ wsi->protocol = &this->protocols[0];
+
+ free(wsi->c_protocol);
+
+ goto check_accept;
+ }
+
+ while (*pc && !okay) {
+ if ((!strncmp(pc,
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
+ (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
+ pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
+ okay = 1;
+ continue;
+ }
+ while (*pc && *pc != ',')
+ pc++;
+ while (*pc && *pc != ' ')
+ pc++;
+ }
+
+ /* done with him now */
+
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+
+
+ if (!okay) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad protocol '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+ /*
+ * identify the selected protocol struct and set it
+ */
+ n = 0;
+ wsi->protocol = NULL;
+ while (this->protocols[n].callback) {
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ this->protocols[n].name) == 0)
+ wsi->protocol = &this->protocols[n];
+ n++;
+ }
+
+ if (wsi->protocol == NULL) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "requested protocol '%s', which we "
+ "said we supported but we don't!\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+ check_accept:
+ /*
+ * Confirm his accept token is the one we precomputed
+ */
+
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64)) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad ACCEPT '%s' vs computed '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64);
+ goto bail2;
+ }
+
+ /*
+ * Calculate the masking key to use when sending data to server
+ */
+
+ strcpy((char *)buf, wsi->key_b64);
+ p = (char *)buf + strlen(wsi->key_b64);
+ strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
+ p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
+ strcpy(p, magic_websocket_04_masking_guid);
+ SHA1(buf, strlen((char *)buf), wsi->masking_key_04);
+
+ /* allocate the per-connection user memory (if any) */
+
+ if (wsi->protocol->per_session_data_size) {
+ wsi->user_space = malloc(
+ wsi->protocol->per_session_data_size);
+ if (wsi->user_space == NULL) {
+ fprintf(stderr, "Out of memory for "
+ "conn user space\n");
+ goto bail2;
+ }
+ } else
+ wsi->user_space = NULL;
+
+ /* clear his proxy connection timeout */
+
+ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* mark him as being alive */
+
+ wsi->state = WSI_STATE_ESTABLISHED;
+ wsi->mode = LWS_CONNMODE_WS_CLIENT;
+
+ fprintf(stderr, "handshake OK for protocol %s\n",
+ wsi->protocol->name);
+
+ /* call him back to inform him he is up */
+
+ wsi->protocol->callback(this, wsi,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
+ wsi->user_space,
+ NULL, 0);
+
+ break;
+
+bail3:
+ if (wsi->c_protocol)
+ free(wsi->c_protocol);
+
+bail2:
+ libwebsocket_close_and_free_session(this, wsi);
+ return 1;
+
+
case LWS_CONNMODE_WS_SERVING:
case LWS_CONNMODE_WS_CLIENT:
return 0;
}
+/**
+ * libwebsocket_set_timeout() - marks the wsi as subject to a timeout
+ *
+ * You will not need this unless you are doing something special
+ *
+ * @wsi: Websocket connection instance
+ * @reason: timeout reason
+ * @secs: how many seconds
+ */
+
+void
+libwebsocket_set_timeout(struct libwebsocket *wsi,
+ enum pending_timeout reason, int secs)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ wsi->pending_timeout_limit = tv.tv_sec + secs;
+ wsi->pending_timeout = reason;
+}
+
/**
* libwebsocket_get_socket_fd() - returns the socket file descriptor
WSI_STATE_DEAD_SOCKET,
WSI_STATE_ESTABLISHED,
WSI_STATE_CLIENT_UNCONNECTED,
- WSI_STATE_RETURNED_CLOSE_ALREADY
+ WSI_STATE_RETURNED_CLOSE_ALREADY,
};
enum lws_rx_parse_state {
LWS_CONNMODE_WS_SERVING,
LWS_CONNMODE_WS_CLIENT,
+ /* transient modes */
+ LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY,
+ LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE,
+ LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY,
+
/* special internal types */
LWS_CONNMODE_SERVER_LISTENER,
LWS_CONNMODE_BROADCAST_PROXY_LISTENER,
enum pending_timeout {
NO_PENDING_TIMEOUT = 0,
+ PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
+ PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
PENDING_TIMEOUT_AWAITING_PING,
};
/* 04 protocol specific */
+ char key_b64[150];
unsigned char masking_key_04[20];
unsigned char frame_masking_nonce_04[4];
unsigned char frame_mask_04[20];
/* client support */
char initial_handshake_hash_base64[30];
enum connection_mode mode;
+ char *c_path;
+ char *c_host;
+ char *c_origin;
+ char *c_protocol;
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
BIO *client_bio;
+ int use_ssl;
#endif
void *user_space;
extern int
delete_from_fd(struct libwebsocket_context *this, int fd);
+extern void
+libwebsocket_set_timeout(struct libwebsocket *wsi,
+ enum pending_timeout reason, int secs);
+
#ifndef LWS_OPENSSL_SUPPORT
unsigned char *
<dd>Protocol whose connections will get callbacks
</dl>
<hr>
+<h2>libwebsocket_set_timeout - marks the wsi as subject to a timeout</h2>
+<i>void</i>
+<b>libwebsocket_set_timeout</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>,
+<i>enum pending_timeout</i> <b>reason</b>,
+<i>int</i> <b>secs</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance
+<dt><b>reason</b>
+<dd>timeout reason
+<dt><b>secs</b>
+<dd>how many seconds
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+You will not need this unless you are doing something special
+</blockquote>
+<hr>
<h2>libwebsocket_get_socket_fd - returns the socket file descriptor</h2>
<i>int</i>
<b>libwebsocket_get_socket_fd</b>