228 lines
8.3 KiB
C++
228 lines
8.3 KiB
C++
// Copyright 2021 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/base/proxy_string_util.h"
|
|
|
|
#include <string>
|
|
|
|
#include "base/notreached.h"
|
|
#include "base/strings/strcat.h"
|
|
#include "base/strings/string_piece.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "net/base/proxy_server.h"
|
|
#include "net/base/url_util.h"
|
|
#include "net/http/http_util.h"
|
|
#include "url/third_party/mozilla/url_parse.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
// Parses the proxy type from a PAC string, to a ProxyServer::Scheme.
|
|
// This mapping is case-insensitive. If no type could be matched
|
|
// returns SCHEME_INVALID.
|
|
ProxyServer::Scheme GetSchemeFromPacTypeInternal(base::StringPiece type) {
|
|
if (base::EqualsCaseInsensitiveASCII(type, "proxy"))
|
|
return ProxyServer::SCHEME_HTTP;
|
|
if (base::EqualsCaseInsensitiveASCII(type, "socks")) {
|
|
// Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5
|
|
// notation didn't originally exist, so if a client returns SOCKS they
|
|
// really meant SOCKS4.
|
|
return ProxyServer::SCHEME_SOCKS4;
|
|
}
|
|
if (base::EqualsCaseInsensitiveASCII(type, "socks4"))
|
|
return ProxyServer::SCHEME_SOCKS4;
|
|
if (base::EqualsCaseInsensitiveASCII(type, "socks5"))
|
|
return ProxyServer::SCHEME_SOCKS5;
|
|
if (base::EqualsCaseInsensitiveASCII(type, "direct"))
|
|
return ProxyServer::SCHEME_DIRECT;
|
|
if (base::EqualsCaseInsensitiveASCII(type, "https"))
|
|
return ProxyServer::SCHEME_HTTPS;
|
|
if (base::EqualsCaseInsensitiveASCII(type, "quic"))
|
|
return ProxyServer::SCHEME_QUIC;
|
|
|
|
return ProxyServer::SCHEME_INVALID;
|
|
}
|
|
|
|
ProxyServer FromSchemeHostAndPort(ProxyServer::Scheme scheme,
|
|
base::StringPiece host_and_port) {
|
|
// Trim leading/trailing space.
|
|
host_and_port = HttpUtil::TrimLWS(host_and_port);
|
|
|
|
if (scheme == ProxyServer::SCHEME_INVALID)
|
|
return ProxyServer();
|
|
|
|
if (scheme == ProxyServer::SCHEME_DIRECT) {
|
|
if (!host_and_port.empty())
|
|
return ProxyServer(); // Invalid -- DIRECT cannot have a host/port.
|
|
return ProxyServer::Direct();
|
|
}
|
|
|
|
url::Component username_component;
|
|
url::Component password_component;
|
|
url::Component hostname_component;
|
|
url::Component port_component;
|
|
url::ParseAuthority(host_and_port.data(),
|
|
url::Component(0, host_and_port.size()),
|
|
&username_component, &password_component,
|
|
&hostname_component, &port_component);
|
|
if (username_component.is_valid() || password_component.is_valid() ||
|
|
hostname_component.is_empty()) {
|
|
return ProxyServer();
|
|
}
|
|
|
|
base::StringPiece hostname =
|
|
host_and_port.substr(hostname_component.begin, hostname_component.len);
|
|
|
|
// Reject inputs like "foo:". /url parsing and canonicalization code generally
|
|
// allows it and treats it the same as a URL without a specified port, but
|
|
// Chrome has traditionally disallowed it in proxy specifications.
|
|
if (port_component.is_valid() && port_component.is_empty())
|
|
return ProxyServer();
|
|
base::StringPiece port =
|
|
port_component.is_nonempty()
|
|
? host_and_port.substr(port_component.begin, port_component.len)
|
|
: "";
|
|
|
|
return ProxyServer::FromSchemeHostAndPort(scheme, hostname, port);
|
|
}
|
|
|
|
std::string ConstructHostPortString(base::StringPiece hostname, uint16_t port) {
|
|
DCHECK(!hostname.empty());
|
|
DCHECK((hostname.front() == '[' && hostname.back() == ']') ||
|
|
hostname.find(":") == base::StringPiece::npos);
|
|
|
|
return base::StrCat({hostname, ":", base::NumberToString(port)});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ProxyServer PacResultElementToProxyServer(
|
|
base::StringPiece pac_result_element) {
|
|
// Trim the leading/trailing whitespace.
|
|
pac_result_element = HttpUtil::TrimLWS(pac_result_element);
|
|
|
|
// Input should match:
|
|
// "DIRECT" | ( <type> 1*(LWS) <host-and-port> )
|
|
|
|
// Start by finding the first space (if any).
|
|
size_t space = 0;
|
|
for (; space < pac_result_element.size(); space++) {
|
|
if (HttpUtil::IsLWS(pac_result_element[space])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Everything to the left of the space is the scheme.
|
|
ProxyServer::Scheme scheme =
|
|
GetSchemeFromPacTypeInternal(pac_result_element.substr(0, space));
|
|
|
|
// And everything to the right of the space is the
|
|
// <host>[":" <port>].
|
|
return FromSchemeHostAndPort(scheme, pac_result_element.substr(space));
|
|
}
|
|
|
|
std::string ProxyServerToPacResultElement(const ProxyServer& proxy_server) {
|
|
switch (proxy_server.scheme()) {
|
|
case ProxyServer::SCHEME_DIRECT:
|
|
return "DIRECT";
|
|
case ProxyServer::SCHEME_HTTP:
|
|
return std::string("PROXY ") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_SOCKS4:
|
|
// For compatibility send SOCKS instead of SOCKS4.
|
|
return std::string("SOCKS ") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_SOCKS5:
|
|
return std::string("SOCKS5 ") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_HTTPS:
|
|
return std::string("HTTPS ") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_QUIC:
|
|
return std::string("QUIC ") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
default:
|
|
// Got called with an invalid scheme.
|
|
NOTREACHED();
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
ProxyServer ProxyUriToProxyServer(base::StringPiece uri,
|
|
ProxyServer::Scheme default_scheme) {
|
|
// We will default to |default_scheme| if no scheme specifier was given.
|
|
ProxyServer::Scheme scheme = default_scheme;
|
|
|
|
// Trim the leading/trailing whitespace.
|
|
uri = HttpUtil::TrimLWS(uri);
|
|
|
|
// Check for [<scheme> "://"]
|
|
size_t colon = uri.find(':');
|
|
if (colon != base::StringPiece::npos && uri.size() - colon >= 3 &&
|
|
uri[colon + 1] == '/' && uri[colon + 2] == '/') {
|
|
scheme = GetSchemeFromUriScheme(uri.substr(0, colon));
|
|
uri = uri.substr(colon + 3); // Skip past the "://"
|
|
}
|
|
|
|
// Now parse the <host>[":"<port>].
|
|
return FromSchemeHostAndPort(scheme, uri);
|
|
}
|
|
|
|
std::string ProxyServerToProxyUri(const ProxyServer& proxy_server) {
|
|
switch (proxy_server.scheme()) {
|
|
case ProxyServer::SCHEME_DIRECT:
|
|
return "direct://";
|
|
case ProxyServer::SCHEME_HTTP:
|
|
// Leave off "http://" since it is our default scheme.
|
|
return ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_SOCKS4:
|
|
return std::string("socks4://") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_SOCKS5:
|
|
return std::string("socks5://") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_HTTPS:
|
|
return std::string("https://") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
case ProxyServer::SCHEME_QUIC:
|
|
return std::string("quic://") +
|
|
ConstructHostPortString(proxy_server.GetHost(),
|
|
proxy_server.GetPort());
|
|
default:
|
|
// Got called with an invalid scheme.
|
|
NOTREACHED();
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
ProxyServer::Scheme GetSchemeFromUriScheme(base::StringPiece scheme) {
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
|
|
return ProxyServer::SCHEME_HTTP;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
|
|
return ProxyServer::SCHEME_SOCKS4;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
|
|
return ProxyServer::SCHEME_SOCKS5;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
|
|
return ProxyServer::SCHEME_SOCKS5;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "direct"))
|
|
return ProxyServer::SCHEME_DIRECT;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
|
|
return ProxyServer::SCHEME_HTTPS;
|
|
if (base::EqualsCaseInsensitiveASCII(scheme, "quic"))
|
|
return ProxyServer::SCHEME_QUIC;
|
|
return ProxyServer::SCHEME_INVALID;
|
|
}
|
|
|
|
} // namespace net
|