/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 | } |