35#include <sys/socket.h>
38#include <netinet/in.h>
52#include "../dpip/dpip.h"
59#define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
68#define a_List_add(list,num_items,alloc_step) \
70 list = dMalloc(alloc_step * sizeof((*list))); \
72 if (num_items >= alloc_step){ \
73 while ( num_items >= alloc_step ) \
75 list = dRealloc(list, alloc_step * sizeof((*list))); \
79#define LINE_MAXLEN 4096
81#define MAX_DOMAIN_COOKIES 20
82#define MAX_TOTAL_COOKIES 1200
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";
143static struct tm
cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
153static int Cookies_cmp(
const void *a,
const void *b);
160 const DomainNode *n1 = v1, *n2 = v2;
170 const DomainNode *node = v1;
171 const char *domain = v2;
192 const char *init_str)
197 if ((F_in = fopen(filename, mode)) == NULL) {
199 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
202 rc = write(fd, init_str, strlen(init_str));
204 MSG(
"Cookies: Could not write initial string to file %s: %s\n",
210 MSG(
"Created file: %s\n", filename);
211 F_in = fopen(filename, mode);
213 MSG(
"Could not create file: %s!\n", filename);
219 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
228 dFree(cookie->value);
229 dFree(cookie->domain);
256 while (!feof(stream)) {
258 if ((fgets(line,
LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
259 MSG(
"Error while reading from cookies.txt: %s\n",
dStrerror(errno));
266 if ((line[0] !=
'\0') && (line[0] !=
'#')) {
279 char *line_marker = line;
280 CookieData_t *cookie =
dNew0(CookieData_t, 1);
282 cookie->session_only =
FALSE;
284 piece =
dStrsep(&line_marker,
"\t");
285 if (piece != NULL && piece[0] ==
'F')
286 cookie->host_only =
TRUE;
288 piece =
dStrsep(&line_marker,
"\t");
289 if (piece != NULL && piece[0] ==
'T')
290 cookie->secure =
TRUE;
291 piece =
dStrsep(&line_marker,
"\t");
296 long seconds = strtol(piece, NULL, 10);
299 tm.tm_min += seconds / 60;
300 tm.tm_sec += seconds % 60;
301 cookie->expires_at = mktime(&tm);
303 cookie->expires_at = (time_t) -1;
306 cookie->value =
dStrdup(line_marker ? line_marker :
"");
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");
321 cookie->session_only =
TRUE;
341 struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
351 MSG(
"Disabling cookies.\n");
362 MSG(
"ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
372 lck.l_type = F_WRLCK;
373 lck.l_whence = SEEK_SET;
378 MSG(
"The cookies file has a file lock; disabling cookies!\n");
382 MSG(
"Enabling cookies as per cookiesrc...\n");
392 int i, fd, saved = 0;
394 CookieData_t *cookie;
408 if (ftruncate(fd, 0) == -1)
409 MSG(
"Cookies: Truncate file stream failed: %s\n",
dStrerror(errno));
415 if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
419 len = snprintf(buf,
LINE_MAXLEN,
"%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
421 cookie->host_only ?
"FALSE" :
"TRUE",
423 cookie->secure ?
"TRUE" :
"FALSE",
424 (long) difftime(cookie->expires_at,
432 MSG(
"Not saving overly long cookie for %s.\n", cookie->domain);
443 lockf(fd, F_ULOCK, 0);
447 lck.l_type = F_UNLCK;
448 lck.l_whence = SEEK_SET;
454 MSG(
"Cookies saved: %d.\n", saved);
462 static const char *
const months[] =
463 {
"Jan",
"Feb",
"Mar",
470 for (i = 0; i < 12; i++) {
472 _MSG(
"Found month: %s\n", months[i]);
488 const char *s = *str;
510 const char *s = *str;
536 const char *s = *str;
551 const char *s = *str;
574 if (n >= 70 && n <= 99)
579 tm->tm_year = n - 1900;
590 return (c ==
'\x09' ||
591 (c >=
'\x20' && c <=
'\x2F') ||
592 (c >=
'\x3B' && c <=
'\x40') ||
593 (c >=
'\x5B' && c <=
'\x60') ||
594 (c >=
'\x7B' && c <=
'\x7E'));
609 found_year =
FALSE, matched;
610 struct tm *tm =
dNew0(
struct tm, 1);
611 const char *s = date;
618 if (!matched && !found_day)
620 if (!matched && !found_month)
622 if (!matched && !found_year)
629 if (!found_time || !found_day || !found_month || !found_year) {
632 MSG(
"In date \"%s\", format not understood.\n", date);
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);
661 for (i = 1; i < n; i++) {
664 if (curr->last_used < lru->last_used)
682 time_t now = time(NULL);
687 if (difftime(c->expires_at, now) < 0) {
688 DomainNode *currnode = node ? node :
712 MSG(
"Too many cookies! "
713 "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
714 lru->name, lru->value);
727 Dlist *domain_cookies;
732 domain_cookies = (node) ? node->cookies : NULL;
734 if (domain_cookies) {
743 if ((cookie->expires_at == (time_t) -1) ||
744 (difftime(cookie->expires_at, time(NULL)) <= 0)) {
749 _MSG(
"Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
750 cookie->value, cookie->domain, cookie->path);
762 domain_cookies = (node) ? node->cookies : NULL;
768 }
else if (domain_cookies) {
772 domain_cookies = (node) ? node->cookies : NULL;
781 if (!domain_cookies) {
784 node =
dNew(DomainNode, 1);
785 node->domain =
dStrdup(cookie->domain);
786 node->cookies = domain_cookies;
792 if (domain_cookies && (
dList_length(domain_cookies) == 0))
809 len = strcspn(str,
"=;");
812 while (len && (str[len - 1] ==
' ' || str[len - 1] ==
'\t'))
825 if (**cookie_str ==
'=') {
832 len = strcspn(str,
";");
835 while (len && (str[len - 1] ==
' ' || str[len - 1] ==
'\t'))
849 if (**cookie_str ==
'=')
850 *cookie_str += strcspn(*cookie_str,
";");
865 time_t server_time = mktime(server_tm);
867 if (server_time != (time_t) -1)
868 ret = difftime(time(NULL), server_time);
877 if (str && str[0] ==
'\"') {
880 if (len > 1 && str[len - 1] ==
'\"') {
882 while ((*str = str[1]))
892static CookieData_t *
Cookies_parse(
char *cookie_str,
const char *server_date)
894 CookieData_t *cookie = NULL;
895 char *str = cookie_str;
913 if (*str !=
'=' || *attr ==
'\0') {
918 cookie =
dNew0(CookieData_t, 1);
926 cookie->expires_at = mktime(tm);
927 if (cookie->expires_at == (time_t) -1)
932 cookie->path = value;
935 dFree(cookie->domain);
936 cookie->domain = value;
939 if (isdigit(*value) || *value ==
'-') {
941 time_t now = time(NULL);
942 struct tm *tm = gmtime(&now);
945 age = (*value ==
'-') ? 0 : strtol(value, NULL, 10);
947 if (errno == ERANGE ||
948 (age > 0 && (age > INT_MAX - tm->tm_sec))) {
950 tm->tm_sec = INT_MAX;
954 cookie->expires_at = mktime(tm);
955 if (age > 0 && cookie->expires_at == (time_t) -1) {
958 _MSG(
"Cookie to expire at %s", ctime(&cookie->expires_at));
959 expires = max_age =
TRUE;
968 _MSG(
"Expires attribute gives %s\n", value);
972 cookie->expires_at = mktime(tm);
973 if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
979 _MSG(
"Cookie to expire at %s", ctime(&cookie->expires_at));
982 cookie->expires_at = (time_t) -1;
990 cookie->secure =
TRUE;
995 MSG(
"Cookie contains unknown attribute: '%s'\n", attr);
1007 cookie->session_only = expires ==
FALSE;
1016 const CookieData_t *ca = a, *cb = b;
1018 return (ca->host_only != cb->host_only) ||
1019 (strcmp(ca->name, cb->name) != 0) ||
1020 (strcmp(ca->path, cb->path) != 0);
1033 len = strlen(domain);
1035 if (len == strspn(domain,
"0123456789.")) {
1036 _MSG(
"an IPv4 address\n");
1039 if (strchr(domain,
':') &&
1040 (len == strspn(domain,
"0123456789abcdefABCDEF:."))) {
1042 MSG(
"an IPv6 address\n");
1055 const char *cookie_path)
1059 if (!url_path || !cookie_path) {
1062 uint_t c_len = strlen(cookie_path);
1063 uint_t u_len = strlen(url_path);
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] ==
'/')));
1078 if (!cookie->path || cookie->path[0] !=
'/') {
1079 dFree(cookie->path);
1082 uint_t len = strlen(url_path);
1084 while (len && url_path[len] !=
'/')
1086 cookie->path =
dStrndup(url_path, len ? len : 1);
1100 if (!A || !*A || !B || !*B)
1117 diff = strlen(A) - strlen(B);
1141 int start, after, tld_len;
1146 after = strlen(host);
1147 if (after > 0 && host[after - 1] ==
'.')
1150 while (start > 0 && host[start - 1] !=
'.')
1152 tld_len = after - start;
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]);
1165 for (i = 0; i < tld_num; i++) {
1166 if (strlen(tlds[i]) == (
uint_t) tld_len &&
1168 _MSG(
"TLD code matched %s\n", tlds[i]);
1185 if (!cookie->domain) {
1186 cookie->domain =
dStrdup(host);
1187 cookie->host_only =
TRUE;
1195 for (i = 1; i < strlen(cookie->domain) - 1; i++) {
1196 if (cookie->domain[i] ==
'.')
1204 MSG(
"not enough dots in %s\n", cookie->domain);
1208 _MSG(
"host %s and domain %s is all right\n", host, cookie->domain);
1217 char *url_path,
char *server_date)
1220 CookieData_t *cookie;
1228 MSG(
"denied SET for %s\n", url_host);
1232 MSG(
"%s SETTING: %s\n", url_host, cookie_string);
1238 cookie->session_only =
TRUE;
1242 MSG(
"Rejecting cookie for domain %s from host %s path %s\n",
1243 cookie->domain, url_host, url_path);
1258 if (cookie->host_only != host_only_val)
1263 if (cookie->secure && !is_tls)
1274 const char *url_path,
1276 Dlist *matching_cookies,
1283 CookieData_t *cookie;
1284 Dlist *domain_cookies = node->cookies;
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);
1297 if (
Cookies_match(cookie, url_path, host_only_val, is_tls)) {
1300 uint_t path_length = strlen(cookie->path);
1307 strlen(curr->path) >= path_length;
1324 char *domain_str, *str;
1325 CookieData_t *cookie;
1326 Dlist *matching_cookies;
1327 bool_t is_tls, is_ip_addr, host_only_val;
1329 Dstr *cookie_dstring;
1349 host_only_val =
FALSE;
1352 domain_str =
dStrconcat(
".", url_host, NULL);
1354 matching_cookies, is_tls);
1357 host_only_val =
TRUE;
1360 matching_cookies, is_tls);
1361 host_only_val =
FALSE;
1364 matching_cookies, is_tls);
1367 for (domain_str = strchr(url_host+1,
'.');
1368 domain_str != NULL && *domain_str;
1369 domain_str = strchr(domain_str+1,
'.')) {
1372 matching_cookies, is_tls);
1373 if (domain_str[1]) {
1377 matching_cookies, is_tls);
1388 for (i = 0; (cookie =
dList_nth_data(matching_cookies, i)); ++i) {
1389 dStr_sprintfa(cookie_dstring,
"%s=%s", cookie->name, cookie->value);
1391 dList_length(matching_cookies) > i + 1 ?
"; " :
"\r\n");
1396 str = cookie_dstring->
str;
1400 MSG(
"%s GETTING: %s", url_host, str);
1422 char *filename, *rc;
1437 while (!feof(stream)) {
1440 if (!rc && ferror(stream)) {
1441 MSG(
"Error while reading rule from cookiesrc: %s\n",
1449 if (line[0] !=
'\0' && line[0] !=
'#') {
1453 while (line[i] !=
'\0' && !
dIsspace(line[i]))
1454 domain[j++] = line[i++];
1463 while (line[i] !=
'\0' && !
dIsspace(line[i]))
1464 rule[j++] = line[i++];
1474 MSG(
"Cookies: rule '%s' for domain '%s' is not recognised.\n",
1486 uint_t len = strlen(cc.domain);
1491 i > 0 && (len > strlen(
ccontrol[i-1].domain));
1505 return (enabled ? 0 : 1);
1518 if (
ccontrol[i].domain[0] ==
'.') {
1519 diff = strlen(domain) - strlen(
ccontrol[i].domain);
1547 char *cmd, *cookie, *host, *
path;
1549 size_t BufSize = strlen(Buf);
1555 }
else if (client->status == 0) {
1561 }
else if (strcmp(cmd,
"DpiBye") == 0) {
1563 MSG(
"(pid %d): Got DpiBye.\n", (
int)getpid());
1566 }
else if (strcmp(cmd,
"set_cookie") == 0) {
1579 st == 0 ?
"ok" :
"not set");
1588 }
else if (strcmp(cmd,
"get_cookie") == 0) {
1605 _MSG(
"a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
1640 struct sockaddr_in sin;
1641 socklen_t address_size;
1652 signal (SIGINT, SIG_IGN);
1654 signal (SIGHUP, SIG_IGN);
1656 signal (SIGTERM, SIG_IGN);
1659 MSG(
"(v.1) accepting connections...\n");
1665 address_size =
sizeof(
struct sockaddr_in);
1668 sock_fd = accept(STDIN_FILENO, (
struct sockaddr *)&sin, &address_size);
1669 if (sock_fd == -1) {
1676 client =
dNew(ClientInfo,1);
1684 _MSG(
" buf = {%s}\n", buf);
1689 _MSG(
" code = %d %s\n", code, code == 1 ?
"EXIT" :
"BREAK");
1692 }
else if (code == 2) {
1697 _MSG(
"Closing Dsh\n");
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
char * dStrsep(char **orig, const char *delim)
strsep() implementation
int dStrAsciiCasecmp(const char *s1, const char *s2)
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
char * dStrstrip(char *s)
Remove leading and trailing whitespace.
void dStr_append(Dstr *ds, const char *s)
Append a C string to a Dstr.
void dList_insert_pos(Dlist *lp, void *data, int pos0)
Insert an element at a given position [0 based].
char * dStrdup(const char *s)
Dlist * dList_new(int size)
Create a new empty list.
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
int dList_length(Dlist *lp)
For completing the ADT.
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
void dStr_free(Dstr *ds, int all)
Free a dillo string.
char * dStrndup(const char *s, size_t sz)
Dstr * dStr_new(const char *s)
Create a new string.
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
void dList_free(Dlist *lp)
Free a list (not its elements)
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
void dList_remove(Dlist *lp, const void *data)
char * dGethomedir(void)
Return the home directory in a static string (don't free)
#define dNew0(type, count)
#define dNew(type, count)
static int Cookies_rm_expired_cookies(DomainNode *node)
static bool_t Cookies_get_time(struct tm *tm, const char **str)
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)
static CookieData_t * Cookies_get_LRU(Dlist *cookies)
static struct tm cookies_epoch_tm
#define MAX_DOMAIN_COOKIES
static struct tm * Cookies_parse_date(const char *date)
static bool_t Cookies_domain_matches(char *A, char *B)
static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
static bool_t Cookies_date_delim(char c)
static Dlist * all_cookies
static void Cookies_too_many(DomainNode *node)
static void Cookies_load_cookies(FILE *stream)
static int num_ccontrol_max
static void Cookies_delete_node(DomainNode *node)
static long cookies_use_counter
static int Domain_node_cmp(const void *v1, const void *v2)
static CookieControl * ccontrol
static char * Cookies_parse_attr(char **cookie_str)
static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
static void cleanup(void)
static bool_t Cookies_get_day(struct tm *tm, const char **str)
static int Cookies_get_timefield(const char **str)
static bool_t Cookies_get_year(struct tm *tm, const char **str)
static void Cookies_init(void)
static int Cookie_control_init(void)
static time_t cookies_epoch_time
static uint_t Cookies_internal_dots_required(const char *host)
#define MAX_TOTAL_COOKIES
#define a_List_add(list, num_items, alloc_step)
static CookieControlAction default_action
static void termination_handler(int signum)
static bool_t Cookies_get_month(struct tm *tm, const char **str)
static bool_t Cookies_match(CookieData_t *cookie, const char *url_path, bool_t host_only_val, bool_t is_tls)
static bool_t Cookies_path_matches(const char *url_path, const char *cookie_path)
static bool_t Cookies_domain_is_ip(const char *domain)
static const char *const cookies_txt_header_str
static FILE * file_stream
static void Cookies_eat_value(char **cookie_str)
static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
static CookieControlAction Cookies_control_check_domain(const char *domain)
static void Cookies_add_cookie(CookieData_t *cookie)
static CookieData_t * Cookies_parse(char *cookie_str, const char *server_date)
static int Cookies_set(char *cookie_string, char *url_host, char *url_path, char *server_date)
static void Cookies_save_and_free(void)
static void Cookies_tm_init(struct tm *tm)
static double Cookies_server_timediff(const char *server_date)
static int Cookies_cmp(const void *a, const void *b)
static void Cookies_free_cookie(CookieData_t *cookie)
static FILE * Cookies_fopen(const char *filename, const char *mode, const char *init_str)
static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
static time_t cookies_future_time
static char * Cookies_parse_value(char **cookie_str)
static void Cookies_unquote_string(char *str)
static char * Cookies_get(char *url_host, char *url_path, char *url_scheme)
void a_Dpip_dsh_free(Dsh *dsh)
Free the SockHandler structure.
char * a_Dpip_build_cmd(const char *format,...)
Printf like function for building dpip commands.
int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str)
Convenience function.
char * a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
Return a newlly allocated string with the next dpip token in the socket.
void a_Dpip_dsh_close(Dsh *dsh)
Close this socket for reading and writing.
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...
int a_Dpip_check_auth(const char *auth_tag)
Check whether the given 'auth' string equals what dpid saved.
Dsh * a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz)
Create and initialize a dpip socket handler.
Dpip socket handler type.
int status
status code: DPIP_EAGAIN | DPIP_ERROR | DPIP_EOF