Blob Blame Raw
From 4ce37d79704623778fbe266397c85ed2b735e4dd Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Fri, 15 Mar 2013 14:18:16 +0100
Subject: [PATCH 1/7] HTTP proxy: insert slash in URL if missing

curl has been accepting URLs using slightly wrong syntax for a long
time, such as when completely missing as slash "http://example.org" or
missing a slash when a query part is given
"http://example.org?q=foobar".

curl would translate these into a legitimate HTTP request to servers,
although as was shown in bug #1206 it was not adjusted properly in the
cases where a HTTP proxy was used.

Test 1213 and 1214 were added to the test suite to verify this fix.

The test HTTP server was adjusted to allow us to specify test number in
the host name only without using any slashes in a given URL.

Bug: http://curl.haxx.se/bug/view.cgi?id=1206
Reported by: ScottJi

Upstream-commit: e4b733e3f1a771bd1017cdcfb355fcb9caffe646
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/url.c              | 38 ++++++++++++++++++++++++++++++++++
 tests/FILEFORMAT       |  4 ++++
 tests/data/Makefile.am |  2 +-
 tests/data/Makefile.in |  2 +-
 tests/data/test1213    | 53 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/data/test1214    | 53 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/server/sws.c     | 56 ++++++++++++++++++++++++++++++++++++++++++--------
 7 files changed, 198 insertions(+), 10 deletions(-)
 create mode 100644 tests/data/test1213
 create mode 100644 tests/data/test1214

diff --git a/lib/url.c b/lib/url.c
index 181f0a4..77549ba 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -3584,6 +3584,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
   char protobuf[16];
   const char *protop;
   CURLcode result;
+  bool fix_slash = FALSE;
 
   *prot_missing = FALSE;
 
@@ -3730,12 +3731,14 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
     memcpy(path+1, query, hostlen);
 
     path[0]='/'; /* prepend the missing slash */
+    fix_slash = TRUE;
 
     *query=0; /* now cut off the hostname at the ? */
   }
   else if(!path[0]) {
     /* if there's no path set, use a single slash */
     strcpy(path, "/");
+    fix_slash = TRUE;
   }
 
   /* If the URL is malformatted (missing a '/' after hostname before path) we
@@ -3748,6 +3751,41 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
        is bigger than the path. Use +1 to move the zero byte too. */
     memmove(&path[1], path, strlen(path)+1);
     path[0] = '/';
+    fix_slash = TRUE;
+  }
+
+
+  /*
+   * "fix_slash" means that the URL was malformatted so we need to generate an
+   * updated version with the new slash inserted at the right place!  We need
+   * the corrected URL when communicating over HTTP proxy and we don't know at
+   * this point if we're using a proxy or not.
+   */
+  if(fix_slash) {
+    char *reurl;
+
+    size_t plen = strlen(path); /* new path, should be 1 byte longer than
+                                   the original */
+    size_t urllen = strlen(data->change.url); /* original URL length */
+
+    reurl = malloc(urllen + 2); /* 2 for zerobyte + slash */
+    if(!reurl)
+      return CURLE_OUT_OF_MEMORY;
+
+    /* copy the prefix */
+    memcpy(reurl, data->change.url, urllen - (plen-1));
+
+    /* append the trailing piece + zerobyte */
+    memcpy(&reurl[urllen - (plen-1)], path, plen + 1);
+
+    /* possible free the old one */
+    if(data->change.url_alloc) {
+      Curl_safefree(data->change.url);
+      data->change.url_alloc = FALSE;
+    }
+
+    data->change.url = reurl;
+    data->change.url_alloc = TRUE; /* free this later */
   }
 
   /*************************************************************
diff --git a/tests/FILEFORMAT b/tests/FILEFORMAT
index d79cbf7..96cd5c8 100644
--- a/tests/FILEFORMAT
+++ b/tests/FILEFORMAT
@@ -250,6 +250,10 @@ If a CONNECT is used to the server (to emulate HTTPS etc over proxy), the port
 number given in the CONNECT request will be used to identify which test that
 is being run, if the proxy host name is said to start with 'test'.
 
+If there's no non-zero test number found in the above to places, the HTTP test
+server will use the number following the last dot in the given url so that
+"foo.bar.123" gets treated as test case 123.
+
 Set type="perl" to write the test case as a perl script. It implies that
 there's no memory debugging and valgrind gets shut off for this test.
 
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 4e37ed9..5e12f62 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -77,7 +77,7 @@ test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117	\
 test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125	\
 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
-test1208 test1209 test1210 test1211 test1216 test1218 \
+test1208 test1209 test1210 test1211 test1213 test1214 test1216 test1218 \
 test1220 test1221 test1222 test1223 test1233 \
 test1300 test1301 test1302 test1303 test1304 test1305	\
 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
diff --git a/tests/data/Makefile.in b/tests/data/Makefile.in
index d7f9ac2..597c1fb 100644
--- a/tests/data/Makefile.in
+++ b/tests/data/Makefile.in
@@ -341,7 +341,7 @@ test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117	\
 test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125	\
 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
-test1208 test1209 test1210 test1211 test1216 test1218 \
+test1208 test1209 test1210 test1211 test1213 test1214 test1216 test1218 \
 test1220 test1221 test1222 test1223 \
 test1300 test1301 test1302 test1303 test1304 test1305	\
 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
diff --git a/tests/data/test1213 b/tests/data/test1213
new file mode 100644
index 0000000..d0d12b4
--- /dev/null
+++ b/tests/data/test1213
@@ -0,0 +1,53 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP proxy
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Funny-head: yesyes
+Content-Length: 22
+
+the content goes here
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP with proxy and host-only URL
+ </name>
+# the thing here is that this sloppy form is accepted and we convert it
+# for normal server use, and we need to make sure it gets converted to
+# RFC style even for proxies
+ <command>
+-x %HOSTIP:%HTTPPORT we.want.that.site.com.1213
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET HTTP://we.want.that.site.com.1213/ HTTP/1.1
+Host: we.want.that.site.com.1213
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test1214 b/tests/data/test1214
new file mode 100644
index 0000000..8c36ade
--- /dev/null
+++ b/tests/data/test1214
@@ -0,0 +1,53 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP proxy
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Funny-head: yesyes
+Content-Length: 22
+
+the content goes here
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP with proxy and URL with ? and no slash separator
+ </name>
+# the thing here is that this sloppy form is accepted and we convert it
+# for normal server use, and we need to make sure it gets converted to
+# RFC style even for proxies
+ <command>
+-x %HOSTIP:%HTTPPORT http://we.want.that.site.com.1214?moo=foo
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET http://we.want.that.site.com.1214/?moo=foo HTTP/1.1
+Host: we.want.that.site.com.1214
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/server/sws.c b/tests/server/sws.c
index a7de09f..aef55ea 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -507,15 +507,24 @@ static int ProcessRequest(struct httprequest *req)
       else
         req->partno = 0;
 
-      sprintf(logbuf, "Requested test number %ld part %ld",
-              req->testno, req->partno);
-      logmsg("%s", logbuf);
+      if(req->testno) {
+
+        sprintf(logbuf, "Requested test number %ld part %ld",
+                req->testno, req->partno);
+        logmsg("%s", logbuf);
 
-      /* find and parse <servercmd> for this test */
-      parse_servercmd(req);
+        /* find and parse <servercmd> for this test */
+        parse_servercmd(req);
+      }
+      else
+        req->testno = DOCNUMBER_NOTHING;
 
     }
-    else {
+
+    if(req->testno == DOCNUMBER_NOTHING) {
+      /* didn't find any in the first scan, try alternative test case
+         number placements */
+
       if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
                 doc, &prot_major, &prot_minor) == 3) {
         char *portp = NULL;
@@ -563,8 +572,39 @@ static int ProcessRequest(struct httprequest *req)
         parse_servercmd(req);
       }
       else {
-        logmsg("Did not find test number in PATH");
-        req->testno = DOCNUMBER_404;
+        /* there was no trailing slash and it wasn't CONNECT, then we get the
+           the number off the last dot instead, IE we consider the TLD to be
+           the test number. Test 123 can then be written as
+           "example.com.123". */
+
+        /* find the last dot */
+        ptr = strrchr(doc, '.');
+
+        /* get the number after it */
+        if(ptr) {
+          ptr++; /* skip the dot */
+
+          req->testno = strtol(ptr, &ptr, 10);
+
+          if(req->testno > 10000) {
+            req->partno = req->testno % 10000;
+            req->testno /= 10000;
+          }
+          else
+            req->partno = 0;
+
+          sprintf(logbuf, "Requested test number %ld part %ld (from host name)",
+                  req->testno, req->partno);
+          logmsg("%s", logbuf);
+
+        }
+
+        if(!req->testno) {
+          logmsg("Did not find test number in PATH");
+          req->testno = DOCNUMBER_404;
+        }
+        else
+          parse_servercmd(req);
       }
     }
   }
-- 
2.1.0


From 052143a4aaac9ce91ffdb6ae88eb5888c54d66aa Mon Sep 17 00:00:00 2001
From: YAMADA Yasuharu <yasuharu.yamada@access-company.com>
Date: Sat, 18 May 2013 22:51:31 +0200
Subject: [PATCH 2/7] cookies: only consider full path matches

I found a bug which cURL sends cookies to the path not to aim at.
For example:
- cURL sends a request to http://example.fake/hoge/
- server returns cookie which with path=/hoge;
  the point is there is NOT the '/' end of path string.
- cURL sends a request to http://example.fake/hogege/ with the cookie.

The reason for this old "feature" is because that behavior is what is
described in the original netscape cookie spec:
http://curl.haxx.se/rfc/cookie_spec.html

The current cookie spec (RFC6265) clarifies the situation:
http://tools.ietf.org/html/rfc6265#section-5.2.4
Upstream-commit: 04f52e9b4db01bcbf672c9c69303a4e4ad0d0fb9
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/cookie.c           | 33 ++++++++++++++++++++++++++----
 tests/data/Makefile.am |  2 +-
 tests/data/Makefile.in |  2 +-
 tests/data/test1228    | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/data/test46      |  8 ++++----
 tests/data/test8       |  2 +-
 6 files changed, 90 insertions(+), 11 deletions(-)
 create mode 100644 tests/data/test1228

diff --git a/lib/cookie.c b/lib/cookie.c
index ac4d89c..a4480c0 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -143,6 +143,34 @@ static bool tailmatch(const char *cooke_domain, const char *hostname)
   return FALSE;
 }
 
+static bool pathmatch(const char* cookie_path, const char* url_path)
+{
+  size_t cookie_path_len = strlen(cookie_path);
+  size_t url_path_len = strlen(url_path);
+
+  if(url_path_len < cookie_path_len)
+    return FALSE;
+
+  /* not using checkprefix() because matching should be case-sensitive */
+  if(strncmp(cookie_path, url_path, cookie_path_len))
+    return FALSE;
+
+  /* it is true if cookie_path and url_path are the same */
+  if(cookie_path_len == url_path_len)
+    return TRUE;
+
+  /* here, cookie_path_len < url_path_len */
+
+  /* it is false if cookie path is /example and url path is /examples */
+  if(cookie_path[cookie_path_len - 1] != '/') {
+    if(url_path[cookie_path_len] != '/') {
+      return FALSE;
+    }
+  }
+  /* matching! */
+  return TRUE;
+}
+
 /*
  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
  */
@@ -841,10 +869,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 
         /* now check the left part of the path with the cookies path
            requirement */
-        if(!co->path ||
-           /* not using checkprefix() because matching should be
-              case-sensitive */
-           !strncmp(co->path, path, strlen(co->path)) ) {
+        if(!co->path || pathmatch(co->path, path) ) {
 
           /* and now, we know this is a match and we should create an
              entry for the return-linked-list */
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 4e37ed9..64662c6 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -78,7 +78,7 @@ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125	\
 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
 test1208 test1209 test1210 test1211 test1213 test1214 test1216 test1218 \
-test1220 test1221 test1222 test1223 test1233 \
+test1220 test1221 test1222 test1223 test1233 test1236 \
 test1300 test1301 test1302 test1303 test1304 test1305	\
 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
 test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 \
diff --git a/tests/data/Makefile.in b/tests/data/Makefile.in
index 1e6d679..5296c09 100644
--- a/tests/data/Makefile.in
+++ b/tests/data/Makefile.in
@@ -342,7 +342,7 @@ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125	\
 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
 test1208 test1209 test1210 test1211 test1213 test1214 test1216 test1218 \
-test1220 test1221 test1222 test1223 \
+test1220 test1221 test1222 test1223 test1233 test1236 \
 test1300 test1301 test1302 test1303 test1304 test1305	\
 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
 test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 \
diff --git a/tests/data/test1228 b/tests/data/test1228
new file mode 100644
index 0000000..0a76b87
--- /dev/null
+++ b/tests/data/test1228
@@ -0,0 +1,54 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+cookies 
+cookie path
+</keywords>
+</info>
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Tue, 25 Sep 2001 19:37:44 GMT
+Set-Cookie: path1=root; domain=.example.fake; path=/;
+Set-Cookie: path2=depth1; domain=.example.fake; path=/hoge;
+Content-Length: 34
+
+This server says cookie path test
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP cookie path match
+ </name>
+ <command>
+http://example.fake/hoge/1228 http://example.fake/hogege/ -b nonexisting -x %HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET http://example.fake/hoge/1228 HTTP/1.1
+Host: example.fake
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://example.fake/hogege/ HTTP/1.1
+Host: example.fake
+Accept: */*
+Proxy-Connection: Keep-Alive
+Cookie: path1=root
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test46 b/tests/data/test46
index f73acde..b6f8f83 100644
--- a/tests/data/test46
+++ b/tests/data/test46
@@ -52,8 +52,8 @@ TZ=GMT
 www.fake.come	FALSE	/	FALSE	1022144953	cookiecliente	si
 www.loser.com	FALSE	/	FALSE	1139150993	UID	99
 %HOSTIP	FALSE	/	FALSE	1439150993	mooo	indeed
-#HttpOnly_%HOSTIP	FALSE	/w	FALSE	1439150993	mooo2	indeed2
-%HOSTIP	FALSE	/wa	FALSE	0	empty	
+#HttpOnly_%HOSTIP	FALSE	/want	FALSE	1439150993	mooo2	indeed2
+%HOSTIP	FALSE	/want	FALSE	0	empty	
 </file>
 </client>
 
@@ -77,8 +77,8 @@ Cookie: empty=; mooo2=indeed2; mooo=indeed
 www.fake.come	FALSE	/	FALSE	1022144953	cookiecliente	si
 www.loser.com	FALSE	/	FALSE	1139150993	UID	99
 %HOSTIP	FALSE	/	FALSE	1439150993	mooo	indeed
-#HttpOnly_%HOSTIP	FALSE	/w	FALSE	1439150993	mooo2	indeed2
-%HOSTIP	FALSE	/wa	FALSE	0	empty	
+#HttpOnly_%HOSTIP	FALSE	/want	FALSE	1439150993	mooo2	indeed2
+%HOSTIP	FALSE	/want	FALSE	0	empty	
 %HOSTIP	FALSE	/	FALSE	2054030187	ckyPersistent	permanent
 %HOSTIP	FALSE	/	FALSE	0	ckySession	temporary
 %HOSTIP	FALSE	/	FALSE	0	ASPSESSIONIDQGGQQSJJ	GKNBDIFAAOFDPDAIEAKDIBKE
diff --git a/tests/data/test8 b/tests/data/test8
index c36408a..4d54541 100644
--- a/tests/data/test8
+++ b/tests/data/test8
@@ -59,7 +59,7 @@ perl -e 'if ("%HOSTIP" !~ /\.0\.0\.1$/) {print "Test only works for HOSTIPs endi
 GET /we/want/8 HTTP/1.1
 Host: %HOSTIP:%HTTPPORT
 Accept: */*
-Cookie: cookie=perhaps; cookie=yes; partmatch=present; foobar=name; blexp=yesyes
+Cookie: cookie=perhaps; cookie=yes; foobar=name; blexp=yesyes
 
 </protocol>
 </verify>
-- 
2.1.0


From 847085920706380e75f8cb23f2a161cdcdfcb384 Mon Sep 17 00:00:00 2001
From: YAMADA Yasuharu <yasuharu.yamada@access-company.com>
Date: Wed, 12 Jun 2013 11:19:56 +0200
Subject: [PATCH 3/7] cookies: follow-up fix for path checking

The initial fix to only compare full path names were done in commit
04f52e9b4db0 but found out to be incomplete. This takes should make the
change more complete and there's now two additional tests to verify
(test 31 and 62).
Upstream-commit: f24dc09d209a2f91ca38d854f0c15ad93f3d7e2d
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/cookie.c      | 143 ++++++++++++++++++++++++++++++++++++++++++++++--------
 lib/cookie.h      |   3 +-
 tests/data/test31 |   3 ++
 3 files changed, 128 insertions(+), 21 deletions(-)

diff --git a/lib/cookie.c b/lib/cookie.c
index a4480c0..1d226cf 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -106,6 +106,8 @@ static void freecookie(struct Cookie *co)
     free(co->domain);
   if(co->path)
     free(co->path);
+  if(co->spath)
+    free(co->spath);
   if(co->name)
     free(co->name);
   if(co->value)
@@ -143,32 +145,114 @@ static bool tailmatch(const char *cooke_domain, const char *hostname)
   return FALSE;
 }
 
-static bool pathmatch(const char* cookie_path, const char* url_path)
+/*
+ * matching cookie path and url path
+ * RFC6265 5.1.4 Paths and Path-Match
+ */
+static bool pathmatch(const char* cookie_path, const char* request_uri)
 {
-  size_t cookie_path_len = strlen(cookie_path);
-  size_t url_path_len = strlen(url_path);
+  size_t cookie_path_len;
+  size_t uri_path_len;
+  char* uri_path = NULL;
+  char* pos;
+  bool ret = FALSE;
+
+  /* cookie_path must not have last '/' separator. ex: /sample */
+  cookie_path_len = strlen(cookie_path);
+  if(1 == cookie_path_len) {
+    /* cookie_path must be '/' */
+    return TRUE;
+  }
 
-  if(url_path_len < cookie_path_len)
+  uri_path = strdup(request_uri);
+  if(!uri_path)
     return FALSE;
+  pos = strchr(uri_path, '?');
+  if(pos)
+    *pos = 0x0;
+
+  /* #-fragments are already cut off! */
+  if(0 == strlen(uri_path) || uri_path[0] != '/') {
+    free(uri_path);
+    uri_path = strdup("/");
+    if(!uri_path)
+      return FALSE;
+  }
+
+  /* here, RFC6265 5.1.4 says
+     4. Output the characters of the uri-path from the first character up
+        to, but not including, the right-most %x2F ("/").
+     but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
+     without redirect.
+     Ignore this algorithm because /hoge is uri path for this case
+     (uri path is not /).
+   */
+
+  uri_path_len = strlen(uri_path);
+
+  if(uri_path_len < cookie_path_len) {
+    ret = FALSE;
+    goto pathmatched;
+  }
 
   /* not using checkprefix() because matching should be case-sensitive */
-  if(strncmp(cookie_path, url_path, cookie_path_len))
-    return FALSE;
+  if(strncmp(cookie_path, uri_path, cookie_path_len)) {
+    ret = FALSE;
+    goto pathmatched;
+  }
 
-  /* it is true if cookie_path and url_path are the same */
-  if(cookie_path_len == url_path_len)
-    return TRUE;
+  /* The cookie-path and the uri-path are identical. */
+  if(cookie_path_len == uri_path_len) {
+    ret = TRUE;
+    goto pathmatched;
+  }
 
   /* here, cookie_path_len < url_path_len */
+  if(uri_path[cookie_path_len] == '/') {
+    ret = TRUE;
+    goto pathmatched;
+  }
 
-  /* it is false if cookie path is /example and url path is /examples */
-  if(cookie_path[cookie_path_len - 1] != '/') {
-    if(url_path[cookie_path_len] != '/') {
-      return FALSE;
-    }
+  ret = FALSE;
+
+pathmatched:
+  free(uri_path);
+  return ret;
+}
+
+/*
+ * cookie path sanitize
+ */
+static char *sanitize_cookie_path(const char *cookie_path)
+{
+  size_t len;
+  char *new_path = strdup(cookie_path);
+  if(!new_path)
+    return NULL;
+
+  /* some stupid site sends path attribute with '"'. */
+  if(new_path[0] == '\"') {
+    memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path));
+  }
+  if(new_path[strlen(new_path) - 1] == '\"') {
+    new_path[strlen(new_path) - 1] = 0x0;
+  }
+
+  /* RFC6265 5.2.4 The Path Attribute */
+  if(new_path[0] != '/') {
+    /* Let cookie-path be the default-path. */
+    free(new_path);
+    new_path = strdup("/");
+    return new_path;
+  }
+
+  /* convert /hoge/ to /hoge */
+  len = strlen(new_path);
+  if(1 < len && new_path[len - 1] == '/') {
+    new_path[len - 1] = 0x0;
   }
-  /* matching! */
-  return TRUE;
+
+  return new_path;
 }
 
 /*
@@ -316,6 +400,11 @@ Curl_cookie_add(struct SessionHandle *data,
             badcookie = TRUE; /* out of memory bad */
             break;
           }
+          co->spath = sanitize_cookie_path(co->path);
+          if(!co->spath) {
+            badcookie = TRUE; /* out of memory bad */
+            break;
+          }
         }
         else if(Curl_raw_equal("domain", name)) {
           /* note that this name may or may not have a preceding dot, but
@@ -489,6 +578,9 @@ Curl_cookie_add(struct SessionHandle *data,
         if(co->path) {
           memcpy(co->path, path, pathlen);
           co->path[pathlen]=0; /* zero terminate */
+          co->spath = sanitize_cookie_path(co->path);
+          if(!co->spath)
+            badcookie = TRUE; /* out of memory bad */
         }
         else
           badcookie = TRUE;
@@ -580,12 +672,21 @@ Curl_cookie_add(struct SessionHandle *data,
           co->path = strdup(ptr);
           if(!co->path)
             badcookie = TRUE;
+          else {
+            co->spath = sanitize_cookie_path(co->path);
+            if(!co->spath) {
+              badcookie = TRUE; /* out of memory bad */
+            }
+          }
           break;
         }
         /* this doesn't look like a path, make one up! */
         co->path = strdup("/");
         if(!co->path)
           badcookie = TRUE;
+        co->spath = strdup("/");
+        if(!co->spath)
+          badcookie = TRUE;
         fields++; /* add a field and fall down to secure */
         /* FALLTHROUGH */
       case 3:
@@ -656,14 +757,14 @@ Curl_cookie_add(struct SessionHandle *data,
       if(replace_old) {
         /* the domains were identical */
 
-        if(clist->path && co->path) {
-          if(Curl_raw_equal(clist->path, co->path)) {
+        if(clist->spath && co->spath) {
+          if(Curl_raw_equal(clist->spath, co->spath)) {
             replace_old = TRUE;
           }
           else
             replace_old = FALSE;
         }
-        else if(!clist->path && !co->path)
+        else if(!clist->spath && !co->spath)
           replace_old = TRUE;
         else
           replace_old = FALSE;
@@ -692,6 +793,8 @@ Curl_cookie_add(struct SessionHandle *data,
           free(clist->domain);
         if(clist->path)
           free(clist->path);
+        if(clist->spath)
+          free(clist->spath);
         if(clist->expirestr)
           free(clist->expirestr);
 
@@ -869,7 +972,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 
         /* now check the left part of the path with the cookies path
            requirement */
-        if(!co->path || pathmatch(co->path, path) ) {
+        if(!co->spath || pathmatch(co->spath, path) ) {
 
           /* and now, we know this is a match and we should create an
              entry for the return-linked-list */
diff --git a/lib/cookie.h b/lib/cookie.h
index d3b63f7..bd89082 100644
--- a/lib/cookie.h
+++ b/lib/cookie.h
@@ -29,7 +29,8 @@ struct Cookie {
   struct Cookie *next; /* next in the chain */
   char *name;        /* <this> = value */
   char *value;       /* name = <this> */
-  char *path;         /* path = <this> */
+  char *path;         /* path = <this> which is in Set-Cookie: */
+  char *spath;        /* sanitized cookie path */
   char *domain;      /* domain = <this> */
   curl_off_t expires;  /* expires = <this> */
   char *expirestr;   /* the plain text version */
diff --git a/tests/data/test31 b/tests/data/test31
index b1171d8..38af83b 100644
--- a/tests/data/test31
+++ b/tests/data/test31
@@ -18,6 +18,8 @@ Content-Type: text/html
 Funny-head: yesyes
 Set-Cookie: foobar=name; domain=anything.com; path=/ ; secure
 Set-Cookie:ismatch=this  ; domain=127.0.0.1; path=/silly/
+Set-Cookie: overwrite=this  ; domain=127.0.0.1; path=/overwrite/
+Set-Cookie: overwrite=this2  ; domain=127.0.0.1; path=/overwrite
 Set-Cookie: sec1value=secure1  ; domain=127.0.0.1; path=/secure1/ ; secure
 Set-Cookie: sec2value=secure2  ; domain=127.0.0.1; path=/secure2/ ; secure=
 Set-Cookie: sec3value=secure3  ; domain=127.0.0.1; path=/secure3/ ; secure=
@@ -94,6 +96,7 @@ Accept: */*
 # This file was generated by libcurl! Edit at your own risk.
 
 .127.0.0.1	TRUE	/silly/	FALSE	0	ismatch	this
+.127.0.0.1	TRUE	/overwrite	FALSE	0	overwrite	this2
 .127.0.0.1	TRUE	/secure1/	TRUE	0	sec1value	secure1
 .127.0.0.1	TRUE	/secure2/	TRUE	0	sec2value	secure2
 .127.0.0.1	TRUE	/secure3/	TRUE	0	sec3value	secure3
-- 
2.1.0


From ffe02d8f3950b5fcf0470890a112125327606f35 Mon Sep 17 00:00:00 2001
From: YAMADA Yasuharu <yasuharu.yamada@access-company.com>
Date: Tue, 17 Sep 2013 15:51:22 +0900
Subject: [PATCH 4/7] cookies: add expiration

Implement: Expired Cookies These following situation, curl removes
cookie(s) from struct CookieInfo if the cookie expired.
 - Curl_cookie_add()
 - Curl_cookie_getlist()
 - cookie_output()

Upstream-commit: 4cfbb201c4f823ba31ba4b895044088fba6ae535
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/cookie.c           | 37 ++++++++++++++++++++++++++
 tests/data/Makefile.am |  2 +-
 tests/data/Makefile.in |  2 +-
 tests/data/test1415    | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 111 insertions(+), 2 deletions(-)
 create mode 100644 tests/data/test1415

diff --git a/lib/cookie.c b/lib/cookie.c
index 1d226cf..0ca0829 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -289,6 +289,34 @@ static void strstore(char **str, const char *newstr)
   *str = strdup(newstr);
 }
 
+/*
+ * remove_expired() removes expired cookies.
+ */
+static void remove_expired(struct CookieInfo *cookies)
+{
+  struct Cookie *co, *nx, *pv;
+  curl_off_t now = (curl_off_t)time(NULL);
+
+  co = cookies->cookies;
+  pv = NULL;
+  while(co) {
+    nx = co->next;
+    if((co->expirestr || co->maxage) && co->expires < now) {
+      if(co == cookies->cookies) {
+        cookies->cookies = co->next;
+      }
+      else {
+        pv->next = co->next;
+      }
+      cookies->numcookies--;
+      freecookie(co);
+    }
+    else {
+      pv = co;
+    }
+    co = nx;
+  }
+}
 
 /****************************************************************************
  *
@@ -740,6 +768,9 @@ Curl_cookie_add(struct SessionHandle *data,
      superceeds an already existing cookie, which it may if the previous have
      the same domain and path as this */
 
+  /* at first, remove expired cookies */
+  remove_expired(c);
+
   clist = c->cookies;
   replace_old = FALSE;
   while(clist) {
@@ -954,6 +985,9 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   if(!c || !c->cookies)
     return NULL; /* no cookie struct or no cookies in the struct */
 
+  /* at first, remove expired cookies */
+  remove_expired(c);
+
   co = c->cookies;
 
   while(co) {
@@ -1196,6 +1230,9 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
        destination file */
     return 0;
 
+  /* at first, remove expired cookies */
+  remove_expired(c);
+
   if(strequal("-", dumphere)) {
     /* use stdout */
     out = stdout;
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 64662c6..f34268c 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -92,7 +92,7 @@ test1371 test1372 test1373 test1374 test1375 test1376 test1377 test1378 \
 test1379 test1380 test1381 test1382 test1383 test1384 test1385 test1386 \
 test1387 test1388 test1389 test1390 test1391 test1392 test1393 \
 test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 \
-test1408 test1409 test1410 test1411 test1412 test1413 \
+test1408 test1409 test1410 test1411 test1412 test1413 test1415 \
 test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
 test1508 \
 test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \
diff --git a/tests/data/Makefile.in b/tests/data/Makefile.in
index 791fa1e..cd2c322 100644
--- a/tests/data/Makefile.in
+++ b/tests/data/Makefile.in
@@ -356,7 +356,7 @@ test1371 test1372 test1373 test1374 test1375 test1376 test1377 test1378 \
 test1379 test1380 test1381 test1382 test1383 test1384 test1385 test1386 \
 test1387 test1388 test1389 test1390 test1391 test1392 test1393 \
 test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 \
-test1408 test1409 test1410 test1411 test1412 test1413 \
+test1408 test1409 test1410 test1411 test1412 test1413 test1415 \
 test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
 test1508 \
 test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \
diff --git a/tests/data/test1415 b/tests/data/test1415
new file mode 100644
index 0000000..cc6bd70
--- /dev/null
+++ b/tests/data/test1415
@@ -0,0 +1,72 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+cookies
+cookiejar
+delete expired cookie
+</keywords>
+</info>
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 4
+Content-Type: text/html
+Funny-head: yesyes
+Set-Cookie: test1value=test1; domain=example.com; path=/;
+Set-Cookie: test2value=test2; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test3value=test3; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
+Set-Cookie: test4value=test4; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test5value=test5; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
+Set-Cookie: test6value=test6; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
+Set-Cookie: test7value=test7; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test8value=test8; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
+
+boo
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+Delete expired cookies
+</name>
+<setenv>
+TZ=GMT
+</setenv>
+<command>
+http://example.com/we/want/1415 -b none -c log/jar1415.txt -x %HOSTIP:%HTTPPORT
+</command>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET http://example.com/we/want/1415 HTTP/1.1
+Host: example.com
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+
+<file name="log/jar1415.txt" mode="text">
+# Netscape HTTP Cookie File
+# http://curl.haxx.se/docs/http-cookies.html
+# This file was generated by libcurl! Edit at your own risk.
+
+.example.com	TRUE	/	FALSE	0	test1value	test1
+.example.com	TRUE	/	FALSE	2145916800	test2value	test2
+.example.com	TRUE	/	FALSE	2145916800	test4value	test4
+.example.com	TRUE	/	FALSE	2145916800	test7value	test7
+</file>
+</verify>
+</testcase>
-- 
2.1.0


From f866a6369316df97b777289a34db03444f7dedbe Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sat, 21 Sep 2013 13:43:39 -0500
Subject: [PATCH 5/7] test1415: adjusted to work for 32bit time_t

The libcurl date parser returns INT_MAX for all dates > 2037 so this
test is now made to use 2037 instead of 2038 to work the same for both
32bit and 64bit time_t systems.

Upstream-commit: 34df869f99477edda61d639151b1edf75998abd9
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 tests/data/test1415 | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/data/test1415 b/tests/data/test1415
index cc6bd70..51eed3e 100644
--- a/tests/data/test1415
+++ b/tests/data/test1415
@@ -18,12 +18,12 @@ Content-Length: 4
 Content-Type: text/html
 Funny-head: yesyes
 Set-Cookie: test1value=test1; domain=example.com; path=/;
-Set-Cookie: test2value=test2; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test2value=test2; expires=Friday, 01-Jan-2037 00:00:00 GMT; domain=example.com; path=/;
 Set-Cookie: test3value=test3; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
-Set-Cookie: test4value=test4; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test4value=test4; expires=Friday, 01-Jan-2037 00:00:00 GMT; domain=example.com; path=/;
 Set-Cookie: test5value=test5; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
 Set-Cookie: test6value=test6; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
-Set-Cookie: test7value=test7; expires=Friday, 01-Jan-2038 00:00:00 GMT; domain=example.com; path=/;
+Set-Cookie: test7value=test7; expires=Friday, 01-Jan-2037 00:00:00 GMT; domain=example.com; path=/;
 Set-Cookie: test8value=test8; expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=example.com; path=/;
 
 boo
@@ -64,9 +64,9 @@ Proxy-Connection: Keep-Alive
 # This file was generated by libcurl! Edit at your own risk.
 
 .example.com	TRUE	/	FALSE	0	test1value	test1
-.example.com	TRUE	/	FALSE	2145916800	test2value	test2
-.example.com	TRUE	/	FALSE	2145916800	test4value	test4
-.example.com	TRUE	/	FALSE	2145916800	test7value	test7
+.example.com	TRUE	/	FALSE	2114380800	test2value	test2
+.example.com	TRUE	/	FALSE	2114380800	test4value	test4
+.example.com	TRUE	/	FALSE	2114380800	test7value	test7
 </file>
 </verify>
 </testcase>
-- 
2.1.0


From 8471ac93e881f7f17fe598086b6b548289716279 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 16 Jan 2014 08:51:30 +0100
Subject: [PATCH 6/7] cookie: max-age fixes

1 - allow >31 bit max-age values

2 - don't overflow on extremely large max-age values when we add the
value to the current time

3 - make sure max-age takes precedence over expires as dictated by
RFC6265

Bug: http://curl.haxx.se/mail/lib-2014-01/0130.html
Reported-by: Chen Prog
Upstream-commit: ecaf2f02f1df70f0bbcbbbf48914bfc83c8f2a56
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/cookie.c | 38 ++++++++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/lib/cookie.c b/lib/cookie.c
index 0ca0829..d4fe9a3 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -523,9 +523,6 @@ Curl_cookie_add(struct SessionHandle *data,
             badcookie = TRUE;
             break;
           }
-          co->expires =
-            strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
-            + (long)now;
         }
         else if(Curl_raw_equal("expires", name)) {
           strstore(&co->expirestr, whatptr);
@@ -533,17 +530,6 @@ Curl_cookie_add(struct SessionHandle *data,
             badcookie = TRUE;
             break;
           }
-          /* Note that if the date couldn't get parsed for whatever reason,
-             the cookie will be treated as a session cookie */
-          co->expires = curl_getdate(what, &now);
-
-          /* Session cookies have expires set to 0 so if we get that back
-             from the date parser let's add a second to make it a
-             non-session cookie */
-          if(co->expires == 0)
-            co->expires = 1;
-          else if(co->expires < 0)
-            co->expires = 0;
         }
         else if(!co->name) {
           co->name = strdup(name);
@@ -578,6 +564,30 @@ Curl_cookie_add(struct SessionHandle *data,
         semiptr=strchr(ptr, '\0');
     } while(semiptr);
 
+    if(co->maxage) {
+      co->expires =
+        curlx_strtoofft((*co->maxage=='\"')?
+                        &co->maxage[1]:&co->maxage[0], NULL, 10);
+      if(CURL_OFF_T_MAX - now < co->expires)
+        /* avoid overflow */
+        co->expires = CURL_OFF_T_MAX;
+      else
+        co->expires += now;
+    }
+    else if(co->expirestr) {
+      /* Note that if the date couldn't get parsed for whatever reason,
+         the cookie will be treated as a session cookie */
+      co->expires = curl_getdate(co->expirestr, NULL);
+
+      /* Session cookies have expires set to 0 so if we get that back
+         from the date parser let's add a second to make it a
+         non-session cookie */
+      if(co->expires == 0)
+        co->expires = 1;
+      else if(co->expires < 0)
+        co->expires = 0;
+    }
+
     if(!badcookie && !co->domain) {
       if(domain) {
         /* no domain was given in the header line, set the default */
-- 
2.1.0


From ee52a81c23b918895b363b904ebb5fc9908b55cc Mon Sep 17 00:00:00 2001
From: Tim Ruehsen <tim.ruehsen@gmx.de>
Date: Tue, 19 Aug 2014 21:01:28 +0200
Subject: [PATCH 7/7] cookies: only use full host matches for hosts used as IP
 address

By not detecting and rejecting domain names for partial literal IP
addresses properly when parsing received HTTP cookies, libcurl can be
fooled to both send cookies to wrong sites and to allow arbitrary sites
to set cookies for others.

CVE-2014-3613

Bug: http://curl.haxx.se/docs/adv_20140910A.html
Upstream-commit: 8a75dbeb2305297640453029b7905ef51b87e8dd
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/cookie.c        | 50 ++++++++++++++++++++++++++++++++++++++----------
 tests/data/test1105 |  3 +--
 tests/data/test31   | 55 +++++++++++++++++++++++++++--------------------------
 tests/data/test8    |  3 ++-
 4 files changed, 71 insertions(+), 40 deletions(-)

diff --git a/lib/cookie.c b/lib/cookie.c
index d4fe9a3..956efd4 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -94,6 +94,7 @@ Example set of cookies:
 #include "strtoofft.h"
 #include "rawstr.h"
 #include "curl_memrchr.h"
+#include "inet_pton.h"
 
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -318,6 +319,28 @@ static void remove_expired(struct CookieInfo *cookies)
   }
 }
 
+/*
+ * Return true if the given string is an IP(v4|v6) address.
+ */
+static bool isip(const char *domain)
+{
+  struct in_addr addr;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr6;
+#endif
+
+  if(Curl_inet_pton(AF_INET, domain, &addr)
+#ifdef ENABLE_IPV6
+     || Curl_inet_pton(AF_INET6, domain, &addr6)
+#endif
+    ) {
+    /* domain name given as IP address */
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 /****************************************************************************
  *
  * Curl_cookie_add()
@@ -472,24 +495,27 @@ Curl_cookie_add(struct SessionHandle *data,
                   whatptr);
           }
           else {
+            bool is_ip;
+
             /* Now, we make sure that our host is within the given domain,
                or the given domain is not valid and thus cannot be set. */
 
             if('.' == whatptr[0])
               whatptr++; /* ignore preceding dot */
 
-            if(!domain || tailmatch(whatptr, domain)) {
-              const char *tailptr=whatptr;
-              if(tailptr[0] == '.')
-                tailptr++;
-              strstore(&co->domain, tailptr); /* don't prefix w/dots
-                                                 internally */
+            is_ip = isip(domain ? domain : whatptr);
+
+            if(!domain
+               || (is_ip && !strcmp(whatptr, domain))
+               || (!is_ip && tailmatch(whatptr, domain))) {
+              strstore(&co->domain, whatptr);
               if(!co->domain) {
                 badcookie = TRUE;
                 break;
               }
-              co->tailmatch=TRUE; /* we always do that if the domain name was
-                                     given */
+              if(!is_ip)
+                co->tailmatch=TRUE; /* we always do that if the domain name was
+                                       given */
             }
             else {
               /* we did not get a tailmatch and then the attempted set domain
@@ -991,6 +1017,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   time_t now = time(NULL);
   struct Cookie *mainco=NULL;
   size_t matches = 0;
+  bool is_ip;
 
   if(!c || !c->cookies)
     return NULL; /* no cookie struct or no cookies in the struct */
@@ -998,6 +1025,9 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   /* at first, remove expired cookies */
   remove_expired(c);
 
+  /* check if host is an IP(v4|v6) address */
+  is_ip = isip(host);
+
   co = c->cookies;
 
   while(co) {
@@ -1009,8 +1039,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 
       /* now check if the domain is correct */
       if(!co->domain ||
-         (co->tailmatch && tailmatch(co->domain, host)) ||
-         (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
+         (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+         ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
         /* the right part of the host matches the domain stuff in the
            cookie data */
 
diff --git a/tests/data/test1105 b/tests/data/test1105
index 922346f..ea7b198 100644
--- a/tests/data/test1105
+++ b/tests/data/test1105
@@ -59,8 +59,7 @@ userid=myname&password=mypassword
 # This file was generated by libcurl! Edit at your own risk.
 
 127.0.0.1	FALSE	/we/want/	FALSE	0	foobar	name
-.127.0.0.1	TRUE	"/silly/"	FALSE	0	mismatch	this
-.0.0.1	TRUE	/	FALSE	0	partmatch	present
+127.0.0.1	FALSE	"/silly/"	FALSE	0	mismatch	this
 </file>
 </verify>
 </testcase>
diff --git a/tests/data/test31 b/tests/data/test31
index 38af83b..dfcac04 100644
--- a/tests/data/test31
+++ b/tests/data/test31
@@ -51,7 +51,8 @@ Set-Cookie: novalue; domain=reallysilly
 Set-Cookie: test=yes; domain=foo.com; expires=Sat Feb 2 11:56:27 GMT 2030
 Set-Cookie: test2=yes; domain=se; expires=Sat Feb 2 11:56:27 GMT 2030
 Set-Cookie: magic=yessir; path=/silly/; HttpOnly
-Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad;
+Set-Cookie: blexp=yesyes; domain=127.0.0.1; domain=127.0.0.1; expiry=totally bad;
+Set-Cookie: partialip=nono; domain=.0.0.1;
 
 boo
 </data>
@@ -95,34 +96,34 @@ Accept: */*
 # http://curl.haxx.se/docs/http-cookies.html
 # This file was generated by libcurl! Edit at your own risk.
 
-.127.0.0.1	TRUE	/silly/	FALSE	0	ismatch	this
-.127.0.0.1	TRUE	/overwrite	FALSE	0	overwrite	this2
-.127.0.0.1	TRUE	/secure1/	TRUE	0	sec1value	secure1
-.127.0.0.1	TRUE	/secure2/	TRUE	0	sec2value	secure2
-.127.0.0.1	TRUE	/secure3/	TRUE	0	sec3value	secure3
-.127.0.0.1	TRUE	/secure4/	TRUE	0	sec4value	secure4
-.127.0.0.1	TRUE	/secure5/	TRUE	0	sec5value	secure5
-.127.0.0.1	TRUE	/secure6/	TRUE	0	sec6value	secure6
-.127.0.0.1	TRUE	/secure7/	TRUE	0	sec7value	secure7
-.127.0.0.1	TRUE	/secure8/	TRUE	0	sec8value	secure8
-.127.0.0.1	TRUE	/secure9/	TRUE	0	secure	very1
-#HttpOnly_.127.0.0.1	TRUE	/p1/	FALSE	0	httpo1	value1
-#HttpOnly_.127.0.0.1	TRUE	/p2/	FALSE	0	httpo2	value2
-#HttpOnly_.127.0.0.1	TRUE	/p3/	FALSE	0	httpo3	value3
-#HttpOnly_.127.0.0.1	TRUE	/p4/	FALSE	0	httpo4	value4
-#HttpOnly_.127.0.0.1	TRUE	/p4/	FALSE	0	httponly	myvalue1
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec	myvalue2
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec2	myvalue3
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec3	myvalue4
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec4	myvalue5
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec5	myvalue6
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec6	myvalue7
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec7	myvalue8
-#HttpOnly_.127.0.0.1	TRUE	/p4/	TRUE	0	httpandsec8	myvalue9
-.127.0.0.1	TRUE	/	FALSE	0	partmatch	present
+127.0.0.1	FALSE	/silly/	FALSE	0	ismatch	this
+127.0.0.1	FALSE	/overwrite	FALSE	0	overwrite	this2
+127.0.0.1	FALSE	/secure1/	TRUE	0	sec1value	secure1
+127.0.0.1	FALSE	/secure2/	TRUE	0	sec2value	secure2
+127.0.0.1	FALSE	/secure3/	TRUE	0	sec3value	secure3
+127.0.0.1	FALSE	/secure4/	TRUE	0	sec4value	secure4
+127.0.0.1	FALSE	/secure5/	TRUE	0	sec5value	secure5
+127.0.0.1	FALSE	/secure6/	TRUE	0	sec6value	secure6
+127.0.0.1	FALSE	/secure7/	TRUE	0	sec7value	secure7
+127.0.0.1	FALSE	/secure8/	TRUE	0	sec8value	secure8
+127.0.0.1	FALSE	/secure9/	TRUE	0	secure	very1
+#HttpOnly_127.0.0.1	FALSE	/p1/	FALSE	0	httpo1	value1
+#HttpOnly_127.0.0.1	FALSE	/p2/	FALSE	0	httpo2	value2
+#HttpOnly_127.0.0.1	FALSE	/p3/	FALSE	0	httpo3	value3
+#HttpOnly_127.0.0.1	FALSE	/p4/	FALSE	0	httpo4	value4
+#HttpOnly_127.0.0.1	FALSE	/p4/	FALSE	0	httponly	myvalue1
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec	myvalue2
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec2	myvalue3
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec3	myvalue4
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec4	myvalue5
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec5	myvalue6
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec6	myvalue7
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec7	myvalue8
+#HttpOnly_127.0.0.1	FALSE	/p4/	TRUE	0	httpandsec8	myvalue9
+127.0.0.1	FALSE	/	FALSE	0	partmatch	present
 127.0.0.1	FALSE	/we/want/	FALSE	2054030187	nodomain	value
 #HttpOnly_127.0.0.1	FALSE	/silly/	FALSE	0	magic	yessir
-.0.0.1	TRUE	/we/want/	FALSE	0	blexp	yesyes
+127.0.0.1	FALSE	/we/want/	FALSE	0	blexp	yesyes
 </file>
 </verify>
 </testcase>
diff --git a/tests/data/test8 b/tests/data/test8
index 4d54541..030fd55 100644
--- a/tests/data/test8
+++ b/tests/data/test8
@@ -42,7 +42,8 @@ Set-Cookie: duplicate=test; domain=.0.0.1; domain=.0.0.1; path=/donkey;
 Set-Cookie: cookie=yes; path=/we;
 Set-Cookie: cookie=perhaps; path=/we/want;
 Set-Cookie: nocookie=yes; path=/WE;
-Set-Cookie: blexp=yesyes; domain=.0.0.1; domain=.0.0.1; expiry=totally bad;
+Set-Cookie: blexp=yesyes; domain=%HOSTIP; domain=%HOSTIP; expiry=totally bad;
+Set-Cookie: partialip=nono; domain=.0.0.1;
 
 </file>
 <precheck>
-- 
2.1.0