001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027package org.apache.http.impl.cookie; 028 029import java.util.Locale; 030 031import org.apache.http.annotation.Contract; 032import org.apache.http.annotation.ThreadingBehavior; 033import org.apache.http.conn.util.InetAddressUtils; 034import org.apache.http.cookie.ClientCookie; 035import org.apache.http.cookie.CommonCookieAttributeHandler; 036import org.apache.http.cookie.Cookie; 037import org.apache.http.cookie.CookieOrigin; 038import org.apache.http.cookie.CookieRestrictionViolationException; 039import org.apache.http.cookie.MalformedCookieException; 040import org.apache.http.cookie.SetCookie; 041import org.apache.http.util.Args; 042import org.apache.http.util.TextUtils; 043 044/** 045 * 046 * @since 4.0 047 */ 048@Contract(threading = ThreadingBehavior.IMMUTABLE) 049public class BasicDomainHandler implements CommonCookieAttributeHandler { 050 051 public BasicDomainHandler() { 052 super(); 053 } 054 055 @Override 056 public void parse(final SetCookie cookie, final String value) 057 throws MalformedCookieException { 058 Args.notNull(cookie, "Cookie"); 059 if (TextUtils.isBlank(value)) { 060 throw new MalformedCookieException("Blank or null value for domain attribute"); 061 } 062 // Ignore domain attributes ending with '.' per RFC 6265, 4.1.2.3 063 if (value.endsWith(".")) { 064 return; 065 } 066 String domain = value; 067 if (domain.startsWith(".")) { 068 domain = domain.substring(1); 069 } 070 domain = domain.toLowerCase(Locale.ROOT); 071 cookie.setDomain(domain); 072 } 073 074 @Override 075 public void validate(final Cookie cookie, final CookieOrigin origin) 076 throws MalformedCookieException { 077 Args.notNull(cookie, "Cookie"); 078 Args.notNull(origin, "Cookie origin"); 079 // Validate the cookies domain attribute. NOTE: Domains without 080 // any dots are allowed to support hosts on private LANs that don't 081 // have DNS names. Since they have no dots, to domain-match the 082 // request-host and domain must be identical for the cookie to sent 083 // back to the origin-server. 084 final String host = origin.getHost(); 085 final String domain = cookie.getDomain(); 086 if (domain == null) { 087 throw new CookieRestrictionViolationException("Cookie 'domain' may not be null"); 088 } 089 if (!host.equals(domain) && !domainMatch(domain, host)) { 090 throw new CookieRestrictionViolationException( 091 "Illegal 'domain' attribute \"" + domain + "\". Domain of origin: \"" + host + "\""); 092 } 093 } 094 095 static boolean domainMatch(final String domain, final String host) { 096 if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host)) { 097 return false; 098 } 099 final String normalizedDomain = domain.startsWith(".") ? domain.substring(1) : domain; 100 if (host.endsWith(normalizedDomain)) { 101 final int prefix = host.length() - normalizedDomain.length(); 102 // Either a full match or a prefix endidng with a '.' 103 if (prefix == 0) { 104 return true; 105 } 106 if (prefix > 1 && host.charAt(prefix - 1) == '.') { 107 return true; 108 } 109 } 110 return false; 111 } 112 113 @Override 114 public boolean match(final Cookie cookie, final CookieOrigin origin) { 115 Args.notNull(cookie, "Cookie"); 116 Args.notNull(origin, "Cookie origin"); 117 final String host = origin.getHost(); 118 String domain = cookie.getDomain(); 119 if (domain == null) { 120 return false; 121 } 122 if (domain.startsWith(".")) { 123 domain = domain.substring(1); 124 } 125 domain = domain.toLowerCase(Locale.ROOT); 126 if (host.equals(domain)) { 127 return true; 128 } 129 if (cookie instanceof ClientCookie) { 130 if (((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) { 131 return domainMatch(domain, host); 132 } 133 } 134 return false; 135 } 136 137 @Override 138 public String getAttributeName() { 139 return ClientCookie.DOMAIN_ATTR; 140 } 141 142}