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 13namespace net { 14 15namespace { 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. 20ProxyServer::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. 46ProxyServer::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 63std::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 73ProxyServer::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 84const 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 92ProxyServer ProxyServer::FromURI(const std::string& uri, 93 Scheme default_scheme) { 94 return FromURI(uri.begin(), uri.end(), default_scheme); 95} 96 97// static 98ProxyServer 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 121std::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 142ProxyServer ProxyServer::FromPacString(const std::string& pac_string) { 143 return FromPacString(pac_string.begin(), pac_string.end()); 144} 145 146// static 147ProxyServer 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 171std::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 192int 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 207ProxyServer::Scheme ProxyServer::GetSchemeFromURI(const std::string& scheme) { 208 return GetSchemeFromURIInternal(scheme.begin(), scheme.end()); 209} 210 211// static 212ProxyServer ProxyServer::FromSchemeHostAndPort( 213 Scheme scheme, 214 std::string::const_iterator begin, 215 std::string::const_iterator end) { 216 217 // Trim leading/trailing space. 218 HttpUtil::TrimLWS(&begin, &end); 219 220 if (scheme == SCHEME_DIRECT && begin != end) 221 return ProxyServer(); // Invalid -- DIRECT cannot have a host/port. 222 223 HostPortPair host_port_pair; 224 225 if (scheme != SCHEME_INVALID && scheme != SCHEME_DIRECT) { 226 std::string host; 227 int port = -1; 228 // If the scheme has a host/port, parse it. 229 bool ok = net::ParseHostAndPort(begin, end, &host, &port); 230 if (!ok) 231 return ProxyServer(); // Invalid -- failed parsing <host>[":"<port>] 232 233 // Choose a default port number if none was given. 234 if (port == -1) 235 port = GetDefaultPortForScheme(scheme); 236 237 host_port_pair = HostPortPair(HostNoBrackets(host), port); 238 } 239 240 return ProxyServer(scheme, host_port_pair); 241} 242 243} // namespace net 244