35#include <sys/socket.h>
38#include <netinet/in.h>
51#include "../dpip/dpip.h"
58#define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
67#define a_List_add(list,num_items,alloc_step) \
69 list = dMalloc(alloc_step * sizeof((*list))); \
71 if (num_items >= alloc_step){ \
72 while ( num_items >= alloc_step ) \
74 list = dRealloc(list, alloc_step * sizeof((*list))); \
78#define LINE_MAXLEN 4096
80#define MAX_DOMAIN_COOKIES 20
81#define MAX_TOTAL_COOKIES 1200
135"# HTTP Cookie File\n"
136"# This is a generated file! Do not edit.\n"
137"# [domain subdomains path secure expiry_time name value]\n\n";
142static struct tm
cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
152static int Cookies_cmp(
const void *a,
const void *b);
159 const DomainNode *n1 = v1, *n2 = v2;
169 const DomainNode *node = v1;
170 const char *domain = v2;
191 const char *init_str)
196 if ((F_in = fopen(filename, mode)) == NULL) {
198 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
201 rc = write(fd, init_str, strlen(init_str));
203 MSG(
"Cookies: Could not write initial string to file %s: %s\n",
209 MSG(
"Created file: %s\n", filename);
210 F_in = fopen(filename, mode);
212 MSG(
"Could not create file: %s!\n", filename);
218 fcntl(fileno(F_in), F_SETFD, FD_CLOEXEC | fcntl(fileno(F_in), F_GETFD));
227 dFree(cookie->value);
228 dFree(cookie->domain);
255 while (!feof(stream)) {
257 if ((fgets(line,
LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
258 MSG(
"Error while reading from cookies.txt: %s\n",
dStrerror(errno));
265 if ((line[0] !=
'\0') && (line[0] !=
'#')) {
278 char *line_marker = line;
279 CookieData_t *cookie =
dNew0(CookieData_t, 1);
281 cookie->session_only =
FALSE;
283 piece =
dStrsep(&line_marker,
"\t");
284 if (piece != NULL && piece[0] ==
'F')
285 cookie->host_only =
TRUE;
287 piece =
dStrsep(&line_marker,
"\t");
288 if (piece != NULL && piece[0] ==
'T')
289 cookie->secure =
TRUE;
290 piece =
dStrsep(&line_marker,
"\t");
295 long seconds = strtol(piece, NULL, 10);
298 tm.tm_min += seconds / 60;
299 tm.tm_sec += seconds % 60;
300 cookie->expires_at = mktime(&tm);
302 cookie->expires_at = (time_t) -1;
305 cookie->value =
dStrdup(line_marker ? line_marker :
"");
307 if (!cookie->domain || cookie->domain[0] ==
'\0' ||
308 !cookie->path || cookie->path[0] !=
'/' ||
309 !cookie->name || !cookie->value) {
310 MSG(
"Malformed line in cookies.txt file!\n");
320 cookie->session_only =
TRUE;
340 struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
350 MSG(
"Disabling cookies.\n");
361 MSG(
"ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
371 lck.l_type = F_WRLCK;
372 lck.l_whence = SEEK_SET;
377 MSG(
"The cookies file has a file lock; disabling cookies!\n");
381 MSG(
"Enabling cookies as per cookiesrc...\n");
391 int i, fd, saved = 0;
393 CookieData_t *cookie;
407 if (ftruncate(fd, 0) == -1)
408 MSG(
"Cookies: Truncate file stream failed: %s\n",
dStrerror(errno));
414 if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
418 len = snprintf(buf,
LINE_MAXLEN,
"%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
420 cookie->host_only ?
"FALSE" :
"TRUE",
422 cookie->secure ?
"TRUE" :
"FALSE",
423 (long) difftime(cookie->expires_at,
431 MSG(
"Not saving overly long cookie for %s.\n", cookie->domain);
442 lockf(fd, F_ULOCK, 0);
446 lck.l_type = F_UNLCK;
447 lck.l_whence = SEEK_SET;
453 MSG(
"Cookies saved: %d.\n", saved);
461 static const char *
const months[] =
462 {
"Jan",
"Feb",
"Mar",
469 for (i = 0; i < 12; i++) {
471 _MSG(
"Found month: %s\n", months[i]);
487 const char *s = *str;
509 const char *s = *str;
535 const char *s = *str;
550 const char *s = *str;
573 if (n >= 70 && n <= 99)
578 tm->tm_year = n - 1900;
589 return (c ==
'\x09' ||
590 (c >=
'\x20' && c <=
'\x2F') ||
591 (c >=
'\x3B' && c <=
'\x40') ||
592 (c >=
'\x5B' && c <=
'\x60') ||
593 (c >=
'\x7B' && c <=
'\x7E'));
608 found_year =
FALSE, matched;
609 struct tm *tm =
dNew0(
struct tm, 1);
610 const char *s = date;
617 if (!matched && !found_day)
619 if (!matched && !found_month)
621 if (!matched && !found_year)
628 if (!found_time || !found_day || !found_month || !found_year) {
631 MSG(
"In date \"%s\", format not understood.\n", date);
640 !(tm->tm_mday > 0 && tm->tm_mday < 32 && tm->tm_mon >= 0 &&
641 tm->tm_mon < 12 && tm->tm_year >= 0 && tm->tm_hour >= 0 &&
642 tm->tm_hour < 24 && tm->tm_min >= 0 && tm->tm_min < 60 &&
643 tm->tm_sec >= 0 && tm->tm_sec < 60)) {
644 MSG(
"Date \"%s\" values not in range.\n", date);
660 for (i = 1; i < n; i++) {
663 if (curr->last_used < lru->last_used)
681 time_t now = time(NULL);
686 if (difftime(c->expires_at, now) < 0) {
687 DomainNode *currnode = node ? node :
711 MSG(
"Too many cookies! "
712 "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
713 lru->name, lru->value);
726 Dlist *domain_cookies;
731 domain_cookies = (node) ? node->cookies : NULL;
733 if (domain_cookies) {
742 if ((cookie->expires_at == (time_t) -1) ||
743 (difftime(cookie->expires_at, time(NULL)) <= 0)) {
748 _MSG(
"Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
749 cookie->value, cookie->domain, cookie->path);
761 domain_cookies = (node) ? node->cookies : NULL;
767 }
else if (domain_cookies) {
771 domain_cookies = (node) ? node->cookies : NULL;
780 if (!domain_cookies) {
783 node =
dNew(DomainNode, 1);
784 node->domain =
dStrdup(cookie->domain);
785 node->cookies = domain_cookies;
791 if (domain_cookies && (
dList_length(domain_cookies) == 0))
808 len = strcspn(str,
"=;");
811 while (len && (str[len - 1] ==
' ' || str[len - 1] ==
'\t'))
824 if (**cookie_str ==
'=') {
831 len = strcspn(str,
";");
834 while (len && (str[len - 1] ==
' ' || str[len - 1] ==
'\t'))
848 if (**cookie_str ==
'=')
849 *cookie_str += strcspn(*cookie_str,
";");
864 time_t server_time = mktime(server_tm);
866 if (server_time != (time_t) -1)
867 ret = difftime(time(NULL), server_time);
876 if (str && str[0] ==
'\"') {
879 if (len > 1 && str[len - 1] ==
'\"') {
881 while ((*str = str[1]))
891static CookieData_t *
Cookies_parse(
char *cookie_str,
const char *server_date)
893 CookieData_t *cookie = NULL;
894 char *str = cookie_str;
912 if (*str !=
'=' || *attr ==
'\0') {
917 cookie =
dNew0(CookieData_t, 1);
925 cookie->expires_at = mktime(tm);
926 if (cookie->expires_at == (time_t) -1)
931 cookie->path = value;
934 dFree(cookie->domain);
935 cookie->domain = value;
938 if (
dIsdigit(*value) || *value ==
'-') {
940 time_t now = time(NULL);
941 struct tm *tm = gmtime(&now);
944 age = (*value ==
'-') ? 0 : strtol(value, NULL, 10);
946 if (errno == ERANGE ||
947 (age > 0 && (age > INT_MAX - tm->tm_sec))) {
949 tm->tm_sec = INT_MAX;
953 cookie->expires_at = mktime(tm);
954 if (age > 0 && cookie->expires_at == (time_t) -1) {
957 _MSG(
"Cookie to expire at %s", ctime(&cookie->expires_at));
958 expires = max_age =
TRUE;
967 _MSG(
"Expires attribute gives %s\n", value);
971 cookie->expires_at = mktime(tm);
972 if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
978 _MSG(
"Cookie to expire at %s", ctime(&cookie->expires_at));
981 cookie->expires_at = (time_t) -1;
989 cookie->secure =
TRUE;
994 MSG(
"Cookie contains unknown attribute: '%s'\n", attr);
1006 cookie->session_only = expires ==
FALSE;
1015 const CookieData_t *ca = a, *cb = b;
1017 return (ca->host_only != cb->host_only) ||
1018 (strcmp(ca->name, cb->name) != 0) ||
1019 (strcmp(ca->path, cb->path) != 0);
1032 len = strlen(domain);
1034 if (len == strspn(domain,
"0123456789.")) {
1035 _MSG(
"an IPv4 address\n");
1038 if (strchr(domain,
':') &&
1039 (len == strspn(domain,
"0123456789abcdefABCDEF:."))) {
1041 MSG(
"an IPv6 address\n");
1054 const char *cookie_path)
1058 if (!url_path || !cookie_path) {
1061 uint_t c_len = strlen(cookie_path);
1062 uint_t u_len = strlen(url_path);
1064 ret = (!strncmp(cookie_path, url_path, c_len) &&
1065 ((c_len == u_len) ||
1066 (c_len > 0 && cookie_path[c_len - 1] ==
'/') ||
1067 (url_path[c_len] ==
'/')));
1077 if (!cookie->path || cookie->path[0] !=
'/') {
1078 dFree(cookie->path);
1081 uint_t len = strlen(url_path);
1083 while (len && url_path[len] !=
'/')
1085 cookie->path =
dStrndup(url_path, len ? len : 1);
1099 if (!A || !*A || !B || !*B)
1116 diff = strlen(A) - strlen(B);
1140 int start, after, tld_len;
1145 after = strlen(host);
1146 if (after > 0 && host[after - 1] ==
'.')
1149 while (start > 0 && host[start - 1] !=
'.')
1151 tld_len = after - start;
1159 const char *
const tlds[] = {
"bd",
"bn",
"ck",
"cy",
"er",
"fj",
"fk",
1160 "gu",
"il",
"jm",
"ke",
"kh",
"kw",
"mm",
"mz",
1161 "ni",
"np",
"pg",
"ye",
"za",
"zm",
"zw"};
1162 uint_t i, tld_num =
sizeof(tlds) /
sizeof(tlds[0]);
1164 for (i = 0; i < tld_num; i++) {
1165 if (strlen(tlds[i]) == (
uint_t) tld_len &&
1167 _MSG(
"TLD code matched %s\n", tlds[i]);
1184 if (!cookie->domain) {
1185 cookie->domain =
dStrdup(host);
1186 cookie->host_only =
TRUE;
1194 for (i = 1; i < strlen(cookie->domain) - 1; i++) {
1195 if (cookie->domain[i] ==
'.')
1203 MSG(
"not enough dots in %s\n", cookie->domain);
1207 _MSG(
"host %s and domain %s is all right\n", host, cookie->domain);
1216 char *url_path,
char *server_date)
1219 CookieData_t *cookie;
1227 MSG(
"denied SET for %s\n", url_host);
1231 MSG(
"%s SETTING: %s\n", url_host, cookie_string);
1237 cookie->session_only =
TRUE;
1241 MSG(
"Rejecting cookie for domain %s from host %s path %s\n",
1242 cookie->domain, url_host, url_path);
1257 if (cookie->host_only != host_only_val)
1262 if (cookie->secure && !is_tls)
1273 const char *url_path,
1275 Dlist *matching_cookies,
1282 CookieData_t *cookie;
1283 Dlist *domain_cookies = node->cookies;
1287 if (difftime(cookie->expires_at, time(NULL)) < 0) {
1288 _MSG(
"Goodbye, expired cookie %s=%s d:%s p:%s\n", cookie->name,
1289 cookie->value, cookie->domain, cookie->path);
1296 if (
Cookies_match(cookie, url_path, host_only_val, is_tls)) {
1299 uint_t path_length = strlen(cookie->path);
1306 strlen(curr->path) >= path_length;
1323 char *domain_str, *str;
1324 CookieData_t *cookie;
1325 Dlist *matching_cookies;
1326 bool_t is_tls, is_ip_addr, host_only_val;
1328 Dstr *cookie_dstring;
1348 host_only_val =
FALSE;
1351 domain_str =
dStrconcat(
".", url_host, NULL);
1353 matching_cookies, is_tls);
1356 host_only_val =
TRUE;
1359 matching_cookies, is_tls);
1360 host_only_val =
FALSE;
1363 matching_cookies, is_tls);
1366 for (domain_str = strchr(url_host+1,
'.');
1367 domain_str != NULL && *domain_str;
1368 domain_str = strchr(domain_str+1,
'.')) {
1371 matching_cookies, is_tls);
1372 if (domain_str[1]) {
1376 matching_cookies, is_tls);
1387 for (i = 0; (cookie =
dList_nth_data(matching_cookies, i)); ++i) {
1388 dStr_sprintfa(cookie_dstring,
"%s=%s", cookie->name, cookie->value);
1390 dList_length(matching_cookies) > i + 1 ?
"; " :
"\r\n");
1395 str = cookie_dstring->
str;
1399 MSG(
"%s GETTING: %s", url_host, str);
1421 char *filename, *rc;
1436 while (!feof(stream)) {
1439 if (!rc && ferror(stream)) {
1440 MSG(
"Error while reading rule from cookiesrc: %s\n",
1448 if (line[0] !=
'\0' && line[0] !=
'#') {
1452 while (line[i] !=
'\0' && !
dIsspace(line[i]))
1453 domain[j++] = line[i++];
1462 while (line[i] !=
'\0' && !
dIsspace(line[i]))
1463 rule[j++] = line[i++];
1473 MSG(
"Cookies: rule '%s' for domain '%s' is not recognised.\n",
1485 uint_t len = strlen(cc.domain);
1490 i > 0 && (len > strlen(
ccontrol[i-1].domain));
1504 return (enabled ? 0 : 1);
1517 if (
ccontrol[i].domain[0] ==
'.') {
1518 diff = strlen(domain) - strlen(
ccontrol[i].domain);
1546 char *cmd, *cookie, *host, *
path;
1548 size_t BufSize = strlen(Buf);
1554 }
else if (client->status == 0) {
1560 }
else if (strcmp(cmd,
"DpiBye") == 0) {
1562 MSG(
"(pid %d): Got DpiBye.\n", (
int)getpid());
1565 }
else if (strcmp(cmd,
"set_cookie") == 0) {
1578 st == 0 ?
"ok" :
"not set");
1587 }
else if (strcmp(cmd,
"get_cookie") == 0) {
1604 _MSG(
"a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
1639 struct sockaddr_in sin;
1640 socklen_t address_size;
1651 signal (SIGINT, SIG_IGN);
1653 signal (SIGHUP, SIG_IGN);
1655 signal (SIGTERM, SIG_IGN);
1658 MSG(
"(v.1) accepting connections...\n");
1664 address_size =
sizeof(
struct sockaddr_in);
1667 sock_fd = accept(STDIN_FILENO, (
struct sockaddr *)&sin, &address_size);
1668 if (sock_fd == -1) {
1675 client =
dNew(ClientInfo,1);
1683 _MSG(
" buf = {%s}\n", buf);
1688 _MSG(
" code = %d %s\n", code, code == 1 ?
"EXIT" :
"BREAK");
1691 }
else if (code == 2) {
1696 _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)
static int dIsdigit(unsigned char c)
#define dNew0(type, count)
static int dIsspace(unsigned char c)
#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