• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	ah->unk_pos = 0;
100 #if defined(LWS_WITH_CUSTOM_HEADERS)
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->a.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->a.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->a.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->a.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->a.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: %s\n", __func__, lws_wsi_tag(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->a.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 %s\n", __func__, lws_wsi_tag(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->a.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: %s: ah %p (tsi %d, count = %d) in\n", __func__,
220 		  lws_wsi_tag(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 == (int)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 #endif
242 	if (n) {
243 		/*
244 		 * Pool is either all busy, or we don't want to give this
245 		 * particular guy an ah right now...
246 		 *
247 		 * Make sure we are on the waiting list, and return that we
248 		 * weren't able to provide the ah
249 		 */
250 		_lws_header_ensure_we_are_on_waiting_list(wsi);
251 
252 		goto bail;
253 	}
254 
255 	__lws_remove_from_ah_waiting_list(wsi);
256 
257 	wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
258 	if (!wsi->http.ah) { /* we could not create an ah */
259 		_lws_header_ensure_we_are_on_waiting_list(wsi);
260 
261 		goto bail;
262 	}
263 
264 	wsi->http.ah->in_use = 1;
265 	wsi->http.ah->wsi = wsi; /* mark our owner */
266 	pt->http.ah_count_in_use++;
267 
268 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
269     defined(LWS_ROLE_H2))
270 	lws_context_lock(context, "ah attach"); /* <========================= */
271 	if (wsi->peer)
272 		wsi->peer->http.count_ah++;
273 	lws_context_unlock(context); /* ====================================> */
274 #endif
275 
276 	_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
277 
278 	lwsl_info("%s: did attach wsi %s: ah %p: count %d (on exit)\n", __func__,
279 		  lws_wsi_tag(wsi), (void *)wsi->http.ah, pt->http.ah_count_in_use);
280 
281 reset:
282 	__lws_header_table_reset(wsi, autoservice);
283 
284 	lws_pt_unlock(pt);
285 
286 #if defined(LWS_WITH_CLIENT)
287 #if defined(LWS_ROLE_MQTT)
288 connect_via_info2:
289 #endif
290 	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
291 		if (!lws_http_client_connect_via_info2(wsi))
292 			/* our client connect has failed, the wsi
293 			 * has been closed
294 			 */
295 			return -1;
296 #endif
297 
298 	return 0;
299 
300 bail:
301 	lws_pt_unlock(pt);
302 
303 	return 1;
304 }
305 
__lws_header_table_detach(struct lws * wsi,int autoservice)306 int __lws_header_table_detach(struct lws *wsi, int autoservice)
307 {
308 	struct lws_context *context = wsi->a.context;
309 	struct allocated_headers *ah = wsi->http.ah;
310 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
311 	struct lws_pollargs pa;
312 	struct lws **pwsi, **pwsi_eligible;
313 	time_t now;
314 
315 	__lws_remove_from_ah_waiting_list(wsi);
316 
317 	if (!ah)
318 		return 0;
319 
320 	lwsl_info("%s: %s: ah %p (tsi=%d, count = %d)\n", __func__,
321 		  lws_wsi_tag(wsi), (void *)ah, wsi->tsi,
322 		  pt->http.ah_count_in_use);
323 
324 	/* we did have an ah attached */
325 	time(&now);
326 	if (ah->assigned && now - ah->assigned > 3) {
327 		/*
328 		 * we're detaching the ah, but it was held an
329 		 * unreasonably long time
330 		 */
331 		lwsl_debug("%s: %s: ah held %ds, role/state 0x%lx 0x%x,"
332 			    "\n", __func__, lws_wsi_tag(wsi),
333 			    (int)(now - ah->assigned),
334 			    (unsigned long)lwsi_role(wsi), lwsi_state(wsi));
335 	}
336 
337 	ah->assigned = 0;
338 
339 	/* if we think we're detaching one, there should be one in use */
340 	assert(pt->http.ah_count_in_use > 0);
341 	/* and this specific one should have been in use */
342 	assert(ah->in_use);
343 	memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
344 
345 #if defined(LWS_WITH_PEER_LIMITS)
346 	if (ah->wsi)
347 		lws_peer_track_ah_detach(context, wsi->peer);
348 #endif
349 	ah->wsi = NULL; /* no owner */
350 	wsi->http.ah = NULL;
351 
352 	pwsi = &pt->http.ah_wait_list;
353 
354 	/* oh there is nobody on the waiting list... leave the ah unattached */
355 	if (!*pwsi)
356 		goto nobody_usable_waiting;
357 
358 	/*
359 	 * at least one wsi on the same tsi is waiting, give it to oldest guy
360 	 * who is allowed to take it (if any)
361 	 */
362 	lwsl_info("%s: pt wait list %s\n", __func__, lws_wsi_tag(*pwsi));
363 	wsi = NULL;
364 	pwsi_eligible = NULL;
365 
366 	while (*pwsi) {
367 #if defined(LWS_WITH_PEER_LIMITS)
368 		/* are we willing to give this guy an ah? */
369 		if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
370 #endif
371 		{
372 			wsi = *pwsi;
373 			pwsi_eligible = pwsi;
374 		}
375 
376 		pwsi = &(*pwsi)->http.ah_wait_list;
377 	}
378 
379 	if (!wsi) /* everybody waiting already has too many ah... */
380 		goto nobody_usable_waiting;
381 
382 	lwsl_info("%s: transferring ah to last eligible wsi in wait list "
383 		  "%s (wsistate 0x%lx)\n", __func__, lws_wsi_tag(wsi),
384 		  (unsigned long)wsi->wsistate);
385 
386 	wsi->http.ah = ah;
387 	ah->wsi = wsi; /* new owner */
388 
389 	__lws_header_table_reset(wsi, autoservice);
390 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
391     defined(LWS_ROLE_H2))
392 	lws_context_lock(context, "ah detach"); /* <========================= */
393 	if (wsi->peer)
394 		wsi->peer->http.count_ah++;
395 	lws_context_unlock(context); /* ====================================> */
396 #endif
397 
398 	/* clients acquire the ah and then insert themselves in fds table... */
399 	if (wsi->position_in_fds_table != LWS_NO_FDS_POS) {
400 		lwsl_info("%s: Enabling %s POLLIN\n", __func__, lws_wsi_tag(wsi));
401 
402 		/* he has been stuck waiting for an ah, but now his wait is
403 		 * over, let him progress */
404 
405 		_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
406 	}
407 
408 	/* point prev guy to next guy in list instead */
409 	*pwsi_eligible = wsi->http.ah_wait_list;
410 	/* the guy who got one is out of the list */
411 	wsi->http.ah_wait_list = NULL;
412 	pt->http.ah_wait_list_length--;
413 
414 #if defined(LWS_WITH_CLIENT)
415 	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
416 		lws_pt_unlock(pt);
417 
418 		if (!lws_http_client_connect_via_info2(wsi)) {
419 			/* our client connect has failed, the wsi
420 			 * has been closed
421 			 */
422 
423 			return -1;
424 		}
425 		return 0;
426 	}
427 #endif
428 
429 	assert(!!pt->http.ah_wait_list_length ==
430 			!!(lws_intptr_t)pt->http.ah_wait_list);
431 bail:
432 	lwsl_info("%s: %s: ah %p (tsi=%d, count = %d)\n", __func__,
433 		  lws_wsi_tag(wsi), (void *)ah, pt->tid, pt->http.ah_count_in_use);
434 
435 	return 0;
436 
437 nobody_usable_waiting:
438 	lwsl_info("%s: nobody usable waiting\n", __func__);
439 	_lws_destroy_ah(pt, ah);
440 	pt->http.ah_count_in_use--;
441 
442 	goto bail;
443 }
444 
lws_header_table_detach(struct lws * wsi,int autoservice)445 int lws_header_table_detach(struct lws *wsi, int autoservice)
446 {
447 	struct lws_context *context = wsi->a.context;
448 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
449 	int n;
450 
451 	lws_pt_lock(pt, __func__);
452 	n = __lws_header_table_detach(wsi, autoservice);
453 	lws_pt_unlock(pt);
454 
455 	return n;
456 }
457 
458 int
lws_hdr_fragment_length(struct lws * wsi,enum lws_token_indexes h,int frag_idx)459 lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
460 {
461 	int n;
462 
463 	if (!wsi->http.ah)
464 		return 0;
465 
466 	n = wsi->http.ah->frag_index[h];
467 	if (!n)
468 		return 0;
469 	do {
470 		if (!frag_idx)
471 			return wsi->http.ah->frags[n].len;
472 		n = wsi->http.ah->frags[n].nfrag;
473 	} while (frag_idx-- && n);
474 
475 	return 0;
476 }
477 
lws_hdr_total_length(struct lws * wsi,enum lws_token_indexes h)478 int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
479 {
480 	int n;
481 	int len = 0;
482 
483 	if (!wsi->http.ah)
484 		return 0;
485 
486 	n = wsi->http.ah->frag_index[h];
487 	if (!n)
488 		return 0;
489 	do {
490 		len += wsi->http.ah->frags[n].len;
491 		n = wsi->http.ah->frags[n].nfrag;
492 
493 		if (n)
494 			len++;
495 
496 	} while (n);
497 
498 	return len;
499 }
500 
lws_hdr_copy_fragment(struct lws * wsi,char * dst,int len,enum lws_token_indexes h,int frag_idx)501 int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
502 				      enum lws_token_indexes h, int frag_idx)
503 {
504 	int n = 0;
505 	int f;
506 
507 	if (!wsi->http.ah)
508 		return -1;
509 
510 	f = wsi->http.ah->frag_index[h];
511 
512 	if (!f)
513 		return -1;
514 
515 	while (n < frag_idx) {
516 		f = wsi->http.ah->frags[f].nfrag;
517 		if (!f)
518 			return -1;
519 		n++;
520 	}
521 
522 	if (wsi->http.ah->frags[f].len >= len)
523 		return -1;
524 
525 	memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
526 	       wsi->http.ah->frags[f].len);
527 	dst[wsi->http.ah->frags[f].len] = '\0';
528 
529 	return wsi->http.ah->frags[f].len;
530 }
531 
lws_hdr_copy(struct lws * wsi,char * dst,int len,enum lws_token_indexes h)532 int lws_hdr_copy(struct lws *wsi, char *dst, int len,
533 			     enum lws_token_indexes h)
534 {
535 	int toklen = lws_hdr_total_length(wsi, h), n, comma;
536 
537 	*dst = '\0';
538 	if (!toklen)
539 		return 0;
540 
541 	if (toklen >= len)
542 		return -1;
543 
544 	if (!wsi->http.ah)
545 		return -1;
546 
547 	n = wsi->http.ah->frag_index[h];
548 	if (!n)
549 		return 0;
550 	do {
551 		comma = (wsi->http.ah->frags[n].nfrag) ? 1 : 0;
552 
553 		if (h == WSI_TOKEN_HTTP_URI_ARGS)
554 			lwsl_notice("%s: WSI_TOKEN_HTTP_URI_ARGS '%.*s'\n",
555 				    __func__, (int)wsi->http.ah->frags[n].len,
556 				    &wsi->http.ah->data[
557 				                wsi->http.ah->frags[n].offset]);
558 
559 		if (wsi->http.ah->frags[n].len + comma >= len) {
560 			lwsl_notice("blowout len\n");
561 			return -1;
562 		}
563 		strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
564 		        wsi->http.ah->frags[n].len);
565 		dst += wsi->http.ah->frags[n].len;
566 		len -= wsi->http.ah->frags[n].len;
567 		n = wsi->http.ah->frags[n].nfrag;
568 
569 		/*
570 		 * Note if you change this logic, take care about updating len
571 		 * and make sure lws_hdr_total_length() gives the same resulting
572 		 * length
573 		 */
574 
575 		if (comma) {
576 			if (h == WSI_TOKEN_HTTP_COOKIE ||
577 			    h == WSI_TOKEN_HTTP_SET_COOKIE)
578 				*dst++ = ';';
579 			else
580 				if (h == WSI_TOKEN_HTTP_URI_ARGS)
581 					*dst++ = '&';
582 				else
583 					*dst++ = ',';
584 			len--;
585 		}
586 
587 	} while (n);
588 	*dst = '\0';
589 
590 	if (h == WSI_TOKEN_HTTP_URI_ARGS)
591 		lwsl_err("%s: WSI_TOKEN_HTTP_URI_ARGS toklen %d\n", __func__, (int)toklen);
592 
593 	return toklen;
594 }
595 
596 #if defined(LWS_WITH_CUSTOM_HEADERS)
597 int
lws_hdr_custom_length(struct lws * wsi,const char * name,int nlen)598 lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen)
599 {
600 	ah_data_idx_t ll;
601 
602 	if (!wsi->http.ah || wsi->mux_substream)
603 		return -1;
604 
605 	ll = wsi->http.ah->unk_ll_head;
606 	while (ll) {
607 		if (ll >= wsi->http.ah->data_length)
608 			return -1;
609 		if (nlen == lws_ser_ru16be(
610 			(uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
611 		    !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], (unsigned int)nlen))
612 			return lws_ser_ru16be(
613 				(uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
614 
615 		ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
616 	}
617 
618 	return -1;
619 }
620 
621 int
lws_hdr_custom_copy(struct lws * wsi,char * dst,int len,const char * name,int nlen)622 lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
623 		    int nlen)
624 {
625 	ah_data_idx_t ll;
626 	int n;
627 
628 	if (!wsi->http.ah || wsi->mux_substream)
629 		return -1;
630 
631 	*dst = '\0';
632 
633 	ll = wsi->http.ah->unk_ll_head;
634 	while (ll) {
635 		if (ll >= wsi->http.ah->data_length)
636 			return -1;
637 		if (nlen == lws_ser_ru16be(
638 			(uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
639 		    !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], (unsigned int)nlen)) {
640 			n = lws_ser_ru16be(
641 				(uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
642 			if (n + 1 > len)
643 				return -1;
644 			strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + (unsigned int)nlen], (unsigned int)n);
645 			dst[n] = '\0';
646 
647 			return n;
648 		}
649 		ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
650 	}
651 
652 	return -1;
653 }
654 
655 int
lws_hdr_custom_name_foreach(struct lws * wsi,lws_hdr_custom_fe_cb_t cb,void * custom)656 lws_hdr_custom_name_foreach(struct lws *wsi, lws_hdr_custom_fe_cb_t cb,
657 			    void *custom)
658 {
659 	ah_data_idx_t ll;
660 
661 	if (!wsi->http.ah || wsi->mux_substream)
662 		return -1;
663 
664 	ll = wsi->http.ah->unk_ll_head;
665 
666 	while (ll) {
667 		if (ll >= wsi->http.ah->data_length)
668 			return -1;
669 
670 		cb(&wsi->http.ah->data[ll + UHO_NAME],
671 		   lws_ser_ru16be((uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]),
672 		   custom);
673 
674 		ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
675 	}
676 
677 	return 0;
678 }
679 #endif
680 
lws_hdr_simple_ptr(struct lws * wsi,enum lws_token_indexes h)681 char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
682 {
683 	int n;
684 
685 	if (!wsi->http.ah)
686 		return NULL;
687 
688 	n = wsi->http.ah->frag_index[h];
689 	if (!n)
690 		return NULL;
691 
692 	return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
693 }
694 
695 static int LWS_WARN_UNUSED_RESULT
lws_pos_in_bounds(struct lws * wsi)696 lws_pos_in_bounds(struct lws *wsi)
697 {
698 	if (!wsi->http.ah)
699 		return -1;
700 
701 	if (wsi->http.ah->pos <
702 	    (unsigned int)wsi->a.context->max_http_header_data)
703 		return 0;
704 
705 	if ((int)wsi->http.ah->pos >= (int)wsi->a.context->max_http_header_data - 1) {
706 		lwsl_err("Ran out of header data space\n");
707 		return 1;
708 	}
709 
710 	/*
711 	 * with these tests everywhere, it should never be able to exceed
712 	 * the limit, only meet it
713 	 */
714 	lwsl_err("%s: pos %ld, limit %ld\n", __func__,
715 		 (unsigned long)wsi->http.ah->pos,
716 		 (unsigned long)wsi->a.context->max_http_header_data);
717 	assert(0);
718 
719 	return 1;
720 }
721 
722 int LWS_WARN_UNUSED_RESULT
lws_hdr_simple_create(struct lws * wsi,enum lws_token_indexes h,const char * s)723 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
724 {
725 	if (!*s) {
726 		/*
727 		 * If we get an empty string, then remove any entry for the
728 		 * header
729 		 */
730 		wsi->http.ah->frag_index[h] = 0;
731 
732 		return 0;
733 	}
734 
735 	wsi->http.ah->nfrag++;
736 	if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) {
737 		lwsl_warn("More hdr frags than we can deal with, dropping\n");
738 		return -1;
739 	}
740 
741 	wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
742 
743 	wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
744 	wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
745 	wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
746 
747 	do {
748 		if (lws_pos_in_bounds(wsi))
749 			return -1;
750 
751 		wsi->http.ah->data[wsi->http.ah->pos++] = *s;
752 		if (*s)
753 			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
754 	} while (*s++);
755 
756 	return 0;
757 }
758 
759 static int LWS_WARN_UNUSED_RESULT
issue_char(struct lws * wsi,unsigned char c)760 issue_char(struct lws *wsi, unsigned char c)
761 {
762 	unsigned short frag_len;
763 
764 	if (lws_pos_in_bounds(wsi))
765 		return -1;
766 
767 	frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
768 	/*
769 	 * If we haven't hit the token limit, just copy the character into
770 	 * the header
771 	 */
772 	if (!wsi->http.ah->current_token_limit ||
773 	    frag_len < wsi->http.ah->current_token_limit) {
774 		wsi->http.ah->data[wsi->http.ah->pos++] = (char)c;
775 		wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
776 		return 0;
777 	}
778 
779 	/* Insert a null character when we *hit* the limit: */
780 	if (frag_len == wsi->http.ah->current_token_limit) {
781 		if (lws_pos_in_bounds(wsi))
782 			return -1;
783 
784 		wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
785 		lwsl_warn("header %li exceeds limit %ld\n",
786 			  (long)wsi->http.ah->parser_state,
787 			  (long)wsi->http.ah->current_token_limit);
788 	}
789 
790 	return 1;
791 }
792 
793 int
lws_parse_urldecode(struct lws * wsi,uint8_t * _c)794 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
795 {
796 	struct allocated_headers *ah = wsi->http.ah;
797 	unsigned int enc = 0;
798 	uint8_t c = *_c;
799 
800 	// lwsl_notice("ah->ups %d\n", ah->ups);
801 
802 	/*
803 	 * PRIORITY 1
804 	 * special URI processing... convert %xx
805 	 */
806 	switch (ah->ues) {
807 	case URIES_IDLE:
808 		if (c == '%') {
809 			ah->ues = URIES_SEEN_PERCENT;
810 			goto swallow;
811 		}
812 		break;
813 	case URIES_SEEN_PERCENT:
814 		if (char_to_hex((char)c) < 0)
815 			/* illegal post-% char */
816 			goto forbid;
817 
818 		ah->esc_stash = (char)c;
819 		ah->ues = URIES_SEEN_PERCENT_H1;
820 		goto swallow;
821 
822 	case URIES_SEEN_PERCENT_H1:
823 		if (char_to_hex((char)c) < 0)
824 			/* illegal post-% char */
825 			goto forbid;
826 
827 		*_c = (uint8_t)(unsigned int)((char_to_hex(ah->esc_stash) << 4) |
828 				char_to_hex((char)c));
829 		c = *_c;
830 		enc = 1;
831 		ah->ues = URIES_IDLE;
832 		break;
833 	}
834 
835 	/*
836 	 * PRIORITY 2
837 	 * special URI processing...
838 	 *  convert /.. or /... or /../ etc to /
839 	 *  convert /./ to /
840 	 *  convert // or /// etc to /
841 	 *  leave /.dir or whatever alone
842 	 */
843 
844 	if (!c && (!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] ||
845 		   !ah->post_literal_equal)) {
846 		/*
847 		 * Since user code is typically going to parse the path using
848 		 * NUL-terminated apis, it's too dangerous to allow NUL
849 		 * injection here.
850 		 *
851 		 * It's allowed in the urlargs, because the apis to access
852 		 * those only allow retreival with explicit length.
853 		 */
854 		lwsl_warn("%s: saw NUL outside of uri args\n", __func__);
855 		return -1;
856 	}
857 
858 	switch (ah->ups) {
859 	case URIPS_IDLE:
860 
861 		/* genuine delimiter */
862 		if ((c == '&' || c == ';') && !enc) {
863 			if (issue_char(wsi, '\0') < 0)
864 				return -1;
865 			/* don't account for it */
866 			wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
867 			/* link to next fragment */
868 			ah->frags[ah->nfrag].nfrag = (uint8_t)(ah->nfrag + 1);
869 			ah->nfrag++;
870 			if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
871 				goto excessive;
872 			/* start next fragment after the & */
873 			ah->post_literal_equal = 0;
874 			ah->frags[ah->nfrag].offset = ++ah->pos;
875 			ah->frags[ah->nfrag].len = 0;
876 			ah->frags[ah->nfrag].nfrag = 0;
877 			goto swallow;
878 		}
879 		/* uriencoded = in the name part, disallow */
880 		if (c == '=' && enc &&
881 		    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
882 		    !ah->post_literal_equal) {
883 			c = '_';
884 			*_c =c;
885 		}
886 
887 		/* after the real =, we don't care how many = */
888 		if (c == '=' && !enc)
889 			ah->post_literal_equal = 1;
890 
891 		/* + to space */
892 		if (c == '+' && !enc) {
893 			c = ' ';
894 			*_c = c;
895 		}
896 		/* issue the first / always */
897 		if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
898 			ah->ups = URIPS_SEEN_SLASH;
899 		break;
900 	case URIPS_SEEN_SLASH:
901 		/* swallow subsequent slashes */
902 		if (c == '/')
903 			goto swallow;
904 		/* track and swallow the first . after / */
905 		if (c == '.') {
906 			ah->ups = URIPS_SEEN_SLASH_DOT;
907 			goto swallow;
908 		}
909 		ah->ups = URIPS_IDLE;
910 		break;
911 	case URIPS_SEEN_SLASH_DOT:
912 		/* swallow second . */
913 		if (c == '.') {
914 			ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
915 			goto swallow;
916 		}
917 		/* change /./ to / */
918 		if (c == '/') {
919 			ah->ups = URIPS_SEEN_SLASH;
920 			goto swallow;
921 		}
922 		/* it was like /.dir ... regurgitate the . */
923 		ah->ups = URIPS_IDLE;
924 		if (issue_char(wsi, '.') < 0)
925 			return -1;
926 		break;
927 
928 	case URIPS_SEEN_SLASH_DOT_DOT:
929 
930 		/* /../ or /..[End of URI] --> backup to last / */
931 		if (c == '/' || c == '?') {
932 			/*
933 			 * back up one dir level if possible
934 			 * safe against header fragmentation because
935 			 * the method URI can only be in 1 fragment
936 			 */
937 			if (ah->frags[ah->nfrag].len > 2) {
938 				ah->pos--;
939 				ah->frags[ah->nfrag].len--;
940 				do {
941 					ah->pos--;
942 					ah->frags[ah->nfrag].len--;
943 				} while (ah->frags[ah->nfrag].len > 1 &&
944 					 ah->data[ah->pos] != '/');
945 			}
946 			ah->ups = URIPS_SEEN_SLASH;
947 			if (ah->frags[ah->nfrag].len > 1)
948 				break;
949 			goto swallow;
950 		}
951 
952 		/*  /..[^/] ... regurgitate and allow */
953 
954 		if (issue_char(wsi, '.') < 0)
955 			return -1;
956 		if (issue_char(wsi, '.') < 0)
957 			return -1;
958 		ah->ups = URIPS_IDLE;
959 		break;
960 	}
961 
962 	if (c == '?' && !enc &&
963 	    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
964 		if (ah->ues != URIES_IDLE)
965 			goto forbid;
966 
967 		/* seal off uri header */
968 		if (issue_char(wsi, '\0') < 0)
969 			return -1;
970 
971 		/* don't account for it */
972 		wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
973 
974 		/* move to using WSI_TOKEN_HTTP_URI_ARGS */
975 		ah->nfrag++;
976 		if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
977 			goto excessive;
978 		ah->frags[ah->nfrag].offset = ++ah->pos;
979 		ah->frags[ah->nfrag].len = 0;
980 		ah->frags[ah->nfrag].nfrag = 0;
981 
982 		ah->post_literal_equal = 0;
983 		ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
984 		ah->ups = URIPS_IDLE;
985 		goto swallow;
986 	}
987 
988 	return LPUR_CONTINUE;
989 
990 swallow:
991 	return LPUR_SWALLOW;
992 
993 forbid:
994 	return LPUR_FORBID;
995 
996 excessive:
997 	return LPUR_EXCESSIVE;
998 }
999 
1000 static const unsigned char methods[] = {
1001 	WSI_TOKEN_GET_URI,
1002 	WSI_TOKEN_POST_URI,
1003 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
1004 	WSI_TOKEN_OPTIONS_URI,
1005 	WSI_TOKEN_PUT_URI,
1006 	WSI_TOKEN_PATCH_URI,
1007 	WSI_TOKEN_DELETE_URI,
1008 #endif
1009 	WSI_TOKEN_CONNECT,
1010 	WSI_TOKEN_HEAD_URI,
1011 };
1012 
1013 /*
1014  * possible returns:, -1 fail, 0 ok or 2, transition to raw
1015  */
1016 
1017 lws_parser_return_t LWS_WARN_UNUSED_RESULT
lws_parse(struct lws * wsi,unsigned char * buf,int * len)1018 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
1019 {
1020 	struct allocated_headers *ah = wsi->http.ah;
1021 	struct lws_context *context = wsi->a.context;
1022 	unsigned int n, m;
1023 	unsigned char c;
1024 	int r, pos;
1025 
1026 	assert(wsi->http.ah);
1027 
1028 	do {
1029 		(*len)--;
1030 		c = *buf++;
1031 
1032 		switch (ah->parser_state) {
1033 #if defined(LWS_WITH_CUSTOM_HEADERS)
1034 		case WSI_TOKEN_UNKNOWN_VALUE_PART:
1035 
1036 			if (c == '\r')
1037 				break;
1038 			if (c == '\n') {
1039 				lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos + 2],
1040 					       (uint16_t)(ah->pos - ah->unk_value_pos));
1041 				ah->parser_state = WSI_TOKEN_NAME_PART;
1042 				ah->unk_pos = 0;
1043 				ah->lextable_pos = 0;
1044 				break;
1045 			}
1046 
1047 			/* trim leading whitespace */
1048 			if (ah->pos != ah->unk_value_pos ||
1049 			    (c != ' ' && c != '\t')) {
1050 
1051 				if (lws_pos_in_bounds(wsi))
1052 					return LPR_FAIL;
1053 
1054 				ah->data[ah->pos++] = (char)c;
1055 			}
1056 			pos = ah->lextable_pos;
1057 			break;
1058 #endif
1059 		default:
1060 
1061 			lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
1062 
1063 			/* collect into malloc'd buffers */
1064 			/* optional initial space swallow */
1065 			if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
1066 			    c == ' ')
1067 				break;
1068 
1069 			for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1070 				if (ah->parser_state == methods[m])
1071 					break;
1072 			if (m == LWS_ARRAY_SIZE(methods))
1073 				/* it was not any of the methods */
1074 				goto check_eol;
1075 
1076 			/* special URI processing... end at space */
1077 
1078 			if (c == ' ') {
1079 				/* enforce starting with / */
1080 				if (!ah->frags[ah->nfrag].len)
1081 					if (issue_char(wsi, '/') < 0)
1082 						return LPR_FAIL;
1083 
1084 				if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
1085 					/*
1086 					 * back up one dir level if possible
1087 					 * safe against header fragmentation
1088 					 * because the method URI can only be
1089 					 * in 1 fragment
1090 					 */
1091 					if (ah->frags[ah->nfrag].len > 2) {
1092 						ah->pos--;
1093 						ah->frags[ah->nfrag].len--;
1094 						do {
1095 							ah->pos--;
1096 							ah->frags[ah->nfrag].len--;
1097 						} while (ah->frags[ah->nfrag].len > 1 &&
1098 							 ah->data[ah->pos] != '/');
1099 					}
1100 				}
1101 
1102 				/* begin parsing HTTP version: */
1103 				if (issue_char(wsi, '\0') < 0)
1104 					return LPR_FAIL;
1105 				/* don't account for it */
1106 				wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
1107 				ah->parser_state = WSI_TOKEN_HTTP;
1108 				goto start_fragment;
1109 			}
1110 
1111 			r = lws_parse_urldecode(wsi, &c);
1112 			switch (r) {
1113 			case LPUR_CONTINUE:
1114 				break;
1115 			case LPUR_SWALLOW:
1116 				goto swallow;
1117 			case LPUR_FORBID:
1118 				goto forbid;
1119 			case LPUR_EXCESSIVE:
1120 				goto excessive;
1121 			default:
1122 				return LPR_FAIL;
1123 			}
1124 check_eol:
1125 			/* bail at EOL */
1126 			if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
1127 			    (c == '\x0d' || c == '\x0a')) {
1128 				if (ah->ues != URIES_IDLE)
1129 					goto forbid;
1130 
1131 				if (c == '\x0a') {
1132 					/* broken peer */
1133 					ah->parser_state = WSI_TOKEN_NAME_PART;
1134 					ah->unk_pos = 0;
1135 					ah->lextable_pos = 0;
1136 				} else
1137 					ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1138 
1139 				c = '\0';
1140 				lwsl_parser("*\n");
1141 			}
1142 
1143 			n = (unsigned int)issue_char(wsi, c);
1144 			if ((int)n < 0)
1145 				return LPR_FAIL;
1146 			if (n > 0)
1147 				ah->parser_state = WSI_TOKEN_SKIPPING;
1148 			else {
1149 				/*
1150 				 * Explicit zeroes are legal in URI ARGS.
1151 				 * They can only exist as a safety terminator
1152 				 * after the valid part of the token contents
1153 				 * for other types.
1154 				 */
1155 				if (!c && ah->parser_state != WSI_TOKEN_HTTP_URI_ARGS)
1156 					/* don't account for safety terminator */
1157 					wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
1158 			}
1159 
1160 swallow:
1161 			/* per-protocol end of headers management */
1162 
1163 			if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1164 				goto set_parsing_complete;
1165 			break;
1166 
1167 			/* collecting and checking a name part */
1168 		case WSI_TOKEN_NAME_PART:
1169 			lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X "
1170 				    "(role=0x%lx) "
1171 				    "wsi->lextable_pos=%d\n", c, c,
1172 				    (unsigned long)lwsi_role(wsi),
1173 				    ah->lextable_pos);
1174 
1175 			if (!ah->unk_pos && c == '\x0a')
1176 				/* broken peer */
1177 				goto set_parsing_complete;
1178 
1179 			if (c >= 'A' && c <= 'Z')
1180 				c = (unsigned char)(c + 'a' - 'A');
1181 			/*
1182 			 * ...in case it's an unknown header, speculatively
1183 			 * store it as the name comes in.  If we recognize it as
1184 			 * a known header, we'll snip this.
1185 			 */
1186 
1187 			if (!wsi->mux_substream && !ah->unk_pos) {
1188 				ah->unk_pos = ah->pos;
1189 
1190 #if defined(LWS_WITH_CUSTOM_HEADERS)
1191 				/*
1192 				 * Prepare new unknown header linked-list entry
1193 				 *
1194 				 *  - 16-bit BE: name part length
1195 				 *  - 16-bit BE: value part length
1196 				 *  - 32-bit BE: data offset of next, or 0
1197 				 */
1198 				for (n = 0; n < 8; n++)
1199 					if (!lws_pos_in_bounds(wsi))
1200 						ah->data[ah->pos++] = 0;
1201 #endif
1202 			}
1203 
1204 			if (lws_pos_in_bounds(wsi))
1205 				return LPR_FAIL;
1206 
1207 			ah->data[ah->pos++] = (char)c;
1208 			pos = ah->lextable_pos;
1209 
1210 #if defined(LWS_WITH_CUSTOM_HEADERS)
1211 			if (!wsi->mux_substream && pos < 0 && c == ':') {
1212 #if defined(_DEBUG)
1213 				char dotstar[64];
1214 				int uhlen;
1215 #endif
1216 
1217 				/*
1218 				 * process unknown headers
1219 				 *
1220 				 * register us in the unknown hdr ll
1221 				 */
1222 
1223 				if (!ah->unk_ll_head)
1224 					ah->unk_ll_head = ah->unk_pos;
1225 
1226 				if (ah->unk_ll_tail)
1227 					lws_ser_wu32be(
1228 				(uint8_t *)&ah->data[ah->unk_ll_tail + UHO_LL],
1229 						       ah->unk_pos);
1230 
1231 				ah->unk_ll_tail = ah->unk_pos;
1232 
1233 #if defined(_DEBUG)
1234 				uhlen = (int)(ah->pos - (ah->unk_pos + UHO_NAME));
1235 				lws_strnncpy(dotstar,
1236 					&ah->data[ah->unk_pos + UHO_NAME],
1237 					uhlen, sizeof(dotstar));
1238 				lwsl_debug("%s: unk header %d '%s'\n",
1239 					    __func__,
1240 					    ah->pos - (ah->unk_pos + UHO_NAME),
1241 					    dotstar);
1242 #endif
1243 
1244 				/* set the unknown header name part length */
1245 
1246 				lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos],
1247 					       (uint16_t)((ah->pos - ah->unk_pos) - UHO_NAME));
1248 
1249 				ah->unk_value_pos = ah->pos;
1250 
1251 				/*
1252 				 * collect whatever's coming for the unknown header
1253 				 * argument until the next CRLF
1254 				 */
1255 				ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART;
1256 				break;
1257 			}
1258 #endif
1259 			if (pos < 0)
1260 				break;
1261 
1262 			while (1) {
1263 				if (lextable_h1[pos] & (1 << 7)) {
1264 					/* 1-byte, fail on mismatch */
1265 					if ((lextable_h1[pos] & 0x7f) != c) {
1266 nope:
1267 						ah->lextable_pos = -1;
1268 						break;
1269 					}
1270 					/* fall thru */
1271 					pos++;
1272 					if (lextable_h1[pos] == FAIL_CHAR)
1273 						goto nope;
1274 
1275 					ah->lextable_pos = (int16_t)pos;
1276 					break;
1277 				}
1278 
1279 				if (lextable_h1[pos] == FAIL_CHAR)
1280 					goto nope;
1281 
1282 				/* b7 = 0, end or 3-byte */
1283 				if (lextable_h1[pos] < FAIL_CHAR) {
1284 					if (!wsi->mux_substream) {
1285 						/*
1286 						 * We hit a terminal marker, so
1287 						 * we recognized this header...
1288 						 * drop the speculative name
1289 						 * part storage
1290 						 */
1291 						ah->pos = ah->unk_pos;
1292 						ah->unk_pos = 0;
1293 					}
1294 
1295 					ah->lextable_pos = (int16_t)pos;
1296 					break;
1297 				}
1298 
1299 				if (lextable_h1[pos] == c) { /* goto */
1300 					ah->lextable_pos = (int16_t)(pos +
1301 						(lextable_h1[pos + 1]) +
1302 						(lextable_h1[pos + 2] << 8));
1303 					break;
1304 				}
1305 
1306 				/* fall thru goto */
1307 				pos += 3;
1308 				/* continue */
1309 			}
1310 
1311 			/*
1312 			 * If it's h1, server needs to be on the look out for
1313 			 * unknown methods...
1314 			 */
1315 			if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
1316 			    lwsi_role_server(wsi)) {
1317 				/*
1318 				 * this is not a header we know about... did
1319 				 * we get a valid method (GET, POST etc)
1320 				 * already, or is this the bogus method?
1321 				 */
1322 				for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1323 					if (ah->frag_index[methods[m]]) {
1324 						/*
1325 						 * already had the method
1326 						 */
1327 #if !defined(LWS_WITH_CUSTOM_HEADERS)
1328 						ah->parser_state = WSI_TOKEN_SKIPPING;
1329 #endif
1330 						if (wsi->mux_substream)
1331 							ah->parser_state = WSI_TOKEN_SKIPPING;
1332 						break;
1333 					}
1334 
1335 				if (m != LWS_ARRAY_SIZE(methods)) {
1336 #if defined(LWS_WITH_CUSTOM_HEADERS)
1337 					/*
1338 					 * We have the method, this is just an
1339 					 * unknown header then
1340 					 */
1341 					if (!wsi->mux_substream)
1342 						goto unknown_hdr;
1343 					else
1344 						break;
1345 #else
1346 					break;
1347 #endif
1348 				}
1349 				/*
1350 				 * ...it's an unknown http method from a client
1351 				 * in fact, it cannot be valid http.
1352 				 *
1353 				 * Are we set up to transition to another role
1354 				 * in these cases?
1355 				 */
1356 				if (lws_check_opt(wsi->a.vhost->options,
1357 		    LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) {
1358 					lwsl_notice("%s: http fail fallback\n",
1359 						    __func__);
1360 					 /* transition to other role */
1361 					return LPR_DO_FALLBACK;
1362 				}
1363 
1364 				lwsl_info("Unknown method - dropping\n");
1365 				goto forbid;
1366 			}
1367 			if (ah->lextable_pos < 0) {
1368 				/*
1369 				 * It's not a header that lws knows about...
1370 				 */
1371 #if defined(LWS_WITH_CUSTOM_HEADERS)
1372 				if (!wsi->mux_substream)
1373 					goto unknown_hdr;
1374 #endif
1375 				/*
1376 				 * ...otherwise for a client, let him ignore
1377 				 * unknown headers coming from the server
1378 				 */
1379 				ah->parser_state = WSI_TOKEN_SKIPPING;
1380 				break;
1381 			}
1382 
1383 			if (lextable_h1[ah->lextable_pos] < FAIL_CHAR) {
1384 				/* terminal state */
1385 
1386 				n = ((unsigned int)lextable_h1[ah->lextable_pos] << 8) |
1387 						lextable_h1[ah->lextable_pos + 1];
1388 
1389 				lwsl_parser("known hdr %d\n", n);
1390 				for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1391 					if (n == methods[m] &&
1392 					    ah->frag_index[methods[m]]) {
1393 						lwsl_warn("Duplicated method\n");
1394 						return LPR_FAIL;
1395 					}
1396 
1397 				if (!wsi->mux_substream) {
1398 					/*
1399 					 * Whether we are collecting unknown names or not,
1400 					 * if we matched an internal header we can dispense
1401 					 * with the header name part we were keeping
1402 					 */
1403 					ah->pos = ah->unk_pos;
1404 					ah->unk_pos = 0;
1405 				}
1406 
1407 #if defined(LWS_ROLE_WS)
1408 				/*
1409 				 * WSORIGIN is protocol equiv to ORIGIN,
1410 				 * JWebSocket likes to send it, map to ORIGIN
1411 				 */
1412 				if (n == WSI_TOKEN_SWORIGIN)
1413 					n = WSI_TOKEN_ORIGIN;
1414 #endif
1415 
1416 				ah->parser_state = (uint8_t)
1417 							(WSI_TOKEN_GET_URI + n);
1418 				ah->ups = URIPS_IDLE;
1419 
1420 				if (context->token_limits)
1421 					ah->current_token_limit = context->
1422 						token_limits->token_limit[
1423 							      ah->parser_state];
1424 				else
1425 					ah->current_token_limit =
1426 						wsi->a.context->max_http_header_data;
1427 
1428 				if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1429 					goto set_parsing_complete;
1430 
1431 				goto start_fragment;
1432 			}
1433 			break;
1434 
1435 #if defined(LWS_WITH_CUSTOM_HEADERS)
1436 unknown_hdr:
1437 			//ah->parser_state = WSI_TOKEN_SKIPPING;
1438 			//break;
1439 			if (!wsi->mux_substream)
1440 				break;
1441 #endif
1442 
1443 start_fragment:
1444 			ah->nfrag++;
1445 excessive:
1446 			if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
1447 				lwsl_warn("More hdr frags than we can deal with\n");
1448 				return LPR_FAIL;
1449 			}
1450 
1451 			ah->frags[ah->nfrag].offset = ah->pos;
1452 			ah->frags[ah->nfrag].len = 0;
1453 			ah->frags[ah->nfrag].nfrag = 0;
1454 			ah->frags[ah->nfrag].flags = 2;
1455 
1456 			n = ah->frag_index[ah->parser_state];
1457 			if (!n) { /* first fragment */
1458 				ah->frag_index[ah->parser_state] = ah->nfrag;
1459 				ah->hdr_token_idx = ah->parser_state;
1460 				break;
1461 			}
1462 			/* continuation */
1463 			while (ah->frags[n].nfrag)
1464 				n = ah->frags[n].nfrag;
1465 			ah->frags[n].nfrag = ah->nfrag;
1466 
1467 			if (issue_char(wsi, ' ') < 0)
1468 				return LPR_FAIL;
1469 			break;
1470 
1471 			/* skipping arg part of a name we didn't recognize */
1472 		case WSI_TOKEN_SKIPPING:
1473 			lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
1474 
1475 			if (c == '\x0a') {
1476 				/* broken peer */
1477 				ah->parser_state = WSI_TOKEN_NAME_PART;
1478 				ah->unk_pos = 0;
1479 				ah->lextable_pos = 0;
1480 			}
1481 
1482 			if (c == '\x0d')
1483 				ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1484 			break;
1485 
1486 		case WSI_TOKEN_SKIPPING_SAW_CR:
1487 			lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
1488 			if (ah->ues != URIES_IDLE)
1489 				goto forbid;
1490 			if (c == '\x0a') {
1491 				ah->parser_state = WSI_TOKEN_NAME_PART;
1492 				ah->unk_pos = 0;
1493 				ah->lextable_pos = 0;
1494 			} else
1495 				ah->parser_state = WSI_TOKEN_SKIPPING;
1496 			break;
1497 			/* we're done, ignore anything else */
1498 
1499 		case WSI_PARSING_COMPLETE:
1500 			lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
1501 			break;
1502 		}
1503 
1504 	} while (*len);
1505 
1506 	return LPR_OK;
1507 
1508 set_parsing_complete:
1509 	if (ah->ues != URIES_IDLE)
1510 		goto forbid;
1511 
1512 	if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1513 #if defined(LWS_ROLE_WS)
1514 		const char *pv = lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION);
1515 		if (pv)
1516 			wsi->rx_frame_type = (char)atoi(pv);
1517 
1518 		lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
1519 #endif
1520 	}
1521 	ah->parser_state = WSI_PARSING_COMPLETE;
1522 	wsi->hdr_parsing_completed = 1;
1523 
1524 	return LPR_OK;
1525 
1526 forbid:
1527 	lwsl_info(" forbidding on uri sanitation\n");
1528 #if defined(LWS_WITH_SERVER)
1529 	lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1530 #endif
1531 
1532 	return LPR_FORBIDDEN;
1533 }
1534 
1535 int
lws_http_cookie_get(struct lws * wsi,const char * name,char * buf,size_t * max_len)1536 lws_http_cookie_get(struct lws *wsi, const char *name, char *buf,
1537 		    size_t *max_len)
1538 {
1539 	size_t max = *max_len, bl = strlen(name);
1540 	char *p, *bo = buf;
1541 	int n;
1542 
1543 	n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
1544 	if ((unsigned int)n < bl + 1)
1545 		return 1;
1546 
1547 	/*
1548 	 * This can come to us two ways, in ah fragments (h2) or as a single
1549 	 * semicolon-delimited string (h1)
1550 	 */
1551 
1552 #if defined(LWS_ROLE_H2)
1553 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD)) {
1554 
1555 		/*
1556 		 * The h2 way...
1557 		 */
1558 
1559 		int f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_COOKIE];
1560 		size_t fl;
1561 
1562 		while (f) {
1563 			p = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
1564 			fl = (size_t)wsi->http.ah->frags[f].len;
1565 			if (fl >= bl + 1 &&
1566 			    p[bl] == '=' &&
1567 			    !memcmp(p, name, bl)) {
1568 				fl -= bl + 1;
1569 				if (max - 1 < fl)
1570 					fl = max - 1;
1571 				if (fl)
1572 					memcpy(buf, p + bl + 1, fl);
1573 				*max_len = fl;
1574 				buf[fl] = '\0';
1575 
1576 				return 0;
1577 			}
1578 			f = wsi->http.ah->frags[f].nfrag;
1579 		}
1580 
1581 		return -1;
1582 	}
1583 #endif
1584 
1585 	/*
1586 	 * The h1 way...
1587 	 */
1588 
1589 	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE);
1590 	if (!p)
1591 		return 1;
1592 
1593 	p += bl;
1594 	n -= (int)bl;
1595 	while (n-- > 0) {
1596 		if (*p == '=' && !memcmp(p - bl, name, (unsigned int)bl)) {
1597 			p++;
1598 			while (*p != ';' && n-- && max) {
1599 				*buf++ = *p++;
1600 				max--;
1601 			}
1602 			if (!max)
1603 				return 2;
1604 
1605 			*buf = '\0';
1606 			*max_len = lws_ptr_diff_size_t(buf, bo);
1607 
1608 			return 0;
1609 		}
1610 		p++;
1611 	}
1612 
1613 	return 1;
1614 }
1615 
1616 #if defined(LWS_WITH_JOSE)
1617 
1618 #define MAX_JWT_SIZE 1024
1619 
1620 int
lws_jwt_get_http_cookie_validate_jwt(struct lws * wsi,struct lws_jwt_sign_set_cookie * i,char * out,size_t * out_len)1621 lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi,
1622 				     struct lws_jwt_sign_set_cookie *i,
1623 				     char *out, size_t *out_len)
1624 {
1625 	char temp[MAX_JWT_SIZE * 2];
1626 	size_t cml = *out_len;
1627 	const char *cp;
1628 
1629 	/* first use out to hold the encoded JWT */
1630 
1631 	if (lws_http_cookie_get(wsi, i->cookie_name, out, out_len)) {
1632 		lwsl_debug("%s: cookie %s not provided\n", __func__,
1633 				i->cookie_name);
1634 		return 1;
1635 	}
1636 
1637 	/* decode the JWT into temp */
1638 
1639 	if (lws_jwt_signed_validate(wsi->a.context, i->jwk, i->alg, out,
1640 				    *out_len, temp, sizeof(temp), out, &cml)) {
1641 		lwsl_info("%s: jwt validation failed\n", __func__);
1642 		return 1;
1643 	}
1644 
1645 	/*
1646 	 * Copy out the decoded JWT payload into out, overwriting the
1647 	 * original encoded JWT taken from the cookie (that has long ago been
1648 	 * translated into allocated buffers in the JOSE object)
1649 	 */
1650 
1651 	if (lws_jwt_token_sanity(out, cml, i->iss, i->aud, i->csrf_in,
1652 				 i->sub, sizeof(i->sub),
1653 				 &i->expiry_unix_time)) {
1654 		lwsl_notice("%s: jwt sanity failed\n", __func__);
1655 		return 1;
1656 	}
1657 
1658 	/*
1659 	 * If he's interested in his private JSON part, point him to that in
1660 	 * the args struct (it's pointing to the data in out
1661 	 */
1662 
1663 	cp = lws_json_simple_find(out, cml, "\"ext\":", &i->extra_json_len);
1664 	if (cp)
1665 		i->extra_json = cp;
1666 
1667 	if (!cp)
1668 		lwsl_notice("%s: no ext JWT payload\n", __func__);
1669 
1670 	return 0;
1671 }
1672 
1673 int
lws_jwt_sign_token_set_http_cookie(struct lws * wsi,const struct lws_jwt_sign_set_cookie * i,uint8_t ** p,uint8_t * end)1674 lws_jwt_sign_token_set_http_cookie(struct lws *wsi,
1675 				   const struct lws_jwt_sign_set_cookie *i,
1676 				   uint8_t **p, uint8_t *end)
1677 {
1678 	char plain[MAX_JWT_SIZE + 1], temp[MAX_JWT_SIZE * 2], csrf[17];
1679 	size_t pl = sizeof(plain);
1680 	unsigned long long ull;
1681 	int n;
1682 
1683 	/*
1684 	 * Create a 16-char random csrf token with the same lifetime as the JWT
1685 	 */
1686 
1687 	lws_hex_random(wsi->a.context, csrf, sizeof(csrf));
1688 	ull = lws_now_secs();
1689 	if (lws_jwt_sign_compact(wsi->a.context, i->jwk, i->alg, plain, &pl,
1690 			         temp, sizeof(temp),
1691 			         "{\"iss\":\"%s\",\"aud\":\"%s\","
1692 			          "\"iat\":%llu,\"nbf\":%llu,\"exp\":%llu,"
1693 			          "\"csrf\":\"%s\",\"sub\":\"%s\"%s%s%s}",
1694 			         i->iss, i->aud, ull, ull - 60,
1695 			         ull + i->expiry_unix_time,
1696 			         csrf, i->sub,
1697 			         i->extra_json ? ",\"ext\":{" : "",
1698 			         i->extra_json ? i->extra_json : "",
1699 			         i->extra_json ? "}" : "")) {
1700 		lwsl_err("%s: failed to create JWT\n", __func__);
1701 
1702 		return 1;
1703 	}
1704 
1705 	/*
1706 	 * There's no point the browser holding on to a JWT beyond the JWT's
1707 	 * expiry time, so set it to be the same.
1708 	 */
1709 
1710 	n = lws_snprintf(temp, sizeof(temp), "__Host-%s=%s;"
1711 			 "HttpOnly;"
1712 			 "Secure;"
1713 			 "SameSite=strict;"
1714 			 "Path=/;"
1715 			 "Max-Age=%lu",
1716 			 i->cookie_name, plain, i->expiry_unix_time);
1717 
1718 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SET_COOKIE,
1719 					 (uint8_t *)temp, n, p, end)) {
1720 		lwsl_err("%s: failed to add JWT cookie header\n", __func__);
1721 		return 1;
1722 	}
1723 
1724 	return 0;
1725 }
1726 #endif
1727