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