Coverage Report

Created: 2026-04-09 06:20

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