• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
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/base/proxy_string_util.h"
6 
7 #include <string>
8 
9 #include "base/notreached.h"
10 #include "base/strings/strcat.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "net/base/proxy_server.h"
14 #include "net/base/url_util.h"
15 #include "net/http/http_util.h"
16 #include "url/third_party/mozilla/url_parse.h"
17 
18 namespace net {
19 
20 namespace {
21 
22 // Parses the proxy type from a PAC string, to a ProxyServer::Scheme.
23 // This mapping is case-insensitive. If no type could be matched
24 // returns SCHEME_INVALID.
GetSchemeFromPacTypeInternal(base::StringPiece type)25 ProxyServer::Scheme GetSchemeFromPacTypeInternal(base::StringPiece type) {
26   if (base::EqualsCaseInsensitiveASCII(type, "proxy"))
27     return ProxyServer::SCHEME_HTTP;
28   if (base::EqualsCaseInsensitiveASCII(type, "socks")) {
29     // Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5
30     // notation didn't originally exist, so if a client returns SOCKS they
31     // really meant SOCKS4.
32     return ProxyServer::SCHEME_SOCKS4;
33   }
34   if (base::EqualsCaseInsensitiveASCII(type, "socks4"))
35     return ProxyServer::SCHEME_SOCKS4;
36   if (base::EqualsCaseInsensitiveASCII(type, "socks5"))
37     return ProxyServer::SCHEME_SOCKS5;
38   if (base::EqualsCaseInsensitiveASCII(type, "direct"))
39     return ProxyServer::SCHEME_DIRECT;
40   if (base::EqualsCaseInsensitiveASCII(type, "https"))
41     return ProxyServer::SCHEME_HTTPS;
42   if (base::EqualsCaseInsensitiveASCII(type, "quic"))
43     return ProxyServer::SCHEME_QUIC;
44 
45   return ProxyServer::SCHEME_INVALID;
46 }
47 
FromSchemeHostAndPort(ProxyServer::Scheme scheme,base::StringPiece host_and_port)48 ProxyServer FromSchemeHostAndPort(ProxyServer::Scheme scheme,
49                                   base::StringPiece host_and_port) {
50   // Trim leading/trailing space.
51   host_and_port = HttpUtil::TrimLWS(host_and_port);
52 
53   if (scheme == ProxyServer::SCHEME_INVALID)
54     return ProxyServer();
55 
56   if (scheme == ProxyServer::SCHEME_DIRECT) {
57     if (!host_and_port.empty())
58       return ProxyServer();  // Invalid -- DIRECT cannot have a host/port.
59     return ProxyServer::Direct();
60   }
61 
62   url::Component username_component;
63   url::Component password_component;
64   url::Component hostname_component;
65   url::Component port_component;
66   url::ParseAuthority(host_and_port.data(),
67                       url::Component(0, host_and_port.size()),
68                       &username_component, &password_component,
69                       &hostname_component, &port_component);
70   if (username_component.is_valid() || password_component.is_valid() ||
71       hostname_component.is_empty()) {
72     return ProxyServer();
73   }
74 
75   base::StringPiece hostname =
76       host_and_port.substr(hostname_component.begin, hostname_component.len);
77 
78   // Reject inputs like "foo:". /url parsing and canonicalization code generally
79   // allows it and treats it the same as a URL without a specified port, but
80   // Chrome has traditionally disallowed it in proxy specifications.
81   if (port_component.is_valid() && port_component.is_empty())
82     return ProxyServer();
83   base::StringPiece port =
84       port_component.is_nonempty()
85           ? host_and_port.substr(port_component.begin, port_component.len)
86           : "";
87 
88   return ProxyServer::FromSchemeHostAndPort(scheme, hostname, port);
89 }
90 
ConstructHostPortString(base::StringPiece hostname,uint16_t port)91 std::string ConstructHostPortString(base::StringPiece hostname, uint16_t port) {
92   DCHECK(!hostname.empty());
93   DCHECK((hostname.front() == '[' && hostname.back() == ']') ||
94          hostname.find(":") == base::StringPiece::npos);
95 
96   return base::StrCat({hostname, ":", base::NumberToString(port)});
97 }
98 
99 }  // namespace
100 
PacResultElementToProxyServer(base::StringPiece pac_result_element)101 ProxyServer PacResultElementToProxyServer(
102     base::StringPiece pac_result_element) {
103   // Trim the leading/trailing whitespace.
104   pac_result_element = HttpUtil::TrimLWS(pac_result_element);
105 
106   // Input should match:
107   // "DIRECT" | ( <type> 1*(LWS) <host-and-port> )
108 
109   // Start by finding the first space (if any).
110   size_t space = 0;
111   for (; space < pac_result_element.size(); space++) {
112     if (HttpUtil::IsLWS(pac_result_element[space])) {
113       break;
114     }
115   }
116 
117   // Everything to the left of the space is the scheme.
118   ProxyServer::Scheme scheme =
119       GetSchemeFromPacTypeInternal(pac_result_element.substr(0, space));
120 
121   // And everything to the right of the space is the
122   // <host>[":" <port>].
123   return FromSchemeHostAndPort(scheme, pac_result_element.substr(space));
124 }
125 
ProxyServerToPacResultElement(const ProxyServer & proxy_server)126 std::string ProxyServerToPacResultElement(const ProxyServer& proxy_server) {
127   switch (proxy_server.scheme()) {
128     case ProxyServer::SCHEME_DIRECT:
129       return "DIRECT";
130     case ProxyServer::SCHEME_HTTP:
131       return std::string("PROXY ") +
132              ConstructHostPortString(proxy_server.GetHost(),
133                                      proxy_server.GetPort());
134     case ProxyServer::SCHEME_SOCKS4:
135       // For compatibility send SOCKS instead of SOCKS4.
136       return std::string("SOCKS ") +
137              ConstructHostPortString(proxy_server.GetHost(),
138                                      proxy_server.GetPort());
139     case ProxyServer::SCHEME_SOCKS5:
140       return std::string("SOCKS5 ") +
141              ConstructHostPortString(proxy_server.GetHost(),
142                                      proxy_server.GetPort());
143     case ProxyServer::SCHEME_HTTPS:
144       return std::string("HTTPS ") +
145              ConstructHostPortString(proxy_server.GetHost(),
146                                      proxy_server.GetPort());
147     case ProxyServer::SCHEME_QUIC:
148       return std::string("QUIC ") +
149              ConstructHostPortString(proxy_server.GetHost(),
150                                      proxy_server.GetPort());
151     default:
152       // Got called with an invalid scheme.
153       NOTREACHED();
154       return std::string();
155   }
156 }
157 
ProxyUriToProxyServer(base::StringPiece uri,ProxyServer::Scheme default_scheme)158 ProxyServer ProxyUriToProxyServer(base::StringPiece uri,
159                                   ProxyServer::Scheme default_scheme) {
160   // We will default to |default_scheme| if no scheme specifier was given.
161   ProxyServer::Scheme scheme = default_scheme;
162 
163   // Trim the leading/trailing whitespace.
164   uri = HttpUtil::TrimLWS(uri);
165 
166   // Check for [<scheme> "://"]
167   size_t colon = uri.find(':');
168   if (colon != base::StringPiece::npos && uri.size() - colon >= 3 &&
169       uri[colon + 1] == '/' && uri[colon + 2] == '/') {
170     scheme = GetSchemeFromUriScheme(uri.substr(0, colon));
171     uri = uri.substr(colon + 3);  // Skip past the "://"
172   }
173 
174   // Now parse the <host>[":"<port>].
175   return FromSchemeHostAndPort(scheme, uri);
176 }
177 
ProxyServerToProxyUri(const ProxyServer & proxy_server)178 std::string ProxyServerToProxyUri(const ProxyServer& proxy_server) {
179   switch (proxy_server.scheme()) {
180     case ProxyServer::SCHEME_DIRECT:
181       return "direct://";
182     case ProxyServer::SCHEME_HTTP:
183       // Leave off "http://" since it is our default scheme.
184       return ConstructHostPortString(proxy_server.GetHost(),
185                                      proxy_server.GetPort());
186     case ProxyServer::SCHEME_SOCKS4:
187       return std::string("socks4://") +
188              ConstructHostPortString(proxy_server.GetHost(),
189                                      proxy_server.GetPort());
190     case ProxyServer::SCHEME_SOCKS5:
191       return std::string("socks5://") +
192              ConstructHostPortString(proxy_server.GetHost(),
193                                      proxy_server.GetPort());
194     case ProxyServer::SCHEME_HTTPS:
195       return std::string("https://") +
196              ConstructHostPortString(proxy_server.GetHost(),
197                                      proxy_server.GetPort());
198     case ProxyServer::SCHEME_QUIC:
199       return std::string("quic://") +
200              ConstructHostPortString(proxy_server.GetHost(),
201                                      proxy_server.GetPort());
202     default:
203       // Got called with an invalid scheme.
204       NOTREACHED();
205       return std::string();
206   }
207 }
208 
GetSchemeFromUriScheme(base::StringPiece scheme)209 ProxyServer::Scheme GetSchemeFromUriScheme(base::StringPiece scheme) {
210   if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
211     return ProxyServer::SCHEME_HTTP;
212   if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
213     return ProxyServer::SCHEME_SOCKS4;
214   if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
215     return ProxyServer::SCHEME_SOCKS5;
216   if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
217     return ProxyServer::SCHEME_SOCKS5;
218   if (base::EqualsCaseInsensitiveASCII(scheme, "direct"))
219     return ProxyServer::SCHEME_DIRECT;
220   if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
221     return ProxyServer::SCHEME_HTTPS;
222   if (base::EqualsCaseInsensitiveASCII(scheme, "quic"))
223     return ProxyServer::SCHEME_QUIC;
224   return ProxyServer::SCHEME_INVALID;
225 }
226 
227 }  // namespace net
228