unionize mutually exclusive wsi members
[platform/upstream/libwebsockets.git] / lib / client-parser.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2013 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 #ifdef WIN32
25 #include <io.h>
26 #endif
27
28 int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
29 {
30         int n;
31         int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
32         int handled;
33         struct lws_tokens eff_buf;
34 #ifndef LWS_NO_EXTENSIONS
35         int m;
36 #endif
37
38 //      lwsl_parser(" CRX: %02X %d\n", c, wsi->lws_rx_parse_state);
39
40         switch (wsi->lws_rx_parse_state) {
41         case LWS_RXPS_NEW:
42
43                 switch (wsi->ietf_spec_revision) {
44
45                 case 13:
46                         wsi->u.ws.opcode = c & 0xf;
47                         wsi->u.ws.rsv = (c & 0x70);
48                         wsi->u.ws.final = !!((c >> 7) & 1);
49                         switch (wsi->u.ws.opcode) {
50                         case LWS_WS_OPCODE_07__TEXT_FRAME:
51                         case LWS_WS_OPCODE_07__BINARY_FRAME:
52                                 wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == LWS_WS_OPCODE_07__BINARY_FRAME;
53                                 break;
54                         }
55                         wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
56                         break;
57
58                 default:
59                         lwsl_err("client_rx_sm doesn't know how "
60                                 "to handle spec version %02d\n",
61                                                        wsi->ietf_spec_revision);
62                         break;
63                 }
64                 break;
65
66
67         case LWS_RXPS_04_FRAME_HDR_LEN:
68
69                 wsi->u.ws.this_frame_masked = !!(c & 0x80);
70
71                 switch (c & 0x7f) {
72                 case 126:
73                         /* control frames are not allowed to have big lengths */
74                         if (wsi->u.ws.opcode & 8)
75                                 goto illegal_ctl_length;
76                         wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
77                         break;
78                 case 127:
79                         /* control frames are not allowed to have big lengths */
80                         if (wsi->u.ws.opcode & 8)
81                                 goto illegal_ctl_length;
82                         wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
83                         break;
84                 default:
85                         wsi->u.ws.rx_packet_length = c;
86                         if (wsi->u.ws.this_frame_masked)
87                                 wsi->lws_rx_parse_state =
88                                                 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
89                         else {
90                                 if (c)
91                                         wsi->lws_rx_parse_state =
92                                         LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
93                                 else {
94                                         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
95                                         goto spill;
96                                 }
97                         }
98                         break;
99                 }
100                 break;
101
102         case LWS_RXPS_04_FRAME_HDR_LEN16_2:
103                 wsi->u.ws.rx_packet_length = c << 8;
104                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
105                 break;
106
107         case LWS_RXPS_04_FRAME_HDR_LEN16_1:
108                 wsi->u.ws.rx_packet_length |= c;
109                 if (wsi->u.ws.this_frame_masked)
110                         wsi->lws_rx_parse_state =
111                                         LWS_RXPS_07_COLLECT_FRAME_KEY_1;
112                 else {
113                         if (wsi->u.ws.rx_packet_length)
114                                 wsi->lws_rx_parse_state =
115                                         LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
116                         else {
117                                 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
118                                 goto spill;
119                         }
120                 }
121                 break;
122
123         case LWS_RXPS_04_FRAME_HDR_LEN64_8:
124                 if (c & 0x80) {
125                         lwsl_warn("b63 of length must be zero\n");
126                         /* kill the connection */
127                         return -1;
128                 }
129 #if defined __LP64__
130                 wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
131 #else
132                 wsi->u.ws.rx_packet_length = 0;
133 #endif
134                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
135                 break;
136
137         case LWS_RXPS_04_FRAME_HDR_LEN64_7:
138 #if defined __LP64__
139                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
140 #endif
141                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
142                 break;
143
144         case LWS_RXPS_04_FRAME_HDR_LEN64_6:
145 #if defined __LP64__
146                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
147 #endif
148                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
149                 break;
150
151         case LWS_RXPS_04_FRAME_HDR_LEN64_5:
152 #if defined __LP64__
153                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
154 #endif
155                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
156                 break;
157
158         case LWS_RXPS_04_FRAME_HDR_LEN64_4:
159                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
160                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
161                 break;
162
163         case LWS_RXPS_04_FRAME_HDR_LEN64_3:
164                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
165                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
166                 break;
167
168         case LWS_RXPS_04_FRAME_HDR_LEN64_2:
169                 wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
170                 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
171                 break;
172
173         case LWS_RXPS_04_FRAME_HDR_LEN64_1:
174                 wsi->u.ws.rx_packet_length |= (size_t)c;
175                 if (wsi->u.ws.this_frame_masked)
176                         wsi->lws_rx_parse_state =
177                                         LWS_RXPS_07_COLLECT_FRAME_KEY_1;
178                 else {
179                         if (wsi->u.ws.rx_packet_length)
180                                 wsi->lws_rx_parse_state =
181                                         LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
182                         else {
183                                 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
184                                 goto spill;
185                         }
186                 }
187                 break;
188
189         case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
190                 wsi->u.ws.frame_masking_nonce_04[0] = c;
191                 if (c)
192                         wsi->u.ws.all_zero_nonce = 0;
193                 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
194                 break;
195
196         case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
197                 wsi->u.ws.frame_masking_nonce_04[1] = c;
198                 if (c)
199                         wsi->u.ws.all_zero_nonce = 0;
200                 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
201                 break;
202
203         case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
204                 wsi->u.ws.frame_masking_nonce_04[2] = c;
205                 if (c)
206                         wsi->u.ws.all_zero_nonce = 0;
207                 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
208                 break;
209
210         case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
211                 wsi->u.ws.frame_masking_nonce_04[3] = c;
212                 if (c)
213                         wsi->u.ws.all_zero_nonce = 0;
214
215                 if (wsi->u.ws.rx_packet_length)
216                         wsi->lws_rx_parse_state =
217                                         LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
218                 else {
219                         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
220                         goto spill;
221                 }
222                 break;
223
224         case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
225                 if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
226                         wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
227                                (wsi->u.ws.rx_user_buffer_head++)] = c;
228                 else
229                         wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
230                                (wsi->u.ws.rx_user_buffer_head++)] =
231                         c ^ wsi->u.ws.frame_masking_nonce_04[(wsi->u.ws.frame_mask_index++) & 3];
232
233                 if (--wsi->u.ws.rx_packet_length == 0) {
234                         wsi->lws_rx_parse_state = LWS_RXPS_NEW;
235                         goto spill;
236                 }
237                 if (wsi->u.ws.rx_user_buffer_head != MAX_USER_RX_BUFFER)
238                         break;
239 spill:
240
241                 handled = 0;
242
243                 /*
244                  * is this frame a control packet we should take care of at this
245                  * layer?  If so service it and hide it from the user callback
246                  */
247
248                 switch (wsi->u.ws.opcode) {
249                 case LWS_WS_OPCODE_07__CLOSE:
250                         /* is this an acknowledgement of our close? */
251                         if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
252                                 /*
253                                  * fine he has told us he is closing too, let's
254                                  * finish our close
255                                  */
256                                 lwsl_parser("seen server's close ack\n");
257                                 return -1;
258                         }
259                         lwsl_parser("client sees server close packet len = %d\n", wsi->u.ws.rx_user_buffer_head);
260                         /* parrot the close packet payload back */
261                         n = libwebsocket_write(wsi, (unsigned char *)
262                            &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
263                                      wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
264                         lwsl_parser("client writing close ack returned %d\n", n);
265                         wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
266                         /* close the connection */
267                         return -1;
268
269                 case LWS_WS_OPCODE_07__PING:
270                         lwsl_info("client received ping, doing pong\n");
271                         /* parrot the ping packet payload back as a pong*/
272                         n = libwebsocket_write(wsi, (unsigned char *)
273                             &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
274                                     wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG);
275                         handled = 1;
276                         break;
277
278                 case LWS_WS_OPCODE_07__PONG:
279                         lwsl_info("client receied pong\n");
280                         lwsl_hexdump(&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
281                                     wsi->u.ws.rx_user_buffer_head);
282                         /* keep the statistics... */
283                         wsi->u.ws.pings_vs_pongs--;
284
285                         /* issue it */
286                         callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
287                         break;
288
289                 case LWS_WS_OPCODE_07__CONTINUATION:
290                 case LWS_WS_OPCODE_07__TEXT_FRAME:
291                 case LWS_WS_OPCODE_07__BINARY_FRAME:
292                         break;
293
294                 default:
295
296                         lwsl_parser("Reserved opcode 0x%2X\n", wsi->u.ws.opcode);
297 #ifndef LWS_NO_EXTENSIONS
298                         /*
299                          * It's something special we can't understand here.
300                          * Pass the payload up to the extension's parsing
301                          * state machine.
302                          */
303
304                         eff_buf.token = &wsi->u.ws.rx_user_buffer[
305                                                    LWS_SEND_BUFFER_PRE_PADDING];
306                         eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
307
308                         for (n = 0; n < wsi->count_active_extensions; n++) {
309                                 m = wsi->active_extensions[n]->callback(
310                                         wsi->protocol->owning_server,
311                                         wsi->active_extensions[n], wsi,
312                                         LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
313                                             wsi->active_extensions_user[n],
314                                                                    &eff_buf, 0);
315                                 if (m)
316                                         handled = 1;
317                         }
318
319                         if (!handled) {
320 #else
321                         {
322 #endif
323                                 lwsl_ext("Unhandled extended opcode "
324                                         "0x%x - ignoring frame\n", wsi->u.ws.opcode);
325                                 wsi->u.ws.rx_user_buffer_head = 0;
326
327                                 return 0;
328                         }
329
330                         break;
331                 }
332
333                 /*
334                  * No it's real payload, pass it up to the user callback.
335                  * It's nicely buffered with the pre-padding taken care of
336                  * so it can be sent straight out again using libwebsocket_write
337                  */
338                 if (handled)
339                         goto already_done;
340
341                 eff_buf.token = &wsi->u.ws.rx_user_buffer[
342                                                 LWS_SEND_BUFFER_PRE_PADDING];
343                 eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
344 #ifndef LWS_NO_EXTENSIONS
345                 for (n = 0; n < wsi->count_active_extensions; n++) {
346                         m = wsi->active_extensions[n]->callback(
347                                 wsi->protocol->owning_server,
348                                 wsi->active_extensions[n], wsi,
349                                 LWS_EXT_CALLBACK_PAYLOAD_RX,
350                                 wsi->active_extensions_user[n],
351                                 &eff_buf, 0);
352                         if (m < 0) {
353                                 lwsl_ext(
354                                         "Extension '%s' failed to handle payload!\n",
355                                                 wsi->active_extensions[n]->name);
356                                 return -1;
357                         }
358                 }
359 #endif
360                 if (eff_buf.token_len > 0) {
361                         eff_buf.token[eff_buf.token_len] = '\0';
362
363                         if (wsi->protocol->callback) {
364                                 if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
365                                         lwsl_info("Client doing pong callback\n");
366                                 wsi->protocol->callback(
367                                                 wsi->protocol->owning_server,
368                                                 wsi,
369                         (enum libwebsocket_callback_reasons)callback_action,
370                                                 wsi->user_space,
371                                                 eff_buf.token,
372                                                 eff_buf.token_len);
373                         }
374                 }
375 already_done:
376                 wsi->u.ws.rx_user_buffer_head = 0;
377                 break;
378         default:
379                 lwsl_err("client rx illegal state\n");
380                 return 1;
381         }
382
383         return 0;
384
385 illegal_ctl_length:
386
387         lwsl_warn("Control frame asking for "
388                                 "extended length is illegal\n");
389         /* kill the connection */
390         return -1;
391
392 }
393
394