Coverage Report

Created: 2026-02-14 07:00

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