blob: a8e76715a709b38f035153e55744618702b5da46 [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2021 The Chromium Authors
Eric Orth5ccc3f02021-09-23 00:01:572// 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"
Eric Orth5dbd4592021-09-24 21:21:2410#include "base/strings/strcat.h"
Eric Orth5ccc3f02021-09-23 00:01:5711#include "base/strings/string_piece.h"
12#include "base/strings/string_util.h"
Eric Orth5ccc3f02021-09-23 00:01:5713#include "net/base/proxy_server.h"
14#include "net/base/url_util.h"
15#include "net/http/http_util.h"
Eric Orth5dbd4592021-09-24 21:21:2416#include "url/third_party/mozilla/url_parse.h"
Eric Orth5ccc3f02021-09-23 00:01:5717
18namespace net {
19
20namespace {
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.
25ProxyServer::Scheme GetSchemeFromPacTypeInternal(base::StringPiece type) {
Dan McArdle841e11d2022-05-27 22:00:5626 if (base::EqualsCaseInsensitiveASCII(type, "proxy"))
Eric Orth5ccc3f02021-09-23 00:01:5727 return ProxyServer::SCHEME_HTTP;
Dan McArdle841e11d2022-05-27 22:00:5628 if (base::EqualsCaseInsensitiveASCII(type, "socks")) {
Eric Orth5ccc3f02021-09-23 00:01:5729 // 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 }
Dan McArdle841e11d2022-05-27 22:00:5634 if (base::EqualsCaseInsensitiveASCII(type, "socks4"))
Eric Orth5ccc3f02021-09-23 00:01:5735 return ProxyServer::SCHEME_SOCKS4;
Dan McArdle841e11d2022-05-27 22:00:5636 if (base::EqualsCaseInsensitiveASCII(type, "socks5"))
Eric Orth5ccc3f02021-09-23 00:01:5737 return ProxyServer::SCHEME_SOCKS5;
Dan McArdle841e11d2022-05-27 22:00:5638 if (base::EqualsCaseInsensitiveASCII(type, "direct"))
Eric Orth5ccc3f02021-09-23 00:01:5739 return ProxyServer::SCHEME_DIRECT;
Dan McArdle841e11d2022-05-27 22:00:5640 if (base::EqualsCaseInsensitiveASCII(type, "https"))
Eric Orth5ccc3f02021-09-23 00:01:5741 return ProxyServer::SCHEME_HTTPS;
Dan McArdle841e11d2022-05-27 22:00:5642 if (base::EqualsCaseInsensitiveASCII(type, "quic"))
Eric Orth5ccc3f02021-09-23 00:01:5743 return ProxyServer::SCHEME_QUIC;
44
45 return ProxyServer::SCHEME_INVALID;
46}
47
48ProxyServer 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
Eric Orth5dbd4592021-09-24 21:21:2453 if (scheme == ProxyServer::SCHEME_INVALID)
54 return ProxyServer();
Eric Orth5ccc3f02021-09-23 00:01:5755
Eric Orth5dbd4592021-09-24 21:21:2456 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();
Eric Orth5ccc3f02021-09-23 00:01:5760 }
61
Eric Orth5dbd4592021-09-24 21:21:2462 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() ||
Tom Sepez40fbf43e2022-11-15 00:11:0371 hostname_component.is_empty()) {
Eric Orth5dbd4592021-09-24 21:21:2472 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.
Tom Sepez40fbf43e2022-11-15 00:11:0381 if (port_component.is_valid() && port_component.is_empty())
Eric Orth5dbd4592021-09-24 21:21:2482 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
91std::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)});
Eric Orth5ccc3f02021-09-23 00:01:5797}
98
99} // namespace
100
101ProxyServer 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
126std::string ProxyServerToPacResultElement(const ProxyServer& proxy_server) {
127 switch (proxy_server.scheme()) {
128 case ProxyServer::SCHEME_DIRECT:
129 return "DIRECT";
130 case ProxyServer::SCHEME_HTTP:
Eric Orth5dbd4592021-09-24 21:21:24131 return std::string("PROXY ") +
132 ConstructHostPortString(proxy_server.GetHost(),
133 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57134 case ProxyServer::SCHEME_SOCKS4:
135 // For compatibility send SOCKS instead of SOCKS4.
Eric Orth5dbd4592021-09-24 21:21:24136 return std::string("SOCKS ") +
137 ConstructHostPortString(proxy_server.GetHost(),
138 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57139 case ProxyServer::SCHEME_SOCKS5:
Eric Orth5dbd4592021-09-24 21:21:24140 return std::string("SOCKS5 ") +
141 ConstructHostPortString(proxy_server.GetHost(),
142 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57143 case ProxyServer::SCHEME_HTTPS:
Eric Orth5dbd4592021-09-24 21:21:24144 return std::string("HTTPS ") +
145 ConstructHostPortString(proxy_server.GetHost(),
146 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57147 case ProxyServer::SCHEME_QUIC:
Eric Orth5dbd4592021-09-24 21:21:24148 return std::string("QUIC ") +
149 ConstructHostPortString(proxy_server.GetHost(),
150 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57151 default:
152 // Got called with an invalid scheme.
153 NOTREACHED();
154 return std::string();
155 }
156}
157
158ProxyServer 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
178std::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.
Eric Orth5dbd4592021-09-24 21:21:24184 return ConstructHostPortString(proxy_server.GetHost(),
185 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57186 case ProxyServer::SCHEME_SOCKS4:
187 return std::string("socks4://") +
Eric Orth5dbd4592021-09-24 21:21:24188 ConstructHostPortString(proxy_server.GetHost(),
189 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57190 case ProxyServer::SCHEME_SOCKS5:
191 return std::string("socks5://") +
Eric Orth5dbd4592021-09-24 21:21:24192 ConstructHostPortString(proxy_server.GetHost(),
193 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57194 case ProxyServer::SCHEME_HTTPS:
Eric Orth5dbd4592021-09-24 21:21:24195 return std::string("https://") +
196 ConstructHostPortString(proxy_server.GetHost(),
197 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57198 case ProxyServer::SCHEME_QUIC:
Eric Orth5dbd4592021-09-24 21:21:24199 return std::string("quic://") +
200 ConstructHostPortString(proxy_server.GetHost(),
201 proxy_server.GetPort());
Eric Orth5ccc3f02021-09-23 00:01:57202 default:
203 // Got called with an invalid scheme.
204 NOTREACHED();
205 return std::string();
206 }
207}
208
209ProxyServer::Scheme GetSchemeFromUriScheme(base::StringPiece scheme) {
Dan McArdle841e11d2022-05-27 22:00:56210 if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
Eric Orth5ccc3f02021-09-23 00:01:57211 return ProxyServer::SCHEME_HTTP;
Dan McArdle841e11d2022-05-27 22:00:56212 if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
Eric Orth5ccc3f02021-09-23 00:01:57213 return ProxyServer::SCHEME_SOCKS4;
Dan McArdle841e11d2022-05-27 22:00:56214 if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
Eric Orth5ccc3f02021-09-23 00:01:57215 return ProxyServer::SCHEME_SOCKS5;
Dan McArdle841e11d2022-05-27 22:00:56216 if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
Eric Orth5ccc3f02021-09-23 00:01:57217 return ProxyServer::SCHEME_SOCKS5;
Dan McArdle841e11d2022-05-27 22:00:56218 if (base::EqualsCaseInsensitiveASCII(scheme, "direct"))
Eric Orth5ccc3f02021-09-23 00:01:57219 return ProxyServer::SCHEME_DIRECT;
Dan McArdle841e11d2022-05-27 22:00:56220 if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
Eric Orth5ccc3f02021-09-23 00:01:57221 return ProxyServer::SCHEME_HTTPS;
Dan McArdle841e11d2022-05-27 22:00:56222 if (base::EqualsCaseInsensitiveASCII(scheme, "quic"))
Eric Orth5ccc3f02021-09-23 00:01:57223 return ProxyServer::SCHEME_QUIC;
224 return ProxyServer::SCHEME_INVALID;
225}
226
227} // namespace net