e42aae54ebd30681fd4590e78e8332f84846149d
[profile/ivi/libwebsockets.git] / lib / handshake.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "private-libwebsockets.h"
23
24
25 static int
26 interpret_key(const char *key, unsigned long *result)
27 {
28         char digits[20];
29         int digit_pos = 0;
30         const char *p = key;
31         unsigned int spaces = 0;
32         unsigned long acc = 0;
33         int rem = 0;
34
35         while (*p) {
36                 if (isdigit(*p)) {
37                         if (digit_pos == sizeof(digits) - 1)
38                                 return -1;
39                         digits[digit_pos++] = *p;
40                 }
41                 p++;
42         }
43         digits[digit_pos] = '\0';
44         if (!digit_pos)
45                 return -2;
46
47         while (*key) {
48                 if (*key == ' ')
49                         spaces++;
50                 key++;
51         }
52
53         if (!spaces)
54                 return -3;
55
56         p = &digits[0];
57         while (*p) {
58                 rem = (rem * 10) + ((*p++) - '0');
59                 acc = (acc * 10) + (rem / spaces);
60                 rem -= (rem / spaces) * spaces;
61         }
62
63         if (rem) {
64                 fprintf(stderr, "nonzero handshake remainder\n");
65                 return -1;
66         }
67
68         *result = acc;
69
70         return 0;
71 }
72
73
74 static int
75 handshake_00(struct libwebsocket *wsi)
76 {
77         unsigned long key1, key2;
78         unsigned char sum[16];
79         char *response;
80         char *p;
81         int n;
82
83         /* Confirm we have all the necessary pieces */
84
85         if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
86                 !wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
87                 !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
88                 !wsi->utf8_token[WSI_TOKEN_KEY1].token_len ||
89                              !wsi->utf8_token[WSI_TOKEN_KEY2].token_len)
90                 /* completed header processing, but missing some bits */
91                 goto bail;
92
93         /* allocate the per-connection user memory (if any) */
94
95         if (wsi->protocol->per_session_data_size) {
96                 wsi->user_space = malloc(
97                                   wsi->protocol->per_session_data_size);
98                 if (wsi->user_space  == NULL) {
99                         fprintf(stderr, "Out of memory for "
100                                                    "conn user space\n");
101                         goto bail;
102                 }
103         } else
104                 wsi->user_space = NULL;
105
106         /* create the response packet */
107
108         /* make a buffer big enough for everything */
109
110         response = malloc(256 +
111                 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
112                 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
113                 wsi->utf8_token[WSI_TOKEN_HOST].token_len +
114                 wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len +
115                 wsi->utf8_token[WSI_TOKEN_GET_URI].token_len +
116                 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
117         if (!response) {
118                 fprintf(stderr, "Out of memory for response buffer\n");
119                 goto bail;
120         }
121
122         p = response;
123         strcpy(p,   "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
124                                           "Upgrade: WebSocket\x0d\x0a");
125         p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
126                                           "Upgrade: WebSocket\x0d\x0a");
127         strcpy(p,   "Connection: Upgrade\x0d\x0a"
128                     "Sec-WebSocket-Origin: ");
129         p += strlen("Connection: Upgrade\x0d\x0a"
130                     "Sec-WebSocket-Origin: ");
131         strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
132         p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
133 #ifdef LWS_OPENSSL_SUPPORT
134         if (wsi->ssl) {
135                 strcpy(p,   "\x0d\x0aSec-WebSocket-Location: wss://");
136                 p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
137         } else {
138 #endif
139                 strcpy(p,   "\x0d\x0aSec-WebSocket-Location: ws://");
140                 p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
141 #ifdef LWS_OPENSSL_SUPPORT
142         }
143 #endif
144         strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
145         p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
146         strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
147         p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len;
148
149         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
150                 strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
151                 p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
152                 strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
153                 p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
154         }
155
156         strcpy(p,   "\x0d\x0a\x0d\x0a");
157         p += strlen("\x0d\x0a\x0d\x0a");
158
159         /* convert the two keys into 32-bit integers */
160
161         if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
162                 goto bail;
163         if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
164                 goto bail;
165
166         /* lay them out in network byte order (MSB first */
167
168         sum[0] = key1 >> 24;
169         sum[1] = key1 >> 16;
170         sum[2] = key1 >> 8;
171         sum[3] = key1;
172         sum[4] = key2 >> 24;
173         sum[5] = key2 >> 16;
174         sum[6] = key2 >> 8;
175         sum[7] = key2;
176
177         /* follow them with the challenge token we were sent */
178
179         memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
180
181         /*
182          * compute the md5sum of that 16-byte series and use as our
183          * payload after our headers
184          */
185
186         MD5(sum, 16, (unsigned char *)p);
187         p += 16;
188
189         /* it's complete: go ahead and send it */
190
191         debug("issuing response packet %d len\n", (int)(p - response));
192 #ifdef DEBUG
193         fwrite(response, 1,  p - response, stderr);
194 #endif
195         n = libwebsocket_write(wsi, (unsigned char *)response,
196                                           p - response, LWS_WRITE_HTTP);
197         if (n < 0) {
198                 fprintf(stderr, "ERROR writing to socket");
199                 goto bail;
200         }
201
202         /* alright clean up and set ourselves into established state */
203
204         free(response);
205         wsi->state = WSI_STATE_ESTABLISHED;
206         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
207
208         /* notify user code that we're ready to roll */
209
210         if (wsi->protocol->callback)
211                 wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
212                                           wsi->user_space, NULL, 0);
213
214         return 0;
215
216 bail:
217         return -1;
218 }
219
220 /*
221  * Perform the newer BASE64-encoded handshake scheme
222  */
223
224 static int
225 handshake_0405(struct libwebsocket *wsi)
226 {
227         static const char *websocket_magic_guid_04 =
228                                          "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
229         static const char *websocket_magic_guid_04_masking =
230                                          "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
231         char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
232         char nonce_buf[256];
233         char mask_summing_buf[256 + MAX_WEBSOCKET_04_KEY_LEN + 37];
234         unsigned char hash[20];
235         int n;
236         char *response;
237         char *p;
238         char *m = mask_summing_buf;
239         int fd;
240         int nonce_len;
241         int accept_len;
242
243         if (!wsi->utf8_token[WSI_TOKEN_SWORIGIN].token_len ||
244             !wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
245             !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
246                 debug("handshake_04 missing pieces\n");
247                 /* completed header processing, but missing some bits */
248                 goto bail;
249         }
250
251         if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
252                 fprintf(stderr, "extensions not supported\n");
253                 goto bail;
254         }
255
256         if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
257                                                      MAX_WEBSOCKET_04_KEY_LEN) {
258                 fprintf(stderr, "Client sent handshake key longer "
259                            "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
260                 goto bail;
261         }
262
263         strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
264         strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
265                                                        websocket_magic_guid_04);
266
267         SHA1((unsigned char *)accept_buf,
268                         wsi->utf8_token[WSI_TOKEN_KEY].token_len +
269                                          strlen(websocket_magic_guid_04), hash);
270
271         accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
272                                                              sizeof accept_buf);
273         if (accept_len < 0) {
274                 fprintf(stderr, "Base64 encoded hash too long\n");
275                 goto bail;
276         }
277
278         /* allocate the per-connection user memory (if any) */
279
280         if (wsi->protocol->per_session_data_size) {
281                 wsi->user_space = malloc(
282                                   wsi->protocol->per_session_data_size);
283                 if (wsi->user_space  == NULL) {
284                         fprintf(stderr, "Out of memory for "
285                                                    "conn user space\n");
286                         goto bail;
287                 }
288         } else
289                 wsi->user_space = NULL;
290
291         /* create the response packet */
292
293         /* make a buffer big enough for everything */
294
295         response = malloc(256 +
296                 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
297                 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
298                 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
299         if (!response) {
300                 fprintf(stderr, "Out of memory for response buffer\n");
301                 goto bail;
302         }
303
304         p = response;
305         strcpy(p,   "HTTP/1.1 101 Switching Protocols\x0d\x0a"
306                                           "Upgrade: WebSocket\x0d\x0a");
307         p += strlen("HTTP/1.1 101 Switching Protocols\x0d\x0a"
308                                           "Upgrade: WebSocket\x0d\x0a");
309         strcpy(p,   "Connection: Upgrade\x0d\x0a"
310                     "Sec-WebSocket-Accept: ");
311         p += strlen("Connection: Upgrade\x0d\x0a"
312                     "Sec-WebSocket-Accept: ");
313         strcpy(p, accept_buf);
314         p += accept_len;
315
316         strcpy(p,   "\x0d\x0aSec-WebSocket-Nonce: ");
317         p += strlen("\x0d\x0aSec-WebSocket-Nonce: ");
318
319         /* select the nonce */
320
321         fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
322         if (fd < 1) {
323                 fprintf(stderr, "Unable to open random device %s\n",
324                                                         SYSTEM_RANDOM_FILEPATH);
325                 if (wsi->user_space)
326                         free(wsi->user_space);
327                 goto bail;
328         }
329         n = read(fd, hash, 16);
330         if (n != 16) {
331                 fprintf(stderr, "Unable to read from random device %s %d\n",
332                                                      SYSTEM_RANDOM_FILEPATH, n);
333                 if (wsi->user_space)
334                         free(wsi->user_space);
335                 goto bail;
336         }
337         close(fd);
338
339         /* encode the nonce */
340
341         nonce_len = lws_b64_encode_string((const char *)hash, 16, nonce_buf,
342                                                               sizeof nonce_buf);
343         if (nonce_len < 0) {
344                 fprintf(stderr, "Failed to base 64 encode the nonce\n");
345                 if (wsi->user_space)
346                         free(wsi->user_space);
347                 goto bail;
348         }
349
350         /* apply the nonce */
351
352         strcpy(p, nonce_buf);
353         p += nonce_len;
354
355         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
356                 strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
357                 p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
358                 strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
359                 p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
360         }
361
362         /* end of response packet */
363
364         strcpy(p,   "\x0d\x0a\x0d\x0a");
365         p += strlen("\x0d\x0a\x0d\x0a");
366
367         /*
368          * precompute the masking key the client will use from the SHA1 hash of
369          * ( base 64 client key we were sent, concatenated with the base 64
370          * nonce we sent, concatenated with a magic constant guid specified by
371          * the 04 standard )
372          *
373          * We store the hash in the connection's wsi ready to use with
374          * undoing the masking the client has done on framed data it sends
375          * (we send our data to the client in clear).
376          */
377
378         strcpy(mask_summing_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
379         m += wsi->utf8_token[WSI_TOKEN_KEY].token_len;
380         strcpy(m, nonce_buf);
381         m += nonce_len;
382         strcpy(m, websocket_magic_guid_04_masking);
383         m += strlen(websocket_magic_guid_04_masking);
384
385         SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf,
386                                                            wsi->masking_key_04);
387
388         /* okay send the handshake response accepting the connection */
389
390         debug("issuing response packet %d len\n", (int)(p - response));
391 #ifdef DEBUG
392         fwrite(response, 1,  p - response, stderr);
393 #endif
394         n = libwebsocket_write(wsi, (unsigned char *)response,
395                                           p - response, LWS_WRITE_HTTP);
396         if (n < 0) {
397                 fprintf(stderr, "ERROR writing to socket");
398                 goto bail;
399         }
400
401         /* alright clean up and set ourselves into established state */
402
403         free(response);
404         wsi->state = WSI_STATE_ESTABLISHED;
405         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
406         wsi->rx_packet_length = 0;
407
408         /* notify user code that we're ready to roll */
409
410         if (wsi->protocol->callback)
411                 wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
412                                           wsi->user_space, NULL, 0);
413
414         return 0;
415
416
417 bail:
418         return -1;
419 }
420
421
422 /*
423  * -04 of the protocol (actually the 80th version) has a radically different
424  * handshake.  The 04 spec gives the following idea
425  *
426  *    The handshake from the client looks as follows:
427  *
428  *      GET /chat HTTP/1.1
429  *      Host: server.example.com
430  *      Upgrade: websocket
431  *      Connection: Upgrade
432  *      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
433  *      Sec-WebSocket-Origin: http://example.com
434  *      Sec-WebSocket-Protocol: chat, superchat
435  *      Sec-WebSocket-Version: 4
436  *
437  *  The handshake from the server looks as follows:
438  *
439  *       HTTP/1.1 101 Switching Protocols
440  *       Upgrade: websocket
441  *       Connection: Upgrade
442  *       Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
443  *       Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
444  *       Sec-WebSocket-Protocol: chat
445  */
446
447 /*
448  * We have to take care about parsing because the headers may be split
449  * into multiple fragments.  They may contain unknown headers with arbitrary
450  * argument lengths.  So, we parse using a single-character at a time state
451  * machine that is completely independent of packet size.
452  */
453
454 int
455 libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
456 {
457         size_t n;
458
459         switch (wsi->state) {
460         case WSI_STATE_HTTP:
461                 wsi->state = WSI_STATE_HTTP_HEADERS;
462                 wsi->parser_state = WSI_TOKEN_NAME_PART;
463                 /* fallthru */
464         case WSI_STATE_HTTP_HEADERS:
465
466                 debug("issuing %d bytes to parser\n", (int)len);
467 #ifdef DEBUG
468                 fwrite(buf, 1, len, stderr);
469 #endif
470
471                 switch (wsi->mode) {
472                 case LWS_CONNMODE_WS_CLIENT:
473                         for (n = 0; n < len; n++)
474                                 libwebsocket_client_rx_sm(wsi, *buf++);
475
476                         return 0;
477                 default:
478                         break;
479                 }
480                 
481                 /* LWS_CONNMODE_WS_SERVING */
482
483                 for (n = 0; n < len; n++)
484                         libwebsocket_parse(wsi, *buf++);
485
486                 if (wsi->parser_state != WSI_PARSING_COMPLETE)
487                         break;
488
489                 debug("libwebsocket_parse sees parsing complete\n");
490
491                 /* is this websocket protocol or normal http 1.0? */
492
493                 if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
494                              !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
495                         if (wsi->protocol->callback)
496                                 (wsi->protocol->callback)(wsi,
497                                    LWS_CALLBACK_HTTP, wsi->user_space,
498                                    wsi->utf8_token[WSI_TOKEN_GET_URI].token, 0);
499                         wsi->state = WSI_STATE_HTTP;
500                         return 0;
501                 }
502
503                 /*
504                  * It's websocket
505                  *
506                  * Make sure user side is happy about protocol
507                  */
508
509                 while (wsi->protocol->callback) {
510
511                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
512                                 if (wsi->protocol->name == NULL)
513                                         break;
514                         } else
515                                 if (strcmp(
516                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
517                                                       wsi->protocol->name) == 0)
518                                         break;
519
520                         wsi->protocol++;
521                 }
522
523                 /* we didn't find a protocol he wanted? */
524
525                 if (wsi->protocol->callback == NULL) {
526                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
527                                 fprintf(stderr, "[no protocol] "
528                                         "not supported (use NULL .name)\n");
529                         else
530                                 fprintf(stderr, "Requested protocol %s "
531                                                 "not supported\n",
532                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
533                         goto bail;
534                 }
535
536                 /*
537                  * find out which spec version the client is using
538                  * if this header is not given, we default to 00 (aka 76)
539                  */
540
541                 if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
542                         wsi->ietf_spec_revision =
543                                  atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);
544
545                 /*
546                  * Perform the handshake according to the protocol version the
547                  * client announced
548                  */
549
550                 switch (wsi->ietf_spec_revision) {
551                 case 0: /* applies to 76 and 00 */
552                         wsi->xor_mask = xor_no_mask;
553                         if (handshake_00(wsi))
554                                 goto bail;
555                         break;
556                 case 4: /* 04 */
557                         wsi->xor_mask = xor_mask_04;
558                         debug("libwebsocket_parse calling handshake_04\n");
559                         if (handshake_0405(wsi))
560                                 goto bail;
561                         break;
562                 case 5: /* 05 */
563                         wsi->xor_mask = xor_mask_05;
564                         debug("libwebsocket_parse calling handshake_04\n");
565                         if (handshake_0405(wsi))
566                                 goto bail;
567                         break;
568
569                 default:
570                         fprintf(stderr, "Unknown client spec version %d\n",
571                                                        wsi->ietf_spec_revision);
572                         goto bail;
573                 }
574
575                 fprintf(stderr, "accepted v%02d connection\n",
576                                                        wsi->ietf_spec_revision);
577
578                 break;
579
580         case WSI_STATE_ESTABLISHED:
581                 switch (wsi->mode) {
582                 case LWS_CONNMODE_WS_CLIENT:
583                         for (n = 0; n < len; n++)
584                                 libwebsocket_client_rx_sm(wsi, *buf++);
585
586                         return 0;
587                 default:
588                         break;
589                 }
590
591                 /* LWS_CONNMODE_WS_SERVING */
592
593                 if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
594                         goto bail;
595
596                 break;
597         default:
598                 break;
599         }
600
601         return 0;
602
603 bail:
604         libwebsocket_close_and_free_session(wsi);
605
606         return -1;
607 }