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