Coverage Report

Created: 2023-11-27 06:39

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