1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/proxy/proxy_server.h" 6 7 #include <algorithm> 8 9 #include "base/strings/string_util.h" 10 #include "net/base/net_util.h" 11 #include "net/http/http_util.h" 12 13 namespace net { 14 15 namespace { 16 17 // Parses the proxy type from a PAC string, to a ProxyServer::Scheme. 18 // This mapping is case-insensitive. If no type could be matched 19 // returns SCHEME_INVALID. 20 ProxyServer::Scheme GetSchemeFromPacTypeInternal( 21 std::string::const_iterator begin, 22 std::string::const_iterator end) { 23 if (LowerCaseEqualsASCII(begin, end, "proxy")) 24 return ProxyServer::SCHEME_HTTP; 25 if (LowerCaseEqualsASCII(begin, end, "socks")) { 26 // Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5 27 // notation didn't originally exist, so if a client returns SOCKS they 28 // really meant SOCKS4. 29 return ProxyServer::SCHEME_SOCKS4; 30 } 31 if (LowerCaseEqualsASCII(begin, end, "socks4")) 32 return ProxyServer::SCHEME_SOCKS4; 33 if (LowerCaseEqualsASCII(begin, end, "socks5")) 34 return ProxyServer::SCHEME_SOCKS5; 35 if (LowerCaseEqualsASCII(begin, end, "direct")) 36 return ProxyServer::SCHEME_DIRECT; 37 if (LowerCaseEqualsASCII(begin, end, "https")) 38 return ProxyServer::SCHEME_HTTPS; 39 40 return ProxyServer::SCHEME_INVALID; 41 } 42 43 // Parses the proxy scheme from a URL-like representation, to a 44 // ProxyServer::Scheme. This corresponds with the values used in 45 // ProxyServer::ToURI(). If no type could be matched, returns SCHEME_INVALID. 46 ProxyServer::Scheme GetSchemeFromURIInternal(std::string::const_iterator begin, 47 std::string::const_iterator end) { 48 if (LowerCaseEqualsASCII(begin, end, "http")) 49 return ProxyServer::SCHEME_HTTP; 50 if (LowerCaseEqualsASCII(begin, end, "socks4")) 51 return ProxyServer::SCHEME_SOCKS4; 52 if (LowerCaseEqualsASCII(begin, end, "socks")) 53 return ProxyServer::SCHEME_SOCKS5; 54 if (LowerCaseEqualsASCII(begin, end, "socks5")) 55 return ProxyServer::SCHEME_SOCKS5; 56 if (LowerCaseEqualsASCII(begin, end, "direct")) 57 return ProxyServer::SCHEME_DIRECT; 58 if (LowerCaseEqualsASCII(begin, end, "https")) 59 return ProxyServer::SCHEME_HTTPS; 60 return ProxyServer::SCHEME_INVALID; 61 } 62 63 std::string HostNoBrackets(const std::string& host) { 64 // Remove brackets from an RFC 2732-style IPv6 literal address. 65 const std::string::size_type len = host.size(); 66 if (len >= 2 && host[0] == '[' && host[len - 1] == ']') 67 return host.substr(1, len - 2); 68 return host; 69 } 70 71 } // namespace 72 73 ProxyServer::ProxyServer(Scheme scheme, const HostPortPair& host_port_pair) 74 : scheme_(scheme), host_port_pair_(host_port_pair) { 75 if (scheme_ == SCHEME_DIRECT || scheme_ == SCHEME_INVALID) { 76 // |host_port_pair| isn't relevant for these special schemes, so none should 77 // have been specified. It is important for this to be consistent since we 78 // do raw field comparisons in the equality and comparison functions. 79 DCHECK(host_port_pair.Equals(HostPortPair())); 80 host_port_pair_ = HostPortPair(); 81 } 82 } 83 84 const HostPortPair& ProxyServer::host_port_pair() const { 85 // Doesn't make sense to call this if the URI scheme doesn't 86 // have concept of a host. 87 DCHECK(is_valid() && !is_direct()); 88 return host_port_pair_; 89 } 90 91 // static 92 ProxyServer ProxyServer::FromURI(const std::string& uri, 93 Scheme default_scheme) { 94 return FromURI(uri.begin(), uri.end(), default_scheme); 95 } 96 97 // static 98 ProxyServer ProxyServer::FromURI(std::string::const_iterator begin, 99 std::string::const_iterator end, 100 Scheme default_scheme) { 101 // We will default to |default_scheme| if no scheme specifier was given. 102 Scheme scheme = default_scheme; 103 104 // Trim the leading/trailing whitespace. 105 HttpUtil::TrimLWS(&begin, &end); 106 107 // Check for [<scheme> "://"] 108 std::string::const_iterator colon = std::find(begin, end, ':'); 109 if (colon != end && 110 (end - colon) >= 3 && 111 *(colon + 1) == '/' && 112 *(colon + 2) == '/') { 113 scheme = GetSchemeFromURIInternal(begin, colon); 114 begin = colon + 3; // Skip past the "://" 115 } 116 117 // Now parse the <host>[":"<port>]. 118 return FromSchemeHostAndPort(scheme, begin, end); 119 } 120 121 std::string ProxyServer::ToURI() const { 122 switch (scheme_) { 123 case SCHEME_DIRECT: 124 return "direct://"; 125 case SCHEME_HTTP: 126 // Leave off "http://" since it is our default scheme. 127 return host_port_pair().ToString(); 128 case SCHEME_SOCKS4: 129 return std::string("socks4://") + host_port_pair().ToString(); 130 case SCHEME_SOCKS5: 131 return std::string("socks5://") + host_port_pair().ToString(); 132 case SCHEME_HTTPS: 133 return std::string("https://") + host_port_pair().ToString(); 134 default: 135 // Got called with an invalid scheme. 136 NOTREACHED(); 137 return std::string(); 138 } 139 } 140 141 // static 142 ProxyServer ProxyServer::FromPacString(const std::string& pac_string) { 143 return FromPacString(pac_string.begin(), pac_string.end()); 144 } 145 146 // static 147 ProxyServer ProxyServer::FromPacString(std::string::const_iterator begin, 148 std::string::const_iterator end) { 149 // Trim the leading/trailing whitespace. 150 HttpUtil::TrimLWS(&begin, &end); 151 152 // Input should match: 153 // "DIRECT" | ( <type> 1*(LWS) <host-and-port> ) 154 155 // Start by finding the first space (if any). 156 std::string::const_iterator space; 157 for (space = begin; space != end; ++space) { 158 if (HttpUtil::IsLWS(*space)) { 159 break; 160 } 161 } 162 163 // Everything to the left of the space is the scheme. 164 Scheme scheme = GetSchemeFromPacTypeInternal(begin, space); 165 166 // And everything to the right of the space is the 167 // <host>[":" <port>]. 168 return FromSchemeHostAndPort(scheme, space, end); 169 } 170 171 std::string ProxyServer::ToPacString() const { 172 switch (scheme_) { 173 case SCHEME_DIRECT: 174 return "DIRECT"; 175 case SCHEME_HTTP: 176 return std::string("PROXY ") + host_port_pair().ToString(); 177 case SCHEME_SOCKS4: 178 // For compatibility send SOCKS instead of SOCKS4. 179 return std::string("SOCKS ") + host_port_pair().ToString(); 180 case SCHEME_SOCKS5: 181 return std::string("SOCKS5 ") + host_port_pair().ToString(); 182 case SCHEME_HTTPS: 183 return std::string("HTTPS ") + host_port_pair().ToString(); 184 default: 185 // Got called with an invalid scheme. 186 NOTREACHED(); 187 return std::string(); 188 } 189 } 190 191 // static 192 int ProxyServer::GetDefaultPortForScheme(Scheme scheme) { 193 switch (scheme) { 194 case SCHEME_HTTP: 195 return 80; 196 case SCHEME_SOCKS4: 197 case SCHEME_SOCKS5: 198 return 1080; 199 case SCHEME_HTTPS: 200 return 443; 201 default: 202 return -1; 203 } 204 } 205 206 // static 207 ProxyServer::Scheme ProxyServer::GetSchemeFromURI(const std::string& scheme) { 208 return GetSchemeFromURIInternal(scheme.begin(), scheme.end()); 209 } 210 211 // TODO(bengr): Use |scheme_| to indicate that this is the data reduction proxy. 212 #if defined(SPDY_PROXY_AUTH_ORIGIN) 213 bool ProxyServer::isDataReductionProxy() const { 214 return host_port_pair_.Equals( 215 HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN))); 216 } 217 218 bool ProxyServer::isDataReductionProxyFallback() const { 219 #if defined(DATA_REDUCTION_FALLBACK_HOST) 220 return host_port_pair_.Equals( 221 HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST))); 222 #endif // defined(DATA_REDUCTION_FALLBACK_HOST) 223 return false; 224 } 225 #endif // defined(SPDY_PROXY_AUTH_ORIGIN) 226 227 // static 228 ProxyServer ProxyServer::FromSchemeHostAndPort( 229 Scheme scheme, 230 std::string::const_iterator begin, 231 std::string::const_iterator end) { 232 233 // Trim leading/trailing space. 234 HttpUtil::TrimLWS(&begin, &end); 235 236 if (scheme == SCHEME_DIRECT && begin != end) 237 return ProxyServer(); // Invalid -- DIRECT cannot have a host/port. 238 239 HostPortPair host_port_pair; 240 241 if (scheme != SCHEME_INVALID && scheme != SCHEME_DIRECT) { 242 std::string host; 243 int port = -1; 244 // If the scheme has a host/port, parse it. 245 bool ok = net::ParseHostAndPort(begin, end, &host, &port); 246 if (!ok) 247 return ProxyServer(); // Invalid -- failed parsing <host>[":"<port>] 248 249 // Choose a default port number if none was given. 250 if (port == -1) 251 port = GetDefaultPortForScheme(scheme); 252 253 host_port_pair = HostPortPair(HostNoBrackets(host), port); 254 } 255 256 return ProxyServer(scheme, host_port_pair); 257 } 258 259 } // namespace net 260