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