1 #include "private-libwebsockets.h"
6 * In-place str to lower case
19 libwebsocket_client_close(struct libwebsocket *wsi)
22 struct libwebsocket_context *clients;
24 if (n == WSI_STATE_DEAD_SOCKET)
27 /* mark the WSI as dead and let the callback know */
29 wsi->state = WSI_STATE_DEAD_SOCKET;
32 if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED)
33 wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
34 wsi->user_space, NULL, 0);
36 /* remove it from the client polling list */
37 clients = wsi->protocol->owning_server;
39 for (n = 0; n < clients->fds_count; n++) {
40 if (clients->wsi[n] != wsi)
42 while (n < clients->fds_count - 1) {
43 clients->fds[n] = clients->fds[n + 1];
44 clients->wsi[n] = clients->wsi[n + 1];
47 /* we only have to deal with one */
48 n = clients->fds_count;
53 /* clean out any parsing allocations */
55 for (n = 0; n < WSI_TOKEN_COUNT; n++)
56 if (wsi->utf8_token[n].token)
57 free(wsi->utf8_token[n].token);
59 /* shut down reasonably cleanly */
61 #ifdef LWS_OPENSSL_SUPPORT
63 n = SSL_get_fd(wsi->ssl);
64 SSL_shutdown(wsi->ssl);
69 shutdown(wsi->sock, SHUT_RDWR);
71 #ifdef LWS_OPENSSL_SUPPORT
78 * libwebsocket_client_connect() - Connect to another websocket server
79 * @this: Websocket context
80 * @address: Remote server address, eg, "myserver.com"
81 * @port: Port to connect to on the remote server, eg, 80
82 * @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
84 * @path: Websocket path on server
85 * @host: Hostname on server
86 * @origin: Socket origin name
87 * @protocol: Comma-separated list of protocols being asked for from
88 * the server, or just one. The server will pick the one it
91 * This function creates a connection to a remote server
95 libwebsocket_client_connect(struct libwebsocket_context *this,
102 const char *protocol)
104 struct hostent *server_hostent;
105 struct sockaddr_in server_addr;
111 static const char magic_websocket_guid[] =
112 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
113 static const char magic_websocket_04_masking_guid[] =
114 "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
120 struct libwebsocket *wsi;
123 #ifdef LWS_OPENSSL_SUPPORT
124 char ssl_err_buf[512];
126 if (ssl_connection) {
127 fprintf(stderr, "libwebsockets not configured for ssl\n");
132 wsi = malloc(sizeof(struct libwebsocket));
134 fprintf(stderr, "Out of memory allocing new connection\n");
138 this->wsi[this->fds_count] = wsi;
140 wsi->ietf_spec_revision = 4;
141 wsi->name_buffer_pos = 0;
142 wsi->user_space = NULL;
143 wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
144 wsi->pings_vs_pongs = 0;
145 wsi->protocol = NULL;
147 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
148 wsi->utf8_token[n].token = NULL;
149 wsi->utf8_token[n].token_len = 0;
156 if (this->http_proxy_port) {
157 plen = sprintf(pkt, "CONNECT %s:%u HTTP/1.0\x0d\x0a"
158 "User-agent: libwebsockets\x0d\x0a"
159 /*Proxy-authorization: basic aGVsbG86d29ybGQ= */
160 "\x0d\x0a", address, port);
162 /* OK from now on we talk via the proxy */
164 address = this->http_proxy_address;
165 port = this->http_proxy_port;
169 * prepare the actual connection (to the proxy, if any)
172 server_hostent = gethostbyname(address);
173 if (server_hostent == NULL) {
174 fprintf(stderr, "Unable to get host name from %s\n", address);
178 wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
181 fprintf(stderr, "Unable to open socket\n");
186 server_addr.sin_family = AF_INET;
187 server_addr.sin_port = htons(port);
188 server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
189 bzero(&server_addr.sin_zero, 8);
191 if (connect(wsi->sock, (struct sockaddr *)&server_addr,
192 sizeof(struct sockaddr)) == -1) {
193 fprintf(stderr, "Connect failed\n");
197 /* we are connected to server, or proxy */
199 if (this->http_proxy_port) {
201 n = send(wsi->sock, pkt, plen, 0);
204 fprintf(stderr, "ERROR writing to proxy socket\n");
212 n = poll(&pfd, 1, 5000);
215 fprintf(stderr, "libwebsocket_client_handshake "
216 "timeout on proxy response");
220 n = recv(wsi->sock, pkt, sizeof pkt, 0);
223 fprintf(stderr, "ERROR reading from proxy socket\n");
228 if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
230 fprintf(stderr, "ERROR from proxy: %s\n", pkt);
234 /* we can just start sending to proxy */
237 #ifdef LWS_OPENSSL_SUPPORT
238 if (ssl_connection) {
240 wsi->ssl = SSL_new(this->ssl_client_ctx);
241 wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
242 SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
244 if (SSL_connect(wsi->ssl) <= 0) {
245 fprintf(stderr, "SSL connect error %s\n",
246 ERR_error_string(ERR_get_error(), ssl_err_buf));
250 n = SSL_get_verify_result(wsi->ssl);
251 if (n != X509_V_OK) {
252 if (n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
253 ssl_connection != 2) {
255 fprintf(stderr, "server's cert didn't "
256 "look good %d\n", n);
265 #ifdef LWS_OPENSSL_SUPPORT
270 * create the random key
273 fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
275 fprintf(stderr, "Unable to open random device %s\n",
276 SYSTEM_RANDOM_FILEPATH);
279 n = read(fd, hash, 16);
281 fprintf(stderr, "Unable to read from random device %s\n",
282 SYSTEM_RANDOM_FILEPATH);
288 lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
291 * 04 example client handshake
294 * Host: server.example.com
296 * Connection: Upgrade
297 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
298 * Sec-WebSocket-Origin: http://example.com
299 * Sec-WebSocket-Protocol: chat, superchat
300 * Sec-WebSocket-Version: 4
303 p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", path);
304 p += sprintf(p, "Host: %s\x0d\x0a", host);
305 p += sprintf(p, "Upgrade: websocket\x0d\x0a");
306 p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
308 p += strlen(key_b64);
309 p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin);
310 if (protocol != NULL)
311 p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol);
312 p += sprintf(p, "Sec-WebSocket-Version: 4\x0d\x0a\x0d\x0a");
315 /* prepare the expected server accept response */
317 strcpy(buf, key_b64);
318 strcpy(&buf[strlen(buf)], magic_websocket_guid);
320 SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
322 lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
323 sizeof wsi->initial_handshake_hash_base64);
325 /* send our request to the server */
327 #ifdef LWS_OPENSSL_SUPPORT
329 n = SSL_write(wsi->ssl, pkt, p - pkt);
332 n = send(wsi->sock, pkt, p - pkt, 0);
335 fprintf(stderr, "ERROR writing to client socket\n");
339 wsi->parser_state = WSI_TOKEN_NAME_PART;
345 n = poll(&pfd, 1, 5000);
347 fprintf(stderr, "libwebsocket_client_handshake socket error "
348 "while waiting for handshake response");
352 fprintf(stderr, "libwebsocket_client_handshake timeout "
353 "while waiting for handshake response");
357 /* interpret the server response */
360 * HTTP/1.1 101 Switching Protocols
362 * Connection: Upgrade
363 * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
364 * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
365 * Sec-WebSocket-Protocol: chat
368 #ifdef LWS_OPENSSL_SUPPORT
370 len = SSL_read(wsi->ssl, pkt, sizeof pkt);
373 len = recv(wsi->sock, pkt, sizeof pkt, 0);
376 fprintf(stderr, "libwebsocket_client_handshake read error\n");
381 for (n = 0; n < len; n++)
382 libwebsocket_parse(wsi, *p++);
384 if (wsi->parser_state != WSI_PARSING_COMPLETE) {
385 fprintf(stderr, "libwebsocket_client_handshake server response"
386 " failed parsing\n");
391 * well, what the server sent looked reasonable for syntax.
392 * Now let's confirm it sent all the necessary headers
395 if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
396 !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
397 !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
398 !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
399 !wsi->utf8_token[WSI_TOKEN_NONCE].token_len ||
400 (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
402 fprintf(stderr, "libwebsocket_client_handshake "
403 "missing required header(s)\n");
405 fprintf(stderr, "%s", pkt);
410 * Everything seems to be there, now take a closer look at what is in
414 strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
415 if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
416 "101 switching protocols")) {
417 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
418 " HTTP response '%s'\n",
419 wsi->utf8_token[WSI_TOKEN_HTTP].token);
423 strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
424 if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
425 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
426 " Upgrade header '%s'\n",
427 wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
431 strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
432 if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
433 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
434 " Connection hdr '%s'\n",
435 wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
439 * confirm the protocol the server wants to talk was in the list of
440 * protocols we offered
443 if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
445 /* no protocol name to work from, default to first protocol */
446 wsi->protocol = &this->protocols[0];
452 while (*pc && !okay) {
453 if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
454 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
455 (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
456 pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
460 while (*pc && *pc != ',')
462 while (*pc && *pc != ' ')
466 fprintf(stderr, "libwebsocket_client_handshake server "
467 "sent bad protocol '%s'\n",
468 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
473 * identify the selected protocol struct and set it
476 wsi->protocol = NULL;
477 while (this->protocols[n].callback) {
478 if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
479 this->protocols[n].name) == 0)
480 wsi->protocol = &this->protocols[n];
484 if (wsi->protocol == NULL) {
485 fprintf(stderr, "libwebsocket_client_handshake server "
486 "requested protocol '%s', which we "
487 "said we supported but we don't!\n",
488 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
494 * Confirm his accept token is the same as the one we precomputed
497 if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
498 wsi->initial_handshake_hash_base64)) {
499 fprintf(stderr, "libwebsocket_client_handshake server sent "
500 "bad ACCEPT '%s' vs computed '%s'\n",
501 wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
502 wsi->initial_handshake_hash_base64);
507 * Calculate the masking key to use when sending data to server
510 strcpy(buf, key_b64);
511 p = buf + strlen(key_b64);
512 strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
513 p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
514 strcpy(p, magic_websocket_04_masking_guid);
515 SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
517 /* allocate the per-connection user memory (if any) */
519 if (wsi->protocol->per_session_data_size) {
520 wsi->user_space = malloc(
521 wsi->protocol->per_session_data_size);
522 if (wsi->user_space == NULL) {
523 fprintf(stderr, "Out of memory for "
524 "conn user space\n");
528 wsi->user_space = NULL;
530 /* okay he is good to go */
532 this->fds[this->fds_count].fd = wsi->sock;
533 this->fds[this->fds_count].revents = 0;
534 this->fds[this->fds_count++].events = POLLIN;
536 wsi->state = WSI_STATE_ESTABLISHED;
537 wsi->mode = LWS_CONNMODE_WS_CLIENT;
539 fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
541 /* call him back to inform him he is up */
543 wsi->protocol->callback(wsi,
544 LWS_CALLBACK_CLIENT_ESTABLISHED,
551 libwebsocket_client_close(wsi);