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