Coverage Report

Created: 2026-09-04 06:34

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