mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 06:16:30 +03:00
Support proxy chaining
This commit is contained in:
parent
9dc63fbb8c
commit
f8c564d3a0
@ -3,14 +3,27 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
#include "net/tools/naive/naive_config.h"
|
#include "net/tools/naive/naive_config.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "base/strings/escape.h"
|
#include "base/strings/escape.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
|
#include "base/strings/string_tokenizer.h"
|
||||||
|
#include "net/base/proxy_server.h"
|
||||||
|
#include "net/base/proxy_string_util.h"
|
||||||
#include "net/base/url_util.h"
|
#include "net/base/url_util.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
|
namespace {
|
||||||
|
ProxyServer MyProxyUriToProxyServer(std::string_view uri) {
|
||||||
|
if (uri.compare(0, 7, "quic://") == 0) {
|
||||||
|
return ProxySchemeHostAndPortToProxyServer(ProxyServer::SCHEME_QUIC,
|
||||||
|
uri.substr(7));
|
||||||
|
}
|
||||||
|
return ProxyUriToProxyServer(uri, ProxyServer::SCHEME_INVALID);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
NaiveListenConfig::NaiveListenConfig() = default;
|
NaiveListenConfig::NaiveListenConfig() = default;
|
||||||
NaiveListenConfig::NaiveListenConfig(const NaiveListenConfig&) = default;
|
NaiveListenConfig::NaiveListenConfig(const NaiveListenConfig&) = default;
|
||||||
@ -114,22 +127,79 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
|||||||
|
|
||||||
if (const base::Value* v = value.Find("proxy")) {
|
if (const base::Value* v = value.Find("proxy")) {
|
||||||
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
|
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
|
||||||
GURL url(*str);
|
base::StringTokenizer proxy_uri_list(*str, ",");
|
||||||
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
|
std::vector<ProxyServer> proxy_servers;
|
||||||
|
bool seen_tcp = false;
|
||||||
|
while (proxy_uri_list.GetNext()) {
|
||||||
|
std::string token(proxy_uri_list.token());
|
||||||
|
GURL url(token);
|
||||||
|
|
||||||
GURL::Replacements remove_auth;
|
std::u16string proxy_user;
|
||||||
remove_auth.ClearUsername();
|
std::u16string proxy_pass;
|
||||||
remove_auth.ClearPassword();
|
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
|
||||||
GURL url_no_auth = url.ReplaceComponents(remove_auth);
|
GURL::Replacements remove_auth;
|
||||||
proxy_url = url_no_auth.GetWithEmptyPath().spec();
|
remove_auth.ClearUsername();
|
||||||
if (proxy_url.empty()) {
|
remove_auth.ClearPassword();
|
||||||
std::cerr << "Invalid proxy" << std::endl;
|
GURL url_no_auth = url.ReplaceComponents(remove_auth);
|
||||||
|
std::string proxy_uri = url_no_auth.GetWithEmptyPath().spec();
|
||||||
|
if (proxy_uri.back() == '/') {
|
||||||
|
proxy_uri.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_servers.emplace_back(MyProxyUriToProxyServer(proxy_uri));
|
||||||
|
const ProxyServer& last = proxy_servers.back();
|
||||||
|
if (last.is_quic()) {
|
||||||
|
if (seen_tcp) {
|
||||||
|
std::cerr << "QUIC proxy cannot follow TCP-based proxies"
|
||||||
|
<< std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
origins_to_force_quic_on.insert(HostPortPair::FromURL(url));
|
||||||
|
} else if (last.is_https() || last.is_http()) {
|
||||||
|
seen_tcp = true;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Invalid proxy scheme" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthCredentials auth(proxy_user, proxy_pass);
|
||||||
|
if (!auth.Empty()) {
|
||||||
|
if (last.is_socks()) {
|
||||||
|
std::cerr << "SOCKS proxy with auth is not supported" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::string proxy_url(token);
|
||||||
|
if (proxy_url.compare(0, 7, "quic://") == 0) {
|
||||||
|
proxy_url.replace(0, 4, "https");
|
||||||
|
}
|
||||||
|
auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proxy_servers.size() > 1 &&
|
||||||
|
std::any_of(proxy_servers.begin(), proxy_servers.end(),
|
||||||
|
[](const ProxyServer& s) { return s.is_socks(); })) {
|
||||||
|
// See net/socket/connect_job_params_factory.cc
|
||||||
|
// DCHECK(proxy_server.is_socks());
|
||||||
|
// DCHECK_EQ(1u, proxy_chain.length());
|
||||||
|
std::cerr
|
||||||
|
<< "Multi-proxy chain containing SOCKS proxies is not supported."
|
||||||
|
<< std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (std::any_of(proxy_servers.begin(), proxy_servers.end(),
|
||||||
|
[](const ProxyServer& s) { return s.is_quic(); })) {
|
||||||
|
proxy_chain = ProxyChain::ForIpProtection(proxy_servers);
|
||||||
|
} else {
|
||||||
|
proxy_chain = ProxyChain(proxy_servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proxy_chain.IsValid()) {
|
||||||
|
std::cerr << "Invalid proxy chain" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
} else if (proxy_url.back() == '/') {
|
|
||||||
proxy_url.pop_back();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Invalid proxy" << std::endl;
|
std::cerr << "Invalid proxy argument" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,22 @@
|
|||||||
#ifndef NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
#ifndef NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
||||||
#define NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
#define NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/values.h"
|
#include "base/values.h"
|
||||||
|
#include "net/base/auth.h"
|
||||||
|
#include "net/base/host_port_pair.h"
|
||||||
#include "net/base/ip_address.h"
|
#include "net/base/ip_address.h"
|
||||||
|
#include "net/base/proxy_chain.h"
|
||||||
#include "net/http/http_request_headers.h"
|
#include "net/http/http_request_headers.h"
|
||||||
#include "net/tools/naive/naive_protocol.h"
|
#include "net/tools/naive/naive_protocol.h"
|
||||||
|
#include "url/scheme_host_port.h"
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
@ -37,10 +43,10 @@ struct NaiveConfig {
|
|||||||
|
|
||||||
HttpRequestHeaders extra_headers;
|
HttpRequestHeaders extra_headers;
|
||||||
|
|
||||||
std::string proxy_url = "direct://";
|
// The last server is assumed to be Naive.
|
||||||
|
ProxyChain proxy_chain = ProxyChain::Direct();
|
||||||
std::u16string proxy_user;
|
std::set<HostPortPair> origins_to_force_quic_on;
|
||||||
std::u16string proxy_pass;
|
std::map<url::SchemeHostPort, AuthCredentials> auth_store;
|
||||||
|
|
||||||
std::string host_resolver_rules;
|
std::string host_resolver_rules;
|
||||||
|
|
||||||
|
@ -175,22 +175,11 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
|||||||
builder.DisableHttpCache();
|
builder.DisableHttpCache();
|
||||||
builder.set_net_log(net_log);
|
builder.set_net_log(net_log);
|
||||||
|
|
||||||
std::string proxy_url = config.proxy_url;
|
|
||||||
bool force_quic = false;
|
|
||||||
if (proxy_url.compare(0, 7, "quic://") == 0) {
|
|
||||||
proxy_url.replace(0, 4, "https");
|
|
||||||
force_quic = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyConfig proxy_config;
|
ProxyConfig proxy_config;
|
||||||
proxy_config.proxy_rules().ParseFromString(proxy_url);
|
proxy_config.proxy_rules().type =
|
||||||
if (force_quic) {
|
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
|
||||||
const ProxyServer& proxy_server =
|
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
||||||
proxy_config.proxy_rules().single_proxies.First().First();
|
config.proxy_chain);
|
||||||
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
|
||||||
ProxyChain::ForIpProtection({ProxyServer(
|
|
||||||
ProxyServer::Scheme::SCHEME_QUIC, proxy_server.host_port_pair())}));
|
|
||||||
}
|
|
||||||
LOG(INFO) << "Proxying via "
|
LOG(INFO) << "Proxying via "
|
||||||
<< proxy_config.proxy_rules().single_proxies.ToDebugString();
|
<< proxy_config.proxy_rules().single_proxies.ToDebugString();
|
||||||
auto proxy_service =
|
auto proxy_service =
|
||||||
@ -209,7 +198,7 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
|||||||
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
|
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
|
||||||
|
|
||||||
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(
|
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(
|
||||||
config.extra_headers,
|
config.extra_headers,
|
||||||
std::vector<PaddingType>{PaddingType::kVariant1, PaddingType::kNone}));
|
std::vector<PaddingType>{PaddingType::kVariant1, PaddingType::kNone}));
|
||||||
|
|
||||||
if (config.no_post_quantum == true) {
|
if (config.no_post_quantum == true) {
|
||||||
@ -229,22 +218,20 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
|||||||
|
|
||||||
auto context = builder.Build();
|
auto context = builder.Build();
|
||||||
|
|
||||||
if (!config.proxy_url.empty() && !config.proxy_user.empty() &&
|
if (!config.origins_to_force_quic_on.empty()) {
|
||||||
!config.proxy_pass.empty()) {
|
auto* quic = context->quic_context()->params();
|
||||||
|
quic->supported_versions = {quic::ParsedQuicVersion::RFCv1()};
|
||||||
|
quic->origins_to_force_quic_on.insert(
|
||||||
|
config.origins_to_force_quic_on.begin(),
|
||||||
|
config.origins_to_force_quic_on.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [k, v] : config.auth_store) {
|
||||||
auto* session = context->http_transaction_factory()->GetSession();
|
auto* session = context->http_transaction_factory()->GetSession();
|
||||||
auto* auth_cache = session->http_auth_cache();
|
auto* auth_cache = session->http_auth_cache();
|
||||||
GURL proxy_gurl(proxy_url);
|
auth_cache->Add(k, HttpAuth::AUTH_PROXY,
|
||||||
if (force_quic) {
|
|
||||||
auto* quic = context->quic_context()->params();
|
|
||||||
quic->supported_versions = {quic::ParsedQuicVersion::RFCv1()};
|
|
||||||
quic->origins_to_force_quic_on.insert(
|
|
||||||
net::HostPortPair::FromURL(proxy_gurl));
|
|
||||||
}
|
|
||||||
url::SchemeHostPort auth_origin(proxy_gurl);
|
|
||||||
AuthCredentials credentials(config.proxy_user, config.proxy_pass);
|
|
||||||
auth_cache->Add(auth_origin, HttpAuth::AUTH_PROXY,
|
|
||||||
/*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {},
|
/*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {},
|
||||||
/*challenge=*/"Basic", credentials, /*path=*/"/");
|
/*challenge=*/"Basic", v, /*path=*/"/");
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
@ -72,8 +72,13 @@ Error NaiveProxyDelegate::OnBeforeTunnelRequest(
|
|||||||
// protocols.
|
// protocols.
|
||||||
if (proxy_chain.is_direct())
|
if (proxy_chain.is_direct())
|
||||||
return OK;
|
return OK;
|
||||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
|
||||||
if (proxy_chain.GetProxyServer(chain_index).is_socks())
|
if (proxy_server.is_socks())
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
// Only the last server is attempted for padding
|
||||||
|
// because proxy chaining will corrupt the padding.
|
||||||
|
if (chain_index != proxy_chain.length() - 1)
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
// Sends client-side padding header regardless of server support
|
// Sends client-side padding header regardless of server support
|
||||||
@ -83,7 +88,7 @@ Error NaiveProxyDelegate::OnBeforeTunnelRequest(
|
|||||||
|
|
||||||
// Enables Fast Open in H2/H3 proxy client socket once the state of server
|
// Enables Fast Open in H2/H3 proxy client socket once the state of server
|
||||||
// padding support is known.
|
// padding support is known.
|
||||||
if (padding_type_by_server_[proxy_chain].has_value()) {
|
if (padding_type_by_server_[proxy_server].has_value()) {
|
||||||
extra_headers->SetHeader("fastopen", "1");
|
extra_headers->SetHeader("fastopen", "1");
|
||||||
}
|
}
|
||||||
extra_headers->MergeFrom(extra_headers_);
|
extra_headers->MergeFrom(extra_headers_);
|
||||||
@ -123,8 +128,13 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
|||||||
// protocols.
|
// protocols.
|
||||||
if (proxy_chain.is_direct())
|
if (proxy_chain.is_direct())
|
||||||
return OK;
|
return OK;
|
||||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
|
||||||
if (proxy_chain.GetProxyServer(chain_index).is_socks())
|
if (proxy_server.is_socks())
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
// Only the last server is attempted for padding
|
||||||
|
// because proxy chaining will corrupt the padding.
|
||||||
|
if (chain_index != proxy_chain.length() - 1)
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
// Detects server padding support, even if it changes dynamically.
|
// Detects server padding support, even if it changes dynamically.
|
||||||
@ -134,26 +144,23 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
|||||||
return ERR_INVALID_RESPONSE;
|
return ERR_INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
std::optional<PaddingType>& padding_type =
|
std::optional<PaddingType>& padding_type =
|
||||||
padding_type_by_server_[proxy_chain];
|
padding_type_by_server_[proxy_server];
|
||||||
if (!padding_type.has_value() || padding_type != new_padding_type) {
|
if (!padding_type.has_value() || padding_type != new_padding_type) {
|
||||||
LOG(INFO) << proxy_chain.ToDebugString() << " negotiated padding type: "
|
LOG(INFO) << ProxyServerToProxyUri(proxy_server)
|
||||||
|
<< " negotiated padding type: "
|
||||||
<< ToReadableString(*new_padding_type);
|
<< ToReadableString(*new_padding_type);
|
||||||
padding_type = new_padding_type;
|
padding_type = new_padding_type;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<PaddingType> NaiveProxyDelegate::GetProxyServerPaddingType(
|
std::optional<PaddingType> NaiveProxyDelegate::GetProxyChainPaddingType(
|
||||||
const ProxyChain& proxy_chain) {
|
const ProxyChain& proxy_chain) {
|
||||||
// Not possible to negotiate padding capability given the underlying
|
// Not possible to negotiate padding capability given the underlying
|
||||||
// protocols.
|
// protocols.
|
||||||
if (proxy_chain.is_direct())
|
if (proxy_chain.is_direct())
|
||||||
return PaddingType::kNone;
|
return PaddingType::kNone;
|
||||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
return padding_type_by_server_[proxy_chain.Last()];
|
||||||
if (proxy_chain.GetProxyServer(0).is_socks())
|
|
||||||
return PaddingType::kNone;
|
|
||||||
|
|
||||||
return padding_type_by_server_[proxy_chain];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PaddingDetectorDelegate::PaddingDetectorDelegate(
|
PaddingDetectorDelegate::PaddingDetectorDelegate(
|
||||||
@ -186,7 +193,7 @@ std::optional<PaddingType> PaddingDetectorDelegate::GetServerPaddingType() {
|
|||||||
if (cached_server_padding_type_.has_value())
|
if (cached_server_padding_type_.has_value())
|
||||||
return cached_server_padding_type_;
|
return cached_server_padding_type_;
|
||||||
cached_server_padding_type_ =
|
cached_server_padding_type_ =
|
||||||
naive_proxy_delegate_->GetProxyServerPaddingType(proxy_chain_);
|
naive_proxy_delegate_->GetProxyChainPaddingType(proxy_chain_);
|
||||||
return cached_server_padding_type_;
|
return cached_server_padding_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
|||||||
|
|
||||||
// This only affects h2 proxy client socket.
|
// This only affects h2 proxy client socket.
|
||||||
Error OnBeforeTunnelRequest(const ProxyChain& proxy_chain,
|
Error OnBeforeTunnelRequest(const ProxyChain& proxy_chain,
|
||||||
size_t chain_index,
|
size_t chain_index,
|
||||||
HttpRequestHeaders* extra_headers) override;
|
HttpRequestHeaders* extra_headers) override;
|
||||||
|
|
||||||
Error OnTunnelHeadersReceived(
|
Error OnTunnelHeadersReceived(
|
||||||
const ProxyChain& proxy_chain,
|
const ProxyChain& proxy_chain,
|
||||||
@ -56,7 +56,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
|||||||
ProxyResolutionService* proxy_resolution_service) override {}
|
ProxyResolutionService* proxy_resolution_service) override {}
|
||||||
|
|
||||||
// Returns empty if the padding type has not been negotiated.
|
// Returns empty if the padding type has not been negotiated.
|
||||||
std::optional<PaddingType> GetProxyServerPaddingType(
|
std::optional<PaddingType> GetProxyChainPaddingType(
|
||||||
const ProxyChain& proxy_chain);
|
const ProxyChain& proxy_chain);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -66,7 +66,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
|||||||
HttpRequestHeaders extra_headers_;
|
HttpRequestHeaders extra_headers_;
|
||||||
|
|
||||||
// Empty value means padding type has not been negotiated.
|
// Empty value means padding type has not been negotiated.
|
||||||
std::map<ProxyChain, std::optional<PaddingType>> padding_type_by_server_;
|
std::map<ProxyServer, std::optional<PaddingType>> padding_type_by_server_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientPaddingDetectorDelegate {
|
class ClientPaddingDetectorDelegate {
|
||||||
|
Loading…
Reference in New Issue
Block a user