HTTP::Cookies vs. Netscape cookies, part 1: domain matching

Andreas Gustafsson (gson@araneus.fi)
Wed, 30 Sep 1998 15:18:34 +0300 (EEST)


Here's one case where HTTP::Cookies still fails to handle
Netscape-style cookies correctly. 

Suppose the web server foo.bar.fi issues a cookie:

  Set-Cookie: NAME=VALUE; PATH=/; DOMAIN=foo.bar.fi

Later, we contact a second web server baz.foo.bar.fi.
The above cookie should be sent to the server baz.foo.bar.fi
according to the Netscape rules, but HTTP::Cookies won't.

My analysis of the problems is as follows.  In
HTTP::Cookies::extract_cookies, the test 
"if (defined($domain) && $domain ne $req_host)" 
becomes false, because the DOMAIN= attribute matches
the host name of the issuing host.  Therefore,
no leading dot is added to the domain foo.bar.fi.

Later, in add_cookie_header, the following attempts
are made to match the host name baz.foo.bar.fi
with the domain:

  baz.foo.bar.fi	!= foo.bar.fi
  .foo.bar.fi		!= foo.bar.fi
  .bar.fi		!= foo.bar.fi

These attempts all fail, thus the Cookie header is never sent.

The patch below fixes the problem for me, but it is not as clean
as I would like.

*** Cookies.pm.orig	Fri Apr 10 15:26:13 1998
--- Cookies.pm	Tue Sep 29 09:45:42 1998
***************
*** 108,113 ****
--- 108,114 ----
  
      my @cval;    # cookie values for the "Cookie" header
      my $set_ver;
+     my $netscape_only = 0; # An exact domain match applies to any cookie
  
      while (($domain =~ tr/././) >= 2 || # must be at least 2 dots
             $domain =~ /\.local$/)
***************
*** 156,161 ****
--- 157,167 ----
  			next;
  		    }
  		}
+ 		if ($version > 0 && $netscape_only) {
+ 		    LWP::Debug::debug("   domain $domain applies to " .
+ 				      "Netscape-style cookies only");
+ 		}
+ 		
  	        LWP::Debug::debug("   it's a match");
  
  		# set version number of cookie header.
***************
*** 191,198 ****
          }
  
      } continue {
! 	# Try with a more general domain:  www.sol.no ==> .sol.no
! 	$domain =~ s/^\.?[^.]*//;
      }
  
      $request->header(Cookie => join("; ", @cval)) if @cval;
--- 197,218 ----
          }
  
      } continue {
! 	# Try with a more general domain, alternately stripping
! 	# leading name components and leading dots.  When this
! 	# results in a domain with no leading dot, it is for
! 	# Netscape cookie compatibility only:
! 	#   
! 	# a.b.c.net	Any cookie
! 	# .b.c.net	Any cookie
! 	# b.c.net	Netscape cookie only
! 	# .c.net	Any cookie
! 
! 	if ($domain =~ s/^\.+//) {
! 	    $netscape_only = 1;
! 	} else {
! 	    $domain =~ s/[^.]*//;
! 	    $netscape_only = 0;
! 	}
      }
  
      $request->header(Cookie => join("; ", @cval)) if @cval;

-- 
Andreas Gustafsson, gson@araneus.fi