http service break into outer loop states
[platform/upstream/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 #define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
25 #define LWS_CPYAPP_TOKEN(ptr, tok) { strcpy(p, wsi->utf8_token[tok].token); \
26                 p += wsi->utf8_token[tok].token_len; }
27
28 static int
29 interpret_key(const char *key, unsigned long *result)
30 {
31         char digits[20];
32         int digit_pos = 0;
33         const char *p = key;
34         unsigned int spaces = 0;
35         unsigned long acc = 0;
36         int rem = 0;
37
38         while (*p) {
39                 if (!isdigit(*p)) {
40                         p++;
41                         continue;
42                 }
43                 if (digit_pos == sizeof(digits) - 1)
44                         return -1;
45                 digits[digit_pos++] = *p++;
46         }
47         digits[digit_pos] = '\0';
48         if (!digit_pos)
49                 return -2;
50
51         while (*key) {
52                 if (*key == ' ')
53                         spaces++;
54                 key++;
55         }
56
57         if (!spaces)
58                 return -3;
59
60         p = &digits[0];
61         while (*p) {
62                 rem = (rem * 10) + ((*p++) - '0');
63                 acc = (acc * 10) + (rem / spaces);
64                 rem -= (rem / spaces) * spaces;
65         }
66
67         if (rem) {
68                 lwsl_warn("nonzero handshake remainder\n");
69                 return -1;
70         }
71
72         *result = acc;
73
74         return 0;
75 }
76
77
78 static int
79 handshake_00(struct libwebsocket_context *context, struct libwebsocket *wsi)
80 {
81         unsigned long key1, key2;
82         unsigned char sum[16];
83         char *response;
84         char *p;
85         int n;
86
87         /* Confirm we have all the necessary pieces */
88
89         if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
90                 !wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
91                 !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
92                 !wsi->utf8_token[WSI_TOKEN_KEY1].token_len ||
93                              !wsi->utf8_token[WSI_TOKEN_KEY2].token_len)
94                 /* completed header processing, but missing some bits */
95                 goto bail;
96
97         /* allocate the per-connection user memory (if any) */
98         if (wsi->protocol->per_session_data_size &&
99                                           !libwebsocket_ensure_user_space(wsi))
100                 goto bail;
101
102         /* create the response packet */
103
104         /* make a buffer big enough for everything */
105
106         response = (char *)malloc(256 +
107                 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
108                 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
109                 wsi->utf8_token[WSI_TOKEN_HOST].token_len +
110                 wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len +
111                 wsi->utf8_token[WSI_TOKEN_GET_URI].token_len +
112                 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
113         if (!response) {
114                 lwsl_err("Out of memory for response buffer\n");
115                 goto bail;
116         }
117
118         p = response;
119         LWS_CPYAPP(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
120                       "Upgrade: WebSocket\x0d\x0a"
121                       "Connection: Upgrade\x0d\x0a"
122                       "Sec-WebSocket-Origin: ");
123         strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
124         p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
125 #ifdef LWS_OPENSSL_SUPPORT
126         if (wsi->ssl) {
127                 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: wss://");
128         } else {
129                 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: ws://");
130         }
131 #else
132         LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: ws://");
133 #endif
134
135         LWS_CPYAPP_TOKEN(p, WSI_TOKEN_HOST);
136         LWS_CPYAPP_TOKEN(p, WSI_TOKEN_GET_URI);
137
138         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
139                 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
140                 LWS_CPYAPP_TOKEN(p, WSI_TOKEN_PROTOCOL);
141         }
142
143         LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
144
145         /* convert the two keys into 32-bit integers */
146
147         if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
148                 goto bail;
149         if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
150                 goto bail;
151
152         /* lay them out in network byte order (MSB first */
153
154         sum[0] = (unsigned char)(key1 >> 24);
155         sum[1] = (unsigned char)(key1 >> 16);
156         sum[2] = (unsigned char)(key1 >> 8);
157         sum[3] = (unsigned char)(key1);
158         sum[4] = (unsigned char)(key2 >> 24);
159         sum[5] = (unsigned char)(key2 >> 16);
160         sum[6] = (unsigned char)(key2 >> 8);
161         sum[7] = (unsigned char)(key2);
162
163         /* follow them with the challenge token we were sent */
164
165         memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
166
167         /*
168          * compute the md5sum of that 16-byte series and use as our
169          * payload after our headers
170          */
171
172         MD5(sum, 16, (unsigned char *)p);
173         p += 16;
174
175         /* it's complete: go ahead and send it */
176
177         lwsl_parser("issuing response packet %d len\n", (int)(p - response));
178 #ifdef _DEBUG
179         fwrite(response, 1,  p - response, stderr);
180 #endif
181         n = libwebsocket_write(wsi, (unsigned char *)response,
182                                           p - response, LWS_WRITE_HTTP);
183         if (n < 0) {
184                 lwsl_debug("handshake_00: ERROR writing to socket\n");
185                 goto bail;
186         }
187
188         /* alright clean up and set ourselves into established state */
189
190         free(response);
191         wsi->state = WSI_STATE_ESTABLISHED;
192         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
193
194         /* notify user code that we're ready to roll */
195
196         if (wsi->protocol->callback)
197                 wsi->protocol->callback(wsi->protocol->owning_server,
198                                 wsi, LWS_CALLBACK_ESTABLISHED,
199                                           wsi->user_space, NULL, 0);
200
201         return 0;
202
203 bail:
204         return -1;
205 }
206
207 /*
208  * Perform the newer BASE64-encoded handshake scheme
209  */
210
211 static int
212 handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
213 {
214         static const char *websocket_magic_guid_04 =
215                                          "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
216         static const char *websocket_magic_guid_04_masking =
217                                          "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
218         char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
219         char nonce_buf[256];
220         char mask_summing_buf[256 + MAX_WEBSOCKET_04_KEY_LEN + 37];
221         unsigned char hash[20];
222         int n;
223         char *response;
224         char *p;
225         char *m = mask_summing_buf;
226         int nonce_len = 0;
227         int accept_len;
228         char *c;
229         char ext_name[128];
230         struct libwebsocket_extension *ext;
231         int ext_count = 0;
232         int more = 1;
233
234         if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
235             !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
236                 lwsl_parser("handshake_04 missing pieces\n");
237                 /* completed header processing, but missing some bits */
238                 goto bail;
239         }
240
241         if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
242                                                      MAX_WEBSOCKET_04_KEY_LEN) {
243                 lwsl_warn("Client sent handshake key longer "
244                            "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
245                 goto bail;
246         }
247
248         strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
249         strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
250                                                        websocket_magic_guid_04);
251
252         SHA1((unsigned char *)accept_buf,
253                         wsi->utf8_token[WSI_TOKEN_KEY].token_len +
254                                          strlen(websocket_magic_guid_04), hash);
255
256         accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
257                                                              sizeof accept_buf);
258         if (accept_len < 0) {
259                 lwsl_warn("Base64 encoded hash too long\n");
260                 goto bail;
261         }
262
263         /* allocate the per-connection user memory (if any) */
264         if (wsi->protocol->per_session_data_size &&
265                                           !libwebsocket_ensure_user_space(wsi))
266                 goto bail;
267
268         /* create the response packet */
269
270         /* make a buffer big enough for everything */
271
272         response = (char *)malloc(256 +
273                 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
274                 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
275                 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
276         if (!response) {
277                 lwsl_err("Out of memory for response buffer\n");
278                 goto bail;
279         }
280
281         p = response;
282         LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
283                       "Upgrade: WebSocket\x0d\x0a"
284                       "Connection: Upgrade\x0d\x0a"
285                       "Sec-WebSocket-Accept: ");
286         strcpy(p, accept_buf);
287         p += accept_len;
288
289         if (wsi->ietf_spec_revision == 4) {
290                 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Nonce: ");
291
292                 /* select the nonce */
293
294                 n = libwebsockets_get_random(wsi->protocol->owning_server,
295                                                                       hash, 16);
296                 if (n != 16) {
297                         lwsl_err("Unable to read random device %s %d\n",
298                                                      SYSTEM_RANDOM_FILEPATH, n);
299                         if (wsi->user_space)
300                                 free(wsi->user_space);
301                         goto bail;
302                 }
303
304                 /* encode the nonce */
305
306                 nonce_len = lws_b64_encode_string((const char *)hash, 16,
307                                                    nonce_buf, sizeof nonce_buf);
308                 if (nonce_len < 0) {
309                         lwsl_err("Failed to base 64 encode the nonce\n");
310                         if (wsi->user_space)
311                                 free(wsi->user_space);
312                         goto bail;
313                 }
314
315                 /* apply the nonce */
316
317                 strcpy(p, nonce_buf);
318                 p += nonce_len;
319         }
320
321         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
322                 LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
323                 LWS_CPYAPP_TOKEN(p, WSI_TOKEN_PROTOCOL);
324         }
325
326         /*
327          * Figure out which extensions the client has that we want to
328          * enable on this connection, and give him back the list
329          */
330
331         if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
332
333                 /*
334                  * break down the list of client extensions
335                  * and go through them
336                  */
337
338                 c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
339                 lwsl_parser("wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token = %s\n",
340                                   wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token);
341                 wsi->count_active_extensions = 0;
342                 n = 0;
343                 while (more) {
344
345                         if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
346                                 ext_name[n] = *c++;
347                                 if (n < sizeof(ext_name) - 1)
348                                         n++;
349                                 continue;
350                         }
351                         ext_name[n] = '\0';
352                         if (!*c)
353                                 more = 0;
354                         else {
355                                 c++;
356                                 if (!n)
357                                         continue;
358                         }
359
360                         /* check a client's extension against our support */
361
362                         ext = wsi->protocol->owning_server->extensions;
363
364                         while (ext && ext->callback) {
365
366                                 if (strcmp(ext_name, ext->name)) {
367                                         ext++;
368                                         continue;
369                                 }
370
371                                 /*
372                                  * oh, we do support this one he
373                                  * asked for... but let's ask user
374                                  * code if it's OK to apply it on this
375                                  * particular connection + protocol
376                                  */
377
378                                 n = wsi->protocol->owning_server->
379                                         protocols[0].callback(
380                                                 wsi->protocol->owning_server,
381                                                 wsi,
382                                           LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
383                                                   wsi->user_space, ext_name, 0);
384
385                                 /*
386                                  * zero return from callback means
387                                  * go ahead and allow the extension,
388                                  * it's what we get if the callback is
389                                  * unhandled
390                                  */
391
392                                 if (n) {
393                                         ext++;
394                                         continue;
395                                 }
396
397                                 /* apply it */
398
399                                 if (ext_count)
400                                         *p++ = ',';
401                                 else
402                                         LWS_CPYAPP(p,
403                                          "\x0d\x0aSec-WebSocket-Extensions: ");
404                                 p += sprintf(p, "%s", ext_name);
405                                 ext_count++;
406
407                                 /* instantiate the extension on this conn */
408
409                                 wsi->active_extensions_user[
410                                         wsi->count_active_extensions] =
411                                              malloc(ext->per_session_data_size);
412                                 if (wsi->active_extensions_user[
413                                          wsi->count_active_extensions] == NULL) {
414                                         lwsl_err("Out of mem\n");
415                                         free(response);
416                                         goto bail;
417                                 }
418                                 memset(wsi->active_extensions_user[
419                                         wsi->count_active_extensions], 0,
420                                                     ext->per_session_data_size);
421
422                                 wsi->active_extensions[
423                                           wsi->count_active_extensions] = ext;
424
425                                 /* allow him to construct his context */
426
427                                 ext->callback(wsi->protocol->owning_server,
428                                                 ext, wsi,
429                                                 LWS_EXT_CALLBACK_CONSTRUCT,
430                                                 wsi->active_extensions_user[
431                                         wsi->count_active_extensions], NULL, 0);
432
433                                 wsi->count_active_extensions++;
434                                 lwsl_parser("wsi->count_active_extensions <- %d\n",
435                                                   wsi->count_active_extensions);
436
437                                 ext++;
438                         }
439
440                         n = 0;
441                 }
442         }
443
444         /* end of response packet */
445
446         LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
447
448         if (wsi->ietf_spec_revision == 4) {
449
450                 /*
451                  * precompute the masking key the client will use from the SHA1
452                  * hash of ( base 64 client key we were sent, concatenated with
453                  * the bse 64 nonce we sent, concatenated with a magic constant
454                  * guid specified by the 04 standard )
455                  *
456                  * We store the hash in the connection's wsi ready to use with
457                  * undoing the masking the client has done on framed data it
458                  * sends (we send our data to the client in clear).
459                  */
460
461                 strcpy(mask_summing_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
462                 m += wsi->utf8_token[WSI_TOKEN_KEY].token_len;
463                 strcpy(m, nonce_buf);
464                 m += nonce_len;
465                 strcpy(m, websocket_magic_guid_04_masking);
466                 m += strlen(websocket_magic_guid_04_masking);
467
468                 SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf,
469                                                            wsi->masking_key_04);
470         }
471
472         if (!lws_any_extension_handled(context, wsi,
473                         LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
474                                                      response, p - response)) {
475
476                 /* okay send the handshake response accepting the connection */
477
478                 lwsl_parser("issuing response packet %d len\n", (int)(p - response));
479         #ifdef DEBUG
480                 fwrite(response, 1,  p - response, stderr);
481         #endif
482                 n = libwebsocket_write(wsi, (unsigned char *)response,
483                                                   p - response, LWS_WRITE_HTTP);
484                 if (n < 0) {
485                         lwsl_debug("handshake_0405: ERROR writing to socket\n");
486                         goto bail;
487                 }
488
489         }
490
491         /* alright clean up and set ourselves into established state */
492
493         free(response);
494         wsi->state = WSI_STATE_ESTABLISHED;
495         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
496         wsi->rx_packet_length = 0;
497
498         /* notify user code that we're ready to roll */
499
500         if (wsi->protocol->callback)
501                 wsi->protocol->callback(wsi->protocol->owning_server,
502                                 wsi, LWS_CALLBACK_ESTABLISHED,
503                                           wsi->user_space, NULL, 0);
504
505         return 0;
506
507
508 bail:
509         return -1;
510 }
511
512
513 /*
514  * -04 of the protocol (actually the 80th version) has a radically different
515  * handshake.  The 04 spec gives the following idea
516  *
517  *    The handshake from the client looks as follows:
518  *
519  *      GET /chat HTTP/1.1
520  *      Host: server.example.com
521  *      Upgrade: websocket
522  *      Connection: Upgrade
523  *      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
524  *      Sec-WebSocket-Origin: http://example.com
525  *      Sec-WebSocket-Protocol: chat, superchat
526  *      Sec-WebSocket-Version: 4
527  *
528  *  The handshake from the server looks as follows:
529  *
530  *       HTTP/1.1 101 Switching Protocols
531  *       Upgrade: websocket
532  *       Connection: Upgrade
533  *       Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
534  *       Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
535  *       Sec-WebSocket-Protocol: chat
536  */
537
538 /*
539  * We have to take care about parsing because the headers may be split
540  * into multiple fragments.  They may contain unknown headers with arbitrary
541  * argument lengths.  So, we parse using a single-character at a time state
542  * machine that is completely independent of packet size.
543  */
544
545 int
546 libwebsocket_read(struct libwebsocket_context *context,
547                      struct libwebsocket *wsi, unsigned char * buf, size_t len)
548 {
549         size_t n;
550
551         switch (wsi->state) {
552         case WSI_STATE_HTTP_ISSUING_FILE:
553         case WSI_STATE_HTTP:
554                 wsi->state = WSI_STATE_HTTP_HEADERS;
555                 wsi->parser_state = WSI_TOKEN_NAME_PART;
556                 /* fallthru */
557         case WSI_STATE_HTTP_HEADERS:
558
559                 lwsl_parser("issuing %d bytes to parser\n", (int)len);
560 #ifdef _DEBUG
561                 //fwrite(buf, 1, len, stderr);
562 #endif
563
564                 switch (wsi->mode) {
565                 case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
566                 case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
567                 case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
568                 case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
569                 case LWS_CONNMODE_WS_CLIENT:
570                         for (n = 0; n < len; n++)
571                                 libwebsocket_client_rx_sm(wsi, *buf++);
572
573                         return 0;
574                 default:
575                         break;
576                 }
577
578                 /* LWS_CONNMODE_WS_SERVING */
579
580                 for (n = 0; n < len; n++)
581                         libwebsocket_parse(wsi, *buf++);
582
583                 if (wsi->parser_state != WSI_PARSING_COMPLETE)
584                         break;
585
586                 lwsl_parser("seem to be serving, mode is %d\n", wsi->mode);
587
588                 lwsl_parser("libwebsocket_parse sees parsing complete\n");
589
590                 /* is this websocket protocol or normal http 1.0? */
591
592                 if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
593                              !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
594                         wsi->state = WSI_STATE_HTTP;
595                         if (wsi->protocol->callback)
596                                 if (wsi->protocol->callback(context, wsi,
597                                                                 LWS_CALLBACK_HTTP, wsi->user_space,
598                                                                 wsi->utf8_token[WSI_TOKEN_GET_URI].token,
599                                                                 wsi->utf8_token[WSI_TOKEN_GET_URI].token_len))
600                                         goto bail;
601                         return 0;
602                 }
603
604                 if (!wsi->protocol)
605                         lwsl_err("NULL protocol at libwebsocket_read\n");
606
607                 /*
608                  * It's websocket
609                  *
610                  * Make sure user side is happy about protocol
611                  */
612
613                 while (wsi->protocol->callback) {
614
615                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
616                                 if (wsi->protocol->name == NULL)
617                                         break;
618                         } else
619                                 if (wsi->protocol->name && strcmp(
620                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
621                                                       wsi->protocol->name) == 0)
622                                         break;
623
624                         wsi->protocol++;
625                 }
626
627                 /* we didn't find a protocol he wanted? */
628
629                 if (wsi->protocol->callback == NULL) {
630                         if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
631                                 lwsl_err("[no protocol] "
632                                         "not supported (use NULL .name)\n");
633                         else
634                                 lwsl_err("Requested protocol %s "
635                                                 "not supported\n",
636                                      wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
637                         goto bail;
638                 }
639
640                 /*
641                  * find out which spec version the client is using
642                  * if this header is not given, we default to 00 (aka 76)
643                  */
644
645                 if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
646                         wsi->ietf_spec_revision =
647                                  atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);
648
649                 /*
650                  * Give the user code a chance to study the request and
651                  * have the opportunity to deny it
652                  */
653
654                 if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
655                                 LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
656                                                 &wsi->utf8_token[0], NULL, 0)) {
657                         lwsl_warn("User code denied connection\n");
658                         goto bail;
659                 }
660
661
662                 /*
663                  * Perform the handshake according to the protocol version the
664                  * client announced
665                  */
666
667                 switch (wsi->ietf_spec_revision) {
668                 case 0: /* applies to 76 and 00 */
669                         wsi->xor_mask = xor_no_mask;
670                         if (handshake_00(context, wsi))
671                                 goto bail;
672                         break;
673                 case 4: /* 04 */
674                         wsi->xor_mask = xor_mask_04;
675                         lwsl_parser("libwebsocket_parse calling handshake_04\n");
676                         if (handshake_0405(context, wsi))
677                                 goto bail;
678                         break;
679                 case 5:
680                 case 6:
681                 case 7:
682                 case 8:
683                 case 13:
684                         wsi->xor_mask = xor_mask_05;
685                         lwsl_parser("libwebsocket_parse calling handshake_04\n");
686                         if (handshake_0405(context, wsi))
687                                 goto bail;
688                         break;
689
690                 default:
691                         lwsl_warn("Unknown client spec version %d\n",
692                                                        wsi->ietf_spec_revision);
693                         goto bail;
694                 }
695
696                 wsi->mode = LWS_CONNMODE_WS_SERVING;
697
698                 lwsl_parser("accepted v%02d connection\n",
699                                                        wsi->ietf_spec_revision);
700
701                 break;
702
703         case WSI_STATE_AWAITING_CLOSE_ACK:
704         case WSI_STATE_ESTABLISHED:
705                 switch (wsi->mode) {
706                 case LWS_CONNMODE_WS_CLIENT:
707                         for (n = 0; n < len; n++)
708                                 if (libwebsocket_client_rx_sm(wsi, *buf++) < 0)
709                                         goto bail;
710
711                         return 0;
712                 default:
713                         break;
714                 }
715
716                 /* LWS_CONNMODE_WS_SERVING */
717
718                 if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
719                         goto bail;
720
721                 break;
722         default:
723                 lwsl_err("libwebsocket_read: Unhandled state\n");
724                 break;
725         }
726
727         return 0;
728
729 bail:
730         libwebsocket_close_and_free_session(context, wsi,
731                                                      LWS_CLOSE_STATUS_NOSTATUS);
732
733         return -1;
734 }