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 (h == WSI_TOKEN_HTTP_URI_ARGS)
549 		lwsl_err("%s: WSI_TOKEN_HTTP_URI_ARGS start frag %d\n",
550 				__func__, n);
551 
552 
553 	if (!n)
554 		return 0;
555 	do {
556 		comma = (wsi->http.ah->frags[n].nfrag) ? 1 : 0;
557 
558 		if (h == WSI_TOKEN_HTTP_URI_ARGS)
559 			lwsl_notice("%s: WSI_TOKEN_HTTP_URI_ARGS '%.*s'\n",
560 				    __func__, (int)wsi->http.ah->frags[n].len,
561 				    &wsi->http.ah->data[
562 				                wsi->http.ah->frags[n].offset]);
563 
564 		if (wsi->http.ah->frags[n].len + comma >= len) {
565 			lwsl_notice("blowout len\n");
566 			return -1;
567 		}
568 		strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
569 		        wsi->http.ah->frags[n].len);
570 		dst += wsi->http.ah->frags[n].len;
571 		len -= wsi->http.ah->frags[n].len;
572 		n = wsi->http.ah->frags[n].nfrag;
573 
574 		/*
575 		 * Note if you change this logic, take care about updating len
576 		 * and make sure lws_hdr_total_length() gives the same resulting
577 		 * length
578 		 */
579 
580 		if (comma) {
581 			if (h == WSI_TOKEN_HTTP_COOKIE ||
582 			    h == WSI_TOKEN_HTTP_SET_COOKIE)
583 				*dst++ = ';';
584 			else
585 				if (h == WSI_TOKEN_HTTP_URI_ARGS)
586 					*dst++ = '&';
587 				else
588 					*dst++ = ',';
589 			len--;
590 		}
591 
592 	} while (n);
593 	*dst = '\0';
594 
595 	if (h == WSI_TOKEN_HTTP_URI_ARGS)
596 		lwsl_err("%s: WSI_TOKEN_HTTP_URI_ARGS toklen %d\n", __func__, (int)toklen);
597 
598 	return toklen;
599 }
600 
601 #if defined(LWS_WITH_CUSTOM_HEADERS)
602 int
lws_hdr_custom_length(struct lws * wsi,const char * name,int nlen)603 lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen)
604 {
605 	ah_data_idx_t ll;
606 
607 	if (!wsi->http.ah || wsi->mux_substream)
608 		return -1;
609 
610 	ll = wsi->http.ah->unk_ll_head;
611 	while (ll) {
612 		if (ll >= wsi->http.ah->data_length)
613 			return -1;
614 		if (nlen == lws_ser_ru16be(
615 			(uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
616 		    !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], (unsigned int)nlen))
617 			return lws_ser_ru16be(
618 				(uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
619 
620 		ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
621 	}
622 
623 	return -1;
624 }
625 
626 int
lws_hdr_custom_copy(struct lws * wsi,char * dst,int len,const char * name,int nlen)627 lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
628 		    int nlen)
629 {
630 	ah_data_idx_t ll;
631 	int n;
632 
633 	if (!wsi->http.ah || wsi->mux_substream)
634 		return -1;
635 
636 	*dst = '\0';
637 
638 	ll = wsi->http.ah->unk_ll_head;
639 	while (ll) {
640 		if (ll >= wsi->http.ah->data_length)
641 			return -1;
642 		if (nlen == lws_ser_ru16be(
643 			(uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
644 		    !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], (unsigned int)nlen)) {
645 			n = lws_ser_ru16be(
646 				(uint8_t *)&wsi->http.ah->data[ll + UHO_VLEN]);
647 			if (n + 1 > len)
648 				return -1;
649 			strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + (unsigned int)nlen], (unsigned int)n);
650 			dst[n] = '\0';
651 
652 			return n;
653 		}
654 		ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
655 	}
656 
657 	return -1;
658 }
659 #endif
660 
lws_hdr_simple_ptr(struct lws * wsi,enum lws_token_indexes h)661 char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
662 {
663 	int n;
664 
665 	if (!wsi->http.ah)
666 		return NULL;
667 
668 	n = wsi->http.ah->frag_index[h];
669 	if (!n)
670 		return NULL;
671 
672 	return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
673 }
674 
675 static int LWS_WARN_UNUSED_RESULT
lws_pos_in_bounds(struct lws * wsi)676 lws_pos_in_bounds(struct lws *wsi)
677 {
678 	if (!wsi->http.ah)
679 		return -1;
680 
681 	if (wsi->http.ah->pos <
682 	    (unsigned int)wsi->a.context->max_http_header_data)
683 		return 0;
684 
685 	if ((int)wsi->http.ah->pos >= (int)wsi->a.context->max_http_header_data - 1) {
686 		lwsl_err("Ran out of header data space\n");
687 		return 1;
688 	}
689 
690 	/*
691 	 * with these tests everywhere, it should never be able to exceed
692 	 * the limit, only meet it
693 	 */
694 	lwsl_err("%s: pos %ld, limit %ld\n", __func__,
695 		 (unsigned long)wsi->http.ah->pos,
696 		 (unsigned long)wsi->a.context->max_http_header_data);
697 	assert(0);
698 
699 	return 1;
700 }
701 
702 int LWS_WARN_UNUSED_RESULT
lws_hdr_simple_create(struct lws * wsi,enum lws_token_indexes h,const char * s)703 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
704 {
705 	if (!*s) {
706 		/*
707 		 * If we get an empty string, then remove any entry for the
708 		 * header
709 		 */
710 		wsi->http.ah->frag_index[h] = 0;
711 
712 		return 0;
713 	}
714 
715 	wsi->http.ah->nfrag++;
716 	if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) {
717 		lwsl_warn("More hdr frags than we can deal with, dropping\n");
718 		return -1;
719 	}
720 
721 	wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
722 
723 	wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
724 	wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
725 	wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
726 
727 	do {
728 		if (lws_pos_in_bounds(wsi))
729 			return -1;
730 
731 		wsi->http.ah->data[wsi->http.ah->pos++] = *s;
732 		if (*s)
733 			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
734 	} while (*s++);
735 
736 	return 0;
737 }
738 
739 static int LWS_WARN_UNUSED_RESULT
issue_char(struct lws * wsi,unsigned char c)740 issue_char(struct lws *wsi, unsigned char c)
741 {
742 	unsigned short frag_len;
743 
744 	if (lws_pos_in_bounds(wsi))
745 		return -1;
746 
747 	frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
748 	/*
749 	 * If we haven't hit the token limit, just copy the character into
750 	 * the header
751 	 */
752 	if (!wsi->http.ah->current_token_limit ||
753 	    frag_len < wsi->http.ah->current_token_limit) {
754 		wsi->http.ah->data[wsi->http.ah->pos++] = (char)c;
755 		wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
756 		return 0;
757 	}
758 
759 	/* Insert a null character when we *hit* the limit: */
760 	if (frag_len == wsi->http.ah->current_token_limit) {
761 		if (lws_pos_in_bounds(wsi))
762 			return -1;
763 
764 		wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
765 		lwsl_warn("header %li exceeds limit %ld\n",
766 			  (long)wsi->http.ah->parser_state,
767 			  (long)wsi->http.ah->current_token_limit);
768 	}
769 
770 	return 1;
771 }
772 
773 int
lws_parse_urldecode(struct lws * wsi,uint8_t * _c)774 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
775 {
776 	struct allocated_headers *ah = wsi->http.ah;
777 	unsigned int enc = 0;
778 	uint8_t c = *_c;
779 
780 	// lwsl_notice("ah->ups %d\n", ah->ups);
781 
782 	/*
783 	 * PRIORITY 1
784 	 * special URI processing... convert %xx
785 	 */
786 	switch (ah->ues) {
787 	case URIES_IDLE:
788 		if (c == '%') {
789 			ah->ues = URIES_SEEN_PERCENT;
790 			goto swallow;
791 		}
792 		break;
793 	case URIES_SEEN_PERCENT:
794 		if (char_to_hex((char)c) < 0)
795 			/* illegal post-% char */
796 			goto forbid;
797 
798 		ah->esc_stash = (char)c;
799 		ah->ues = URIES_SEEN_PERCENT_H1;
800 		goto swallow;
801 
802 	case URIES_SEEN_PERCENT_H1:
803 		if (char_to_hex((char)c) < 0)
804 			/* illegal post-% char */
805 			goto forbid;
806 
807 		*_c = (uint8_t)(unsigned int)((char_to_hex(ah->esc_stash) << 4) |
808 				char_to_hex((char)c));
809 		c = *_c;
810 		enc = 1;
811 		ah->ues = URIES_IDLE;
812 		break;
813 	}
814 
815 	/*
816 	 * PRIORITY 2
817 	 * special URI processing...
818 	 *  convert /.. or /... or /../ etc to /
819 	 *  convert /./ to /
820 	 *  convert // or /// etc to /
821 	 *  leave /.dir or whatever alone
822 	 */
823 
824 	if (!c && (!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] ||
825 		   !ah->post_literal_equal)) {
826 		/*
827 		 * Since user code is typically going to parse the path using
828 		 * NUL-terminated apis, it's too dangerous to allow NUL
829 		 * injection here.
830 		 *
831 		 * It's allowed in the urlargs, because the apis to access
832 		 * those only allow retreival with explicit length.
833 		 */
834 		lwsl_warn("%s: saw NUL outside of uri args\n", __func__);
835 		return -1;
836 	}
837 
838 	switch (ah->ups) {
839 	case URIPS_IDLE:
840 
841 		/* genuine delimiter */
842 		if ((c == '&' || c == ';') && !enc) {
843 			if (issue_char(wsi, '\0') < 0)
844 				return -1;
845 			/* don't account for it */
846 			wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
847 			/* link to next fragment */
848 			ah->frags[ah->nfrag].nfrag = (uint8_t)(ah->nfrag + 1);
849 			ah->nfrag++;
850 			if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
851 				goto excessive;
852 			/* start next fragment after the & */
853 			ah->post_literal_equal = 0;
854 			ah->frags[ah->nfrag].offset = ++ah->pos;
855 			ah->frags[ah->nfrag].len = 0;
856 			ah->frags[ah->nfrag].nfrag = 0;
857 			goto swallow;
858 		}
859 		/* uriencoded = in the name part, disallow */
860 		if (c == '=' && enc &&
861 		    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
862 		    !ah->post_literal_equal) {
863 			c = '_';
864 			*_c =c;
865 		}
866 
867 		/* after the real =, we don't care how many = */
868 		if (c == '=' && !enc)
869 			ah->post_literal_equal = 1;
870 
871 		/* + to space */
872 		if (c == '+' && !enc) {
873 			c = ' ';
874 			*_c = c;
875 		}
876 		/* issue the first / always */
877 		if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
878 			ah->ups = URIPS_SEEN_SLASH;
879 		break;
880 	case URIPS_SEEN_SLASH:
881 		/* swallow subsequent slashes */
882 		if (c == '/')
883 			goto swallow;
884 		/* track and swallow the first . after / */
885 		if (c == '.') {
886 			ah->ups = URIPS_SEEN_SLASH_DOT;
887 			goto swallow;
888 		}
889 		ah->ups = URIPS_IDLE;
890 		break;
891 	case URIPS_SEEN_SLASH_DOT:
892 		/* swallow second . */
893 		if (c == '.') {
894 			ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
895 			goto swallow;
896 		}
897 		/* change /./ to / */
898 		if (c == '/') {
899 			ah->ups = URIPS_SEEN_SLASH;
900 			goto swallow;
901 		}
902 		/* it was like /.dir ... regurgitate the . */
903 		ah->ups = URIPS_IDLE;
904 		if (issue_char(wsi, '.') < 0)
905 			return -1;
906 		break;
907 
908 	case URIPS_SEEN_SLASH_DOT_DOT:
909 
910 		/* /../ or /..[End of URI] --> backup to last / */
911 		if (c == '/' || c == '?') {
912 			/*
913 			 * back up one dir level if possible
914 			 * safe against header fragmentation because
915 			 * the method URI can only be in 1 fragment
916 			 */
917 			if (ah->frags[ah->nfrag].len > 2) {
918 				ah->pos--;
919 				ah->frags[ah->nfrag].len--;
920 				do {
921 					ah->pos--;
922 					ah->frags[ah->nfrag].len--;
923 				} while (ah->frags[ah->nfrag].len > 1 &&
924 					 ah->data[ah->pos] != '/');
925 			}
926 			ah->ups = URIPS_SEEN_SLASH;
927 			if (ah->frags[ah->nfrag].len > 1)
928 				break;
929 			goto swallow;
930 		}
931 
932 		/*  /..[^/] ... regurgitate and allow */
933 
934 		if (issue_char(wsi, '.') < 0)
935 			return -1;
936 		if (issue_char(wsi, '.') < 0)
937 			return -1;
938 		ah->ups = URIPS_IDLE;
939 		break;
940 	}
941 
942 	if (c == '?' && !enc &&
943 	    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
944 		if (ah->ues != URIES_IDLE)
945 			goto forbid;
946 
947 		/* seal off uri header */
948 		if (issue_char(wsi, '\0') < 0)
949 			return -1;
950 
951 		/* don't account for it */
952 		wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
953 
954 		/* move to using WSI_TOKEN_HTTP_URI_ARGS */
955 		ah->nfrag++;
956 		if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
957 			goto excessive;
958 		ah->frags[ah->nfrag].offset = ++ah->pos;
959 		ah->frags[ah->nfrag].len = 0;
960 		ah->frags[ah->nfrag].nfrag = 0;
961 
962 		ah->post_literal_equal = 0;
963 		ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
964 		ah->ups = URIPS_IDLE;
965 		goto swallow;
966 	}
967 
968 	return LPUR_CONTINUE;
969 
970 swallow:
971 	return LPUR_SWALLOW;
972 
973 forbid:
974 	return LPUR_FORBID;
975 
976 excessive:
977 	return LPUR_EXCESSIVE;
978 }
979 
980 static const unsigned char methods[] = {
981 	WSI_TOKEN_GET_URI,
982 	WSI_TOKEN_POST_URI,
983 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
984 	WSI_TOKEN_OPTIONS_URI,
985 	WSI_TOKEN_PUT_URI,
986 	WSI_TOKEN_PATCH_URI,
987 	WSI_TOKEN_DELETE_URI,
988 #endif
989 	WSI_TOKEN_CONNECT,
990 	WSI_TOKEN_HEAD_URI,
991 };
992 
993 /*
994  * possible returns:, -1 fail, 0 ok or 2, transition to raw
995  */
996 
997 lws_parser_return_t LWS_WARN_UNUSED_RESULT
lws_parse(struct lws * wsi,unsigned char * buf,int * len)998 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
999 {
1000 	struct allocated_headers *ah = wsi->http.ah;
1001 	struct lws_context *context = wsi->a.context;
1002 	unsigned int n, m;
1003 	unsigned char c;
1004 	int r, pos;
1005 
1006 	assert(wsi->http.ah);
1007 
1008 	do {
1009 		(*len)--;
1010 		c = *buf++;
1011 
1012 		switch (ah->parser_state) {
1013 #if defined(LWS_WITH_CUSTOM_HEADERS)
1014 		case WSI_TOKEN_UNKNOWN_VALUE_PART:
1015 
1016 			if (c == '\r')
1017 				break;
1018 			if (c == '\n') {
1019 				lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos + 2],
1020 					       (uint16_t)(ah->pos - ah->unk_value_pos));
1021 				ah->parser_state = WSI_TOKEN_NAME_PART;
1022 				ah->unk_pos = 0;
1023 				ah->lextable_pos = 0;
1024 				break;
1025 			}
1026 
1027 			/* trim leading whitespace */
1028 			if (ah->pos != ah->unk_value_pos ||
1029 			    (c != ' ' && c != '\t')) {
1030 
1031 				if (lws_pos_in_bounds(wsi))
1032 					return LPR_FAIL;
1033 
1034 				ah->data[ah->pos++] = (char)c;
1035 			}
1036 			pos = ah->lextable_pos;
1037 			break;
1038 #endif
1039 		default:
1040 
1041 			lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
1042 
1043 			/* collect into malloc'd buffers */
1044 			/* optional initial space swallow */
1045 			if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
1046 			    c == ' ')
1047 				break;
1048 
1049 			for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1050 				if (ah->parser_state == methods[m])
1051 					break;
1052 			if (m == LWS_ARRAY_SIZE(methods))
1053 				/* it was not any of the methods */
1054 				goto check_eol;
1055 
1056 			/* special URI processing... end at space */
1057 
1058 			if (c == ' ') {
1059 				/* enforce starting with / */
1060 				if (!ah->frags[ah->nfrag].len)
1061 					if (issue_char(wsi, '/') < 0)
1062 						return LPR_FAIL;
1063 
1064 				if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
1065 					/*
1066 					 * back up one dir level if possible
1067 					 * safe against header fragmentation
1068 					 * because the method URI can only be
1069 					 * in 1 fragment
1070 					 */
1071 					if (ah->frags[ah->nfrag].len > 2) {
1072 						ah->pos--;
1073 						ah->frags[ah->nfrag].len--;
1074 						do {
1075 							ah->pos--;
1076 							ah->frags[ah->nfrag].len--;
1077 						} while (ah->frags[ah->nfrag].len > 1 &&
1078 							 ah->data[ah->pos] != '/');
1079 					}
1080 				}
1081 
1082 				/* begin parsing HTTP version: */
1083 				if (issue_char(wsi, '\0') < 0)
1084 					return LPR_FAIL;
1085 				/* don't account for it */
1086 				wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
1087 				ah->parser_state = WSI_TOKEN_HTTP;
1088 				goto start_fragment;
1089 			}
1090 
1091 			r = lws_parse_urldecode(wsi, &c);
1092 			switch (r) {
1093 			case LPUR_CONTINUE:
1094 				break;
1095 			case LPUR_SWALLOW:
1096 				goto swallow;
1097 			case LPUR_FORBID:
1098 				goto forbid;
1099 			case LPUR_EXCESSIVE:
1100 				goto excessive;
1101 			default:
1102 				return LPR_FAIL;
1103 			}
1104 check_eol:
1105 			/* bail at EOL */
1106 			if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
1107 			    (c == '\x0d' || c == '\x0a')) {
1108 				if (ah->ues != URIES_IDLE)
1109 					goto forbid;
1110 
1111 				if (c == '\x0a') {
1112 					/* broken peer */
1113 					ah->parser_state = WSI_TOKEN_NAME_PART;
1114 					ah->unk_pos = 0;
1115 					ah->lextable_pos = 0;
1116 				} else
1117 					ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1118 
1119 				c = '\0';
1120 				lwsl_parser("*\n");
1121 			}
1122 
1123 			n = (unsigned int)issue_char(wsi, c);
1124 			if ((int)n < 0)
1125 				return LPR_FAIL;
1126 			if (n > 0)
1127 				ah->parser_state = WSI_TOKEN_SKIPPING;
1128 			else {
1129 				/*
1130 				 * Explicit zeroes are legal in URI ARGS.
1131 				 * They can only exist as a safety terminator
1132 				 * after the valid part of the token contents
1133 				 * for other types.
1134 				 */
1135 				if (!c && ah->parser_state != WSI_TOKEN_HTTP_URI_ARGS)
1136 					/* don't account for safety terminator */
1137 					wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
1138 			}
1139 
1140 swallow:
1141 			/* per-protocol end of headers management */
1142 
1143 			if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1144 				goto set_parsing_complete;
1145 			break;
1146 
1147 			/* collecting and checking a name part */
1148 		case WSI_TOKEN_NAME_PART:
1149 			lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X "
1150 				    "(role=0x%lx) "
1151 				    "wsi->lextable_pos=%d\n", c, c,
1152 				    (unsigned long)lwsi_role(wsi),
1153 				    ah->lextable_pos);
1154 
1155 			if (!ah->unk_pos && c == '\x0a')
1156 				/* broken peer */
1157 				goto set_parsing_complete;
1158 
1159 			if (c >= 'A' && c <= 'Z')
1160 				c = (unsigned char)(c + 'a' - 'A');
1161 			/*
1162 			 * ...in case it's an unknown header, speculatively
1163 			 * store it as the name comes in.  If we recognize it as
1164 			 * a known header, we'll snip this.
1165 			 */
1166 
1167 			if (!wsi->mux_substream && !ah->unk_pos) {
1168 				ah->unk_pos = ah->pos;
1169 
1170 #if defined(LWS_WITH_CUSTOM_HEADERS)
1171 				/*
1172 				 * Prepare new unknown header linked-list entry
1173 				 *
1174 				 *  - 16-bit BE: name part length
1175 				 *  - 16-bit BE: value part length
1176 				 *  - 32-bit BE: data offset of next, or 0
1177 				 */
1178 				for (n = 0; n < 8; n++)
1179 					if (!lws_pos_in_bounds(wsi))
1180 						ah->data[ah->pos++] = 0;
1181 #endif
1182 			}
1183 
1184 			if (lws_pos_in_bounds(wsi))
1185 				return LPR_FAIL;
1186 
1187 			ah->data[ah->pos++] = (char)c;
1188 			pos = ah->lextable_pos;
1189 
1190 #if defined(LWS_WITH_CUSTOM_HEADERS)
1191 			if (!wsi->mux_substream && pos < 0 && c == ':') {
1192 #if defined(_DEBUG)
1193 				char dotstar[64];
1194 				int uhlen;
1195 #endif
1196 
1197 				/*
1198 				 * process unknown headers
1199 				 *
1200 				 * register us in the unknown hdr ll
1201 				 */
1202 
1203 				if (!ah->unk_ll_head)
1204 					ah->unk_ll_head = ah->unk_pos;
1205 
1206 				if (ah->unk_ll_tail)
1207 					lws_ser_wu32be(
1208 				(uint8_t *)&ah->data[ah->unk_ll_tail + UHO_LL],
1209 						       ah->unk_pos);
1210 
1211 				ah->unk_ll_tail = ah->unk_pos;
1212 
1213 #if defined(_DEBUG)
1214 				uhlen = (int)(ah->pos - (ah->unk_pos + UHO_NAME));
1215 				lws_strnncpy(dotstar,
1216 					&ah->data[ah->unk_pos + UHO_NAME],
1217 					uhlen, sizeof(dotstar));
1218 				lwsl_debug("%s: unk header %d '%s'\n",
1219 					    __func__,
1220 					    ah->pos - (ah->unk_pos + UHO_NAME),
1221 					    dotstar);
1222 #endif
1223 
1224 				/* set the unknown header name part length */
1225 
1226 				lws_ser_wu16be((uint8_t *)&ah->data[ah->unk_pos],
1227 					       (uint16_t)((ah->pos - ah->unk_pos) - UHO_NAME));
1228 
1229 				ah->unk_value_pos = ah->pos;
1230 
1231 				/*
1232 				 * collect whatever's coming for the unknown header
1233 				 * argument until the next CRLF
1234 				 */
1235 				ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART;
1236 				break;
1237 			}
1238 #endif
1239 			if (pos < 0)
1240 				break;
1241 
1242 			while (1) {
1243 				if (lextable_h1[pos] & (1 << 7)) {
1244 					/* 1-byte, fail on mismatch */
1245 					if ((lextable_h1[pos] & 0x7f) != c) {
1246 nope:
1247 						ah->lextable_pos = -1;
1248 						break;
1249 					}
1250 					/* fall thru */
1251 					pos++;
1252 					if (lextable_h1[pos] == FAIL_CHAR)
1253 						goto nope;
1254 
1255 					ah->lextable_pos = (int16_t)pos;
1256 					break;
1257 				}
1258 
1259 				if (lextable_h1[pos] == FAIL_CHAR)
1260 					goto nope;
1261 
1262 				/* b7 = 0, end or 3-byte */
1263 				if (lextable_h1[pos] < FAIL_CHAR) {
1264 					if (!wsi->mux_substream) {
1265 						/*
1266 						 * We hit a terminal marker, so
1267 						 * we recognized this header...
1268 						 * drop the speculative name
1269 						 * part storage
1270 						 */
1271 						ah->pos = ah->unk_pos;
1272 						ah->unk_pos = 0;
1273 					}
1274 
1275 					ah->lextable_pos = (int16_t)pos;
1276 					break;
1277 				}
1278 
1279 				if (lextable_h1[pos] == c) { /* goto */
1280 					ah->lextable_pos = (int16_t)(pos +
1281 						(lextable_h1[pos + 1]) +
1282 						(lextable_h1[pos + 2] << 8));
1283 					break;
1284 				}
1285 
1286 				/* fall thru goto */
1287 				pos += 3;
1288 				/* continue */
1289 			}
1290 
1291 			/*
1292 			 * If it's h1, server needs to be on the look out for
1293 			 * unknown methods...
1294 			 */
1295 			if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
1296 			    lwsi_role_server(wsi)) {
1297 				/*
1298 				 * this is not a header we know about... did
1299 				 * we get a valid method (GET, POST etc)
1300 				 * already, or is this the bogus method?
1301 				 */
1302 				for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1303 					if (ah->frag_index[methods[m]]) {
1304 						/*
1305 						 * already had the method
1306 						 */
1307 #if !defined(LWS_WITH_CUSTOM_HEADERS)
1308 						ah->parser_state = WSI_TOKEN_SKIPPING;
1309 #endif
1310 						if (wsi->mux_substream)
1311 							ah->parser_state = WSI_TOKEN_SKIPPING;
1312 						break;
1313 					}
1314 
1315 				if (m != LWS_ARRAY_SIZE(methods)) {
1316 #if defined(LWS_WITH_CUSTOM_HEADERS)
1317 					/*
1318 					 * We have the method, this is just an
1319 					 * unknown header then
1320 					 */
1321 					if (!wsi->mux_substream)
1322 						goto unknown_hdr;
1323 					else
1324 						break;
1325 #else
1326 					break;
1327 #endif
1328 				}
1329 				/*
1330 				 * ...it's an unknown http method from a client
1331 				 * in fact, it cannot be valid http.
1332 				 *
1333 				 * Are we set up to transition to another role
1334 				 * in these cases?
1335 				 */
1336 				if (lws_check_opt(wsi->a.vhost->options,
1337 		    LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) {
1338 					lwsl_notice("%s: http fail fallback\n",
1339 						    __func__);
1340 					 /* transition to other role */
1341 					return LPR_DO_FALLBACK;
1342 				}
1343 
1344 				lwsl_info("Unknown method - dropping\n");
1345 				goto forbid;
1346 			}
1347 			if (ah->lextable_pos < 0) {
1348 				/*
1349 				 * It's not a header that lws knows about...
1350 				 */
1351 #if defined(LWS_WITH_CUSTOM_HEADERS)
1352 				if (!wsi->mux_substream)
1353 					goto unknown_hdr;
1354 #endif
1355 				/*
1356 				 * ...otherwise for a client, let him ignore
1357 				 * unknown headers coming from the server
1358 				 */
1359 				ah->parser_state = WSI_TOKEN_SKIPPING;
1360 				break;
1361 			}
1362 
1363 			if (lextable_h1[ah->lextable_pos] < FAIL_CHAR) {
1364 				/* terminal state */
1365 
1366 				n = ((unsigned int)lextable_h1[ah->lextable_pos] << 8) |
1367 						lextable_h1[ah->lextable_pos + 1];
1368 
1369 				lwsl_parser("known hdr %d\n", n);
1370 				for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1371 					if (n == methods[m] &&
1372 					    ah->frag_index[methods[m]]) {
1373 						lwsl_warn("Duplicated method\n");
1374 						return LPR_FAIL;
1375 					}
1376 
1377 				if (!wsi->mux_substream) {
1378 					/*
1379 					 * Whether we are collecting unknown names or not,
1380 					 * if we matched an internal header we can dispense
1381 					 * with the header name part we were keeping
1382 					 */
1383 					ah->pos = ah->unk_pos;
1384 					ah->unk_pos = 0;
1385 				}
1386 
1387 #if defined(LWS_ROLE_WS)
1388 				/*
1389 				 * WSORIGIN is protocol equiv to ORIGIN,
1390 				 * JWebSocket likes to send it, map to ORIGIN
1391 				 */
1392 				if (n == WSI_TOKEN_SWORIGIN)
1393 					n = WSI_TOKEN_ORIGIN;
1394 #endif
1395 
1396 				ah->parser_state = (uint8_t)
1397 							(WSI_TOKEN_GET_URI + n);
1398 				ah->ups = URIPS_IDLE;
1399 
1400 				if (context->token_limits)
1401 					ah->current_token_limit = context->
1402 						token_limits->token_limit[
1403 							      ah->parser_state];
1404 				else
1405 					ah->current_token_limit =
1406 						wsi->a.context->max_http_header_data;
1407 
1408 				if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1409 					goto set_parsing_complete;
1410 
1411 				goto start_fragment;
1412 			}
1413 			break;
1414 
1415 #if defined(LWS_WITH_CUSTOM_HEADERS)
1416 unknown_hdr:
1417 			//ah->parser_state = WSI_TOKEN_SKIPPING;
1418 			//break;
1419 			if (!wsi->mux_substream)
1420 				break;
1421 #endif
1422 
1423 start_fragment:
1424 			ah->nfrag++;
1425 excessive:
1426 			if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
1427 				lwsl_warn("More hdr frags than we can deal with\n");
1428 				return LPR_FAIL;
1429 			}
1430 
1431 			ah->frags[ah->nfrag].offset = ah->pos;
1432 			ah->frags[ah->nfrag].len = 0;
1433 			ah->frags[ah->nfrag].nfrag = 0;
1434 			ah->frags[ah->nfrag].flags = 2;
1435 
1436 			n = ah->frag_index[ah->parser_state];
1437 			if (!n) { /* first fragment */
1438 				ah->frag_index[ah->parser_state] = ah->nfrag;
1439 				ah->hdr_token_idx = ah->parser_state;
1440 				break;
1441 			}
1442 			/* continuation */
1443 			while (ah->frags[n].nfrag)
1444 				n = ah->frags[n].nfrag;
1445 			ah->frags[n].nfrag = ah->nfrag;
1446 
1447 			if (issue_char(wsi, ' ') < 0)
1448 				return LPR_FAIL;
1449 			break;
1450 
1451 			/* skipping arg part of a name we didn't recognize */
1452 		case WSI_TOKEN_SKIPPING:
1453 			lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
1454 
1455 			if (c == '\x0a') {
1456 				/* broken peer */
1457 				ah->parser_state = WSI_TOKEN_NAME_PART;
1458 				ah->unk_pos = 0;
1459 				ah->lextable_pos = 0;
1460 			}
1461 
1462 			if (c == '\x0d')
1463 				ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1464 			break;
1465 
1466 		case WSI_TOKEN_SKIPPING_SAW_CR:
1467 			lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
1468 			if (ah->ues != URIES_IDLE)
1469 				goto forbid;
1470 			if (c == '\x0a') {
1471 				ah->parser_state = WSI_TOKEN_NAME_PART;
1472 				ah->unk_pos = 0;
1473 				ah->lextable_pos = 0;
1474 			} else
1475 				ah->parser_state = WSI_TOKEN_SKIPPING;
1476 			break;
1477 			/* we're done, ignore anything else */
1478 
1479 		case WSI_PARSING_COMPLETE:
1480 			lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
1481 			break;
1482 		}
1483 
1484 	} while (*len);
1485 
1486 	return LPR_OK;
1487 
1488 set_parsing_complete:
1489 	if (ah->ues != URIES_IDLE)
1490 		goto forbid;
1491 
1492 	if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1493 #if defined(LWS_ROLE_WS)
1494 		const char *pv = lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION);
1495 		if (pv)
1496 			wsi->rx_frame_type = (char)atoi(pv);
1497 
1498 		lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
1499 #endif
1500 	}
1501 	ah->parser_state = WSI_PARSING_COMPLETE;
1502 	wsi->hdr_parsing_completed = 1;
1503 
1504 	return LPR_OK;
1505 
1506 forbid:
1507 	lwsl_info(" forbidding on uri sanitation\n");
1508 #if defined(LWS_WITH_SERVER)
1509 	lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1510 #endif
1511 
1512 	return LPR_FORBIDDEN;
1513 }
1514 
1515 int
lws_http_cookie_get(struct lws * wsi,const char * name,char * buf,size_t * max_len)1516 lws_http_cookie_get(struct lws *wsi, const char *name, char *buf,
1517 		    size_t *max_len)
1518 {
1519 	size_t max = *max_len, bl = strlen(name);
1520 	char *p, *bo = buf;
1521 	int n;
1522 
1523 	n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
1524 	if ((unsigned int)n < bl + 1)
1525 		return 1;
1526 
1527 	/*
1528 	 * This can come to us two ways, in ah fragments (h2) or as a single
1529 	 * semicolon-delimited string (h1)
1530 	 */
1531 
1532 #if defined(LWS_ROLE_H2)
1533 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD)) {
1534 
1535 		/*
1536 		 * The h2 way...
1537 		 */
1538 
1539 		int f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_COOKIE];
1540 		size_t fl;
1541 
1542 		while (f) {
1543 			p = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
1544 			fl = (size_t)wsi->http.ah->frags[f].len;
1545 			if (fl >= bl + 1 &&
1546 			    p[bl] == '=' &&
1547 			    !memcmp(p, name, bl)) {
1548 				fl -= bl + 1;
1549 				if (max - 1 < fl)
1550 					fl = max - 1;
1551 				if (fl)
1552 					memcpy(buf, p + bl + 1, fl);
1553 				*max_len = fl;
1554 				buf[fl] = '\0';
1555 
1556 				return 0;
1557 			}
1558 			f = wsi->http.ah->frags[f].nfrag;
1559 		}
1560 
1561 		return -1;
1562 	}
1563 #endif
1564 
1565 	/*
1566 	 * The h1 way...
1567 	 */
1568 
1569 	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE);
1570 	if (!p)
1571 		return 1;
1572 
1573 	p += bl;
1574 	n -= (int)bl;
1575 	while (n-- > (int)bl) {
1576 		if (*p == '=' && !memcmp(p - bl, name, (unsigned int)bl)) {
1577 			p++;
1578 			while (*p != ';' && n-- && max) {
1579 				*buf++ = *p++;
1580 				max--;
1581 			}
1582 			if (!max)
1583 				return 2;
1584 
1585 			*buf = '\0';
1586 			*max_len = lws_ptr_diff_size_t(buf, bo);
1587 
1588 			return 0;
1589 		}
1590 		p++;
1591 	}
1592 
1593 	return 1;
1594 }
1595 
1596 #if defined(LWS_WITH_JOSE)
1597 
1598 #define MAX_JWT_SIZE 1024
1599 
1600 int
lws_jwt_get_http_cookie_validate_jwt(struct lws * wsi,struct lws_jwt_sign_set_cookie * i,char * out,size_t * out_len)1601 lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi,
1602 				     struct lws_jwt_sign_set_cookie *i,
1603 				     char *out, size_t *out_len)
1604 {
1605 	char temp[MAX_JWT_SIZE * 2];
1606 	size_t cml = *out_len;
1607 	const char *cp;
1608 
1609 	/* first use out to hold the encoded JWT */
1610 
1611 	if (lws_http_cookie_get(wsi, i->cookie_name, out, out_len)) {
1612 		lwsl_debug("%s: cookie %s not provided\n", __func__,
1613 				i->cookie_name);
1614 		return 1;
1615 	}
1616 
1617 	/* decode the JWT into temp */
1618 
1619 	if (lws_jwt_signed_validate(wsi->a.context, i->jwk, i->alg, out,
1620 				    *out_len, temp, sizeof(temp), out, &cml)) {
1621 		lwsl_info("%s: jwt validation failed\n", __func__);
1622 		return 1;
1623 	}
1624 
1625 	/*
1626 	 * Copy out the decoded JWT payload into out, overwriting the
1627 	 * original encoded JWT taken from the cookie (that has long ago been
1628 	 * translated into allocated buffers in the JOSE object)
1629 	 */
1630 
1631 	if (lws_jwt_token_sanity(out, cml, i->iss, i->aud, i->csrf_in,
1632 				 i->sub, sizeof(i->sub),
1633 				 &i->expiry_unix_time)) {
1634 		lwsl_notice("%s: jwt sanity failed\n", __func__);
1635 		return 1;
1636 	}
1637 
1638 	/*
1639 	 * If he's interested in his private JSON part, point him to that in
1640 	 * the args struct (it's pointing to the data in out
1641 	 */
1642 
1643 	cp = lws_json_simple_find(out, cml, "\"ext\":", &i->extra_json_len);
1644 	if (cp)
1645 		i->extra_json = cp;
1646 
1647 	if (!cp)
1648 		lwsl_notice("%s: no ext JWT payload\n", __func__);
1649 
1650 	return 0;
1651 }
1652 
1653 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)1654 lws_jwt_sign_token_set_http_cookie(struct lws *wsi,
1655 				   const struct lws_jwt_sign_set_cookie *i,
1656 				   uint8_t **p, uint8_t *end)
1657 {
1658 	char plain[MAX_JWT_SIZE + 1], temp[MAX_JWT_SIZE * 2], csrf[17];
1659 	size_t pl = sizeof(plain);
1660 	unsigned long long ull;
1661 	int n;
1662 
1663 	/*
1664 	 * Create a 16-char random csrf token with the same lifetime as the JWT
1665 	 */
1666 
1667 	lws_hex_random(wsi->a.context, csrf, sizeof(csrf));
1668 	ull = lws_now_secs();
1669 	if (lws_jwt_sign_compact(wsi->a.context, i->jwk, i->alg, plain, &pl,
1670 			         temp, sizeof(temp),
1671 			         "{\"iss\":\"%s\",\"aud\":\"%s\","
1672 			          "\"iat\":%llu,\"nbf\":%llu,\"exp\":%llu,"
1673 			          "\"csrf\":\"%s\",\"sub\":\"%s\"%s%s%s}",
1674 			         i->iss, i->aud, ull, ull - 60,
1675 			         ull + i->expiry_unix_time,
1676 			         csrf, i->sub,
1677 			         i->extra_json ? ",\"ext\":{" : "",
1678 			         i->extra_json ? i->extra_json : "",
1679 			         i->extra_json ? "}" : "")) {
1680 		lwsl_err("%s: failed to create JWT\n", __func__);
1681 
1682 		return 1;
1683 	}
1684 
1685 	/*
1686 	 * There's no point the browser holding on to a JWT beyond the JWT's
1687 	 * expiry time, so set it to be the same.
1688 	 */
1689 
1690 	n = lws_snprintf(temp, sizeof(temp), "__Host-%s=%s;"
1691 			 "HttpOnly;"
1692 			 "Secure;"
1693 			 "SameSite=strict;"
1694 			 "Path=/;"
1695 			 "Max-Age=%lu",
1696 			 i->cookie_name, plain, i->expiry_unix_time);
1697 
1698 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SET_COOKIE,
1699 					 (uint8_t *)temp, n, p, end)) {
1700 		lwsl_err("%s: failed to add JWT cookie header\n", __func__);
1701 		return 1;
1702 	}
1703 
1704 	return 0;
1705 }
1706 #endif
1707