Dillo v3.1.1-111-gd4f56d0d
Loading...
Searching...
No Matches
cookies.c
Go to the documentation of this file.
1/*
2 * File: cookies.c
3 * Cookies server.
4 *
5 * Copyright 2001 Lars Clausen <lrclause@cs.uiuc.edu>
6 * Jörgen Viksell <jorgen.viksell@telia.com>
7 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 */
15
16/* The current standard for cookies is RFC 6265.
17 *
18 * Info from 2009 on cookies in the wild:
19 * http://www.ietf.org/mail-archive/web/http-state/current/msg00078.html
20 * And dates specifically:
21 * http://www.ietf.org/mail-archive/web/http-state/current/msg00128.html
22 */
23
24#ifdef DISABLE_COOKIES
25
26int main(void)
27{
28 return 0; /* never called */
29}
30
31#else
32
33
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/un.h>
38#include <netinet/in.h>
39#include <fcntl.h>
40#include <unistd.h>
41#include <errno.h>
42#include <stddef.h>
43#include <string.h>
44#include <stdlib.h>
45#include <stdio.h>
46#include <time.h> /* for time() and time_t */
47#include <ctype.h>
48#include <limits.h>
49#include <netdb.h>
50#include <signal.h>
51#include "dpiutil.h"
52#include "../dpip/dpip.h"
53
54
55/*
56 * Debugging macros
57 */
58#define _MSG(...)
59#define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
60
61/*
62 * a_List_add()
63 *
64 * Make sure there's space for 'num_items' items within the list
65 * (First, allocate an 'alloc_step' sized chunk, after that, double the
66 * list size --to make it faster)
67 */
68#define a_List_add(list,num_items,alloc_step) \
69 if (!list) { \
70 list = dMalloc(alloc_step * sizeof((*list))); \
71 } \
72 if (num_items >= alloc_step){ \
73 while ( num_items >= alloc_step ) \
74 alloc_step <<= 1; \
75 list = dRealloc(list, alloc_step * sizeof((*list))); \
76 }
77
78/* The maximum length of a line in the cookie file */
79#define LINE_MAXLEN 4096
80
81#define MAX_DOMAIN_COOKIES 20
82#define MAX_TOTAL_COOKIES 1200
83
89
90typedef struct {
91 char *domain;
93} CookieControl;
94
95typedef struct {
96 char *domain;
97 Dlist *cookies;
98} DomainNode;
99
100typedef struct {
101 char *name;
102 char *value;
103 char *domain;
104 char *path;
105 time_t expires_at;
106 bool_t host_only;
107 bool_t secure;
108 bool_t session_only;
109 long last_used;
110} CookieData_t;
111
112typedef struct {
113 Dsh *sh;
114 int status;
115} ClientInfo;
116
117/*
118 * Local data
119 */
120
122
123/* List of DomainNode. Each node holds a domain and its list of cookies */
125
126/* Variables for access control */
127static CookieControl *ccontrol = NULL;
128static int num_ccontrol = 0;
129static int num_ccontrol_max = 1;
131
132static long cookies_use_counter = 0;
134static FILE *file_stream;
135static const char *const cookies_txt_header_str =
136"# HTTP Cookie File\n"
137"# This is a generated file! Do not edit.\n"
138"# [domain subdomains path secure expiry_time name value]\n\n";
139
140/* The epoch is Jan 1, 1970. When there is difficulty in representing future
141 * dates, use the (by far) most likely last representable time in Jan 19, 2038.
142 */
143static struct tm cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
145
146/*
147 * Forward declarations
148 */
149
150static CookieControlAction Cookies_control_check_domain(const char *domain);
151static int Cookie_control_init(void);
152static void Cookies_add_cookie(CookieData_t *cookie);
153static int Cookies_cmp(const void *a, const void *b);
154
155/*
156 * Compare function for searching a domain node
157 */
158static int Domain_node_cmp(const void *v1, const void *v2)
159{
160 const DomainNode *n1 = v1, *n2 = v2;
161
162 return dStrAsciiCasecmp(n1->domain, n2->domain);
163}
164
165/*
166 * Compare function for searching a domain node by domain
167 */
168static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
169{
170 const DomainNode *node = v1;
171 const char *domain = v2;
172
173 return dStrAsciiCasecmp(node->domain, domain);
174}
175
176/*
177 * Delete node. This will not free any cookies that might be in node->cookies.
178 */
179static void Cookies_delete_node(DomainNode *node)
180{
181 dList_remove(domains, node);
182 dFree(node->domain);
183 dList_free(node->cookies);
184 dFree(node);
185}
186
187/*
188 * Return a file pointer. If the file doesn't exist, try to create it,
189 * with the optional 'init_str' as its content.
190 */
191static FILE *Cookies_fopen(const char *filename, const char *mode,
192 const char *init_str)
193{
194 FILE *F_in;
195 int fd, rc;
196
197 if ((F_in = fopen(filename, mode)) == NULL) {
198 /* Create the file */
199 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
200 if (fd != -1) {
201 if (init_str) {
202 rc = write(fd, init_str, strlen(init_str));
203 if (rc == -1) {
204 MSG("Cookies: Could not write initial string to file %s: %s\n",
205 filename, dStrerror(errno));
206 }
207 }
208 close(fd);
209
210 MSG("Created file: %s\n", filename);
211 F_in = fopen(filename, mode);
212 } else {
213 MSG("Could not create file: %s!\n", filename);
214 }
215 }
216
217 if (F_in) {
218 /* set close on exec */
219 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
220 }
221
222 return F_in;
223}
224
225static void Cookies_free_cookie(CookieData_t *cookie)
226{
227 dFree(cookie->name);
228 dFree(cookie->value);
229 dFree(cookie->domain);
230 dFree(cookie->path);
231 dFree(cookie);
232}
233
234static void Cookies_tm_init(struct tm *tm)
235{
236 tm->tm_sec = cookies_epoch_tm.tm_sec;
237 tm->tm_min = cookies_epoch_tm.tm_min;
238 tm->tm_hour = cookies_epoch_tm.tm_hour;
239 tm->tm_mday = cookies_epoch_tm.tm_mday;
240 tm->tm_mon = cookies_epoch_tm.tm_mon;
241 tm->tm_year = cookies_epoch_tm.tm_year;
242 tm->tm_isdst = cookies_epoch_tm.tm_isdst;
243}
244
245/*
246 * Read in cookies from 'stream' (cookies.txt)
247 */
248static void Cookies_load_cookies(FILE *stream)
249{
250 char line[LINE_MAXLEN];
251
253 domains = dList_new(32);
254
255 /* Get all lines in the file */
256 while (!feof(stream)) {
257 line[0] = '\0';
258 if ((fgets(line, LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
259 MSG("Error while reading from cookies.txt: %s\n", dStrerror(errno));
260 break; /* bail out */
261 }
262
263 /* Remove leading and trailing whitespaces */
264 dStrstrip(line);
265
266 if ((line[0] != '\0') && (line[0] != '#')) {
267 /*
268 * Split the row into pieces using a tab as the delimiter.
269 * pieces[0] The domain name
270 * pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
271 * pieces[2] The path
272 * pieces[3] TRUE/FALSE: is the cookie for secure use only?
273 * pieces[4] Timestamp of expire date
274 * pieces[5] Name of the cookie
275 * pieces[6] Value of the cookie
276 */
277 CookieControlAction action;
278 char *piece;
279 char *line_marker = line;
280 CookieData_t *cookie = dNew0(CookieData_t, 1);
281
282 cookie->session_only = FALSE;
283 cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
284 piece = dStrsep(&line_marker, "\t");
285 if (piece != NULL && piece[0] == 'F')
286 cookie->host_only = TRUE;
287 cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
288 piece = dStrsep(&line_marker, "\t");
289 if (piece != NULL && piece[0] == 'T')
290 cookie->secure = TRUE;
291 piece = dStrsep(&line_marker, "\t");
292 if (piece != NULL) {
293 /* There is some problem with simply putting the maximum value
294 * into tm.tm_sec (although a value close to it works).
295 */
296 long seconds = strtol(piece, NULL, 10);
297 struct tm tm;
298 Cookies_tm_init(&tm);
299 tm.tm_min += seconds / 60;
300 tm.tm_sec += seconds % 60;
301 cookie->expires_at = mktime(&tm);
302 } else {
303 cookie->expires_at = (time_t) -1;
304 }
305 cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
306 cookie->value = dStrdup(line_marker ? line_marker : "");
307
308 if (!cookie->domain || cookie->domain[0] == '\0' ||
309 !cookie->path || cookie->path[0] != '/' ||
310 !cookie->name || !cookie->value) {
311 MSG("Malformed line in cookies.txt file!\n");
312 Cookies_free_cookie(cookie);
313 continue;
314 }
315
316 action = Cookies_control_check_domain(cookie->domain);
317 if (action == COOKIE_DENY) {
318 Cookies_free_cookie(cookie);
319 continue;
320 } else if (action == COOKIE_ACCEPT_SESSION) {
321 cookie->session_only = TRUE;
322 }
323
324 /* Save cookie in memory */
325 Cookies_add_cookie(cookie);
326 }
327 }
328 MSG("Cookies loaded: %d.\n", dList_length(all_cookies));
329}
330
331/*
332 * Initialize the cookies module
333 * (The 'disabled' variable is writeable only within Cookies_init)
334 */
335static void Cookies_init(void)
336{
337 char *filename;
338#ifndef HAVE_LOCKF
339 struct flock lck;
340#endif
341 struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
342
343 /* Default setting */
344 disabled = TRUE;
345
347 cookies_future_time = mktime(&future_tm);
348
349 /* Read and parse the cookie control file (cookiesrc) */
350 if (Cookie_control_init() != 0) {
351 MSG("Disabling cookies.\n");
352 return;
353 }
354
355 /* Get a stream for the cookies file */
356 filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
358
359 dFree(filename);
360
361 if (!file_stream) {
362 MSG("ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
363 return;
364 }
365
366 /* Try to get a lock from the file descriptor */
367#ifdef HAVE_LOCKF
368 disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
369#else /* POSIX lock */
370 lck.l_start = 0; /* start at beginning of file */
371 lck.l_len = 0; /* lock entire file */
372 lck.l_type = F_WRLCK;
373 lck.l_whence = SEEK_SET; /* absolute offset */
374
375 disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
376#endif
377 if (disabled) {
378 MSG("The cookies file has a file lock; disabling cookies!\n");
379 fclose(file_stream);
380 return;
381 }
382 MSG("Enabling cookies as per cookiesrc...\n");
383
385}
386
387/*
388 * Flush cookies to disk and free all the memory allocated.
389 */
390static void Cookies_save_and_free(void)
391{
392 int i, fd, saved = 0;
393 DomainNode *node;
394 CookieData_t *cookie;
395 time_t now;
396
397#ifndef HAVE_LOCKF
398 struct flock lck;
399#endif
400
401 if (disabled)
402 return;
403
404 now = time(NULL);
405
406 rewind(file_stream);
407 fd = fileno(file_stream);
408 if (ftruncate(fd, 0) == -1)
409 MSG("Cookies: Truncate file stream failed: %s\n", dStrerror(errno));
410 fprintf(file_stream, "%s", cookies_txt_header_str);
411
412 /* Iterate cookies per domain, saving and freeing */
413 while ((node = dList_nth_data(domains, 0))) {
414 for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) {
415 if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
416 int len;
417 char buf[LINE_MAXLEN];
418
419 len = snprintf(buf, LINE_MAXLEN, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
420 cookie->domain,
421 cookie->host_only ? "FALSE" : "TRUE",
422 cookie->path,
423 cookie->secure ? "TRUE" : "FALSE",
424 (long) difftime(cookie->expires_at,
426 cookie->name,
427 cookie->value);
428 if (len < LINE_MAXLEN) {
429 fprintf(file_stream, "%s", buf);
430 saved++;
431 } else {
432 MSG("Not saving overly long cookie for %s.\n", cookie->domain);
433 }
434 }
435 Cookies_free_cookie(cookie);
436 }
438 }
441
442#ifdef HAVE_LOCKF
443 lockf(fd, F_ULOCK, 0);
444#else /* POSIX file lock */
445 lck.l_start = 0; /* start at beginning of file */
446 lck.l_len = 0; /* lock entire file */
447 lck.l_type = F_UNLCK;
448 lck.l_whence = SEEK_SET; /* absolute offset */
449
450 fcntl(fileno(file_stream), F_SETLKW, &lck);
451#endif
452 fclose(file_stream);
453
454 MSG("Cookies saved: %d.\n", saved);
455}
456
457/*
458 * Month parsing
459 */
460static bool_t Cookies_get_month(struct tm *tm, const char **str)
461{
462 static const char *const months[] =
463 { "Jan", "Feb", "Mar",
464 "Apr", "May", "Jun",
465 "Jul", "Aug", "Sep",
466 "Oct", "Nov", "Dec"
467 };
468 int i;
469
470 for (i = 0; i < 12; i++) {
471 if (!dStrnAsciiCasecmp(months[i], *str, 3)) {
472 _MSG("Found month: %s\n", months[i]);
473 tm->tm_mon = i;
474 *str += 3;
475 return TRUE;
476 }
477 }
478 return FALSE;
479}
480
481/*
482 * As seen in the production below, it's just one digit or two.
483 * Return the value, or -1 if no proper value found.
484 */
485static int Cookies_get_timefield(const char **str)
486{
487 int n;
488 const char *s = *str;
489
490 if (!isdigit(*s))
491 return -1;
492
493 n = *(s++) - '0';
494 if (isdigit(*s)) {
495 n *= 10;
496 n += *(s++) - '0';
497 if (isdigit(*s))
498 return -1;
499 }
500 *str = s;
501 return n;
502}
503
504/*
505 * Time parsing: 'time-field ":" time-field ":" time-field'
506 * 'time-field = 1*2DIGIT'
507 */
508static bool_t Cookies_get_time(struct tm *tm, const char **str)
509{
510 const char *s = *str;
511
512 if ((tm->tm_hour = Cookies_get_timefield(&s)) == -1)
513 return FALSE;
514
515 if (*(s++) != ':')
516 return FALSE;
517
518 if ((tm->tm_min = Cookies_get_timefield(&s)) == -1)
519 return FALSE;
520
521 if (*(s++) != ':')
522 return FALSE;
523
524 if ((tm->tm_sec = Cookies_get_timefield(&s)) == -1)
525 return FALSE;
526
527 *str = s;
528 return TRUE;
529}
530
531/*
532 * Day parsing: "day-of-month = 1*2DIGIT"
533 */
534static bool_t Cookies_get_day(struct tm *tm, const char **str)
535{
536 const char *s = *str;
537
538 if ((tm->tm_mday = Cookies_get_timefield(&s)) == -1)
539 return FALSE;
540
541 *str = s;
542 return TRUE;
543}
544
545/*
546 * Date parsing: "year = 2*4DIGIT"
547 */
548static bool_t Cookies_get_year(struct tm *tm, const char **str)
549{
550 int n;
551 const char *s = *str;
552
553 if (isdigit(*s))
554 n = *(s++) - '0';
555 else
556 return FALSE;
557 if (isdigit(*s)) {
558 n *= 10;
559 n += *(s++) - '0';
560 } else
561 return FALSE;
562 if (isdigit(*s)) {
563 n *= 10;
564 n += *(s++) - '0';
565 }
566 if (isdigit(*s)) {
567 n *= 10;
568 n += *(s++) - '0';
569 }
570 if (isdigit(*s)) {
571 /* Sorry, users of prehistoric software in the year 10000! */
572 return FALSE;
573 }
574 if (n >= 70 && n <= 99)
575 n += 1900;
576 else if (n <= 69)
577 n += 2000;
578
579 tm->tm_year = n - 1900;
580
581 *str = s;
582 return TRUE;
583}
584
585/*
586 * As given in RFC 6265.
587 */
589{
590 return (c == '\x09' ||
591 (c >= '\x20' && c <= '\x2F') ||
592 (c >= '\x3B' && c <= '\x40') ||
593 (c >= '\x5B' && c <= '\x60') ||
594 (c >= '\x7B' && c <= '\x7E'));
595}
596
597/*
598 * Parse date string.
599 *
600 * A true nightmare of date formats appear in cookies, so one basically
601 * has to paw through the soup and look for anything that looks sufficiently
602 * like any of the date fields.
603 *
604 * Return a pointer to a struct tm, or NULL on error.
605 */
606static struct tm *Cookies_parse_date(const char *date)
607{
608 bool_t found_time = FALSE, found_day = FALSE, found_month = FALSE,
609 found_year = FALSE, matched;
610 struct tm *tm = dNew0(struct tm, 1);
611 const char *s = date;
612
613 while (*s) {
614 matched = FALSE;
615
616 if (!found_time)
617 matched = found_time = Cookies_get_time(tm, &s);
618 if (!matched && !found_day)
619 matched = found_day = Cookies_get_day(tm, &s);
620 if (!matched && !found_month)
621 matched = found_month = Cookies_get_month(tm, &s);
622 if (!matched && !found_year)
623 matched = found_year = Cookies_get_year(tm, &s);
624 while (*s && !Cookies_date_delim(*s))
625 s++;
626 while (*s && Cookies_date_delim(*s))
627 s++;
628 }
629 if (!found_time || !found_day || !found_month || !found_year) {
630 dFree(tm);
631 tm = NULL;
632 MSG("In date \"%s\", format not understood.\n", date);
633 }
634
635 /* Error checks. This may be overkill.
636 *
637 * RFC 6265: "Note that leap seconds cannot be represented in this
638 * syntax." I'm not sure whether that's good, but that's what it says.
639 */
640 if (tm &&
641 !(tm->tm_mday > 0 && tm->tm_mday < 32 && tm->tm_mon >= 0 &&
642 tm->tm_mon < 12 && tm->tm_year >= 0 && tm->tm_hour >= 0 &&
643 tm->tm_hour < 24 && tm->tm_min >= 0 && tm->tm_min < 60 &&
644 tm->tm_sec >= 0 && tm->tm_sec < 60)) {
645 MSG("Date \"%s\" values not in range.\n", date);
646 dFree(tm);
647 tm = NULL;
648 }
649
650 return tm;
651}
652
653/*
654 * Find the least recently used cookie among those in the provided list.
655 */
656static CookieData_t *Cookies_get_LRU(Dlist *cookies)
657{
658 int i, n = dList_length(cookies);
659 CookieData_t *lru = dList_nth_data(cookies, 0);
660
661 for (i = 1; i < n; i++) {
662 CookieData_t *curr = dList_nth_data(cookies, i);
663
664 if (curr->last_used < lru->last_used)
665 lru = curr;
666 }
667 return lru;
668}
669
670/*
671 * Delete expired cookies.
672 * If node is given, only check those cookies.
673 * Note that nodes can disappear if all of their cookies were expired.
674 *
675 * Return the number of cookies that were expired.
676 */
677static int Cookies_rm_expired_cookies(DomainNode *node)
678{
679 Dlist *cookies = node ? node->cookies : all_cookies;
680 int removed = 0;
681 int i = 0, n = dList_length(cookies);
682 time_t now = time(NULL);
683
684 while (i < n) {
685 CookieData_t *c = dList_nth_data(cookies, i);
686
687 if (difftime(c->expires_at, now) < 0) {
688 DomainNode *currnode = node ? node :
690 dList_remove(currnode->cookies, c);
691 if (dList_length(currnode->cookies) == 0)
692 Cookies_delete_node(currnode);
695 n--;
696 removed++;
697 } else {
698 i++;
699 }
700 }
701 return removed;
702}
703
704/*
705 * There are too many cookies. Choose one to remove and delete.
706 * If node is given, select from among its cookies only.
707 */
708static void Cookies_too_many(DomainNode *node)
709{
710 CookieData_t *lru = Cookies_get_LRU(node ? node->cookies : all_cookies);
711
712 MSG("Too many cookies! "
713 "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
714 lru->name, lru->value);
715 if (!node)
717
718 dList_remove(node->cookies, lru);
721 if (dList_length(node->cookies) == 0)
723}
724
725static void Cookies_add_cookie(CookieData_t *cookie)
726{
727 Dlist *domain_cookies;
728 CookieData_t *c;
729 DomainNode *node;
730
732 domain_cookies = (node) ? node->cookies : NULL;
733
734 if (domain_cookies) {
735 /* Remove any cookies with the same name, path, and host-only values. */
736 while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))) {
737 dList_remove(domain_cookies, c);
740 }
741 }
742
743 if ((cookie->expires_at == (time_t) -1) ||
744 (difftime(cookie->expires_at, time(NULL)) <= 0)) {
745 /*
746 * Don't add an expired cookie. Whether expiring now == expired, exactly,
747 * is arguable, but we definitely do not want to add a Max-Age=0 cookie.
748 */
749 _MSG("Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
750 cookie->value, cookie->domain, cookie->path);
751 Cookies_free_cookie(cookie);
752 } else {
753 if (domain_cookies && dList_length(domain_cookies) >=MAX_DOMAIN_COOKIES){
754 int removed = Cookies_rm_expired_cookies(node);
755
756 if (removed == 0) {
757 Cookies_too_many(node);
758 } else if (removed >= MAX_DOMAIN_COOKIES) {
759 /* So many were removed that the node might have been deleted. */
760 node = dList_find_sorted(domains, cookie->domain,
762 domain_cookies = (node) ? node->cookies : NULL;
763 }
764 }
766 if (Cookies_rm_expired_cookies(NULL) == 0) {
767 Cookies_too_many(NULL);
768 } else if (domain_cookies) {
769 /* Our own node might have just been deleted. */
770 node = dList_find_sorted(domains, cookie->domain,
772 domain_cookies = (node) ? node->cookies : NULL;
773 }
774 }
775
776 cookie->last_used = cookies_use_counter++;
777
778 /* Actually add the cookie! */
779 dList_append(all_cookies, cookie);
780
781 if (!domain_cookies) {
782 domain_cookies = dList_new(5);
783 dList_append(domain_cookies, cookie);
784 node = dNew(DomainNode, 1);
785 node->domain = dStrdup(cookie->domain);
786 node->cookies = domain_cookies;
788 } else {
789 dList_append(domain_cookies, cookie);
790 }
791 }
792 if (domain_cookies && (dList_length(domain_cookies) == 0))
794}
795
796/*
797 * Return the attribute that is present at *cookie_str.
798 */
799static char *Cookies_parse_attr(char **cookie_str)
800{
801 char *str;
802 uint_t len;
803
804 while (dIsspace(**cookie_str))
805 (*cookie_str)++;
806
807 str = *cookie_str;
808 /* find '=' at end of attr, ';' after attr/val pair, '\0' end of string */
809 len = strcspn(str, "=;");
810 *cookie_str += len;
811
812 while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
813 len--;
814 return dStrndup(str, len);
815}
816
817/*
818 * Get the value in *cookie_str.
819 */
820static char *Cookies_parse_value(char **cookie_str)
821{
822 uint_t len;
823 char *str;
824
825 if (**cookie_str == '=') {
826 (*cookie_str)++;
827 while (dIsspace(**cookie_str))
828 (*cookie_str)++;
829
830 str = *cookie_str;
831 /* finds ';' after attr/val pair or '\0' at end of string */
832 len = strcspn(str, ";");
833 *cookie_str += len;
834
835 while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
836 len--;
837 } else {
838 str = *cookie_str;
839 len = 0;
840 }
841 return dStrndup(str, len);
842}
843
844/*
845 * Advance past any value
846 */
847static void Cookies_eat_value(char **cookie_str)
848{
849 if (**cookie_str == '=')
850 *cookie_str += strcspn(*cookie_str, ";");
851}
852
853/*
854 * Return the number of seconds by which our clock is ahead of the server's
855 * clock.
856 */
857static double Cookies_server_timediff(const char *server_date)
858{
859 double ret = 0;
860
861 if (server_date) {
862 struct tm *server_tm = Cookies_parse_date(server_date);
863
864 if (server_tm) {
865 time_t server_time = mktime(server_tm);
866
867 if (server_time != (time_t) -1)
868 ret = difftime(time(NULL), server_time);
869 dFree(server_tm);
870 }
871 }
872 return ret;
873}
874
875static void Cookies_unquote_string(char *str)
876{
877 if (str && str[0] == '\"') {
878 uint_t len = strlen(str);
879
880 if (len > 1 && str[len - 1] == '\"') {
881 str[len - 1] = '\0';
882 while ((*str = str[1]))
883 str++;
884 }
885 }
886}
887
888/*
889 * Parse cookie. A cookie might look something like:
890 * "Name=Val; Domain=example.com; Max-Age=3600; HttpOnly"
891 */
892static CookieData_t *Cookies_parse(char *cookie_str, const char *server_date)
893{
894 CookieData_t *cookie = NULL;
895 char *str = cookie_str;
896 bool_t first_attr = TRUE;
897 bool_t max_age = FALSE;
898 bool_t expires = FALSE;
899
900 /* Iterate until there is nothing left of the string */
901 while (*str) {
902 char *attr;
903 char *value;
904
905 /* Get attribute */
906 attr = Cookies_parse_attr(&str);
907
908 /* Get the value for the attribute and store it */
909 if (first_attr) {
910 time_t now;
911 struct tm *tm;
912
913 if (*str != '=' || *attr == '\0') {
914 /* disregard nameless cookie */
915 dFree(attr);
916 return NULL;
917 }
918 cookie = dNew0(CookieData_t, 1);
919 cookie->name = attr;
920 cookie->value = Cookies_parse_value(&str);
921
922 /* let's arbitrarily initialise with a year for now */
923 now = time(NULL);
924 tm = gmtime(&now);
925 ++tm->tm_year;
926 cookie->expires_at = mktime(tm);
927 if (cookie->expires_at == (time_t) -1)
928 cookie->expires_at = cookies_future_time;
929 } else if (dStrAsciiCasecmp(attr, "Path") == 0) {
930 value = Cookies_parse_value(&str);
931 dFree(cookie->path);
932 cookie->path = value;
933 } else if (dStrAsciiCasecmp(attr, "Domain") == 0) {
934 value = Cookies_parse_value(&str);
935 dFree(cookie->domain);
936 cookie->domain = value;
937 } else if (dStrAsciiCasecmp(attr, "Max-Age") == 0) {
938 value = Cookies_parse_value(&str);
939 if (isdigit(*value) || *value == '-') {
940 long age;
941 time_t now = time(NULL);
942 struct tm *tm = gmtime(&now);
943
944 errno = 0;
945 age = (*value == '-') ? 0 : strtol(value, NULL, 10);
946
947 if (errno == ERANGE ||
948 (age > 0 && (age > INT_MAX - tm->tm_sec))) {
949 /* let's not overflow */
950 tm->tm_sec = INT_MAX;
951 } else {
952 tm->tm_sec += age;
953 }
954 cookie->expires_at = mktime(tm);
955 if (age > 0 && cookie->expires_at == (time_t) -1) {
956 cookie->expires_at = cookies_future_time;
957 }
958 _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
959 expires = max_age = TRUE;
960 }
961 dFree(value);
962 } else if (dStrAsciiCasecmp(attr, "Expires") == 0) {
963 if (!max_age) {
964 struct tm *tm;
965
966 value = Cookies_parse_value(&str);
968 _MSG("Expires attribute gives %s\n", value);
969 tm = Cookies_parse_date(value);
970 if (tm) {
971 tm->tm_sec += Cookies_server_timediff(server_date);
972 cookie->expires_at = mktime(tm);
973 if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
974 /* Just checking tm_year does not ensure that the problem was
975 * inability to represent a distant date...
976 */
977 cookie->expires_at = cookies_future_time;
978 }
979 _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
980 dFree(tm);
981 } else {
982 cookie->expires_at = (time_t) -1;
983 }
984 expires = TRUE;
985 dFree(value);
986 } else {
987 Cookies_eat_value(&str);
988 }
989 } else if (dStrAsciiCasecmp(attr, "Secure") == 0) {
990 cookie->secure = TRUE;
991 Cookies_eat_value(&str);
992 } else if (dStrAsciiCasecmp(attr, "HttpOnly") == 0) {
993 Cookies_eat_value(&str);
994 } else {
995 MSG("Cookie contains unknown attribute: '%s'\n", attr);
996 Cookies_eat_value(&str);
997 }
998
999 if (first_attr)
1000 first_attr = FALSE;
1001 else
1002 dFree(attr);
1003
1004 if (*str == ';')
1005 str++;
1006 }
1007 cookie->session_only = expires == FALSE;
1008 return cookie;
1009}
1010
1011/*
1012 * Compare cookies by host_only, name, and path. Return 0 if equal.
1013 */
1014static int Cookies_cmp(const void *a, const void *b)
1015{
1016 const CookieData_t *ca = a, *cb = b;
1017
1018 return (ca->host_only != cb->host_only) ||
1019 (strcmp(ca->name, cb->name) != 0) ||
1020 (strcmp(ca->path, cb->path) != 0);
1021}
1022
1023/*
1024 * Is the domain an IP address?
1025 */
1026static bool_t Cookies_domain_is_ip(const char *domain)
1027{
1028 uint_t len;
1029
1030 if (!domain)
1031 return FALSE;
1032
1033 len = strlen(domain);
1034
1035 if (len == strspn(domain, "0123456789.")) {
1036 _MSG("an IPv4 address\n");
1037 return TRUE;
1038 }
1039 if (strchr(domain, ':') &&
1040 (len == strspn(domain, "0123456789abcdefABCDEF:."))) {
1041 /* The precise format is shown in section 3.2.2 of rfc 3986 */
1042 MSG("an IPv6 address\n");
1043 return TRUE;
1044 }
1045 return FALSE;
1046}
1047
1048/*
1049 * Check whether url_path path-matches cookie_path
1050 *
1051 * Note different user agents apparently vary in path-matching behaviour,
1052 * but this is the recommended method at the moment.
1053 */
1054static bool_t Cookies_path_matches(const char *url_path,
1055 const char *cookie_path)
1056{
1057 bool_t ret = TRUE;
1058
1059 if (!url_path || !cookie_path) {
1060 ret = FALSE;
1061 } else {
1062 uint_t c_len = strlen(cookie_path);
1063 uint_t u_len = strlen(url_path);
1064
1065 ret = (!strncmp(cookie_path, url_path, c_len) &&
1066 ((c_len == u_len) ||
1067 (c_len > 0 && cookie_path[c_len - 1] == '/') ||
1068 (url_path[c_len] == '/')));
1069 }
1070 return ret;
1071}
1072
1073/*
1074 * If cookie path is not properly set, remedy that.
1075 */
1076static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
1077{
1078 if (!cookie->path || cookie->path[0] != '/') {
1079 dFree(cookie->path);
1080
1081 if (url_path) {
1082 uint_t len = strlen(url_path);
1083
1084 while (len && url_path[len] != '/')
1085 len--;
1086 cookie->path = dStrndup(url_path, len ? len : 1);
1087 } else {
1088 cookie->path = dStrdup("/");
1089 }
1090 }
1091}
1092
1093/*
1094 * Check whether host name A domain-matches host name B.
1095 */
1096static bool_t Cookies_domain_matches(char *A, char *B)
1097{
1098 int diff;
1099
1100 if (!A || !*A || !B || !*B)
1101 return FALSE;
1102
1103 if (*B == '.')
1104 B++;
1105
1106 /* Should we concern ourselves with trailing dots in matching (here or
1107 * elsewhere)? The HTTP State people have found that most user agents
1108 * don't, so: No.
1109 */
1110
1111 if (!dStrAsciiCasecmp(A, B))
1112 return TRUE;
1113
1114 if (Cookies_domain_is_ip(B))
1115 return FALSE;
1116
1117 diff = strlen(A) - strlen(B);
1118
1119 if (diff > 0) {
1120 /* B is the tail of A, and the match is preceded by a '.' */
1121 return (dStrAsciiCasecmp(A + diff, B) == 0 && A[diff - 1] == '.');
1122 } else {
1123 return FALSE;
1124 }
1125}
1126
1127/*
1128 * Based on the host, how many internal dots do we need in a cookie domain
1129 * to make it valid? e.g., "org" is not on the list, so dillo.org is a safe
1130 * cookie domain, but "uk" is on the list, so ac.uk is not safe.
1131 *
1132 * This is imperfect, but it's something. Specifically, checking for these
1133 * TLDs is the solution that Konqueror used once upon a time, according to
1134 * reports.
1135 */
1137{
1138 uint_t ret = 1;
1139
1140 if (host) {
1141 int start, after, tld_len;
1142
1143 /* We may be able to trust the format of the host string more than
1144 * I am here. Trailing dots and no dots are real possibilities, though.
1145 */
1146 after = strlen(host);
1147 if (after > 0 && host[after - 1] == '.')
1148 after--;
1149 start = after;
1150 while (start > 0 && host[start - 1] != '.')
1151 start--;
1152 tld_len = after - start;
1153
1154 if (tld_len > 0) {
1155 /* These TLDs were chosen by examining the current publicsuffix list
1156 * in October 2014 and picking out those where it was simplest for
1157 * them to describe the situation by beginning with a "*.[tld]" rule
1158 * or every rule was "[something].[tld]".
1159 */
1160 const char *const tlds[] = {"bd","bn","ck","cy","er","fj","fk",
1161 "gu","il","jm","ke","kh","kw","mm","mz",
1162 "ni","np","pg","ye","za","zm","zw"};
1163 uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
1164
1165 for (i = 0; i < tld_num; i++) {
1166 if (strlen(tlds[i]) == (uint_t) tld_len &&
1167 !dStrnAsciiCasecmp(tlds[i], host + start, tld_len)) {
1168 _MSG("TLD code matched %s\n", tlds[i]);
1169 ret++;
1170 break;
1171 }
1172 }
1173 }
1174 }
1175 return ret;
1176}
1177
1178/*
1179 * Validate cookies domain against some security checks.
1180 */
1181static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
1182{
1183 uint_t i, internal_dots;
1184
1185 if (!cookie->domain) {
1186 cookie->domain = dStrdup(host);
1187 cookie->host_only = TRUE;
1188 return TRUE;
1189 }
1190
1191 if (!Cookies_domain_matches(host, cookie->domain))
1192 return FALSE;
1193
1194 internal_dots = 0;
1195 for (i = 1; i < strlen(cookie->domain) - 1; i++) {
1196 if (cookie->domain[i] == '.')
1197 internal_dots++;
1198 }
1199
1200 /* All of this dots business is a weak hack.
1201 * TODO: accept the publicsuffix.org list as an optional external file.
1202 */
1203 if (internal_dots < Cookies_internal_dots_required(host)) {
1204 MSG("not enough dots in %s\n", cookie->domain);
1205 return FALSE;
1206 }
1207
1208 _MSG("host %s and domain %s is all right\n", host, cookie->domain);
1209 return TRUE;
1210}
1211
1212/*
1213 * Set the value corresponding to the cookie string
1214 * Return value: 0 set OK, -1 disabled, -2 denied, -3 rejected.
1215 */
1216static int Cookies_set(char *cookie_string, char *url_host,
1217 char *url_path, char *server_date)
1218{
1219 CookieControlAction action;
1220 CookieData_t *cookie;
1221 int ret = -1;
1222
1223 if (disabled)
1224 return ret;
1225
1226 action = Cookies_control_check_domain(url_host);
1227 if (action == COOKIE_DENY) {
1228 MSG("denied SET for %s\n", url_host);
1229 ret = -2;
1230
1231 } else {
1232 MSG("%s SETTING: %s\n", url_host, cookie_string);
1233 ret = -3;
1234 if ((cookie = Cookies_parse(cookie_string, server_date))) {
1235 if (Cookies_validate_domain(cookie, url_host)) {
1236 Cookies_validate_path(cookie, url_path);
1237 if (action == COOKIE_ACCEPT_SESSION)
1238 cookie->session_only = TRUE;
1239 Cookies_add_cookie(cookie);
1240 ret = 0;
1241 } else {
1242 MSG("Rejecting cookie for domain %s from host %s path %s\n",
1243 cookie->domain, url_host, url_path);
1244 Cookies_free_cookie(cookie);
1245 }
1246 }
1247 }
1248
1249 return ret;
1250}
1251
1252/*
1253 * Compare the cookie with the supplied data to see whether it matches
1254 */
1255static bool_t Cookies_match(CookieData_t *cookie, const char *url_path,
1256 bool_t host_only_val, bool_t is_tls)
1257{
1258 if (cookie->host_only != host_only_val)
1259 return FALSE;
1260
1261 /* Insecure cookies match both secure and insecure urls, secure
1262 cookies match only secure urls */
1263 if (cookie->secure && !is_tls)
1264 return FALSE;
1265
1266 if (!Cookies_path_matches(url_path, cookie->path))
1267 return FALSE;
1268
1269 /* It's a match */
1270 return TRUE;
1271}
1272
1273static void Cookies_add_matching_cookies(const char *domain,
1274 const char *url_path,
1275 bool_t host_only_val,
1276 Dlist *matching_cookies,
1277 bool_t is_tls)
1278{
1279 DomainNode *node = dList_find_sorted(domains, domain,
1281 if (node) {
1282 int i;
1283 CookieData_t *cookie;
1284 Dlist *domain_cookies = node->cookies;
1285
1286 for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
1287 /* Remove expired cookie. */
1288 if (difftime(cookie->expires_at, time(NULL)) < 0) {
1289 _MSG("Goodbye, expired cookie %s=%s d:%s p:%s\n", cookie->name,
1290 cookie->value, cookie->domain, cookie->path);
1291 dList_remove(domain_cookies, cookie);
1293 Cookies_free_cookie(cookie);
1294 --i; continue;
1295 }
1296 /* Check if the cookie matches the requesting URL */
1297 if (Cookies_match(cookie, url_path, host_only_val, is_tls)) {
1298 int j;
1299 CookieData_t *curr;
1300 uint_t path_length = strlen(cookie->path);
1301
1302 cookie->last_used = cookies_use_counter;
1303
1304 /* Longest cookies go first */
1305 for (j = 0;
1306 (curr = dList_nth_data(matching_cookies, j)) &&
1307 strlen(curr->path) >= path_length;
1308 j++) ;
1309 dList_insert_pos(matching_cookies, cookie, j);
1310 }
1311 }
1312
1313 if (dList_length(domain_cookies) == 0)
1314 Cookies_delete_node(node);
1315 }
1316}
1317
1318/*
1319 * Return a string that contains all relevant cookies as headers.
1320 */
1321static char *Cookies_get(char *url_host, char *url_path,
1322 char *url_scheme)
1323{
1324 char *domain_str, *str;
1325 CookieData_t *cookie;
1326 Dlist *matching_cookies;
1327 bool_t is_tls, is_ip_addr, host_only_val;
1328
1329 Dstr *cookie_dstring;
1330 int i;
1331
1332 if (disabled)
1333 return dStrdup("");
1334
1335 matching_cookies = dList_new(8);
1336
1337 /* Check if the protocol is secure or not */
1338 is_tls = (!dStrAsciiCasecmp(url_scheme, "https"));
1339
1340 is_ip_addr = Cookies_domain_is_ip(url_host);
1341
1342 /* If a cookie is set that lacks a Domain attribute, its domain is set to
1343 * the server's host and the host_only flag is set for that cookie. Such a
1344 * cookie can only be sent back to that host. Cookies with Domain attrs do
1345 * not have the host_only flag set, and may be sent to subdomains. Domain
1346 * attrs can have leading dots, which should be ignored for matching
1347 * purposes.
1348 */
1349 host_only_val = FALSE;
1350 if (!is_ip_addr) {
1351 /* e.g., sub.example.com set a cookie with domain ".sub.example.com". */
1352 domain_str = dStrconcat(".", url_host, NULL);
1353 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1354 matching_cookies, is_tls);
1355 dFree(domain_str);
1356 }
1357 host_only_val = TRUE;
1358 /* e.g., sub.example.com set a cookie with no domain attribute. */
1359 Cookies_add_matching_cookies(url_host, url_path, host_only_val,
1360 matching_cookies, is_tls);
1361 host_only_val = FALSE;
1362 /* e.g., sub.example.com set a cookie with domain "sub.example.com". */
1363 Cookies_add_matching_cookies(url_host, url_path, host_only_val,
1364 matching_cookies, is_tls);
1365
1366 if (!is_ip_addr) {
1367 for (domain_str = strchr(url_host+1, '.');
1368 domain_str != NULL && *domain_str;
1369 domain_str = strchr(domain_str+1, '.')) {
1370 /* e.g., sub.example.com set a cookie with domain ".example.com". */
1371 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1372 matching_cookies, is_tls);
1373 if (domain_str[1]) {
1374 domain_str++;
1375 /* e.g., sub.example.com set a cookie with domain "example.com".*/
1376 Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
1377 matching_cookies, is_tls);
1378 }
1379 }
1380 }
1381
1382 /* Found the cookies, now make the string */
1383 cookie_dstring = dStr_new("");
1384 if (dList_length(matching_cookies) > 0) {
1385
1386 dStr_sprintfa(cookie_dstring, "Cookie: ");
1387
1388 for (i = 0; (cookie = dList_nth_data(matching_cookies, i)); ++i) {
1389 dStr_sprintfa(cookie_dstring, "%s=%s", cookie->name, cookie->value);
1390 dStr_append(cookie_dstring,
1391 dList_length(matching_cookies) > i + 1 ? "; " : "\r\n");
1392 }
1393 }
1394
1395 dList_free(matching_cookies);
1396 str = cookie_dstring->str;
1397 dStr_free(cookie_dstring, FALSE);
1398
1399 if (*str) {
1400 MSG("%s GETTING: %s", url_host, str);
1402 }
1403 return str;
1404}
1405
1406/* -------------------------------------------------------------
1407 * Access control routines
1408 * ------------------------------------------------------------- */
1409
1410
1411/*
1412 * Get the cookie control rules (from cookiesrc).
1413 * Return value:
1414 * 0 = Parsed OK, with cookies enabled
1415 * 1 = Parsed OK, with cookies disabled
1416 * 2 = Can't open the control file
1417 */
1418static int Cookie_control_init(void)
1419{
1420 CookieControl cc;
1421 FILE *stream;
1422 char *filename, *rc;
1423 char line[LINE_MAXLEN];
1424 char domain[LINE_MAXLEN];
1425 char rule[LINE_MAXLEN];
1426 bool_t enabled = FALSE;
1427
1428 /* Get a file pointer */
1429 filename = dStrconcat(dGethomedir(), "/.dillo/cookiesrc", NULL);
1430 stream = Cookies_fopen(filename, "r", "DEFAULT DENY\n");
1431 dFree(filename);
1432
1433 if (!stream)
1434 return 2;
1435
1436 /* Get all lines in the file */
1437 while (!feof(stream)) {
1438 line[0] = '\0';
1439 rc = fgets(line, LINE_MAXLEN, stream);
1440 if (!rc && ferror(stream)) {
1441 MSG("Error while reading rule from cookiesrc: %s\n",
1442 dStrerror(errno));
1443 break; /* bail out */
1444 }
1445
1446 /* Remove leading and trailing whitespaces */
1447 dStrstrip(line);
1448
1449 if (line[0] != '\0' && line[0] != '#') {
1450 int i = 0, j = 0;
1451
1452 /* Get the domain */
1453 while (line[i] != '\0' && !dIsspace(line[i]))
1454 domain[j++] = line[i++];
1455 domain[j] = '\0';
1456
1457 /* Skip past whitespaces */
1458 while (dIsspace(line[i]))
1459 i++;
1460
1461 /* Get the rule */
1462 j = 0;
1463 while (line[i] != '\0' && !dIsspace(line[i]))
1464 rule[j++] = line[i++];
1465 rule[j] = '\0';
1466
1467 if (dStrAsciiCasecmp(rule, "ACCEPT") == 0)
1468 cc.action = COOKIE_ACCEPT;
1469 else if (dStrAsciiCasecmp(rule, "ACCEPT_SESSION") == 0)
1470 cc.action = COOKIE_ACCEPT_SESSION;
1471 else if (dStrAsciiCasecmp(rule, "DENY") == 0)
1472 cc.action = COOKIE_DENY;
1473 else {
1474 MSG("Cookies: rule '%s' for domain '%s' is not recognised.\n",
1475 rule, domain);
1476 continue;
1477 }
1478
1479 cc.domain = dStrdup(domain);
1480 if (dStrAsciiCasecmp(cc.domain, "DEFAULT") == 0) {
1481 /* Set the default action */
1482 default_action = cc.action;
1483 dFree(cc.domain);
1484 } else {
1485 int i;
1486 uint_t len = strlen(cc.domain);
1487
1488 /* Insert into list such that longest rules come first. */
1490 for (i = num_ccontrol++;
1491 i > 0 && (len > strlen(ccontrol[i-1].domain));
1492 i--) {
1493 ccontrol[i] = ccontrol[i-1];
1494 }
1495 ccontrol[i] = cc;
1496 }
1497
1498 if (cc.action != COOKIE_DENY)
1499 enabled = TRUE;
1500 }
1501 }
1502
1503 fclose(stream);
1504
1505 return (enabled ? 0 : 1);
1506}
1507
1508/*
1509 * Check the rules for an appropriate action for this domain.
1510 * The rules are ordered by domain length, with longest first, so the
1511 * first match is the most specific.
1512 */
1514{
1515 int i, diff;
1516
1517 for (i = 0; i < num_ccontrol; i++) {
1518 if (ccontrol[i].domain[0] == '.') {
1519 diff = strlen(domain) - strlen(ccontrol[i].domain);
1520 if (diff >= 0) {
1521 if (dStrAsciiCasecmp(domain + diff, ccontrol[i].domain) != 0)
1522 continue;
1523 } else {
1524 continue;
1525 }
1526 } else {
1527 if (dStrAsciiCasecmp(domain, ccontrol[i].domain) != 0)
1528 continue;
1529 }
1530
1531 /* If we got here we have a match */
1532 return( ccontrol[i].action );
1533 }
1534
1535 return default_action;
1536}
1537
1538/* -- Dpi parser ----------------------------------------------------------- */
1539
1540/*
1541 * Parse a data stream (dpi protocol)
1542 * Note: Buf is a zero terminated string
1543 * Return code: { 0:OK, 1:Abort, 2:Close }
1544 */
1545static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
1546{
1547 char *cmd, *cookie, *host, *path;
1548 int ret = 1;
1549 size_t BufSize = strlen(Buf);
1550
1551 cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
1552
1553 if (!cmd) {
1554 /* abort */
1555 } else if (client->status == 0) {
1556 /* authenticate */
1557 if (a_Dpip_check_auth(Buf) == 1) {
1558 client->status = 1;
1559 ret = 0;
1560 }
1561 } else if (strcmp(cmd, "DpiBye") == 0) {
1562 dFree(cmd);
1563 MSG("(pid %d): Got DpiBye.\n", (int)getpid());
1564 exit(0);
1565
1566 } else if (strcmp(cmd, "set_cookie") == 0) {
1567 int st;
1568 char *date;
1569
1570 cookie = a_Dpip_get_attr_l(Buf, BufSize, "cookie");
1571 host = a_Dpip_get_attr_l(Buf, BufSize, "host");
1572 path = a_Dpip_get_attr_l(Buf, BufSize, "path");
1573 date = a_Dpip_get_attr_l(Buf, BufSize, "date");
1574
1575 st = Cookies_set(cookie, host, path, date);
1576
1577 dFree(cmd);
1578 cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "set_cookie_answer",
1579 st == 0 ? "ok" : "not set");
1580 a_Dpip_dsh_write_str(sh, 1, cmd);
1581
1582 dFree(date);
1583 dFree(path);
1584 dFree(host);
1585 dFree(cookie);
1586 ret = 2;
1587
1588 } else if (strcmp(cmd, "get_cookie") == 0) {
1589 char *scheme = a_Dpip_get_attr_l(Buf, BufSize, "scheme");
1590
1591 host = a_Dpip_get_attr_l(Buf, BufSize, "host");
1592 path = a_Dpip_get_attr_l(Buf, BufSize, "path");
1593
1594 cookie = Cookies_get(host, path, scheme);
1595 dFree(scheme);
1596 dFree(path);
1597 dFree(host);
1598
1599 dFree(cmd);
1600 cmd = a_Dpip_build_cmd("cmd=%s cookie=%s", "get_cookie_answer", cookie);
1601
1602 if (a_Dpip_dsh_write_str(sh, 1, cmd)) {
1603 ret = 1;
1604 } else {
1605 _MSG("a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
1606 ret = 2;
1607 }
1608 dFree(cookie);
1609 }
1610 dFree(cmd);
1611
1612 return ret;
1613}
1614
1615/* -- Termination handlers ----------------------------------------------- */
1616/*
1617 * (was to delete the local namespace socket),
1618 * but this is handled by 'dpid' now.
1619 */
1620static void cleanup(void)
1621{
1623 MSG("cleanup\n");
1624 /* no more cleanup required */
1625}
1626
1627/*
1628 * Perform any necessary cleanups upon abnormal termination
1629 */
1630static void termination_handler(int signum)
1631{
1632 exit(signum);
1633}
1634
1635
1636/*
1637 * -- MAIN -------------------------------------------------------------------
1638 */
1639int main(void) {
1640 struct sockaddr_in sin;
1641 socklen_t address_size;
1642 ClientInfo *client;
1643 int sock_fd, code;
1644 char *buf;
1645 Dsh *sh;
1646
1647 /* Arrange the cleanup function for terminations via exit() */
1648 atexit(cleanup);
1649
1650 /* Arrange the cleanup function for abnormal terminations */
1651 if (signal (SIGINT, termination_handler) == SIG_IGN)
1652 signal (SIGINT, SIG_IGN);
1653 if (signal (SIGHUP, termination_handler) == SIG_IGN)
1654 signal (SIGHUP, SIG_IGN);
1655 if (signal (SIGTERM, termination_handler) == SIG_IGN)
1656 signal (SIGTERM, SIG_IGN);
1657
1658 Cookies_init();
1659 MSG("(v.1) accepting connections...\n");
1660
1661 if (disabled)
1662 exit(1);
1663
1664 /* some OSes may need this... */
1665 address_size = sizeof(struct sockaddr_in);
1666
1667 while (1) {
1668 sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
1669 if (sock_fd == -1) {
1670 perror("[accept]");
1671 exit(1);
1672 }
1673
1674 /* create the Dsh structure */
1675 sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
1676 client = dNew(ClientInfo,1);
1677 client->sh = sh;
1678 client->status = 0;
1679
1680 while (1) {
1681 code = 1;
1682 if ((buf = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
1683 /* Let's see what we fished... */
1684 _MSG(" buf = {%s}\n", buf);
1685 code = srv_parse_tok(sh, client, buf);
1686 dFree(buf);
1687 }
1688
1689 _MSG(" code = %d %s\n", code, code == 1 ? "EXIT" : "BREAK");
1690 if (code == 1) {
1691 exit(1);
1692 } else if (code == 2) {
1693 break;
1694 }
1695 }
1696
1697 _MSG("Closing Dsh\n");
1700 dFree(client);
1701
1702 }/*while*/
1703
1704 return 0;
1705}
1706
1707#endif /* !DISABLE_COOKIES */
unsigned int uint_t
Definition d_size.h:20
unsigned char bool_t
Definition d_size.h:21
static Dsh * sh
Definition datauri.c:39
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
Definition dlib.c:102
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
Definition dlib.c:769
char * dStrsep(char **orig, const char *delim)
strsep() implementation
Definition dlib.c:159
void dFree(void *mem)
Definition dlib.c:68
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:203
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
Definition dlib.c:464
char * dStrstrip(char *s)
Remove leading and trailing whitespace.
Definition dlib.c:122
void dStr_append(Dstr *ds, const char *s)
Append a C string to a Dstr.
Definition dlib.c:316
void dList_insert_pos(Dlist *lp, void *data, int pos0)
Insert an element at a given position [0 based].
Definition dlib.c:576
char * dStrdup(const char *s)
Definition dlib.c:77
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:548
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
Definition dlib.c:215
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:613
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:662
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:623
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:337
char * dStrndup(const char *s, size_t sz)
Definition dlib.c:88
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:325
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
Definition dlib.c:597
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:796
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:564
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
Definition dlib.c:704
void dList_remove(Dlist *lp, const void *data)
Definition dlib.c:641
char * dGethomedir(void)
Return the home directory in a static string (don't free)
Definition dlib.c:906
#define dStrerror
Definition dlib.h:95
#define dNew0(type, count)
Definition dlib.h:51
#define dIsspace(c)
Definition dlib.h:33
#define TRUE
Definition dlib.h:23
#define FALSE
Definition dlib.h:19
#define dNew(type, count)
Definition dlib.h:49
static int Cookies_rm_expired_cookies(DomainNode *node)
Definition cookies.c:677
static bool_t Cookies_get_time(struct tm *tm, const char **str)
Definition cookies.c:508
static int num_ccontrol
Definition cookies.c:128
static void Cookies_add_matching_cookies(const char *domain, const char *url_path, bool_t host_only_val, Dlist *matching_cookies, bool_t is_tls)
Definition cookies.c:1273
#define _MSG(...)
Definition cookies.c:58
static CookieData_t * Cookies_get_LRU(Dlist *cookies)
Definition cookies.c:656
static struct tm cookies_epoch_tm
Definition cookies.c:143
#define MAX_DOMAIN_COOKIES
Definition cookies.c:81
static struct tm * Cookies_parse_date(const char *date)
Definition cookies.c:606
static bool_t Cookies_domain_matches(char *A, char *B)
Definition cookies.c:1096
static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
Definition cookies.c:1545
#define MSG(...)
Definition cookies.c:59
static bool_t Cookies_date_delim(char c)
Definition cookies.c:588
static Dlist * all_cookies
Definition cookies.c:121
static void Cookies_too_many(DomainNode *node)
Definition cookies.c:708
static void Cookies_load_cookies(FILE *stream)
Definition cookies.c:248
static int num_ccontrol_max
Definition cookies.c:129
static void Cookies_delete_node(DomainNode *node)
Definition cookies.c:179
static long cookies_use_counter
Definition cookies.c:132
static int Domain_node_cmp(const void *v1, const void *v2)
Definition cookies.c:158
static CookieControl * ccontrol
Definition cookies.c:127
static char * Cookies_parse_attr(char **cookie_str)
Definition cookies.c:799
static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
Definition cookies.c:168
static void cleanup(void)
Definition cookies.c:1620
static bool_t Cookies_get_day(struct tm *tm, const char **str)
Definition cookies.c:534
static int Cookies_get_timefield(const char **str)
Definition cookies.c:485
static bool_t Cookies_get_year(struct tm *tm, const char **str)
Definition cookies.c:548
static void Cookies_init(void)
Definition cookies.c:335
static int Cookie_control_init(void)
Definition cookies.c:1418
static time_t cookies_epoch_time
Definition cookies.c:144
static uint_t Cookies_internal_dots_required(const char *host)
Definition cookies.c:1136
int main(void)
Definition cookies.c:1639
#define MAX_TOTAL_COOKIES
Definition cookies.c:82
#define a_List_add(list, num_items, alloc_step)
Definition cookies.c:68
static CookieControlAction default_action
Definition cookies.c:130
static void termination_handler(int signum)
Definition cookies.c:1630
static bool_t Cookies_get_month(struct tm *tm, const char **str)
Definition cookies.c:460
static bool_t Cookies_match(CookieData_t *cookie, const char *url_path, bool_t host_only_val, bool_t is_tls)
Definition cookies.c:1255
static bool_t Cookies_path_matches(const char *url_path, const char *cookie_path)
Definition cookies.c:1054
static bool_t Cookies_domain_is_ip(const char *domain)
Definition cookies.c:1026
static const char *const cookies_txt_header_str
Definition cookies.c:135
static FILE * file_stream
Definition cookies.c:134
static void Cookies_eat_value(char **cookie_str)
Definition cookies.c:847
static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
Definition cookies.c:1181
CookieControlAction
Definition cookies.c:84
@ COOKIE_ACCEPT_SESSION
Definition cookies.c:86
@ COOKIE_ACCEPT
Definition cookies.c:85
@ COOKIE_DENY
Definition cookies.c:87
static CookieControlAction Cookies_control_check_domain(const char *domain)
Definition cookies.c:1513
static void Cookies_add_cookie(CookieData_t *cookie)
Definition cookies.c:725
static CookieData_t * Cookies_parse(char *cookie_str, const char *server_date)
Definition cookies.c:892
static int Cookies_set(char *cookie_string, char *url_host, char *url_path, char *server_date)
Definition cookies.c:1216
static void Cookies_save_and_free(void)
Definition cookies.c:390
static Dlist * domains
Definition cookies.c:124
static void Cookies_tm_init(struct tm *tm)
Definition cookies.c:234
static double Cookies_server_timediff(const char *server_date)
Definition cookies.c:857
static int Cookies_cmp(const void *a, const void *b)
Definition cookies.c:1014
static bool_t disabled
Definition cookies.c:133
static void Cookies_free_cookie(CookieData_t *cookie)
Definition cookies.c:225
static FILE * Cookies_fopen(const char *filename, const char *mode, const char *init_str)
Definition cookies.c:191
static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
Definition cookies.c:1076
static time_t cookies_future_time
Definition cookies.c:144
static char * Cookies_parse_value(char **cookie_str)
Definition cookies.c:820
static void Cookies_unquote_string(char *str)
Definition cookies.c:875
#define LINE_MAXLEN
Definition cookies.c:79
static char * Cookies_get(char *url_host, char *url_path, char *url_scheme)
Definition cookies.c:1321
void a_Dpip_dsh_free(Dsh *dsh)
Free the SockHandler structure.
Definition dpip.c:525
char * a_Dpip_build_cmd(const char *format,...)
Printf like function for building dpip commands.
Definition dpip.c:83
int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str)
Convenience function.
Definition dpip.c:374
char * a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
Return a newlly allocated string with the next dpip token in the socket.
Definition dpip.c:493
void a_Dpip_dsh_close(Dsh *dsh)
Close this socket for reading and writing.
Definition dpip.c:504
char * a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname)
Task: given a tag, its size and an attribute name, return the attribute value (stuffing of ' is remov...
Definition dpip.c:134
int a_Dpip_check_auth(const char *auth_tag)
Check whether the given 'auth' string equals what dpid saved.
Definition dpip.c:201
Dsh * a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz)
Create and initialize a dpip socket handler.
Definition dpip.c:247
Definition dlib.h:131
Dpip socket handler type.
Definition dpip.h:31
int status
status code: DPIP_EAGAIN | DPIP_ERROR | DPIP_EOF
Definition dpip.h:42
Definition dlib.h:102
Dstr_char_t * str
Definition dlib.h:105
static void path()
Definition cookies.c:859