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