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