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