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