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