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