1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26
27 static const unsigned char lextable_h1[] = {
28 #include "lextable.h"
29 };
30
31 #define FAIL_CHAR 0x08
32
33 #if defined(LWS_WITH_CUSTOM_HEADERS)
34
35 #define UHO_NLEN 0
36 #define UHO_VLEN 2
37 #define UHO_LL 4
38 #define UHO_NAME 8
39
40 #endif
41
42 static struct allocated_headers *
_lws_create_ah(struct lws_context_per_thread * pt,ah_data_idx_t data_size)43 _lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
44 {
45 struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
46
47 if (!ah)
48 return NULL;
49
50 ah->data = lws_malloc(data_size, "ah data");
51 if (!ah->data) {
52 lws_free(ah);
53
54 return NULL;
55 }
56 ah->next = pt->http.ah_list;
57 pt->http.ah_list = ah;
58 ah->data_length = data_size;
59 pt->http.ah_pool_length++;
60
61 lwsl_info("%s: created ah %p (size %d): pool length %u\n", __func__,
62 ah, (int)data_size, (unsigned int)pt->http.ah_pool_length);
63
64 return ah;
65 }
66
67 int
_lws_destroy_ah(struct lws_context_per_thread * pt,struct allocated_headers * ah)68 _lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
69 {
70 lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
71 if ((*a) == ah) {
72 *a = ah->next;
73 pt->http.ah_pool_length--;
74 lwsl_info("%s: freed ah %p : pool length %u\n",
75 __func__, ah,
76 (unsigned int)pt->http.ah_pool_length);
77 if (ah->data)
78 lws_free(ah->data);
79 lws_free(ah);
80
81 return 0;
82 }
83 } lws_end_foreach_llp(a, next);
84
85 return 1;
86 }
87
88 void
_lws_header_table_reset(struct allocated_headers * ah)89 _lws_header_table_reset(struct allocated_headers *ah)
90 {
91 /* init the ah to reflect no headers or data have appeared yet */
92 memset(ah->frag_index, 0, sizeof(ah->frag_index));
93 memset(ah->frags, 0, sizeof(ah->frags));
94 ah->nfrag = 0;
95 ah->pos = 0;
96 ah->http_response = 0;
97 ah->parser_state = WSI_TOKEN_NAME_PART;
98 ah->lextable_pos = 0;
99 #if defined(LWS_WITH_CUSTOM_HEADERS)
100 ah->unk_pos = 0;
101 ah->unk_ll_head = 0;
102 ah->unk_ll_tail = 0;
103 #endif
104 }
105
106 // doesn't scrub the ah rxbuffer by default, parent must do if needed
107
108 void
__lws_header_table_reset(struct lws * wsi,int autoservice)109 __lws_header_table_reset(struct lws *wsi, int autoservice)
110 {
111 struct allocated_headers *ah = wsi->http.ah;
112 struct lws_context_per_thread *pt;
113 struct lws_pollfd *pfd;
114
115 /* if we have the idea we're resetting 'our' ah, must be bound to one */
116 assert(ah);
117 /* ah also concurs with ownership */
118 assert(ah->wsi == wsi);
119
120 _lws_header_table_reset(ah);
121
122 /* since we will restart the ah, our new headers are not completed */
123 wsi->hdr_parsing_completed = 0;
124
125 /* while we hold the ah, keep a timeout on the wsi */
126 __lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
127 wsi->vhost->timeout_secs_ah_idle);
128
129 time(&ah->assigned);
130
131 if (wsi->position_in_fds_table != LWS_NO_FDS_POS &&
132 lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
133 autoservice) {
134 lwsl_debug("%s: service on readbuf ah\n", __func__);
135
136 pt = &wsi->context->pt[(int)wsi->tsi];
137 /*
138 * Unlike a normal connect, we have the headers already
139 * (or the first part of them anyway)
140 */
141 pfd = &pt->fds[wsi->position_in_fds_table];
142 pfd->revents |= LWS_POLLIN;
143 lwsl_err("%s: calling service\n", __func__);
144 lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
145 }
146 }
147
148 void
lws_header_table_reset(struct lws * wsi,int autoservice)149 lws_header_table_reset(struct lws *wsi, int autoservice)
150 {
151 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
152
153 lws_pt_lock(pt, __func__);
154
155 __lws_header_table_reset(wsi, autoservice);
156
157 lws_pt_unlock(pt);
158 }
159
160 static void
_lws_header_ensure_we_are_on_waiting_list(struct lws * wsi)161 _lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
162 {
163 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
164 struct lws_pollargs pa;
165 struct lws **pwsi = &pt->http.ah_wait_list;
166
167 while (*pwsi) {
168 if (*pwsi == wsi)
169 return;
170 pwsi = &(*pwsi)->http.ah_wait_list;
171 }
172
173 lwsl_info("%s: wsi: %p\n", __func__, wsi);
174 wsi->http.ah_wait_list = pt->http.ah_wait_list;
175 pt->http.ah_wait_list = wsi;
176 pt->http.ah_wait_list_length++;
177
178 /* we cannot accept input then */
179
180 _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
181 }
182
183 static int
__lws_remove_from_ah_waiting_list(struct lws * wsi)184 __lws_remove_from_ah_waiting_list(struct lws *wsi)
185 {
186 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
187 struct lws **pwsi =&pt->http.ah_wait_list;
188
189 while (*pwsi) {
190 if (*pwsi == wsi) {
191 lwsl_info("%s: wsi %p\n", __func__, wsi);
192 /* point prev guy to our next */
193 *pwsi = wsi->http.ah_wait_list;
194 /* we shouldn't point anywhere now */
195 wsi->http.ah_wait_list = NULL;
196 pt->http.ah_wait_list_length--;
197
198 return 1;
199 }
200 pwsi = &(*pwsi)->http.ah_wait_list;
201 }
202
203 return 0;
204 }
205
206 int LWS_WARN_UNUSED_RESULT
lws_header_table_attach(struct lws * wsi,int autoservice)207 lws_header_table_attach(struct lws *wsi, int autoservice)
208 {
209 struct lws_context *context = wsi->context;
210 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
211 struct lws_pollargs pa;
212 int n;
213
214 #if defined(LWS_ROLE_MQTT) && defined(LWS_WITH_CLIENT)
215 if (lwsi_role_mqtt(wsi))
216 goto connect_via_info2;
217 #endif
218
219 lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
220 (void *)wsi, (void *)wsi->http.ah, wsi->tsi,
221 pt->http.ah_count_in_use);
222
223 if (!lwsi_role_http(wsi)) {
224 lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name);
225 assert(0);
226 return -1;
227 }
228
229 lws_pt_lock(pt, __func__);
230
231 /* if we are already bound to one, just clear it down */
232 if (wsi->http.ah) {
233 lwsl_info("%s: cleardown\n", __func__);
234 goto reset;
235 }
236
237 n = pt->http.ah_count_in_use == context->max_http_header_pool;
238 #if defined(LWS_WITH_PEER_LIMITS)
239 if (!n) {
240 n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
241 if (n)
242 lws_stats_bump(pt, LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
243 }
244 #endif
245 if (n) {
246 /*
247 * Pool is either all busy, or we don't want to give this
248 * particular guy an ah right now...
249 *
250 * Make sure we are on the waiting list, and return that we
251 * weren't able to provide the ah
252 */
253 _lws_header_ensure_we_are_on_waiting_list(wsi);
254
255 goto bail;
256 }
257
258 __lws_remove_from_ah_waiting_list(wsi);
259
260 wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
261 if (!wsi->http.ah) { /* we could not create an ah */
262 _lws_header_ensure_we_are_on_waiting_list(wsi);
263
264 goto bail;
265 }
266
267 wsi->http.ah->in_use = 1;
268 wsi->http.ah->wsi = wsi; /* mark our owner */
269 pt->http.ah_count_in_use++;
270
271 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
272 defined(LWS_ROLE_H2))
273 lws_context_lock(context, "ah attach"); /* <========================= */
274 if (wsi->peer)
275 wsi->peer->http.count_ah++;
276 lws_context_unlock(context); /* ====================================> */
277 #endif
278
279 _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
280
281 lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
282 (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use);
283
284 reset:
285 __lws_header_table_reset(wsi, autoservice);
286
287 lws_pt_unlock(pt);
288
289 #if defined(LWS_WITH_CLIENT)
290 #if defined(LWS_ROLE_MQTT)
291 connect_via_info2:
292 #endif
293 if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
294 if (!lws_http_client_connect_via_info2(wsi))
295 /* our client connect has failed, the wsi
296 * has been closed
297 */
298 return -1;
299 #endif
300
301 return 0;
302
303 bail:
304 lws_pt_unlock(pt);
305
306 return 1;
307 }
308
__lws_header_table_detach(struct lws * wsi,int autoservice)309 int __lws_header_table_detach(struct lws *wsi, int autoservice)
310 {
311 struct lws_context *context = wsi->context;
312 struct allocated_headers *ah = wsi->http.ah;
313 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
314 struct lws_pollargs pa;
315 struct lws **pwsi, **pwsi_eligible;
316 time_t now;
317
318 __lws_remove_from_ah_waiting_list(wsi);
319
320 if (!ah)
321 return 0;
322
323 lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
324 (void *)wsi, (void *)ah, wsi->tsi,
325 pt->http.ah_count_in_use);
326
327 /* we did have an ah attached */
328 time(&now);
329 if (ah->assigned && now - ah->assigned > 3) {
330 /*
331 * we're detaching the ah, but it was held an
332 * unreasonably long time
333 */
334 lwsl_debug("%s: wsi %p: ah held %ds, role/state 0x%lx 0x%x,"
335 "\n", __func__, wsi, (int)(now - ah->assigned),
336 (unsigned long)lwsi_role(wsi), lwsi_state(wsi));
337 }
338
339 ah->assigned = 0;
340
341 /* if we think we're detaching one, there should be one in use */
342 assert(pt->http.ah_count_in_use > 0);
343 /* and this specific one should have been in use */
344 assert(ah->in_use);
345 memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
346
347 #if defined(LWS_WITH_PEER_LIMITS)
348 if (ah->wsi)
349 lws_peer_track_ah_detach(context, wsi->peer);
350 #endif
351 ah->wsi = NULL; /* no owner */
352 wsi->http.ah = NULL;
353
354 pwsi = &pt->http.ah_wait_list;
355
356 /* oh there is nobody on the waiting list... leave the ah unattached */
357 if (!*pwsi)
358 goto nobody_usable_waiting;
359
360 /*
361 * at least one wsi on the same tsi is waiting, give it to oldest guy
362 * who is allowed to take it (if any)
363 */
364 lwsl_info("pt wait list %p\n", *pwsi);
365 wsi = NULL;
366 pwsi_eligible = NULL;
367
368 while (*pwsi) {
369 #if defined(LWS_WITH_PEER_LIMITS)
370 /* are we willing to give this guy an ah? */
371 if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
372 #endif
373 {
374 wsi = *pwsi;
375 pwsi_eligible = pwsi;
376 }
377 #if defined(LWS_WITH_PEER_LIMITS)
378 else
379 if (!(*pwsi)->http.ah_wait_list)
380 lws_stats_bump(pt,
381 LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
382 #endif
383 pwsi = &(*pwsi)->http.ah_wait_list;
384 }
385
386 if (!wsi) /* everybody waiting already has too many ah... */
387 goto nobody_usable_waiting;
388
389 lwsl_info("%s: transferring ah to last eligible wsi in wait list "
390 "%p (wsistate 0x%lx)\n", __func__, wsi,
391 (unsigned long)wsi->wsistate);
392
393 wsi->http.ah = ah;
394 ah->wsi = wsi; /* new owner */
395
396 __lws_header_table_reset(wsi, autoservice);
397 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
398 defined(LWS_ROLE_H2))
399 lws_context_lock(context, "ah detach"); /* <========================= */
400 if (wsi->peer)
401 wsi->peer->http.count_ah++;
402 lws_context_unlock(context); /* ====================================> */
403 #endif
404
405 /* clients acquire the ah and then insert themselves in fds table... */
406 if (wsi->position_in_fds_table != LWS_NO_FDS_POS) {
407 lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
408
409 /* he has been stuck waiting for an ah, but now his wait is
410 * over, let him progress */
411
412 _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
413 }
414
415 /* point prev guy to next guy in list instead */
416 *pwsi_eligible = wsi->http.ah_wait_list;
417 /* the guy who got one is out of the list */
418 wsi->http.ah_wait_list = NULL;
419 pt->http.ah_wait_list_length--;
420
421 #if defined(LWS_WITH_CLIENT)
422 if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
423 lws_pt_unlock(pt);
424
425 if (!lws_http_client_connect_via_info2(wsi)) {
426 /* our client connect has failed, the wsi
427 * has been closed
428 */
429
430 return -1;
431 }
432 return 0;
433 }
434 #endif
435
436 assert(!!pt->http.ah_wait_list_length ==
437 !!(lws_intptr_t)pt->http.ah_wait_list);
438 bail:
439 lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
440 (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use);
441
442 return 0;
443
444 nobody_usable_waiting:
445 lwsl_info("%s: nobody usable waiting\n", __func__);
446 _lws_destroy_ah(pt, ah);
447 pt->http.ah_count_in_use--;
448
449 goto bail;
450 }
451
lws_header_table_detach(struct lws * wsi,int autoservice)452 int lws_header_table_detach(struct lws *wsi, int autoservice)
453 {
454 struct lws_context *context = wsi->context;
455 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
456 int n;
457
458 lws_pt_lock(pt, __func__);
459 n = __lws_header_table_detach(wsi, autoservice);
460 lws_pt_unlock(pt);
461
462 return n;
463 }
464
465 int
lws_hdr_fragment_length(struct lws * wsi,enum lws_token_indexes h,int frag_idx)466 lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
467 {
468 int n;
469
470 if (!wsi->http.ah)
471 return 0;
472
473 n = wsi->http.ah->frag_index[h];
474 if (!n)
475 return 0;
476 do {
477 if (!frag_idx)
478 return wsi->http.ah->frags[n].len;
479 n = wsi->http.ah->frags[n].nfrag;
480 } while (frag_idx-- && n);
481
482 return 0;
483 }
484
lws_hdr_total_length(struct lws * wsi,enum lws_token_indexes h)485 int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
486 {
487 int n;
488 int len = 0;
489
490 if (!wsi->http.ah)
491 return 0;
492
493 n = wsi->http.ah->frag_index[h];
494 if (!n)
495 return 0;
496 do {
497 len += wsi->http.ah->frags[n].len;
498 n = wsi->http.ah->frags[n].nfrag;
499
500 if (n && h != WSI_TOKEN_HTTP_COOKIE)
501 ++len;
502
503 } while (n);
504
505 return len;
506 }
507
lws_hdr_copy_fragment(struct lws * wsi,char * dst,int len,enum lws_token_indexes h,int frag_idx)508 int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
509 enum lws_token_indexes h, int frag_idx)
510 {
511 int n = 0;
512 int f;
513
514 if (!wsi->http.ah)
515 return -1;
516
517 f = wsi->http.ah->frag_index[h];
518
519 if (!f)
520 return -1;
521
522 while (n < frag_idx) {
523 f = wsi->http.ah->frags[f].nfrag;
524 if (!f)
525 return -1;
526 n++;
527 }
528
529 if (wsi->http.ah->frags[f].len >= len)
530 return -1;
531
532 memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
533 wsi->http.ah->frags[f].len);
534 dst[wsi->http.ah->frags[f].len] = '\0';
535
536 return wsi->http.ah->frags[f].len;
537 }
538
lws_hdr_copy(struct lws * wsi,char * dst,int len,enum lws_token_indexes h)539 int lws_hdr_copy(struct lws *wsi, char *dst, int len,
540 enum lws_token_indexes h)
541 {
542 int toklen = lws_hdr_total_length(wsi, h);
543 int n;
544 int comma;
545
546 *dst = '\0';
547 if (!toklen)
548 return 0;
549
550 if (toklen >= len)
551 return -1;
552
553 if (!wsi->http.ah)
554 return -1;
555
556 n = wsi->http.ah->frag_index[h];
557 if (!n)
558 return 0;
559
560 do {
561 comma = (wsi->http.ah->frags[n].nfrag &&
562 h != WSI_TOKEN_HTTP_COOKIE) ? 1 : 0;
563
564 if (wsi->http.ah->frags[n].len + comma >= len)
565 return -1;
566 strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
567 wsi->http.ah->frags[n].len);
568 dst += wsi->http.ah->frags[n].len;
569 len -= wsi->http.ah->frags[n].len;
570 n = wsi->http.ah->frags[n].nfrag;
571
572 if (comma)
573 *dst++ = ',';
574
575 } while (n);
576 *dst = '\0';
577
578 return toklen;
579 }
580
581 #if defined(LWS_WITH_CUSTOM_HEADERS)
582 int
lws_hdr_custom_length(struct lws * wsi,const char * name,int nlen)583 lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen)
584 {
585 ah_data_idx_t ll;
586
587 if (!wsi->http.ah || wsi->mux_substream)
588 return -1;
589
590 ll = wsi->http.ah->unk_ll_head;
591 while (ll) {
592 if (ll >= wsi->http.ah->data_length)
593 return -1;
594 if (nlen == lws_ser_ru16be(
595 (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
596 !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen))
597 return lws_ser_ru16be(
598 (uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
599
600 ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
601 }
602
603 return -1;
604 }
605
606 int
lws_hdr_custom_copy(struct lws * wsi,char * dst,int len,const char * name,int nlen)607 lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
608 int nlen)
609 {
610 ah_data_idx_t ll;
611 int n;
612
613 if (!wsi->http.ah || wsi->mux_substream)
614 return -1;
615
616 *dst = '\0';
617
618 ll = wsi->http.ah->unk_ll_head;
619 while (ll) {
620 if (ll >= wsi->http.ah->data_length)
621 return -1;
622 if (nlen == lws_ser_ru16be(
623 (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
624 !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen)) {
625 n = lws_ser_ru16be(
626 (uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
627 if (n + 1 > len)
628 return -1;
629 strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + nlen], n);
630 dst[n] = '\0';
631
632 return n;
633 }
634 ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
635 }
636
637 return -1;
638 }
639 #endif
640
lws_hdr_simple_ptr(struct lws * wsi,enum lws_token_indexes h)641 char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
642 {
643 int n;
644
645 if (!wsi->http.ah)
646 return NULL;
647
648 n = wsi->http.ah->frag_index[h];
649 if (!n)
650 return NULL;
651
652 return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
653 }
654
655 static int LWS_WARN_UNUSED_RESULT
lws_pos_in_bounds(struct lws * wsi)656 lws_pos_in_bounds(struct lws *wsi)
657 {
658 if (!wsi->http.ah)
659 return -1;
660
661 if (wsi->http.ah->pos <
662 (unsigned int)wsi->context->max_http_header_data)
663 return 0;
664
665 if ((int)wsi->http.ah->pos >= wsi->context->max_http_header_data - 1) {
666 lwsl_err("Ran out of header data space\n");
667 return 1;
668 }
669
670 /*
671 * with these tests everywhere, it should never be able to exceed
672 * the limit, only meet it
673 */
674 lwsl_err("%s: pos %ld, limit %ld\n", __func__,
675 (unsigned long)wsi->http.ah->pos,
676 (unsigned long)wsi->context->max_http_header_data);
677 assert(0);
678
679 return 1;
680 }
681
682 int LWS_WARN_UNUSED_RESULT
lws_hdr_simple_create(struct lws * wsi,enum lws_token_indexes h,const char * s)683 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
684 {
685 if (!*s) {
686 /*
687 * If we get an empty string, then remove any entry for the
688 * header
689 */
690 wsi->http.ah->frag_index[h] = 0;
691
692 return 0;
693 }
694
695 wsi->http.ah->nfrag++;
696 if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) {
697 lwsl_warn("More hdr frags than we can deal with, dropping\n");
698 return -1;
699 }
700
701 wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
702
703 wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
704 wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
705 wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
706
707 do {
708 if (lws_pos_in_bounds(wsi))
709 return -1;
710
711 wsi->http.ah->data[wsi->http.ah->pos++] = *s;
712 if (*s)
713 wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
714 } while (*s++);
715
716 return 0;
717 }
718
719 static int LWS_WARN_UNUSED_RESULT
issue_char(struct lws * wsi,unsigned char c)720 issue_char(struct lws *wsi, unsigned char c)
721 {
722 unsigned short frag_len;
723
724 if (lws_pos_in_bounds(wsi))
725 return -1;
726
727 frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
728 /*
729 * If we haven't hit the token limit, just copy the character into
730 * the header
731 */
732 if (!wsi->http.ah->current_token_limit ||
733 frag_len < wsi->http.ah->current_token_limit) {
734 wsi->http.ah->data[wsi->http.ah->pos++] = c;
735 if (c)
736 wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
737 return 0;
738 }
739
740 /* Insert a null character when we *hit* the limit: */
741 if (frag_len == wsi->http.ah->current_token_limit) {
742 if (lws_pos_in_bounds(wsi))
743 return -1;
744
745 wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
746 lwsl_warn("header %li exceeds limit %ld\n",
747 (long)wsi->http.ah->parser_state,
748 (long)wsi->http.ah->current_token_limit);
749 }
750
751 return 1;
752 }
753
754 int
lws_parse_urldecode(struct lws * wsi,uint8_t * _c)755 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
756 {
757 struct allocated_headers *ah = wsi->http.ah;
758 unsigned int enc = 0;
759 uint8_t c = *_c;
760
761 // lwsl_notice("ah->ups %d\n", ah->ups);
762
763 /*
764 * PRIORITY 1
765 * special URI processing... convert %xx
766 */
767 switch (ah->ues) {
768 case URIES_IDLE:
769 if (c == '%') {
770 ah->ues = URIES_SEEN_PERCENT;
771 goto swallow;
772 }
773 break;
774 case URIES_SEEN_PERCENT:
775 if (char_to_hex(c) < 0)
776 /* illegal post-% char */
777 goto forbid;
778
779 ah->esc_stash = c;
780 ah->ues = URIES_SEEN_PERCENT_H1;
781 goto swallow;
782
783 case URIES_SEEN_PERCENT_H1:
784 if (char_to_hex(c) < 0)
785 /* illegal post-% char */
786 goto forbid;
787
788 *_c = (char_to_hex(ah->esc_stash) << 4) |
789 char_to_hex(c);
790 c = *_c;
791 enc = 1;
792 ah->ues = URIES_IDLE;
793 break;
794 }
795
796 /*
797 * PRIORITY 2
798 * special URI processing...
799 * convert /.. or /... or /../ etc to /
800 * convert /./ to /
801 * convert // or /// etc to /
802 * leave /.dir or whatever alone
803 */
804
805 switch (ah->ups) {
806 case URIPS_IDLE:
807 if (!c)
808 return -1;
809 /* genuine delimiter */
810 if ((c == '&' || c == ';') && !enc) {
811 if (issue_char(wsi, '\0') < 0)
812 return -1;
813 /* link to next fragment */
814 ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
815 ah->nfrag++;
816 if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
817 goto excessive;
818 /* start next fragment after the & */
819 ah->post_literal_equal = 0;
820 ah->frags[ah->nfrag].offset = ++ah->pos;
821 ah->frags[ah->nfrag].len = 0;
822 ah->frags[ah->nfrag].nfrag = 0;
823 goto swallow;
824 }
825 /* uriencoded = in the name part, disallow */
826 if (c == '=' && enc &&
827 ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
828 !ah->post_literal_equal) {
829 c = '_';
830 *_c =c;
831 }
832
833 /* after the real =, we don't care how many = */
834 if (c == '=' && !enc)
835 ah->post_literal_equal = 1;
836
837 /* + to space */
838 if (c == '+' && !enc) {
839 c = ' ';
840 *_c = c;
841 }
842 /* issue the first / always */
843 if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
844 ah->ups = URIPS_SEEN_SLASH;
845 break;
846 case URIPS_SEEN_SLASH:
847 /* swallow subsequent slashes */
848 if (c == '/')
849 goto swallow;
850 /* track and swallow the first . after / */
851 if (c == '.') {
852 ah->ups = URIPS_SEEN_SLASH_DOT;
853 goto swallow;
854 }
855 ah->ups = URIPS_IDLE;
856 break;
857 case URIPS_SEEN_SLASH_DOT:
858 /* swallow second . */
859 if (c == '.') {
860 ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
861 goto swallow;
862 }
863 /* change /./ to / */
864 if (c == '/') {
865 ah->ups = URIPS_SEEN_SLASH;
866 goto swallow;
867 }
868 /* it was like /.dir ... regurgitate the . */
869 ah->ups = URIPS_IDLE;
870 if (issue_char(wsi, '.') < 0)
871 return -1;
872 break;
873
874 case URIPS_SEEN_SLASH_DOT_DOT:
875
876 /* /../ or /..[End of URI] --> backup to last / */
877 if (c == '/' || c == '?') {
878 /*
879 * back up one dir level if possible
880 * safe against header fragmentation because
881 * the method URI can only be in 1 fragment
882 */
883 if (ah->frags[ah->nfrag].len > 2) {
884 ah->pos--;
885 ah->frags[ah->nfrag].len--;
886 do {
887 ah->pos--;
888 ah->frags[ah->nfrag].len--;
889 } while (ah->frags[ah->nfrag].len > 1 &&
890 ah->data[ah->pos] != '/');
891 }
892 ah->ups = URIPS_SEEN_SLASH;
893 if (ah->frags[ah->nfrag].len > 1)
894 break;
895 goto swallow;
896 }
897
898 /* /..[^/] ... regurgitate and allow */
899
900 if (issue_char(wsi, '.') < 0)
901 return -1;
902 if (issue_char(wsi, '.') < 0)
903 return -1;
904 ah->ups = URIPS_IDLE;
905 break;
906 }
907
908 if (c == '?' && !enc &&
909 !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
910 if (ah->ues != URIES_IDLE)
911 goto forbid;
912
913 /* seal off uri header */
914 if (issue_char(wsi, '\0') < 0)
915 return -1;
916
917 /* move to using WSI_TOKEN_HTTP_URI_ARGS */
918 ah->nfrag++;
919 if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
920 goto excessive;
921 ah->frags[ah->nfrag].offset = ++ah->pos;
922 ah->frags[ah->nfrag].len = 0;
923 ah->frags[ah->nfrag].nfrag = 0;
924
925 ah->post_literal_equal = 0;
926 ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
927 ah->ups = URIPS_IDLE;
928 goto swallow;
929 }
930
931 return LPUR_CONTINUE;
932
933 swallow:
934 return LPUR_SWALLOW;
935
936 forbid:
937 return LPUR_FORBID;
938
939 excessive:
940 return LPUR_EXCESSIVE;
941 }
942
943 static const unsigned char methods[] = {
944 WSI_TOKEN_GET_URI,
945 WSI_TOKEN_POST_URI,
946 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
947 WSI_TOKEN_OPTIONS_URI,
948 WSI_TOKEN_PUT_URI,
949 WSI_TOKEN_PATCH_URI,
950 WSI_TOKEN_DELETE_URI,
951 #endif
952 WSI_TOKEN_CONNECT,
953 WSI_TOKEN_HEAD_URI,
954 };
955
956 /*
957 * possible returns:, -1 fail, 0 ok or 2, transition to raw
958 */
959
960 lws_parser_return_t LWS_WARN_UNUSED_RESULT
lws_parse(struct lws * wsi,unsigned char * buf,int * len)961 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
962 {
963 struct allocated_headers *ah = wsi->http.ah;
964 struct lws_context *context = wsi->context;
965 unsigned int n, m;
966 unsigned char c;
967 int r, pos;
968
969 assert(wsi->http.ah);
970
971 do {
972 (*len)--;
973 c = *buf++;
974
975 switch (ah->parser_state) {
976 #if defined(LWS_WITH_CUSTOM_HEADERS)
977 case WSI_TOKEN_UNKNOWN_VALUE_PART:
978
979 if (c == '\r')
980 break;
981 if (c == '\n') {
982 lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos + 2],
983 ah->pos - ah->unk_value_pos);
984 ah->parser_state = WSI_TOKEN_NAME_PART;
985 ah->unk_pos = 0;
986 ah->lextable_pos = 0;
987 break;
988 }
989
990 /* trim leading whitespace */
991 if (ah->pos != ah->unk_value_pos ||
992 (c != ' ' && c != '\t')) {
993
994 if (lws_pos_in_bounds(wsi))
995 return LPR_FAIL;
996
997 ah->data[ah->pos++] = c;
998 }
999 pos = ah->lextable_pos;
1000 break;
1001 #endif
1002 default:
1003
1004 lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
1005
1006 /* collect into malloc'd buffers */
1007 /* optional initial space swallow */
1008 if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
1009 c == ' ')
1010 break;
1011
1012 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1013 if (ah->parser_state == methods[m])
1014 break;
1015 if (m == LWS_ARRAY_SIZE(methods))
1016 /* it was not any of the methods */
1017 goto check_eol;
1018
1019 /* special URI processing... end at space */
1020
1021 if (c == ' ') {
1022 /* enforce starting with / */
1023 if (!ah->frags[ah->nfrag].len)
1024 if (issue_char(wsi, '/') < 0)
1025 return LPR_FAIL;
1026
1027 if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
1028 /*
1029 * back up one dir level if possible
1030 * safe against header fragmentation
1031 * because the method URI can only be
1032 * in 1 fragment
1033 */
1034 if (ah->frags[ah->nfrag].len > 2) {
1035 ah->pos--;
1036 ah->frags[ah->nfrag].len--;
1037 do {
1038 ah->pos--;
1039 ah->frags[ah->nfrag].len--;
1040 } while (ah->frags[ah->nfrag].len > 1 &&
1041 ah->data[ah->pos] != '/');
1042 }
1043 }
1044
1045 /* begin parsing HTTP version: */
1046 if (issue_char(wsi, '\0') < 0)
1047 return LPR_FAIL;
1048 ah->parser_state = WSI_TOKEN_HTTP;
1049 goto start_fragment;
1050 }
1051
1052 r = lws_parse_urldecode(wsi, &c);
1053 switch (r) {
1054 case LPUR_CONTINUE:
1055 break;
1056 case LPUR_SWALLOW:
1057 goto swallow;
1058 case LPUR_FORBID:
1059 goto forbid;
1060 case LPUR_EXCESSIVE:
1061 goto excessive;
1062 default:
1063 return LPR_FAIL;
1064 }
1065 check_eol:
1066 /* bail at EOL */
1067 if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
1068 c == '\x0d') {
1069 if (ah->ues != URIES_IDLE)
1070 goto forbid;
1071
1072 c = '\0';
1073 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1074 lwsl_parser("*\n");
1075 }
1076
1077 n = issue_char(wsi, c);
1078 if ((int)n < 0)
1079 return LPR_FAIL;
1080 if (n > 0)
1081 ah->parser_state = WSI_TOKEN_SKIPPING;
1082
1083 swallow:
1084 /* per-protocol end of headers management */
1085
1086 if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1087 goto set_parsing_complete;
1088 break;
1089
1090 /* collecting and checking a name part */
1091 case WSI_TOKEN_NAME_PART:
1092 lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X "
1093 "(role=0x%lx) "
1094 "wsi->lextable_pos=%d\n", c, c,
1095 (unsigned long)lwsi_role(wsi),
1096 ah->lextable_pos);
1097
1098 if (c >= 'A' && c <= 'Z')
1099 c += 'a' - 'A';
1100
1101 #if defined(LWS_WITH_CUSTOM_HEADERS)
1102
1103 /*
1104 * ...in case it's an unknown header, speculatively
1105 * store it as the name comes in. If we recognize it as
1106 * a known header, we'll snip this.
1107 */
1108
1109 if (!wsi->mux_substream && !ah->unk_pos) {
1110 ah->unk_pos = ah->pos;
1111 /*
1112 * Prepare new unknown header linked-list entry
1113 *
1114 * - 16-bit BE: name part length
1115 * - 16-bit BE: value part length
1116 * - 32-bit BE: data offset of next, or 0
1117 */
1118 for (n = 0; n < 8; n++)
1119 if (!lws_pos_in_bounds(wsi))
1120 ah->data[ah->pos++] = 0;
1121 }
1122 #endif
1123
1124 if (lws_pos_in_bounds(wsi))
1125 return LPR_FAIL;
1126
1127 ah->data[ah->pos++] = c;
1128 pos = ah->lextable_pos;
1129
1130 #if defined(LWS_WITH_CUSTOM_HEADERS)
1131 if (!wsi->mux_substream && pos < 0 && c == ':') {
1132 #if defined(_DEBUG)
1133 char dotstar[64];
1134 int uhlen;
1135 #endif
1136
1137 /*
1138 * process unknown headers
1139 *
1140 * register us in the unknown hdr ll
1141 */
1142
1143 if (!ah->unk_ll_head)
1144 ah->unk_ll_head = ah->unk_pos;
1145
1146 if (ah->unk_ll_tail)
1147 lws_ser_wu32be(
1148 (uint8_t *)&ah->data[ah->unk_ll_tail + UHO_LL],
1149 ah->unk_pos);
1150
1151 ah->unk_ll_tail = ah->unk_pos;
1152
1153 #if defined(_DEBUG)
1154 uhlen = ah->pos - (ah->unk_pos + UHO_NAME);
1155 lws_strnncpy(dotstar,
1156 &ah->data[ah->unk_pos + UHO_NAME],
1157 uhlen, sizeof(dotstar));
1158 lwsl_debug("%s: unk header %d '%s'\n",
1159 __func__,
1160 ah->pos - (ah->unk_pos + UHO_NAME),
1161 dotstar);
1162 #endif
1163
1164 /* set the unknown header name part length */
1165
1166 lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos],
1167 (ah->pos - ah->unk_pos) - UHO_NAME);
1168
1169 ah->unk_value_pos = ah->pos;
1170
1171 /*
1172 * collect whatever's coming for the unknown header
1173 * argument until the next CRLF
1174 */
1175 ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART;
1176 break;
1177 }
1178 #endif
1179 if (pos < 0)
1180 break;
1181
1182 while (1) {
1183 if (lextable_h1[pos] & (1 << 7)) {
1184 /* 1-byte, fail on mismatch */
1185 if ((lextable_h1[pos] & 0x7f) != c) {
1186 nope:
1187 ah->lextable_pos = -1;
1188 break;
1189 }
1190 /* fall thru */
1191 pos++;
1192 if (lextable_h1[pos] == FAIL_CHAR)
1193 goto nope;
1194
1195 ah->lextable_pos = pos;
1196 break;
1197 }
1198
1199 if (lextable_h1[pos] == FAIL_CHAR)
1200 goto nope;
1201
1202 /* b7 = 0, end or 3-byte */
1203 if (lextable_h1[pos] < FAIL_CHAR) {
1204 #if defined(LWS_WITH_CUSTOM_HEADERS)
1205 if (!wsi->mux_substream) {
1206 /*
1207 * We hit a terminal marker, so
1208 * we recognized this header...
1209 * drop the speculative name
1210 * part storage
1211 */
1212 ah->pos = ah->unk_pos;
1213 ah->unk_pos = 0;
1214 }
1215 #endif
1216 ah->lextable_pos = pos;
1217 break;
1218 }
1219
1220 if (lextable_h1[pos] == c) { /* goto */
1221 ah->lextable_pos = pos +
1222 (lextable_h1[pos + 1]) +
1223 (lextable_h1[pos + 2] << 8);
1224 break;
1225 }
1226
1227 /* fall thru goto */
1228 pos += 3;
1229 /* continue */
1230 }
1231
1232 /*
1233 * If it's h1, server needs to be on the look out for
1234 * unknown methods...
1235 */
1236 if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
1237 lwsi_role_server(wsi)) {
1238 /*
1239 * this is not a header we know about... did
1240 * we get a valid method (GET, POST etc)
1241 * already, or is this the bogus method?
1242 */
1243 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1244 if (ah->frag_index[methods[m]]) {
1245 /*
1246 * already had the method
1247 */
1248 #if !defined(LWS_WITH_CUSTOM_HEADERS)
1249 ah->parser_state = WSI_TOKEN_SKIPPING;
1250 #endif
1251 if (wsi->mux_substream)
1252 ah->parser_state = WSI_TOKEN_SKIPPING;
1253 break;
1254 }
1255
1256 if (m != LWS_ARRAY_SIZE(methods)) {
1257 #if defined(LWS_WITH_CUSTOM_HEADERS)
1258 /*
1259 * We have the method, this is just an
1260 * unknown header then
1261 */
1262 if (!wsi->mux_substream)
1263 goto unknown_hdr;
1264 else
1265 break;
1266 #else
1267 break;
1268 #endif
1269 }
1270 /*
1271 * ...it's an unknown http method from a client
1272 * in fact, it cannot be valid http.
1273 *
1274 * Are we set up to transition to another role
1275 * in these cases?
1276 */
1277 if (lws_check_opt(wsi->vhost->options,
1278 LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) {
1279 lwsl_notice("%s: http fail fallback\n",
1280 __func__);
1281 /* transition to other role */
1282 return LPR_DO_FALLBACK;
1283 }
1284
1285 lwsl_info("Unknown method - dropping\n");
1286 goto forbid;
1287 }
1288 if (ah->lextable_pos < 0) {
1289 #if defined(LWS_WITH_CUSTOM_HEADERS)
1290 if (!wsi->mux_substream)
1291 goto unknown_hdr;
1292 #endif
1293 /*
1294 * ...otherwise for a client, let him ignore
1295 * unknown headers coming from the server
1296 */
1297 ah->parser_state = WSI_TOKEN_SKIPPING;
1298 break;
1299 }
1300
1301 if (lextable_h1[ah->lextable_pos] < FAIL_CHAR) {
1302 /* terminal state */
1303
1304 n = ((unsigned int)lextable_h1[ah->lextable_pos] << 8) |
1305 lextable_h1[ah->lextable_pos + 1];
1306
1307 lwsl_parser("known hdr %d\n", n);
1308 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1309 if (n == methods[m] &&
1310 ah->frag_index[methods[m]]) {
1311 lwsl_warn("Duplicated method\n");
1312 return LPR_FAIL;
1313 }
1314
1315 /*
1316 * WSORIGIN is protocol equiv to ORIGIN,
1317 * JWebSocket likes to send it, map to ORIGIN
1318 */
1319 #if defined(LWS_ROLE_WS)
1320 if (n == WSI_TOKEN_SWORIGIN)
1321 n = WSI_TOKEN_ORIGIN;
1322 #endif
1323
1324 ah->parser_state = (enum lws_token_indexes)
1325 (WSI_TOKEN_GET_URI + n);
1326 ah->ups = URIPS_IDLE;
1327
1328 if (context->token_limits)
1329 ah->current_token_limit = context->
1330 token_limits->token_limit[
1331 ah->parser_state];
1332 else
1333 ah->current_token_limit =
1334 wsi->context->max_http_header_data;
1335
1336 if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1337 goto set_parsing_complete;
1338
1339 goto start_fragment;
1340 }
1341 break;
1342
1343 #if defined(LWS_WITH_CUSTOM_HEADERS)
1344 unknown_hdr:
1345 //ah->parser_state = WSI_TOKEN_SKIPPING;
1346 //break;
1347 if (!wsi->mux_substream)
1348 break;
1349 #endif
1350
1351 start_fragment:
1352 ah->nfrag++;
1353 excessive:
1354 if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
1355 lwsl_warn("More hdr frags than we can deal with\n");
1356 return LPR_FAIL;
1357 }
1358
1359 ah->frags[ah->nfrag].offset = ah->pos;
1360 ah->frags[ah->nfrag].len = 0;
1361 ah->frags[ah->nfrag].nfrag = 0;
1362 ah->frags[ah->nfrag].flags = 2;
1363
1364 n = ah->frag_index[ah->parser_state];
1365 if (!n) { /* first fragment */
1366 ah->frag_index[ah->parser_state] = ah->nfrag;
1367 ah->hdr_token_idx = ah->parser_state;
1368 break;
1369 }
1370 /* continuation */
1371 while (ah->frags[n].nfrag)
1372 n = ah->frags[n].nfrag;
1373 ah->frags[n].nfrag = ah->nfrag;
1374
1375 if (issue_char(wsi, ' ') < 0)
1376 return LPR_FAIL;
1377 break;
1378
1379 /* skipping arg part of a name we didn't recognize */
1380 case WSI_TOKEN_SKIPPING:
1381 lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
1382
1383 if (c == '\x0d')
1384 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1385 break;
1386
1387 case WSI_TOKEN_SKIPPING_SAW_CR:
1388 lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
1389 if (ah->ues != URIES_IDLE)
1390 goto forbid;
1391 if (c == '\x0a') {
1392 ah->parser_state = WSI_TOKEN_NAME_PART;
1393 #if defined(LWS_WITH_CUSTOM_HEADERS)
1394 ah->unk_pos = 0;
1395 #endif
1396 ah->lextable_pos = 0;
1397 } else
1398 ah->parser_state = WSI_TOKEN_SKIPPING;
1399 break;
1400 /* we're done, ignore anything else */
1401
1402 case WSI_PARSING_COMPLETE:
1403 lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
1404 break;
1405 }
1406
1407 } while (*len);
1408
1409 return LPR_OK;
1410
1411 set_parsing_complete:
1412 if (ah->ues != URIES_IDLE)
1413 goto forbid;
1414
1415 if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1416 #if defined(LWS_ROLE_WS)
1417 if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
1418 wsi->rx_frame_type = /* temp for ws version index */
1419 atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
1420
1421 lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
1422 #endif
1423 }
1424 ah->parser_state = WSI_PARSING_COMPLETE;
1425 wsi->hdr_parsing_completed = 1;
1426
1427 return LPR_OK;
1428
1429 forbid:
1430 lwsl_info(" forbidding on uri sanitation\n");
1431 #if defined(LWS_WITH_SERVER)
1432 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1433 #endif
1434
1435 return LPR_FORBIDDEN;
1436 }
1437
1438