• 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 #include <string_view>
9 
10 #include "base/notreached.h"
11 #include "base/strings/strcat.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(std::string_view type)25 ProxyServer::Scheme GetSchemeFromPacTypeInternal(std::string_view 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,std::string_view host_and_port)48 ProxyServer FromSchemeHostAndPort(ProxyServer::Scheme scheme,
49                                   std::string_view 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   std::string_view 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   std::string_view 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(std::string_view hostname,uint16_t port)91 std::string ConstructHostPortString(std::string_view hostname, uint16_t port) {
92   DCHECK(!hostname.empty());
93   DCHECK((hostname.front() == '[' && hostname.back() == ']') ||
94          hostname.find(":") == std::string_view::npos);
95 
96   return base::StrCat({hostname, ":", base::NumberToString(port)});
97 }
98 
99 }  // namespace
100 
PacResultElementToProxyChain(std::string_view pac_result_element)101 ProxyChain PacResultElementToProxyChain(std::string_view pac_result_element) {
102   // Proxy chains are not supported in PAC strings, so this is just parsed
103   // as a single server.
104   return ProxyChain(PacResultElementToProxyServer(pac_result_element));
105 }
106 
PacResultElementToProxyServer(std::string_view pac_result_element)107 ProxyServer PacResultElementToProxyServer(std::string_view pac_result_element) {
108   // Trim the leading/trailing whitespace.
109   pac_result_element = HttpUtil::TrimLWS(pac_result_element);
110 
111   // Input should match:
112   // "DIRECT" | ( <type> 1*(LWS) <host-and-port> )
113 
114   // Start by finding the first space (if any).
115   size_t space = 0;
116   for (; space < pac_result_element.size(); space++) {
117     if (HttpUtil::IsLWS(pac_result_element[space])) {
118       break;
119     }
120   }
121 
122   // Everything to the left of the space is the scheme.
123   ProxyServer::Scheme scheme =
124       GetSchemeFromPacTypeInternal(pac_result_element.substr(0, space));
125 
126   // And everything to the right of the space is the
127   // <host>[":" <port>].
128   return FromSchemeHostAndPort(scheme, pac_result_element.substr(space));
129 }
130 
ProxyServerToPacResultElement(const ProxyServer & proxy_server)131 std::string ProxyServerToPacResultElement(const ProxyServer& proxy_server) {
132   switch (proxy_server.scheme()) {
133     case ProxyServer::SCHEME_DIRECT:
134       return "DIRECT";
135     case ProxyServer::SCHEME_HTTP:
136       return std::string("PROXY ") +
137              ConstructHostPortString(proxy_server.GetHost(),
138                                      proxy_server.GetPort());
139     case ProxyServer::SCHEME_SOCKS4:
140       // For compatibility send SOCKS instead of SOCKS4.
141       return std::string("SOCKS ") +
142              ConstructHostPortString(proxy_server.GetHost(),
143                                      proxy_server.GetPort());
144     case ProxyServer::SCHEME_SOCKS5:
145       return std::string("SOCKS5 ") +
146              ConstructHostPortString(proxy_server.GetHost(),
147                                      proxy_server.GetPort());
148     case ProxyServer::SCHEME_HTTPS:
149       return std::string("HTTPS ") +
150              ConstructHostPortString(proxy_server.GetHost(),
151                                      proxy_server.GetPort());
152     case ProxyServer::SCHEME_QUIC:
153       return std::string("QUIC ") +
154              ConstructHostPortString(proxy_server.GetHost(),
155                                      proxy_server.GetPort());
156     default:
157       // Got called with an invalid scheme.
158       NOTREACHED();
159       return std::string();
160   }
161 }
162 
ProxyUriToProxyChain(std::string_view uri,ProxyServer::Scheme default_scheme)163 ProxyChain ProxyUriToProxyChain(std::string_view uri,
164                                 ProxyServer::Scheme default_scheme) {
165   return ProxyChain(ProxyUriToProxyServer(uri, default_scheme));
166 }
167 
ProxyUriToProxyServer(std::string_view uri,ProxyServer::Scheme default_scheme)168 ProxyServer ProxyUriToProxyServer(std::string_view uri,
169                                   ProxyServer::Scheme default_scheme) {
170   // We will default to |default_scheme| if no scheme specifier was given.
171   ProxyServer::Scheme scheme = default_scheme;
172 
173   // Trim the leading/trailing whitespace.
174   uri = HttpUtil::TrimLWS(uri);
175 
176   // Check for [<scheme> "://"]
177   size_t colon = uri.find(':');
178   if (colon != std::string_view::npos && uri.size() - colon >= 3 &&
179       uri[colon + 1] == '/' && uri[colon + 2] == '/') {
180     scheme = GetSchemeFromUriScheme(uri.substr(0, colon));
181     uri = uri.substr(colon + 3);  // Skip past the "://"
182   }
183 
184   // Now parse the <host>[":"<port>].
185   return FromSchemeHostAndPort(scheme, uri);
186 }
187 
ProxyServerToProxyUri(const ProxyServer & proxy_server)188 std::string ProxyServerToProxyUri(const ProxyServer& proxy_server) {
189   switch (proxy_server.scheme()) {
190     case ProxyServer::SCHEME_DIRECT:
191       return "direct://";
192     case ProxyServer::SCHEME_HTTP:
193       // Leave off "http://" since it is our default scheme.
194       return ConstructHostPortString(proxy_server.GetHost(),
195                                      proxy_server.GetPort());
196     case ProxyServer::SCHEME_SOCKS4:
197       return std::string("socks4://") +
198              ConstructHostPortString(proxy_server.GetHost(),
199                                      proxy_server.GetPort());
200     case ProxyServer::SCHEME_SOCKS5:
201       return std::string("socks5://") +
202              ConstructHostPortString(proxy_server.GetHost(),
203                                      proxy_server.GetPort());
204     case ProxyServer::SCHEME_HTTPS:
205       return std::string("https://") +
206              ConstructHostPortString(proxy_server.GetHost(),
207                                      proxy_server.GetPort());
208     case ProxyServer::SCHEME_QUIC:
209       return std::string("quic://") +
210              ConstructHostPortString(proxy_server.GetHost(),
211                                      proxy_server.GetPort());
212     default:
213       // Got called with an invalid scheme.
214       NOTREACHED();
215       return std::string();
216   }
217 }
218 
GetSchemeFromUriScheme(std::string_view scheme)219 ProxyServer::Scheme GetSchemeFromUriScheme(std::string_view scheme) {
220   if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
221     return ProxyServer::SCHEME_HTTP;
222   if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
223     return ProxyServer::SCHEME_SOCKS4;
224   if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
225     return ProxyServer::SCHEME_SOCKS5;
226   if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
227     return ProxyServer::SCHEME_SOCKS5;
228   if (base::EqualsCaseInsensitiveASCII(scheme, "direct"))
229     return ProxyServer::SCHEME_DIRECT;
230   if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
231     return ProxyServer::SCHEME_HTTPS;
232   if (base::EqualsCaseInsensitiveASCII(scheme, "quic"))
233     return ProxyServer::SCHEME_QUIC;
234   return ProxyServer::SCHEME_INVALID;
235 }
236 
237 }  // namespace net
238