91214b8312d5fbe446b56b51bf29aac8817d6ff6
[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->protocol->owning_server,
212                                 wsi, LWS_CALLBACK_ESTABLISHED,
213                                           wsi->user_space, NULL, 0);
214
215         return 0;
216
217 bail:
218         return -1;
219 }
220
221 /*
222  * Perform the newer BASE64-encoded handshake scheme
223  */
224
225 static int
226 handshake_0405(struct libwebsocket *wsi)
227 {
228         static const char *websocket_magic_guid_04 =
229                                          "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
230         static const char *websocket_magic_guid_04_masking =
231                                          "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
232         char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
233         char nonce_buf[256];
234         char mask_summing_buf[256 + MAX_WEBSOCKET_04_KEY_LEN + 37];
235         unsigned char hash[20];
236         int n;
237         char *response;
238         char *p;
239         char *m = mask_summing_buf;
240         int nonce_len = 0;
241         int accept_len;
242
243         if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
244             !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
245                 debug("handshake_04 missing pieces\n");
246                 /* completed header processing, but missing some bits */
247                 goto bail;
248         }
249
250         if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
251                 fprintf(stderr, "extensions not supported\n");
252                 goto bail;
253         }
254
255         if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
256                                                      MAX_WEBSOCKET_04_KEY_LEN) {
257                 fprintf(stderr, "Client sent handshake key longer "
258                            "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
259                 goto bail;
260         }
261
262         strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
263         strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
264                                                        websocket_magic_guid_04);
265
266         SHA1((unsigned char *)accept_buf,
267                         wsi->utf8_token[WSI_TOKEN_KEY].token_len +
268                                          strlen(websocket_magic_guid_04), hash);
269
270         accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
271                                                              sizeof accept_buf);
272         if (accept_len < 0) {
273                 fprintf(stderr, "Base64 encoded hash too long\n");
274                 goto bail;
275         }
276
277         /* allocate the per-connection user memory (if any) */
278
279         if (wsi->protocol->per_session_data_size) {
280                 wsi->user_space = malloc(
281                                   wsi->protocol->per_session_data_size);
282                 if (wsi->user_space  == NULL) {
283                         fprintf(stderr, "Out of memory for "
284                                                    "conn user space\n");
285                         goto bail;
286                 }
287         } else
288                 wsi->user_space = NULL;
289
290         /* create the response packet */
291
292         /* make a buffer big enough for everything */
293
294         response = malloc(256 +
295                 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
296                 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
297                 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
298         if (!response) {
299                 fprintf(stderr, "Out of memory for response buffer\n");
300                 goto bail;
301         }
302
303         p = response;
304         strcpy(p,   "HTTP/1.1 101 Switching Protocols\x0d\x0a"
305                                           "Upgrade: WebSocket\x0d\x0a");
306         p += strlen("HTTP/1.1 101 Switching Protocols\x0d\x0a"
307                                           "Upgrade: WebSocket\x0d\x0a");
308         strcpy(p,   "Connection: Upgrade\x0d\x0a"
309                     "Sec-WebSocket-Accept: ");
310         p += strlen("Connection: Upgrade\x0d\x0a"
311                     "Sec-WebSocket-Accept: ");
312         strcpy(p, accept_buf);
313         p += accept_len;
314
315         if (wsi->ietf_spec_revision == 4) {
316                 strcpy(p,   "\x0d\x0aSec-WebSocket-Nonce: ");
317                 p += strlen("\x0d\x0aSec-WebSocket-Nonce: ");
318
319                 /* select the nonce */
320
321                 n = read(wsi->protocol->owning_server->fd_random, hash, 16);
322                 if (n != 16) {
323                         fprintf(stderr, "Unable to read random device %s %d\n",
324                                                      SYSTEM_RANDOM_FILEPATH, n);
325                         if (wsi->user_space)
326                                 free(wsi->user_space);
327                         goto bail;
328                 }
329
330                 /* encode the nonce */
331
332                 nonce_len = lws_b64_encode_string((const char *)hash, 16,
333                                                    nonce_buf, sizeof nonce_buf);
334                 if (nonce_len < 0) {
335                         fprintf(stderr, "Failed to base 64 encode the nonce\n");
336                         if (wsi->user_space)
337                                 free(wsi->user_space);
338                         goto bail;
339                 }
340
341                 /* apply the nonce */
342
343                 strcpy(p, nonce_buf);
344                 p += nonce_len;
345         }
346
347         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
348                 strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
349                 p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
350                 strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
351                 p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
352         }
353
354         /* end of response packet */
355
356         strcpy(p,   "\x0d\x0a\x0d\x0a");
357         p += strlen("\x0d\x0a\x0d\x0a");
358
359         if (wsi->ietf_spec_revision == 4) {
360
361                 /*
362                  * precompute the masking key the client will use from the SHA1
363                  * hash of ( base 64 client key we were sent, concatenated with
364                  * the bse 64 nonce we sent, concatenated with a magic constant
365                  * guid specified by the 04 standard )
366                  *
367                  * We store the hash in the connection's wsi ready to use with
368                  * undoing the masking the client has done on framed data it
369                  * sends (we send our data to the client in clear).
370                  */
371
372                 strcpy(mask_summing_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
373                 m += wsi->utf8_token[WSI_TOKEN_KEY].token_len;
374                 strcpy(m, nonce_buf);
375                 m += nonce_len;
376                 strcpy(m, websocket_magic_guid_04_masking);
377                 m += strlen(websocket_magic_guid_04_masking);
378
379                 SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf,
380                                                            wsi->masking_key_04);
381         }
382
383         /* okay send the handshake response accepting the connection */
384
385         debug("issuing response packet %d len\n", (int)(p - response));
386 #ifdef DEBUG
387         fwrite(response, 1,  p - response, stderr);
388 #endif
389         n = libwebsocket_write(wsi, (unsigned char *)response,
390                                           p - response, LWS_WRITE_HTTP);
391         if (n < 0) {
392                 fprintf(stderr, "ERROR writing to socket");
393                 goto bail;
394         }
395
396         /* alright clean up and set ourselves into established state */
397
398         free(response);
399         wsi->state = WSI_STATE_ESTABLISHED;
400         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
401         wsi->rx_packet_length = 0;
402
403         /* notify user code that we're ready to roll */
404
405         if (wsi->protocol->callback)
406                 wsi->protocol->callback(wsi->protocol->owning_server,
407                                 wsi, LWS_CALLBACK_ESTABLISHED,
408                                           wsi->user_space, NULL, 0);
409
410         return 0;
411
412
413 bail:
414         return -1;
415 }
416
417
418 /*
419  * -04 of the protocol (actually the 80th version) has a radically different
420  * handshake.  The 04 spec gives the following idea
421  *
422  *    The handshake from the client looks as follows:
423  *
424  *      GET /chat HTTP/1.1
425  *      Host: server.example.com
426  *      Upgrade: websocket
427  *      Connection: Upgrade
428  *      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
429  *      Sec-WebSocket-Origin: http://example.com
430  *      Sec-WebSocket-Protocol: chat, superchat
431  *      Sec-WebSocket-Version: 4
432  *
433  *  The handshake from the server looks as follows:
434  *
435  *       HTTP/1.1 101 Switching Protocols
436  *       Upgrade: websocket
437  *       Connection: Upgrade
438  *       Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
439  *       Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
440  *       Sec-WebSocket-Protocol: chat
441  */
442
443 /*
444  * We have to take care about parsing because the headers may be split
445  * into multiple fragments.  They may contain unknown headers with arbitrary
446  * argument lengths.  So, we parse using a single-character at a time state
447  * machine that is completely independent of packet size.
448  */
449
450 int
451 libwebsocket_read(struct libwebsocket_context *this, struct libwebsocket *wsi,
452                                                 unsigned char * buf, size_t len)
453 {
454         size_t n;
455
456         switch (wsi->state) {
457         case WSI_STATE_HTTP:
458                 wsi->state = WSI_STATE_HTTP_HEADERS;
459                 wsi->parser_state = WSI_TOKEN_NAME_PART;
460                 /* fallthru */
461         case WSI_STATE_HTTP_HEADERS:
462
463                 debug("issuing %d bytes to parser\n", (int)len);
464 #ifdef DEBUG
465                 fwrite(buf, 1, len, stderr);
466 #endif
467
468                 switch (wsi->mode) {
469                 case LWS_CONNMODE_WS_CLIENT:
470                         for (n = 0; n < len; n++)
471                                 libwebsocket_client_rx_sm(wsi, *buf++);
472
473                         return 0;
474                 default:
475                         break;
476                 }
477                 
478                 /* LWS_CONNMODE_WS_SERVING */
479
480                 for (n = 0; n < len; n++)
481                         libwebsocket_parse(wsi, *buf++);
482
483                 if (wsi->parser_state != WSI_PARSING_COMPLETE)
484                         break;
485
486                 debug("libwebsocket_parse sees parsing complete\n");
487
488                 /* is this websocket protocol or normal http 1.0? */
489
490                 if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
491                              !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
492                         if (wsi->protocol->callback)
493                                 (wsi->protocol->callback)(this, wsi,
494                                    LWS_CALLBACK_HTTP, wsi->user_space,
495                                    wsi->utf8_token[WSI_TOKEN_GET_URI].token, 0);
496                         wsi->state = WSI_STATE_HTTP;
497                         return 0;
498                 }
499
500                 /*
501                  * It's websocket
502                  *
503                  * Make sure user side is happy about protocol
504                  */
505
506                 while (wsi->protocol->callback) {
507
508                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
509                                 if (wsi->protocol->name == NULL)
510                                         break;
511                         } else
512                                 if (strcmp(
513                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
514                                                       wsi->protocol->name) == 0)
515                                         break;
516
517                         wsi->protocol++;
518                 }
519
520                 /* we didn't find a protocol he wanted? */
521
522                 if (wsi->protocol->callback == NULL) {
523                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
524                                 fprintf(stderr, "[no protocol] "
525                                         "not supported (use NULL .name)\n");
526                         else
527                                 fprintf(stderr, "Requested protocol %s "
528                                                 "not supported\n",
529                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
530                         goto bail;
531                 }
532
533                 /*
534                  * find out which spec version the client is using
535                  * if this header is not given, we default to 00 (aka 76)
536                  */
537
538                 if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
539                         wsi->ietf_spec_revision =
540                                  atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);
541
542                 /*
543                  * Give the user code a chance to study the request and
544                  * have the opportunity to deny it
545                  */
546
547                 if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
548                                 LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
549                                                 &wsi->utf8_token[0], NULL, 0)) {
550                         fprintf(stderr, "User code denied connection\n");
551                         goto bail;
552                 }
553
554
555                 /*
556                  * Perform the handshake according to the protocol version the
557                  * client announced
558                  */
559
560                 switch (wsi->ietf_spec_revision) {
561                 case 0: /* applies to 76 and 00 */
562                         wsi->xor_mask = xor_no_mask;
563                         if (handshake_00(wsi))
564                                 goto bail;
565                         break;
566                 case 4: /* 04 */
567                         wsi->xor_mask = xor_mask_04;
568                         debug("libwebsocket_parse calling handshake_04\n");
569                         if (handshake_0405(wsi))
570                                 goto bail;
571                         break;
572                 case 5: /* 05 */
573                         wsi->xor_mask = xor_mask_05;
574                         debug("libwebsocket_parse calling handshake_04\n");
575                         if (handshake_0405(wsi))
576                                 goto bail;
577                         break;
578
579                 default:
580                         fprintf(stderr, "Unknown client spec version %d\n",
581                                                        wsi->ietf_spec_revision);
582                         goto bail;
583                 }
584
585                 fprintf(stderr, "accepted v%02d connection\n",
586                                                        wsi->ietf_spec_revision);
587
588                 break;
589
590         case WSI_STATE_ESTABLISHED:
591                 switch (wsi->mode) {
592                 case LWS_CONNMODE_WS_CLIENT:
593                         for (n = 0; n < len; n++)
594                                 libwebsocket_client_rx_sm(wsi, *buf++);
595
596                         return 0;
597                 default:
598                         break;
599                 }
600
601                 /* LWS_CONNMODE_WS_SERVING */
602
603                 if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
604                         goto bail;
605
606                 break;
607         default:
608                 break;
609         }
610
611         return 0;
612
613 bail:
614         libwebsocket_close_and_free_session(this, wsi);
615
616         return -1;
617 }