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