changeset 1632:d76eddb72e6d

cookies use host-only as in draft spec Behaviour should be quite similar to before, but it'll be easier for someone else to understand if it matches the spec's way of doing things more closely.
author corvid <corvid@lavabit.com>
date Mon, 22 Mar 2010 17:30:34 +0000
parents ec05ef8ca40c
children 970b8815d82b
files dpi/cookies.c test/cookies.c
diffstat 2 files changed, 94 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/dpi/cookies.c	Mon Mar 22 06:48:21 2010 +0000
+++ b/dpi/cookies.c	Mon Mar 22 17:30:34 2010 +0000
@@ -16,10 +16,6 @@
 /* This is written to follow the HTTP State Working Group's
  * draft-ietf-httpstate-cookie-01.txt.
  *
- * We depart from the draft spec's domain format in that, rather than
- * using a host-only flag, we continue to use the .domain notation
- * internally to indicate cookies that may also be returned to subdomains.
- *
  * Info on cookies in the wild:
  *  http://www.ietf.org/mail-archive/web/http-state/current/msg00078.html
  * And dates specifically:
@@ -107,6 +103,7 @@
    char *domain;
    char *path;
    time_t expires_at;
+   bool_t host_only;
    bool_t secure;
    bool_t session_only;
    long last_used;
@@ -138,7 +135,7 @@
 static const char *const cookies_txt_header_str =
 "# HTTP Cookie File\n"
 "# This is a generated file!  Do not edit.\n"
-"# [domain  TRUE  path  secure  expiry_time  name  value]\n\n";
+"# [domain  subdomains  path  secure  expiry_time  name  value]\n\n";
 
 /* The epoch is Jan 1, 1970. When there is difficulty in representing future
  * dates, use the (by far) most likely last representable time in Jan 19, 2038.
@@ -284,7 +281,9 @@
 
          cookie->session_only = FALSE;
          cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
-         dStrsep(&line_marker, "\t"); /* we use domain always as sufix */
+         piece = dStrsep(&line_marker, "\t");
+         if (piece != NULL && piece[0] == 'F')
+            cookie->host_only = TRUE;
          cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
          piece = dStrsep(&line_marker, "\t");
          if (piece != NULL && piece[0] == 'T')
@@ -414,8 +413,9 @@
    while ((node = dList_nth_data(domains, 0))) {
       for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) {
          if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
-            fprintf(file_stream, "%s\tTRUE\t%s\t%s\t%ld\t%s\t%s\n",
+            fprintf(file_stream, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
                     cookie->domain,
+                    cookie->host_only ? "FALSE" : "TRUE",
                     cookie->path,
                     cookie->secure ? "TRUE" : "FALSE",
                     (long)difftime(cookie->expires_at, cookies_epoch_time),
@@ -622,7 +622,7 @@
    domain_cookies = (node) ? node->cookies : NULL;
 
    if (domain_cookies) {
-      /* Remove any cookies with the same name and path */
+      /* Remove any cookies with the same name, path, and host-only values. */
       while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))) {
          dList_remove(domain_cookies, c);
          dList_remove_fast(all_cookies, c);
@@ -893,16 +893,15 @@
 }
 
 /*
- * Compare cookies by name and path (return 0 if equal)
+ * Compare cookies by host_only, name, and path. Return 0 if equal.
  */
 static int Cookies_cmp(const void *a, const void *b)
 {
    const CookieData_t *ca = a, *cb = b;
-   int ret;
 
-   if (!(ret = strcmp(ca->name, cb->name)))
-      ret = strcmp(ca->path, cb->path);
-   return ret;
+   return (ca->host_only != cb->host_only) ||
+          (strcmp(ca->name, cb->name) != 0) ||
+          (strcmp(ca->path, cb->path) != 0);
 }
 
 /*
@@ -1071,15 +1070,10 @@
 
    if (!cookie->domain) {
       cookie->domain = dStrdup(host);
+      cookie->host_only = TRUE;
       return TRUE;
    }
 
-   if (cookie->domain[0] != '.' && !Cookies_domain_is_ip(cookie->domain)) {
-      char *d = dStrconcat(".", cookie->domain, NULL);
-      dFree(cookie->domain);
-      cookie->domain = d;
-   }
-
    if (!Cookies_domain_matches(host, cookie->domain))
       return FALSE;
 
@@ -1145,8 +1139,11 @@
  * Compare the cookie with the supplied data to see whether it matches
  */
 static bool_t Cookies_match(CookieData_t *cookie, const char *url_path,
-                            bool_t is_ssl)
+                            bool_t host_only_val, bool_t is_ssl)
 {
+   if (cookie->host_only != host_only_val)
+      return FALSE;
+
    /* Insecure cookies matches both secure and insecure urls, secure
       cookies matches only secure urls */
    if (cookie->secure && !is_ssl)
@@ -1161,6 +1158,7 @@
 
 static void Cookies_add_matching_cookies(const char *domain,
                                          const char *url_path,
+                                         bool_t host_only_val,
                                          Dlist *matching_cookies,
                                          bool_t is_ssl)
 {
@@ -1182,7 +1180,7 @@
             --i; continue;
          }
          /* Check if the cookie matches the requesting URL */
-         if (Cookies_match(cookie, url_path, is_ssl)) {
+         if (Cookies_match(cookie, url_path, host_only_val, is_ssl)) {
             int j;
             CookieData_t *curr;
             uint_t path_length = strlen(cookie->path);
@@ -1212,7 +1210,8 @@
    char *domain_str, *str;
    CookieData_t *cookie;
    Dlist *matching_cookies;
-   bool_t is_ssl;
+   bool_t is_ssl, is_ip_addr, host_only_val;
+
    Dstr *cookie_dstring;
    int i;
 
@@ -1224,17 +1223,46 @@
    /* Check if the protocol is secure or not */
    is_ssl = (!dStrcasecmp(url_scheme, "https"));
 
-   for (domain_str = (char *) url_host;
-        domain_str != NULL && *domain_str;
-        domain_str = strchr(domain_str+1, '.')) {
-      Cookies_add_matching_cookies(domain_str, url_path, matching_cookies,
-                                   is_ssl);
+   is_ip_addr = Cookies_domain_is_ip(url_host);
+
+   /* If a cookie is set that lacks a Domain attribute, its domain is set to
+    * the server's host and the host_only flag is set for that cookie. Such a
+    * cookie can only be sent back to that host. Cookies with Domain attrs do
+    * not have the host_only flag set, and may be sent to subdomains. Domain
+    * attrs can have leading dots, which should be ignored for matching
+    * purposes.
+    */
+   host_only_val = FALSE;
+   if (!is_ip_addr) {
+      /* e.g., sub.example.com set a cookie with domain ".sub.example.com". */
+      domain_str = dStrconcat(".", url_host, NULL);
+      Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+                                   matching_cookies, is_ssl);
+      dFree(domain_str);
    }
-   if (!Cookies_domain_is_ip(url_host)) {
-      domain_str = dStrconcat(".", url_host, NULL);
-      Cookies_add_matching_cookies(domain_str, url_path, matching_cookies,
-                                   is_ssl);
-      dFree(domain_str);
+   host_only_val = TRUE;
+   /* e.g., sub.example.com set a cookie with no domain attribute. */
+   Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+                                matching_cookies, is_ssl);
+   host_only_val = FALSE;
+   /* e.g., sub.example.com set a cookie with domain "sub.example.com". */
+   Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+                                matching_cookies, is_ssl);
+
+   if (!is_ip_addr) {
+      for (domain_str = strchr(url_host+1, '.');
+           domain_str != NULL && *domain_str;
+           domain_str = strchr(domain_str+1, '.')) {
+         /* e.g., sub.example.com set a cookie with domain ".example.com". */
+         Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+                                      matching_cookies, is_ssl);
+         if (domain_str[1]) {
+            domain_str++;
+            /* e.g., sub.example.com set a cookie with domain "example.com".*/
+            Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+                                         matching_cookies, is_ssl);
+         }
+      }
    }
 
    /* Found the cookies, now make the string */
--- a/test/cookies.c	Mon Mar 22 06:48:21 2010 +0000
+++ b/test/cookies.c	Mon Mar 22 17:30:34 2010 +0000
@@ -832,17 +832,41 @@
    expect(__LINE__, "Cookie: name=val\r\n", "http", "dotdomain.org", "/");
    expect(__LINE__, "Cookie: name=val\r\n", "http", "www.dotdomain.org", "/");
 
+   /* HOST_ONLY */
+   a_Cookies_set("name=val; domain=.hostonly.org", "hostonly.org", "/", NULL);
+   a_Cookies_set("name2=val2", "hostonly.org", "/", NULL);
+   a_Cookies_set("name3=val3; domain=hostonly.org", "hostonly.org", "/", NULL);
+   expect(__LINE__, "Cookie: name=val; name2=val2; name3=val3\r\n", "http",
+          "hostonly.org", "/");
+   a_Cookies_set("name=new; domain=.hostonly.org", "hostonly.org", "/", NULL);
+   expect(__LINE__, "Cookie: name=new; name2=val2; name3=val3\r\n", "http",
+          "hostonly.org", "/");
+   a_Cookies_set("name2=new2", "hostonly.org", "/", NULL);
+   expect(__LINE__, "Cookie: name=new; name2=new2; name3=val3\r\n", "http",
+          "hostonly.org", "/");
+   a_Cookies_set("name3=new3; domain=hostonly.org", "hostonly.org", "/", NULL);
+   expect(__LINE__, "Cookie: name=new; name2=new2; name3=new3\r\n", "http",
+          "hostonly.org", "/");
+
    /* SUBDOMAIN */
    a_Cookies_set("name=val; domain=www.subdomain.com", "subdomain.com", "/",
                  NULL);
+   a_Cookies_set("name=val; domain=.www.subdomain.com", "subdomain.com", "/",
+                 NULL);
    expect(__LINE__, "", "http", "subdomain.com", "/");
    expect(__LINE__, "", "http", "www.subdomain.com", "/");
 
    /* SUPERDOMAIN(?) */
-   a_Cookies_set("name=val; domain=supdomain.com", "www.supdomain.com", "/",
+   a_Cookies_set("name=val; domain=.supdomain.com", "www.supdomain.com", "/",
                  NULL);
-   expect(__LINE__, "Cookie: name=val\r\n", "http", "www.supdomain.com", "/");
-   expect(__LINE__, "Cookie: name=val\r\n", "http", "supdomain.com", "/");
+   a_Cookies_set("name2=val2; domain=supdomain.com", "www.supdomain.com", "/",
+                 NULL);
+   expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+          "sub2.sub.supdomain.com", "/");
+   expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+          "www.supdomain.com", "/");
+   expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+          "supdomain.com", "/");
 
    /* UNRELATED */
    a_Cookies_set("name=val; domain=another.com", "unrelated.com", "/", NULL);
@@ -857,6 +881,13 @@
    a_Cookies_set("name=val; domain=another.com", "verybadguys.com", "/", NULL);
    expect(__LINE__, "", "http", "another.com", "/");
 
+   a_Cookies_set("name=val; domain=similar.com", "imilar.com", "/", NULL);
+   a_Cookies_set("name2=val2; domain=similar.com", "ssimilar.com", "/", NULL);
+   a_Cookies_set("name3=val3; domain=.similar.com", "imilar.com", "/", NULL);
+   a_Cookies_set("name4=val4; domain=.similar.com", "timilar.com", "/", NULL);
+   a_Cookies_set("name4=val4; domain=.similar.com", "tiimilar.com", "/", NULL);
+   expect(__LINE__, "", "http", "similar.com", "/");
+
    /* SECURE */
    a_Cookies_set("name=val; secure", "secure.com", "/", NULL);
    expect(__LINE__, "", "http", "secure.com", "/");