introduce-client-support.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 }
15
16 void
17 libwebsocket_client_close(struct libwebsocket *wsi)
18 {
19         int n = wsi->state;
20         struct libwebsocket_context *clients;
21
22         /* mark the WSI as dead and let the callback know */
23
24         wsi->state = WSI_STATE_DEAD_SOCKET;
25
26         if (wsi->protocol) {
27                 if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED)
28                         wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
29                                                       wsi->user_space, NULL, 0);
30
31                 /* remove it from the client polling list */
32                 clients = wsi->protocol->owning_server;
33                 if (clients)
34                         for (n = 0; n < clients->fds_count; n++) {
35                                 if (clients->wsi[n] != wsi)
36                                         continue;
37                                 while (n < clients->fds_count - 1) {
38                                         clients->fds[n] = clients->fds[n + 1];
39                                         clients->wsi[n] = clients->wsi[n + 1];
40                                 }
41                                 /* we only have to deal with one */
42                                 n = clients->fds_count;
43                         }
44
45         }
46
47         /* clean out any parsing allocations */
48
49         for (n = 0; n < WSI_TOKEN_COUNT; n++)
50                 if (wsi->utf8_token[n].token)
51                         free(wsi->utf8_token[n].token);
52
53         /* shut down reasonably cleanly */
54
55 #ifdef LWS_OPENSSL_SUPPORT
56         if (use_ssl) {
57                 n = SSL_get_fd(wsi->ssl);
58                 SSL_shutdown(wsi->ssl);
59                 close(n);
60                 SSL_free(wsi->ssl);
61         } else {
62 #endif
63                 shutdown(wsi->sock, SHUT_RDWR);
64                 close(wsi->sock);
65 #ifdef LWS_OPENSSL_SUPPORT
66         }
67 #endif
68 }
69
70 struct libwebsocket *
71 libwebsocket_client_connect(struct libwebsocket_context *clients,
72                               const char *address,
73                               int port,
74                               const char *path,
75                               const char *host,
76                               const char *origin,
77                               const char *protocol)
78 {
79         struct hostent *server_hostent;
80         struct sockaddr_in server_addr;
81         char buf[150];
82         char key_b64[150];
83         char hash[20];
84         int fd;
85         struct pollfd pfd;
86         static const char magic_websocket_guid[] =
87                                          "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
88         static const char magic_websocket_04_masking_guid[] =
89                                          "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
90         char pkt[1024];
91         char *p = &pkt[0];
92         const char * pc;
93         int len;
94         int okay = 0;
95         struct libwebsocket *wsi;
96         int n;
97
98         wsi = malloc(sizeof (struct libwebsocket));
99         if (wsi == NULL)
100                 return NULL;
101
102         clients->wsi[clients->fds_count] = wsi;
103
104         wsi->ietf_spec_revision = 4;
105         wsi->name_buffer_pos = 0;
106         wsi->user_space = NULL;
107         wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
108         wsi->pings_vs_pongs = 0;
109
110         for (n = 0; n < WSI_TOKEN_COUNT; n++) {
111                 wsi->utf8_token[n].token = NULL;
112                 wsi->utf8_token[n].token_len = 0;
113         }
114
115         /*
116          * prepare the actual connection
117          */
118
119         server_hostent = gethostbyname(address);
120         if (server_hostent == NULL) {
121                 fprintf(stderr, "Unable to get host name from %s\n", address);
122                 goto bail1;
123         }
124
125         wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
126         
127         if (wsi->sock < 0) {
128                 fprintf(stderr, "Unable to open socket\n");
129                 goto bail1;
130         }
131
132
133         server_addr.sin_family = AF_INET;
134         server_addr.sin_port = htons(port);
135         server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
136         bzero(&server_addr.sin_zero, 8);
137
138         if (connect(wsi->sock, (struct sockaddr *)&server_addr,
139                                               sizeof(struct sockaddr)) == -1)  {
140                 fprintf(stderr, "Connect failed");
141                 goto bail1;
142         }
143
144         /*
145          * create the random key
146          */
147
148         fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
149         if (fd < 1) {
150                 fprintf(stderr, "Unable to open random device %s\n",
151                                                         SYSTEM_RANDOM_FILEPATH);
152                 goto bail2;
153         }
154         n = read(fd, hash, 16);
155         if (n != 16) {
156                 fprintf(stderr, "Unable to read from random device %s\n",
157                                                         SYSTEM_RANDOM_FILEPATH);
158                 close(fd);
159                 goto bail2;
160         }
161         close(fd);
162
163         lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);       
164
165         /*
166          * 04 example client handshake
167          *
168          * GET /chat HTTP/1.1
169          * Host: server.example.com
170          * Upgrade: websocket
171          * Connection: Upgrade
172          * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
173          * Sec-WebSocket-Origin: http://example.com
174          * Sec-WebSocket-Protocol: chat, superchat
175          * Sec-WebSocket-Version: 4
176          */
177
178          p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", path);
179          p += sprintf(p, "Host: %s\x0d\x0a", host);
180          p += sprintf(p, "Upgrade: websocket\x0d\x0a");
181          p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
182          strcpy(p, key_b64);
183          p += strlen(key_b64);
184          p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin);
185          if (protocol != NULL)
186                 p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol);
187          p += sprintf(p, "Sec-WebSocket-Version: 4\x0d\x0a\x0d\x0a");
188
189
190         /* prepare the expected server accept response */
191
192         strcpy(buf, key_b64);
193         strcpy(&buf[strlen(buf)], magic_websocket_guid);
194
195         SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
196
197         lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
198                                   sizeof wsi->initial_handshake_hash_base64);
199
200         /* send our request to the server */
201
202         n = send(wsi->sock, pkt, p - pkt, 0);
203
204         wsi->parser_state = WSI_TOKEN_NAME_PART;
205
206         pfd.fd = wsi->sock;
207         pfd.events = POLLIN;
208         pfd.revents = 0;
209
210         n = poll(&pfd, 1, 5000);
211         if (n < 0) {
212                 fprintf(stderr, "libwebsocket_client_handshake socket error "
213                                 "while waiting for handshake response");
214                 goto bail2;
215         }
216         if (n == 0) {
217                 fprintf(stderr, "libwebsocket_client_handshake timeout "
218                                 "while waiting for handshake response");
219                 goto bail2;
220         }
221
222         /* interpret the server response */
223
224         /*
225          *  HTTP/1.1 101 Switching Protocols
226          *  Upgrade: websocket
227          *  Connection: Upgrade
228          *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
229          *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
230          *  Sec-WebSocket-Protocol: chat
231          */
232
233         len = recv(wsi->sock, pkt, sizeof pkt, 0);
234         if (len < 0) {
235                 fprintf(stderr, "libwebsocket_client_handshake read error\n");
236                 goto bail2;
237         }
238
239         p = pkt;
240         for (n = 0; n < len; n++)
241                 libwebsocket_parse(wsi, *p++);
242
243         if (wsi->parser_state != WSI_PARSING_COMPLETE) {
244                 fprintf(stderr, "libwebsocket_client_handshake server response"
245                                 " failed parsing\n");
246                 goto bail2;
247         }
248
249         /*
250          * well, what the server sent looked reasonable for syntax.
251          * Now let's confirm it sent all the necessary headers
252          */
253
254          if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
255                         !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
256                         !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
257                         !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
258                         !wsi->utf8_token[WSI_TOKEN_NONCE].token_len ||
259                         (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
260                                                             protocol != NULL)) {
261                 fprintf(stderr, "libwebsocket_client_handshake "
262                                                    "missing required header\n");
263                 goto bail2;
264         }
265
266         /*
267          * Everything seems to be there, now take a closer look at what is in
268          * each header
269          */
270
271         strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
272         if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
273                                                    "101 switching protocols")) {
274                 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
275                                 " HTTP response '%s'\n",
276                                          wsi->utf8_token[WSI_TOKEN_HTTP].token);
277                 goto bail2;
278         }
279
280         strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
281         if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
282                 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
283                                 " Upgrade header '%s'\n",
284                                       wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
285                 goto bail2;
286         }       
287
288         strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
289         if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
290                 fprintf(stderr, "libwebsocket_client_handshake server sent bad"
291                                 " Connection hdr '%s'\n",
292                                    wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
293                 goto bail2;
294         }
295         /*
296          * confirm the protocol the server wants to talk was in the list of
297          * protocols we offered
298          */
299
300         if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
301
302                 /* no protocol name to work from, default to first protocol */
303                 wsi->protocol = &clients->protocols[0];
304
305                 goto check_accept;
306         }
307
308         pc = protocol;
309         while (*pc && !okay) {
310                 if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
311                         wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
312                 (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
313                    pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
314                         okay = 1;
315                         continue;
316                 }
317                 while (*pc && *pc != ',')
318                         pc++;
319                 while (*pc && *pc != ' ')
320                         pc++;
321         }
322         if (!okay) {
323                 fprintf(stderr, "libwebsocket_client_handshake server "
324                                         "sent bad protocol '%s'\n",
325                              wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
326                 goto bail2;
327         }
328
329         /*
330          * identify the selected protocol struct and set it
331          */
332         n = 0;
333         wsi->protocol = NULL;
334         while (clients->protocols[n].callback) {
335                 if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
336                                        clients->protocols[n].name) == 0)
337                         wsi->protocol = &clients->protocols[n];
338                 n++;
339         }
340
341         if (wsi->protocol == NULL) {
342                 fprintf(stderr, "libwebsocket_client_handshake server "
343                                 "requested protocol '%s', which we "
344                                 "said we supported but we don't!\n",
345                              wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
346                 goto bail2;
347         }
348
349 check_accept:
350         /*
351          * Confirm his accept token is the same as the one we precomputed
352          */
353
354         if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
355                                           wsi->initial_handshake_hash_base64)) {
356                 fprintf(stderr, "libwebsocket_client_handshake server sent "
357                                 "bad ACCEPT '%s' vs computed '%s'\n",
358                                 wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
359                                             wsi->initial_handshake_hash_base64);
360                 goto bail2;
361         }
362
363         /*
364          * Calculate the masking key to use when sending data to server
365          */
366
367         strcpy(buf, key_b64);
368         p = buf + strlen(key_b64);
369         strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
370         p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
371         strcpy(p, magic_websocket_04_masking_guid);
372         SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
373
374         /* okay he is good to go */
375
376         clients->fds[clients->fds_count].fd = wsi->sock;
377         clients->fds[clients->fds_count].revents = 0;
378         clients->fds[clients->fds_count++].events = POLLIN;
379
380         wsi->state = WSI_STATE_ESTABLISHED;
381         wsi->client_mode = 1;
382
383         fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
384
385         return wsi;
386
387
388 bail2:
389         libwebsocket_client_close(wsi);
390 bail1:
391         free(wsi);
392
393         return NULL;
394 }