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