Coverage Report

Created: 2026-07-16 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/http/cookie.c
Line
Count
Source
1
2
#include <libwebsockets.h>
3
#include "private-lib-core.h"
4
5
//#define LWS_COOKIE_DEBUG
6
7
#if defined(LWS_COOKIE_DEBUG)
8
  #define lwsl_cookie lwsl_notice
9
#else
10
0
  #define lwsl_cookie lwsl_debug
11
#endif
12
13
#define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
14
15
0
#define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
16
0
              (char)((_c) + 'a' - 'A') : \
17
0
              (char)(_c))
18
19
0
#define LWS_COOKIE_NSC_FORMAT     "%.*s\t"\
20
0
            "%s\t"\
21
0
            "%.*s\t"\
22
0
            "%s\t"\
23
0
            "%llu\t"\
24
0
            "%.*s\t"\
25
0
            "%.*s"
26
27
static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
28
29
enum lws_cookie_nsc_f {
30
  LWSC_NSC_DOMAIN,
31
  LWSC_NSC_HOSTONLY,
32
  LWSC_NSC_PATH,
33
  LWSC_NSC_SECURE,
34
  LWSC_NSC_EXPIRES,
35
  LWSC_NSC_NAME,
36
  LWSC_NSC_VALUE,
37
38
  LWSC_NSC_COUNT,
39
};
40
41
enum lws_cookie_elements {
42
  CE_DOMAIN,
43
  CE_PATH,
44
  CE_EXPIRES,
45
  CE_MAXAGE,
46
  CE_NAME,
47
  CE_VALUE,
48
49
  CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
50
  CE_SECURE,
51
52
  CE_COUNT
53
};
54
55
struct lws_cookie {
56
  const char  *f[CE_COUNT];
57
  size_t    l[CE_COUNT];
58
59
  unsigned int httponly:1;
60
};
61
62
static size_t
63
lws_cookie_domain_len(const char *domain)
64
0
{
65
0
  const char *p;
66
67
0
  if (!domain)
68
0
    return 0;
69
70
0
  if (domain[0] == '[') {
71
0
    p = (char *)strchr(domain, ']');
72
0
    if (p && p[1] == ':')
73
0
      return lws_ptr_diff_size_t((p + 1), domain);
74
0
  } else {
75
0
    p = (char *)strchr(domain, ':');
76
0
    if (p)
77
0
      return lws_ptr_diff_size_t(p, domain);
78
0
  }
79
80
0
  return strlen(domain);
81
0
}
82
83
static int
84
lws_cookie_parse_date(const char *d, size_t len, time_t *t)
85
0
{
86
0
  struct tm date;
87
0
  int offset = 0, i;
88
89
0
  memset(&date, 0, sizeof(date));
90
91
0
  while (len) {
92
0
    if (isalnum((int)*d)) {
93
0
      offset++;
94
0
      goto next;
95
0
    }
96
0
    switch (offset) {
97
0
    case 2:
98
0
      if (*d == ':' && len >= 6) {
99
0
        date.tm_hour = atoi(d - 2);
100
0
        if (date.tm_hour < 0 || date.tm_hour > 23)
101
0
          return -1;
102
0
        date.tm_min = atoi(d + 1);
103
0
        if (date.tm_min < 0 || date.tm_min > 60)
104
0
          return -1;
105
0
        date.tm_sec = atoi(d + 4);
106
0
        if (date.tm_sec < 0 || date.tm_sec > 61)
107
          /* leap second */
108
0
          return -1;
109
110
0
        d += 6;
111
0
        len -= 6;
112
0
        offset = 0;
113
0
        continue;
114
0
      }
115
116
0
      if (!date.tm_mday) {
117
0
        date.tm_mday = atoi(d - 2);
118
0
        if (date.tm_mday < 1 || date.tm_mday > 31)
119
0
          return -1;
120
0
        goto next2;
121
0
      }
122
123
0
      if (!date.tm_year) {
124
0
        date.tm_year = atoi(d - 2);
125
0
        if (date.tm_year < 0 || date.tm_year > 99)
126
0
          return -1;
127
0
        if (date.tm_year < 70)
128
0
          date.tm_year += 100;
129
0
      }
130
0
      goto next2;
131
132
0
    case 3:
133
0
      for (i = 0; i < 36; i += 3) {
134
0
        if (lws_tolower(*(d - 3)) == mon[i] &&
135
0
            lws_tolower(*(d - 2)) == mon[i + 1] &&
136
0
            lws_tolower(*(d - 1)) == mon[i + 2]) {
137
0
          date.tm_mon = i / 3;
138
0
          break;
139
0
        }
140
0
      }
141
0
      goto next2;
142
143
0
    case 4:
144
0
      if (!date.tm_year) {
145
0
        date.tm_year = atoi(d - 4);
146
0
        if (date.tm_year < 1601)
147
0
          return -1;
148
0
        date.tm_year -= 1900;
149
0
      }
150
0
      goto next2;
151
152
0
    default:
153
0
      goto next2;
154
0
    }
155
156
0
next2:
157
0
    offset = 0;
158
0
next:
159
0
    d++;
160
0
    len--;
161
0
  }
162
163
0
  *t = mktime(&date);
164
165
0
  if (*t < 0)
166
0
    return -1;
167
168
0
  return 0;
169
0
}
170
171
static void
172
lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
173
0
{
174
0
  const char *buf;
175
0
  size_t len;
176
177
0
  if (!buf_p || !*buf_p || !len_p || !*len_p) {
178
0
    lwsl_err("%s: false parameter\n", __func__);
179
0
    return;
180
0
  }
181
182
0
  buf = *buf_p;
183
0
  len = *len_p;
184
185
0
  while (buf[0] == ' ' && len > 0) {
186
0
    buf++;
187
0
    len--;
188
0
  }
189
190
0
  while (len && buf[len - 1] == ' ')
191
0
    len--;
192
193
0
  *buf_p = buf;
194
0
  *len_p = len;
195
0
}
196
197
static int
198
is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
199
0
{
200
0
  if (!h || !n || nl > hl)
201
0
    return 0;
202
203
0
  while (nl) {
204
0
    nl--;
205
0
    if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
206
0
      return 0;
207
0
  }
208
0
  return 1;
209
0
}
210
211
static int
212
lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
213
0
{
214
0
  if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
215
0
      c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
216
0
    return -1;
217
218
0
  memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
219
0
  buf += c->l[CE_DOMAIN];
220
0
  *buf++ = '|';
221
222
0
  memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
223
0
  buf += c->l[CE_PATH];
224
0
  *buf++ = '|';
225
226
0
  memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
227
0
  buf += c->l[CE_NAME];
228
0
  *buf = '\0';
229
230
0
  return 0;
231
0
}
232
233
static int
234
lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
235
0
{
236
0
  enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
237
0
  size_t n = 0;
238
239
0
  if (!c || !b || l < 13)
240
0
    return -1;
241
242
0
  memset(c, 0, sizeof(*c));
243
0
  lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
244
245
0
  while (l) {
246
0
    l--;
247
0
    if (b[n] != '\t' && l) {
248
0
      n++;
249
0
      continue;
250
0
    }
251
0
    switch (state) {
252
0
    case LWSC_NSC_DOMAIN:
253
0
      c->f[CE_DOMAIN] = b;
254
0
      c->l[CE_DOMAIN] = n;
255
0
      break;
256
0
    case LWSC_NSC_PATH:
257
0
      c->f[CE_PATH] = b;
258
0
      c->l[CE_PATH] = n;
259
0
      break;
260
0
    case LWSC_NSC_EXPIRES:
261
0
      c->f[CE_EXPIRES] = b;
262
0
      c->l[CE_EXPIRES] = n;
263
0
      break;
264
0
    case LWSC_NSC_NAME:
265
0
      c->f[CE_NAME] = b;
266
0
      c->l[CE_NAME] = n;
267
0
      break;
268
269
0
    case LWSC_NSC_HOSTONLY:
270
0
      if (b[0] == 'T') {
271
0
        c->f[CE_HOSTONLY] = b;
272
0
        c->l[CE_HOSTONLY] = 1;
273
0
      }
274
0
      break;
275
0
    case LWSC_NSC_SECURE:
276
0
      if (b[0] == 'T') {
277
0
        c->f[CE_SECURE] = b;
278
0
        c->l[CE_SECURE] = 1;
279
0
      }
280
0
      break;
281
282
0
    case LWSC_NSC_VALUE:
283
0
      c->f[CE_VALUE] = b;
284
0
      c->l[CE_VALUE] = n + 1;
285
286
0
      for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
287
0
        lwsl_cookie("%s: %d: %.*s\n", __func__,
288
0
            (int)n, (int)c->l[n], c->f[n]);
289
290
0
      return 0;
291
0
    default:
292
0
      return -1;
293
0
    }
294
295
0
    b += n + 1;
296
0
    n = 0;
297
0
    state++;
298
0
  }
299
300
0
  return -1;
301
0
}
302
303
static int
304
lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
305
0
{
306
0
  const char *ads, *path;
307
0
  struct lws_cache_ttl_lru *l1;
308
0
  struct client_info_stash *stash;
309
0
  char *cookie_string = NULL, *cache_name = NULL;
310
0
  const char *dl;
311
   /* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
312
0
  size_t size = 6 + 20 + 10 + 1, cnl;
313
0
  time_t expires = 0;
314
0
  lws_usec_t expiry_us = 0;
315
0
  time_t now_s = time(NULL);
316
317
0
  int ret = 0;
318
319
0
  if (!wsi || !c)
320
0
    return -1;
321
322
0
  l1 = wsi->a.context->l1;
323
0
  if (!l1 || !wsi->a.context->nsc)
324
0
    return -1;
325
326
0
  stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
327
0
  if (stash) {
328
0
    ads = stash->cis[CIS_HOST] ? stash->cis[CIS_HOST] : stash->cis[CIS_ADDRESS];
329
0
    path = stash->cis[CIS_PATH];
330
0
  } else {
331
0
    ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST);
332
0
    if (!ads)
333
0
      ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
334
0
    path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
335
0
  }
336
0
  if (!ads || !path)
337
0
    return -1;
338
339
0
  if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
340
0
    lwsl_err("%s: malformed c\n", __func__);
341
342
0
    return -1;
343
0
  }
344
345
346
347
0
  if (!c->f[CE_DOMAIN]) {
348
0
    c->f[CE_HOSTONLY] = "T";
349
0
    c->l[CE_HOSTONLY] = 1;
350
0
    c->f[CE_DOMAIN] = ads;
351
0
    c->l[CE_DOMAIN] = lws_cookie_domain_len(ads);
352
0
  }
353
354
0
  if (!c->f[CE_PATH]) {
355
0
    c->f[CE_PATH] = path;
356
0
    c->l[CE_PATH] = strlen(path);
357
0
    dl = (char *)memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
358
0
    if (dl)
359
0
      c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
360
0
  }
361
362
0
  cnl = c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6;
363
0
  cache_name = lws_malloc(cnl, __func__);
364
0
  if (!cache_name)
365
0
    return -1;
366
367
0
  if (lws_cookie_compile_cache_name(cache_name, cnl, c)) {
368
0
    ret = -1;
369
0
    goto exit;
370
0
  }
371
372
0
  if (c->f[CE_EXPIRES] &&
373
0
      lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
374
0
    lwsl_err("%s: can't parse date %.*s\n", __func__,
375
0
       (int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
376
0
    return -1;
377
0
  }
378
379
0
  if (c->f[CE_EXPIRES])
380
0
    lwsl_cookie("%s: name=%s expires='%.*s'@ t=%lld now=%lld\n", __func__, cache_name,
381
0
          (int)c->l[CE_EXPIRES], c->f[CE_EXPIRES],
382
0
          (long long)expires, (long long)now_s);
383
0
  else
384
0
    lwsl_cookie("%s: name=%s expires='<unset>'@ t=%lld now=%lld\n", __func__, cache_name,
385
0
          (long long)expires, (long long)now_s);
386
387
  /*
388
   * RFC 6265 treats a past-dated Expires as a delete signal for the
389
   * cookie. Ensure we handle expires=0 (PARSED at actual UNIX epoch, t=0)
390
   * as a delete signal as well.
391
   */
392
0
  if (c->f[CE_EXPIRES] && (expires <= now_s)) {
393
0
    lwsl_cookie("%s: dropping already-expired cookie\n", __func__);
394
0
    (void)lws_cache_item_remove(l1, cache_name);
395
0
    ret = 0;
396
0
    goto exit;
397
0
  }
398
399
0
  size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
400
0
  cookie_string = (char *)lws_malloc(size, __func__);
401
0
  if (!cookie_string) {
402
0
    lwsl_err("%s: OOM\n",__func__);
403
404
0
    return -1;  
405
0
  }
406
407
0
  lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
408
0
      (int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
409
0
      c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
410
0
      (int)c->l[CE_PATH], c->f[CE_PATH],
411
0
      c->f[CE_SECURE] ? "TRUE" : "FALSE",
412
0
      (unsigned long long)expires,
413
0
      (int)c->l[CE_NAME], c->f[CE_NAME],
414
0
      (int)c->l[CE_VALUE], c->f[CE_VALUE]);
415
416
0
  lwsl_cookie("%s: name %s\n", __func__, cache_name);
417
0
  lwsl_cookie("%s: c %s\n", __func__, cookie_string);
418
419
  /*
420
   * Convert expiry in UNIX time to lws_usec_t (monotonic clock).
421
   * expires == 0 is a session cookie (no expiry).
422
   */
423
0
  if (expires) {
424
0
    expiry_us = lws_now_usecs() +
425
0
        (lws_usec_t)(expires - now_s) *
426
0
        (lws_usec_t)LWS_US_PER_SEC;
427
0
  }
428
429
0
  if (lws_cache_write_through(l1, cache_name,
430
0
          (const uint8_t *)cookie_string,
431
0
          strlen(cookie_string),
432
0
          expiry_us, NULL)) {
433
0
    ret = -1;
434
0
    goto exit;
435
0
  }
436
437
#if defined(LWS_COOKIE_DEBUG)
438
  char *po;
439
  if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
440
      size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
441
    lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
442
  }
443
444
  if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
445
             (const void **)&po, &size) ||
446
             size != strlen(cookie_string) ||
447
             memcmp(po, cookie_string, size)) {
448
    lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
449
       cache_name, (unsigned long long)size, po);
450
  }
451
#endif
452
453
0
exit:
454
0
  lws_free(cookie_string);
455
0
  lws_free(cache_name);
456
457
0
  return ret;
458
0
}
459
460
static int
461
lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
462
0
{
463
0
  const char *domain, *path, *dl_domain, *dl_path, *po;
464
0
  size_t domain_len, path_len, size, cnl, ret = 0;
465
0
  struct lws_cache_ttl_lru *l1;
466
0
  struct client_info_stash *stash;
467
0
  lws_cache_results_t cr;
468
0
  struct lws_cookie c;
469
0
  int hostdomain = 1;
470
0
  char *p, *p1, *cache_name;
471
472
0
  if (!wsi)
473
0
    return -1;
474
475
  /*
476
   * stash is only guaranteed during the connect phase; lws frees it
477
   * early when LWS_WITH_SOCKS5 is on. Fall back to HTTP headers
478
   * (same pattern as lws_cookie_write_nsc()).
479
   */
480
0
  {
481
0
    const char *fb_domain, *fb_path;
482
483
0
    stash = wsi->stash ? wsi->stash :
484
0
      lws_get_network_wsi(wsi)->stash;
485
0
    if (stash && stash->cis[CIS_HOST] &&
486
0
          stash->cis[CIS_PATH]) {
487
0
      fb_domain = stash->cis[CIS_HOST] ?
488
0
        stash->cis[CIS_HOST] :
489
0
        stash->cis[CIS_ADDRESS];
490
0
      fb_path   = stash->cis[CIS_PATH];
491
0
    } else {
492
0
      fb_domain = lws_hdr_simple_ptr(wsi,
493
0
          _WSI_TOKEN_CLIENT_HOST);
494
0
      if (!fb_domain)
495
0
        fb_domain = lws_hdr_simple_ptr(wsi,
496
0
          _WSI_TOKEN_CLIENT_PEER_ADDRESS);
497
0
      fb_path = lws_hdr_simple_ptr(wsi,
498
0
          _WSI_TOKEN_CLIENT_URI);
499
0
    }
500
0
    if (!fb_domain || !fb_path)
501
0
      return -1;
502
503
0
    domain = fb_domain;
504
0
    path = fb_path;
505
0
  }
506
507
0
  l1 = wsi->a.context->l1;
508
0
  if (!l1 || !wsi->a.context->nsc){
509
0
    lwsl_err("%s:no cookiejar\n", __func__);
510
0
    return -1;
511
0
  }
512
513
0
  memset(&c, 0, sizeof(c));
514
515
516
517
0
  path_len = strlen(path);
518
519
  /* remove query string if exist */
520
0
  dl_path = (char *)memchr(path, '?', path_len);
521
0
  if (dl_path)
522
0
    path_len = lws_ptr_diff_size_t(dl_path,  path);
523
524
  /* remove last slash if exist */
525
0
  if (path_len != 1 && path[path_len - 1] == '/')
526
0
    path_len--;
527
528
0
  if (!path_len)
529
0
    return -1;
530
531
0
  lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
532
533
  /* when dest buf is not provided, we only return size of cookie string */
534
0
  if (!buf || !end)
535
0
    p = NULL;
536
0
  else
537
0
    p = buf;
538
539
  /* iterate through domain and path levels to find matching cookies */
540
0
  dl_domain = domain;
541
0
  while (dl_domain) {
542
0
    domain_len = lws_cookie_domain_len(domain);
543
0
    dl_domain = (char *)memchr(domain, '.', domain_len);
544
    /* don't match top level domain */
545
0
    if (!dl_domain)
546
0
      break;
547
548
0
    cnl = domain_len + path_len + 6;
549
0
    cache_name = lws_malloc(cnl, __func__);
550
0
    if (!cache_name)
551
0
      return -1;
552
553
    /* compile key string "[domain]|[path]|*"" */
554
0
    p1 = cache_name;
555
0
    memcpy(p1, domain, domain_len);
556
0
    p1 += domain_len;
557
0
    *p1 = '|';
558
0
    p1++;
559
0
    memcpy(p1, path, path_len);
560
0
    p1 += path_len;
561
0
    *p1 = '|';
562
0
    p1++;
563
0
    *p1 = '*';
564
0
    p1++;
565
0
    *p1 = '\0';
566
567
0
    lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
568
569
0
    if (!lws_cache_lookup(l1, cache_name,
570
0
              (const void **)&cr.ptr, &cr.size)) {
571
572
0
      while (!lws_cache_results_walk(&cr)) {
573
0
        lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
574
0
            (int)cr.payload_len);
575
576
0
        if (lws_cache_item_get(l1, (const char *)cr.tag,
577
0
               (const void **)&po, &size) ||
578
0
          lws_cookie_parse_nsc(&c, po, size)) {
579
0
          lwsl_err("%s: failed to get c '%s'\n",
580
0
              __func__, cr.tag);
581
0
          break;
582
0
        }
583
584
0
        if (c.f[CE_HOSTONLY] && !hostdomain){
585
0
          lwsl_cookie("%s: not sending this\n",
586
0
              __func__);
587
0
          continue;
588
0
        }
589
590
0
        if (p) {
591
0
          if (ret) {
592
0
            *p = ';';
593
0
            p++;
594
0
            *p = ' ';
595
0
            p++;
596
0
          }
597
598
0
          memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
599
0
          p += c.l[CE_NAME];
600
0
          *p = '=';
601
0
          p++;
602
0
          memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
603
0
          p += c.l[CE_VALUE];
604
0
        }
605
606
0
        if (ret)
607
0
          ret += 2;
608
0
        ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
609
610
0
      }
611
0
    }
612
613
0
    lws_free(cache_name);
614
615
0
    domain = dl_domain + 1;
616
0
    hostdomain = 0;
617
0
  }
618
619
0
  lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
620
621
0
  return (int)ret;
622
0
}
623
624
static struct {
625
  const char    *const name;
626
  uint8_t     len;
627
} cft[] = {
628
  { "domain=",  7 },
629
  { "path=",    5 },
630
  { "expires=", 8 },
631
  { "max-age=", 8 },
632
  { "httponly", 8 },
633
  { "secure",   6 }
634
};
635
636
int
637
lws_parse_set_cookie(struct lws *wsi)
638
0
{
639
0
  char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
640
0
  struct lws_cache_ttl_lru *l1;
641
0
  struct lws_cookie c;
642
0
  size_t fl;
643
0
  int f, n;
644
645
0
  if (!wsi)
646
0
    return -1;
647
648
0
  l1 = wsi->a.context->l1;
649
0
  if (!l1)
650
0
    return -1;
651
652
0
  f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
653
654
0
  while (f) {
655
0
    cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
656
0
    fl = wsi->http.ah->frags[f].len;
657
0
    f = wsi->http.ah->frags[f].nfrag;
658
659
0
    if (!cookiep || !fl)
660
0
      continue;
661
662
#if defined(LWS_COOKIE_DEBUG)
663
    lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
664
#endif
665
666
0
    buf_head = cookiep;
667
0
    buf_end = cookiep + fl - 1;
668
0
    memset(&c, 0, sizeof(struct lws_cookie));
669
670
0
    do {
671
0
      tk_head = buf_head;
672
0
      tk_end = (char *)memchr(buf_head, ';',
673
0
          (size_t)(buf_end - buf_head + 1));
674
0
      if (!tk_end) {
675
0
        tk_end = buf_end;
676
0
        buf_head = buf_end;
677
0
      } else {
678
0
        buf_head = tk_end + 1;
679
0
        tk_end--;
680
0
      }
681
682
0
      if (tk_end < tk_head) {
683
0
        if (!c.f[CE_NAME])
684
0
          return -1;
685
0
        continue;
686
0
      }
687
688
0
      if (c.f[CE_NAME])
689
0
        goto parse_av;
690
691
      /*
692
       * find name value, remove leading trailing
693
       * WS and DQ for value
694
       */
695
696
0
      dl = (char *)memchr(tk_head, '=',
697
0
            lws_ptr_diff_size_t(tk_end, tk_head) + 1);
698
0
      if (!dl || dl == tk_head)
699
0
        return -1;
700
701
0
      c.f[CE_NAME] = tk_head;
702
0
      c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
703
0
      lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
704
705
0
      if (!c.l[CE_NAME])
706
0
        return -1;
707
708
0
      lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
709
0
          (int)c.l[CE_NAME],
710
0
          (int)c.l[CE_NAME], c.f[CE_NAME]);
711
0
      c.f[CE_VALUE] = dl + 1;
712
0
      c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
713
0
               c.f[CE_VALUE]) + 1;
714
715
0
      lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
716
0
      if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
717
0
        c.f[CE_VALUE]++;
718
0
        c.l[CE_VALUE] -= 2;
719
0
      }
720
0
      lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
721
0
            (int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
722
0
            c.f[CE_VALUE]);
723
0
      continue;
724
725
0
parse_av:
726
0
      while (*tk_head == ' ') {
727
0
        if (tk_head == tk_end)
728
0
          return -1;
729
730
0
        tk_head++;
731
0
      }
732
733
0
      for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
734
0
        if (lws_tolower(*tk_head) != cft[n].name[0])
735
0
          continue;
736
737
0
        if (!is_iprefix(tk_head,
738
0
            lws_ptr_diff_size_t(tk_end,
739
0
                   tk_head) + 1,
740
0
            cft[n].name, cft[n].len))
741
0
          continue;
742
743
0
        if (n == 4 || n == 5) {
744
0
          c.f[n] = "T";
745
0
          c.l[n] = 1;
746
0
          break;
747
0
        }
748
749
0
        c.f[n] = tk_head + cft[n].len;
750
0
        c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
751
0
        lws_cookie_rm_sws(&c.f[n], &c.l[n]);
752
753
0
        if (n == CE_DOMAIN && c.l[0] &&
754
0
            c.f[n][0] == '.'){
755
0
          c.f[n]++;
756
0
          c.l[n]--;
757
0
        }
758
759
0
        lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
760
0
              cft[n].name, (int)c.l[n],
761
0
              (int)c.l[n], c.f[n]);
762
0
        break;
763
0
      }
764
765
0
    } while (tk_end != buf_end);
766
767
0
    if (lws_cookie_write_nsc(wsi, &c))
768
0
      lwsl_err("%s:failed to write nsc\n", __func__);
769
0
  }
770
771
0
  return 0;
772
0
}
773
774
int
775
lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
776
0
{
777
0
  char *p;
778
0
  int size;
779
780
0
  if (!wsi || !pp || !(*pp) || !end)
781
0
    return -1;
782
783
0
  size = lws_cookie_attach_cookies(wsi, NULL, NULL);
784
785
0
  if (!size)
786
0
    return 0;
787
0
  if (size < 0) {
788
0
    lwsl_err("%s:failed to get cookie string size\n", __func__);
789
0
    return -1;
790
0
  }
791
792
0
  lwsl_notice("%s: size %d\n", __func__, size);
793
794
#if defined(LWS_COOKIE_DEBUG)
795
    char *p_dbg = *pp;
796
#endif
797
798
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
799
0
                (unsigned char **)pp, (unsigned char *)end))
800
0
    return -1;
801
802
#if defined(LWS_COOKIE_DEBUG)
803
    lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
804
#endif
805
806
807
0
#ifdef LWS_WITH_HTTP2
808
0
  if (lws_wsi_is_h2(wsi)
809
#ifdef LWS_ROLE_H3
810
      || lws_wsi_is_h3(wsi)
811
#endif
812
0
  )
813
0
    p = *pp - size;
814
0
  else
815
0
#endif
816
0
    p = *pp - size - 2;
817
818
0
  if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
819
0
    lwsl_err("%s:failed to attach cookies\n", __func__);
820
0
    return -1;
821
0
  }
822
823
#if defined(LWS_COOKIE_DEBUG)
824
    lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
825
    lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
826
#endif
827
828
0
  return 0;
829
0
}