blob: bb46a7adcbd07f6396a8890928501d41bb73f073 [file] [log] [blame]
Andy Green4739e5c2011-01-22 12:51:57 +00001#include "private-libwebsockets.h"
2#include <netdb.h>
3
4
5/*
6 * In-place str to lower case
7 */
8
9void
10strtolower(char *s)
11{
Andy Green90c7cbc2011-01-27 06:26:52 +000012 while (*s) {
13 *s = tolower(*s);
14 s++;
15 }
Andy Green4739e5c2011-01-22 12:51:57 +000016}
17
18void
19libwebsocket_client_close(struct libwebsocket *wsi)
20{
21 int n = wsi->state;
22 struct libwebsocket_context *clients;
23
Andy Green6a980542011-01-30 20:40:32 +000024 if (n == WSI_STATE_DEAD_SOCKET)
25 return;
26
Andy Green4739e5c2011-01-22 12:51:57 +000027 /* mark the WSI as dead and let the callback know */
28
29 wsi->state = WSI_STATE_DEAD_SOCKET;
30
31 if (wsi->protocol) {
32 if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED)
33 wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
34 wsi->user_space, NULL, 0);
35
36 /* remove it from the client polling list */
37 clients = wsi->protocol->owning_server;
38 if (clients)
39 for (n = 0; n < clients->fds_count; n++) {
40 if (clients->wsi[n] != wsi)
41 continue;
42 while (n < clients->fds_count - 1) {
43 clients->fds[n] = clients->fds[n + 1];
44 clients->wsi[n] = clients->wsi[n + 1];
Andy Greend655cb42011-01-30 12:15:22 +000045 n++;
Andy Green4739e5c2011-01-22 12:51:57 +000046 }
47 /* we only have to deal with one */
48 n = clients->fds_count;
49 }
50
51 }
52
53 /* clean out any parsing allocations */
54
55 for (n = 0; n < WSI_TOKEN_COUNT; n++)
56 if (wsi->utf8_token[n].token)
57 free(wsi->utf8_token[n].token);
58
59 /* shut down reasonably cleanly */
60
61#ifdef LWS_OPENSSL_SUPPORT
Andy Green90c7cbc2011-01-27 06:26:52 +000062 if (wsi->ssl) {
Andy Green4739e5c2011-01-22 12:51:57 +000063 n = SSL_get_fd(wsi->ssl);
64 SSL_shutdown(wsi->ssl);
65 close(n);
66 SSL_free(wsi->ssl);
67 } else {
68#endif
69 shutdown(wsi->sock, SHUT_RDWR);
70 close(wsi->sock);
71#ifdef LWS_OPENSSL_SUPPORT
72 }
73#endif
74}
75
Andy Green90c7cbc2011-01-27 06:26:52 +000076
77/**
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
83 * signed certs
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
89 * likes best.
90 *
91 * This function creates a connection to a remote server
92 */
93
Andy Green4739e5c2011-01-22 12:51:57 +000094struct libwebsocket *
Andy Green90c7cbc2011-01-27 06:26:52 +000095libwebsocket_client_connect(struct libwebsocket_context *this,
Andy Green4739e5c2011-01-22 12:51:57 +000096 const char *address,
97 int port,
Andy Green90c7cbc2011-01-27 06:26:52 +000098 int ssl_connection,
Andy Green4739e5c2011-01-22 12:51:57 +000099 const char *path,
100 const char *host,
101 const char *origin,
102 const char *protocol)
103{
104 struct hostent *server_hostent;
105 struct sockaddr_in server_addr;
106 char buf[150];
107 char key_b64[150];
108 char hash[20];
109 int fd;
110 struct pollfd pfd;
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";
115 char pkt[1024];
116 char *p = &pkt[0];
Andy Green6964bb52011-01-23 16:50:33 +0000117 const char *pc;
Andy Green4739e5c2011-01-22 12:51:57 +0000118 int len;
119 int okay = 0;
120 struct libwebsocket *wsi;
121 int n;
Andy Green9659f372011-01-27 22:01:43 +0000122 int plen = 0;
Andy Green90c7cbc2011-01-27 06:26:52 +0000123#ifdef LWS_OPENSSL_SUPPORT
124 char ssl_err_buf[512];
125#else
126 if (ssl_connection) {
127 fprintf(stderr, "libwebsockets not configured for ssl\n");
128 return NULL;
129 }
130#endif
Andy Green4739e5c2011-01-22 12:51:57 +0000131
Andy Green6964bb52011-01-23 16:50:33 +0000132 wsi = malloc(sizeof(struct libwebsocket));
Andy Green90c7cbc2011-01-27 06:26:52 +0000133 if (wsi == NULL) {
Andy Green9659f372011-01-27 22:01:43 +0000134 fprintf(stderr, "Out of memory allocing new connection\n");
Andy Green4739e5c2011-01-22 12:51:57 +0000135 return NULL;
Andy Green90c7cbc2011-01-27 06:26:52 +0000136 }
Andy Green4739e5c2011-01-22 12:51:57 +0000137
Andy Green90c7cbc2011-01-27 06:26:52 +0000138 this->wsi[this->fds_count] = wsi;
Andy Green4739e5c2011-01-22 12:51:57 +0000139
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;
Andy Green927eb7b2011-02-01 08:52:55 +0000145 wsi->protocol = NULL;
Andy Green4739e5c2011-01-22 12:51:57 +0000146
147 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
148 wsi->utf8_token[n].token = NULL;
149 wsi->utf8_token[n].token_len = 0;
150 }
151
152 /*
Andy Green9659f372011-01-27 22:01:43 +0000153 * proxy?
154 */
155
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);
161
162 /* OK from now on we talk via the proxy */
163
164 address = this->http_proxy_address;
165 port = this->http_proxy_port;
166 }
167
168 /*
169 * prepare the actual connection (to the proxy, if any)
Andy Green4739e5c2011-01-22 12:51:57 +0000170 */
171
172 server_hostent = gethostbyname(address);
173 if (server_hostent == NULL) {
174 fprintf(stderr, "Unable to get host name from %s\n", address);
175 goto bail1;
176 }
177
178 wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
Andy Green6964bb52011-01-23 16:50:33 +0000179
Andy Green4739e5c2011-01-22 12:51:57 +0000180 if (wsi->sock < 0) {
181 fprintf(stderr, "Unable to open socket\n");
182 goto bail1;
183 }
184
185
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);
190
191 if (connect(wsi->sock, (struct sockaddr *)&server_addr,
192 sizeof(struct sockaddr)) == -1) {
Andy Green90c7cbc2011-01-27 06:26:52 +0000193 fprintf(stderr, "Connect failed\n");
Andy Green4739e5c2011-01-22 12:51:57 +0000194 goto bail1;
Andy Green6964bb52011-01-23 16:50:33 +0000195 }
Andy Green4739e5c2011-01-22 12:51:57 +0000196
Andy Green9659f372011-01-27 22:01:43 +0000197 /* we are connected to server, or proxy */
198
Andy Green9659f372011-01-27 22:01:43 +0000199 if (this->http_proxy_port) {
200
201 n = send(wsi->sock, pkt, plen, 0);
202 if (n < 0) {
Andy Green5b9a4c02011-01-28 09:39:29 +0000203 close(wsi->sock);
204 fprintf(stderr, "ERROR writing to proxy socket\n");
205 goto bail1;
206 }
207
208 pfd.fd = wsi->sock;
209 pfd.events = POLLIN;
210 pfd.revents = 0;
211
212 n = poll(&pfd, 1, 5000);
213 if (n <= 0) {
214 close(wsi->sock);
215 fprintf(stderr, "libwebsocket_client_handshake "
216 "timeout on proxy response");
Andy Green02244bb2011-01-28 09:01:19 +0000217 goto bail1;
Andy Green9659f372011-01-27 22:01:43 +0000218 }
219
220 n = recv(wsi->sock, pkt, sizeof pkt, 0);
221 if (n < 0) {
Andy Green02244bb2011-01-28 09:01:19 +0000222 close(wsi->sock);
Andy Green5b9a4c02011-01-28 09:39:29 +0000223 fprintf(stderr, "ERROR reading from proxy socket\n");
Andy Green02244bb2011-01-28 09:01:19 +0000224 goto bail1;
Andy Green9659f372011-01-27 22:01:43 +0000225 }
226
227 pkt[13] = '\0';
228 if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
Andy Green02244bb2011-01-28 09:01:19 +0000229 close(wsi->sock);
Andy Green9659f372011-01-27 22:01:43 +0000230 fprintf(stderr, "ERROR from proxy: %s\n", pkt);
Andy Green02244bb2011-01-28 09:01:19 +0000231 goto bail1;
Andy Green9659f372011-01-27 22:01:43 +0000232 }
233
234 /* we can just start sending to proxy */
235 }
236
Andy Green90c7cbc2011-01-27 06:26:52 +0000237#ifdef LWS_OPENSSL_SUPPORT
238 if (ssl_connection) {
239
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);
243
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));
Andy Green9659f372011-01-27 22:01:43 +0000247 goto bail1;
Andy Green90c7cbc2011-01-27 06:26:52 +0000248 }
249
250 n = SSL_get_verify_result(wsi->ssl);
251 if (n != X509_V_OK) {
Andy Green9659f372011-01-27 22:01:43 +0000252 if (n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
253 ssl_connection != 2) {
Andy Green90c7cbc2011-01-27 06:26:52 +0000254
Andy Green9659f372011-01-27 22:01:43 +0000255 fprintf(stderr, "server's cert didn't "
256 "look good %d\n", n);
257 goto bail2;
258 }
Andy Green90c7cbc2011-01-27 06:26:52 +0000259 }
Andy Green9659f372011-01-27 22:01:43 +0000260 } else {
Andy Green90c7cbc2011-01-27 06:26:52 +0000261 wsi->ssl = NULL;
Andy Green90c7cbc2011-01-27 06:26:52 +0000262#endif
Andy Green9659f372011-01-27 22:01:43 +0000263
264
265#ifdef LWS_OPENSSL_SUPPORT
266 }
267#endif
268
Andy Green6964bb52011-01-23 16:50:33 +0000269 /*
270 * create the random key
271 */
Andy Green4739e5c2011-01-22 12:51:57 +0000272
273 fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
274 if (fd < 1) {
275 fprintf(stderr, "Unable to open random device %s\n",
276 SYSTEM_RANDOM_FILEPATH);
277 goto bail2;
278 }
279 n = read(fd, hash, 16);
280 if (n != 16) {
281 fprintf(stderr, "Unable to read from random device %s\n",
282 SYSTEM_RANDOM_FILEPATH);
283 close(fd);
284 goto bail2;
285 }
286 close(fd);
287
Andy Green6964bb52011-01-23 16:50:33 +0000288 lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
Andy Green4739e5c2011-01-22 12:51:57 +0000289
290 /*
291 * 04 example client handshake
292 *
293 * GET /chat HTTP/1.1
294 * Host: server.example.com
295 * Upgrade: websocket
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
301 */
302
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: ");
307 strcpy(p, key_b64);
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");
313
314
315 /* prepare the expected server accept response */
316
317 strcpy(buf, key_b64);
318 strcpy(&buf[strlen(buf)], magic_websocket_guid);
319
320 SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
321
322 lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
323 sizeof wsi->initial_handshake_hash_base64);
324
325 /* send our request to the server */
326
Andy Green90c7cbc2011-01-27 06:26:52 +0000327#ifdef LWS_OPENSSL_SUPPORT
328 if (ssl_connection)
329 n = SSL_write(wsi->ssl, pkt, p - pkt);
330 else
331#endif
332 n = send(wsi->sock, pkt, p - pkt, 0);
333
334 if (n < 0) {
335 fprintf(stderr, "ERROR writing to client socket\n");
336 goto bail2;
337 }
Andy Green4739e5c2011-01-22 12:51:57 +0000338
339 wsi->parser_state = WSI_TOKEN_NAME_PART;
340
341 pfd.fd = wsi->sock;
342 pfd.events = POLLIN;
343 pfd.revents = 0;
344
345 n = poll(&pfd, 1, 5000);
346 if (n < 0) {
347 fprintf(stderr, "libwebsocket_client_handshake socket error "
348 "while waiting for handshake response");
349 goto bail2;
350 }
351 if (n == 0) {
352 fprintf(stderr, "libwebsocket_client_handshake timeout "
353 "while waiting for handshake response");
354 goto bail2;
355 }
356
357 /* interpret the server response */
358
359 /*
360 * HTTP/1.1 101 Switching Protocols
361 * Upgrade: websocket
362 * Connection: Upgrade
363 * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
364 * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
365 * Sec-WebSocket-Protocol: chat
366 */
367
Andy Green90c7cbc2011-01-27 06:26:52 +0000368#ifdef LWS_OPENSSL_SUPPORT
369 if (ssl_connection)
370 len = SSL_read(wsi->ssl, pkt, sizeof pkt);
371 else
372#endif
373 len = recv(wsi->sock, pkt, sizeof pkt, 0);
374
Andy Green4739e5c2011-01-22 12:51:57 +0000375 if (len < 0) {
376 fprintf(stderr, "libwebsocket_client_handshake read error\n");
377 goto bail2;
378 }
379
380 p = pkt;
381 for (n = 0; n < len; n++)
382 libwebsocket_parse(wsi, *p++);
383
384 if (wsi->parser_state != WSI_PARSING_COMPLETE) {
385 fprintf(stderr, "libwebsocket_client_handshake server response"
386 " failed parsing\n");
387 goto bail2;
388 }
389
390 /*
391 * well, what the server sent looked reasonable for syntax.
392 * Now let's confirm it sent all the necessary headers
393 */
394
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 &&
401 protocol != NULL)) {
402 fprintf(stderr, "libwebsocket_client_handshake "
Andy Green927eb7b2011-02-01 08:52:55 +0000403 "missing required header(s)\n");
404 fprintf(stderr, "%s", pkt);
Andy Green4739e5c2011-01-22 12:51:57 +0000405 goto bail2;
406 }
407
408 /*
409 * Everything seems to be there, now take a closer look at what is in
410 * each header
411 */
412
413 strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
414 if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
415 "101 switching protocols")) {
416 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
417 " HTTP response '%s'\n",
418 wsi->utf8_token[WSI_TOKEN_HTTP].token);
419 goto bail2;
420 }
421
422 strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
423 if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
424 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
425 " Upgrade header '%s'\n",
426 wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
427 goto bail2;
Andy Green6964bb52011-01-23 16:50:33 +0000428 }
Andy Green4739e5c2011-01-22 12:51:57 +0000429
430 strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
431 if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
432 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
433 " Connection hdr '%s'\n",
434 wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
435 goto bail2;
436 }
437 /*
438 * confirm the protocol the server wants to talk was in the list of
439 * protocols we offered
440 */
441
442 if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
443
444 /* no protocol name to work from, default to first protocol */
Andy Green90c7cbc2011-01-27 06:26:52 +0000445 wsi->protocol = &this->protocols[0];
Andy Green4739e5c2011-01-22 12:51:57 +0000446
447 goto check_accept;
448 }
449
450 pc = protocol;
451 while (*pc && !okay) {
452 if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
453 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
454 (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
455 pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
456 okay = 1;
457 continue;
458 }
459 while (*pc && *pc != ',')
460 pc++;
461 while (*pc && *pc != ' ')
462 pc++;
463 }
464 if (!okay) {
465 fprintf(stderr, "libwebsocket_client_handshake server "
466 "sent bad protocol '%s'\n",
467 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
468 goto bail2;
469 }
470
471 /*
472 * identify the selected protocol struct and set it
473 */
474 n = 0;
475 wsi->protocol = NULL;
Andy Green90c7cbc2011-01-27 06:26:52 +0000476 while (this->protocols[n].callback) {
Andy Green4739e5c2011-01-22 12:51:57 +0000477 if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
Andy Green90c7cbc2011-01-27 06:26:52 +0000478 this->protocols[n].name) == 0)
479 wsi->protocol = &this->protocols[n];
Andy Green4739e5c2011-01-22 12:51:57 +0000480 n++;
481 }
482
483 if (wsi->protocol == NULL) {
484 fprintf(stderr, "libwebsocket_client_handshake server "
485 "requested protocol '%s', which we "
486 "said we supported but we don't!\n",
487 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
488 goto bail2;
489 }
490
491check_accept:
492 /*
493 * Confirm his accept token is the same as the one we precomputed
494 */
495
496 if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
497 wsi->initial_handshake_hash_base64)) {
498 fprintf(stderr, "libwebsocket_client_handshake server sent "
499 "bad ACCEPT '%s' vs computed '%s'\n",
500 wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
501 wsi->initial_handshake_hash_base64);
502 goto bail2;
503 }
504
505 /*
506 * Calculate the masking key to use when sending data to server
507 */
508
509 strcpy(buf, key_b64);
510 p = buf + strlen(key_b64);
511 strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
512 p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
513 strcpy(p, magic_websocket_04_masking_guid);
514 SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
515
Andy Green864d9022011-01-30 11:43:51 +0000516 /* allocate the per-connection user memory (if any) */
517
518 if (wsi->protocol->per_session_data_size) {
519 wsi->user_space = malloc(
520 wsi->protocol->per_session_data_size);
521 if (wsi->user_space == NULL) {
522 fprintf(stderr, "Out of memory for "
523 "conn user space\n");
524 goto bail2;
525 }
526 } else
527 wsi->user_space = NULL;
528
Andy Green4739e5c2011-01-22 12:51:57 +0000529 /* okay he is good to go */
530
Andy Green90c7cbc2011-01-27 06:26:52 +0000531 this->fds[this->fds_count].fd = wsi->sock;
532 this->fds[this->fds_count].revents = 0;
533 this->fds[this->fds_count++].events = POLLIN;
Andy Green4739e5c2011-01-22 12:51:57 +0000534
535 wsi->state = WSI_STATE_ESTABLISHED;
536 wsi->client_mode = 1;
537
538 fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
539
Andy Green90c7cbc2011-01-27 06:26:52 +0000540 /* call him back to inform him he is up */
541
542 wsi->protocol->callback(wsi,
543 LWS_CALLBACK_CLIENT_ESTABLISHED,
544 wsi->user_space,
545 NULL, 0);
Andy Green4739e5c2011-01-22 12:51:57 +0000546 return wsi;
547
548
549bail2:
550 libwebsocket_client_close(wsi);
551bail1:
552 free(wsi);
553
554 return NULL;
555}