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