change-client-mode-to-enum.patch
[profile/ivi/libwebsockets.git] / lib / client-handshake.c
1 #include "private-libwebsockets.h"
2 #include <netdb.h>
3
4
5 /*
6  * In-place str to lower case
7  */
8
9 void
10 strtolower(char *s)
11 {
12         while (*s) {
13                 *s = tolower(*s);
14                 s++;
15         }
16 }
17
18 void
19 libwebsocket_client_close(struct libwebsocket *wsi)
20 {
21         int n = wsi->state;
22         struct libwebsocket_context *clients;
23
24         if (n == WSI_STATE_DEAD_SOCKET)
25                 return;
26
27         /* 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];
45                                         n++;
46                                 }
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
62         if (wsi->ssl) {
63                 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
76
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
94 struct libwebsocket *
95 libwebsocket_client_connect(struct libwebsocket_context *this,
96                               const char *address,
97                               int port,
98                               int ssl_connection,
99                               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];
117         const char *pc;
118         int len;
119         int okay = 0;
120         struct libwebsocket *wsi;
121         int n;
122         int plen = 0;
123 #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
131
132         wsi = malloc(sizeof(struct libwebsocket));
133         if (wsi == NULL) {
134                 fprintf(stderr, "Out of memory allocing new connection\n");
135                 return NULL;
136         }
137
138         this->wsi[this->fds_count] = wsi;
139
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;
146
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         /*
153          * 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)
170          */
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);
179
180         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)  {
193                 fprintf(stderr, "Connect failed\n");
194                 goto bail1;
195         }
196
197         /* we are connected to server, or proxy */
198
199         if (this->http_proxy_port) {
200
201                 n = send(wsi->sock, pkt, plen, 0);
202                 if (n < 0) {
203                         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");
217                         goto bail1;
218                 }
219
220                 n = recv(wsi->sock, pkt, sizeof pkt, 0);
221                 if (n < 0) {
222                         close(wsi->sock);
223                         fprintf(stderr, "ERROR reading from proxy socket\n");
224                         goto bail1;
225                 }
226
227                 pkt[13] = '\0';
228                 if (strcmp(pkt, "HTTP/1.0 200 ") != 0) {
229                         close(wsi->sock);
230                         fprintf(stderr, "ERROR from proxy: %s\n", pkt);
231                         goto bail1;
232                 }
233
234                 /* we can just start sending to proxy */
235         }
236
237 #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));
247                         goto bail1;
248                 }
249
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) {
254
255                                 fprintf(stderr, "server's cert didn't "
256                                                            "look good %d\n", n);
257                                 goto bail2;
258                         }
259                 }
260         } else {
261                 wsi->ssl = NULL;
262 #endif
263
264
265 #ifdef LWS_OPENSSL_SUPPORT
266         }
267 #endif
268
269         /*
270          * create the random key
271          */
272
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
288         lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
289
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
327 #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         }
338
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
368 #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
375         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 "
403                                                 "missing required header(s)\n");
404                 pkt[len] = '\0';
405                 fprintf(stderr, "%s", pkt);
406                 goto bail2;
407         }
408
409         /*
410          * Everything seems to be there, now take a closer look at what is in
411          * each header
412          */
413
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);
420                 goto bail2;
421         }
422
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);
428                 goto bail2;
429         }
430
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);
436                 goto bail2;
437         }
438         /*
439          * confirm the protocol the server wants to talk was in the list of
440          * protocols we offered
441          */
442
443         if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
444
445                 /* no protocol name to work from, default to first protocol */
446                 wsi->protocol = &this->protocols[0];
447
448                 goto check_accept;
449         }
450
451         pc = protocol;
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')) {
457                         okay = 1;
458                         continue;
459                 }
460                 while (*pc && *pc != ',')
461                         pc++;
462                 while (*pc && *pc != ' ')
463                         pc++;
464         }
465         if (!okay) {
466                 fprintf(stderr, "libwebsocket_client_handshake server "
467                                         "sent bad protocol '%s'\n",
468                              wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
469                 goto bail2;
470         }
471
472         /*
473          * identify the selected protocol struct and set it
474          */
475         n = 0;
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];
481                 n++;
482         }
483
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);
489                 goto bail2;
490         }
491
492 check_accept:
493         /*
494          * Confirm his accept token is the same as the one we precomputed
495          */
496
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);
503                 goto bail2;
504         }
505
506         /*
507          * Calculate the masking key to use when sending data to server
508          */
509
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);
516
517         /* allocate the per-connection user memory (if any) */
518
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");
525                         goto bail2;
526                 }
527         } else
528                 wsi->user_space = NULL;
529
530         /* okay he is good to go */
531
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;
535
536         wsi->state = WSI_STATE_ESTABLISHED;
537         wsi->mode = LWS_CONNMODE_WS_CLIENT;
538
539         fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
540
541         /* call him back to inform him he is up */
542
543         wsi->protocol->callback(wsi,
544                          LWS_CALLBACK_CLIENT_ESTABLISHED,
545                          wsi->user_space,
546                          NULL, 0);
547         return wsi;
548
549
550 bail2:
551         libwebsocket_client_close(wsi);
552 bail1:
553         free(wsi);
554
555         return NULL;
556 }