mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 00:06:09 +03:00
Support proxy chaining
This commit is contained in:
parent
9dc63fbb8c
commit
f8c564d3a0
@ -3,14 +3,27 @@
|
||||
// found in the LICENSE file.
|
||||
#include "net/tools/naive/naive_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "base/strings/escape.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 "url/gurl.h"
|
||||
|
||||
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(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 std::string* str = v->GetIfString(); str && !str->empty()) {
|
||||
GURL url(*str);
|
||||
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
|
||||
base::StringTokenizer proxy_uri_list(*str, ",");
|
||||
std::vector<ProxyServer> proxy_servers;
|
||||
bool seen_tcp = false;
|
||||
while (proxy_uri_list.GetNext()) {
|
||||
std::string token(proxy_uri_list.token());
|
||||
GURL url(token);
|
||||
|
||||
std::u16string proxy_user;
|
||||
std::u16string proxy_pass;
|
||||
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
|
||||
GURL::Replacements remove_auth;
|
||||
remove_auth.ClearUsername();
|
||||
remove_auth.ClearPassword();
|
||||
GURL url_no_auth = url.ReplaceComponents(remove_auth);
|
||||
proxy_url = url_no_auth.GetWithEmptyPath().spec();
|
||||
if (proxy_url.empty()) {
|
||||
std::cerr << "Invalid proxy" << std::endl;
|
||||
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;
|
||||
} else if (proxy_url.back() == '/') {
|
||||
proxy_url.pop_back();
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Invalid proxy" << std::endl;
|
||||
std::cerr << "Invalid proxy argument" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,22 @@
|
||||
#ifndef NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
||||
#define NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.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/proxy_chain.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
#include "url/scheme_host_port.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@ -37,10 +43,10 @@ struct NaiveConfig {
|
||||
|
||||
HttpRequestHeaders extra_headers;
|
||||
|
||||
std::string proxy_url = "direct://";
|
||||
|
||||
std::u16string proxy_user;
|
||||
std::u16string proxy_pass;
|
||||
// The last server is assumed to be Naive.
|
||||
ProxyChain proxy_chain = ProxyChain::Direct();
|
||||
std::set<HostPortPair> origins_to_force_quic_on;
|
||||
std::map<url::SchemeHostPort, AuthCredentials> auth_store;
|
||||
|
||||
std::string host_resolver_rules;
|
||||
|
||||
|
@ -175,22 +175,11 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
||||
builder.DisableHttpCache();
|
||||
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;
|
||||
proxy_config.proxy_rules().ParseFromString(proxy_url);
|
||||
if (force_quic) {
|
||||
const ProxyServer& proxy_server =
|
||||
proxy_config.proxy_rules().single_proxies.First().First();
|
||||
proxy_config.proxy_rules().type =
|
||||
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
|
||||
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
||||
ProxyChain::ForIpProtection({ProxyServer(
|
||||
ProxyServer::Scheme::SCHEME_QUIC, proxy_server.host_port_pair())}));
|
||||
}
|
||||
config.proxy_chain);
|
||||
LOG(INFO) << "Proxying via "
|
||||
<< proxy_config.proxy_rules().single_proxies.ToDebugString();
|
||||
auto proxy_service =
|
||||
@ -229,22 +218,20 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
||||
|
||||
auto context = builder.Build();
|
||||
|
||||
if (!config.proxy_url.empty() && !config.proxy_user.empty() &&
|
||||
!config.proxy_pass.empty()) {
|
||||
auto* session = context->http_transaction_factory()->GetSession();
|
||||
auto* auth_cache = session->http_auth_cache();
|
||||
GURL proxy_gurl(proxy_url);
|
||||
if (force_quic) {
|
||||
if (!config.origins_to_force_quic_on.empty()) {
|
||||
auto* quic = context->quic_context()->params();
|
||||
quic->supported_versions = {quic::ParsedQuicVersion::RFCv1()};
|
||||
quic->origins_to_force_quic_on.insert(
|
||||
net::HostPortPair::FromURL(proxy_gurl));
|
||||
config.origins_to_force_quic_on.begin(),
|
||||
config.origins_to_force_quic_on.end());
|
||||
}
|
||||
url::SchemeHostPort auth_origin(proxy_gurl);
|
||||
AuthCredentials credentials(config.proxy_user, config.proxy_pass);
|
||||
auth_cache->Add(auth_origin, HttpAuth::AUTH_PROXY,
|
||||
|
||||
for (const auto& [k, v] : config.auth_store) {
|
||||
auto* session = context->http_transaction_factory()->GetSession();
|
||||
auto* auth_cache = session->http_auth_cache();
|
||||
auth_cache->Add(k, HttpAuth::AUTH_PROXY,
|
||||
/*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {},
|
||||
/*challenge=*/"Basic", credentials, /*path=*/"/");
|
||||
/*challenge=*/"Basic", v, /*path=*/"/");
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -72,8 +72,13 @@ Error NaiveProxyDelegate::OnBeforeTunnelRequest(
|
||||
// protocols.
|
||||
if (proxy_chain.is_direct())
|
||||
return OK;
|
||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
||||
if (proxy_chain.GetProxyServer(chain_index).is_socks())
|
||||
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
|
||||
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;
|
||||
|
||||
// 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
|
||||
// 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->MergeFrom(extra_headers_);
|
||||
@ -123,8 +128,13 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
||||
// protocols.
|
||||
if (proxy_chain.is_direct())
|
||||
return OK;
|
||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
||||
if (proxy_chain.GetProxyServer(chain_index).is_socks())
|
||||
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
|
||||
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;
|
||||
|
||||
// Detects server padding support, even if it changes dynamically.
|
||||
@ -134,26 +144,23 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
||||
return ERR_INVALID_RESPONSE;
|
||||
}
|
||||
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) {
|
||||
LOG(INFO) << proxy_chain.ToDebugString() << " negotiated padding type: "
|
||||
LOG(INFO) << ProxyServerToProxyUri(proxy_server)
|
||||
<< " negotiated padding type: "
|
||||
<< ToReadableString(*new_padding_type);
|
||||
padding_type = new_padding_type;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
std::optional<PaddingType> NaiveProxyDelegate::GetProxyServerPaddingType(
|
||||
std::optional<PaddingType> NaiveProxyDelegate::GetProxyChainPaddingType(
|
||||
const ProxyChain& proxy_chain) {
|
||||
// Not possible to negotiate padding capability given the underlying
|
||||
// protocols.
|
||||
if (proxy_chain.is_direct())
|
||||
return PaddingType::kNone;
|
||||
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
|
||||
if (proxy_chain.GetProxyServer(0).is_socks())
|
||||
return PaddingType::kNone;
|
||||
|
||||
return padding_type_by_server_[proxy_chain];
|
||||
return padding_type_by_server_[proxy_chain.Last()];
|
||||
}
|
||||
|
||||
PaddingDetectorDelegate::PaddingDetectorDelegate(
|
||||
@ -186,7 +193,7 @@ std::optional<PaddingType> PaddingDetectorDelegate::GetServerPaddingType() {
|
||||
if (cached_server_padding_type_.has_value())
|
||||
return 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_;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
||||
ProxyResolutionService* proxy_resolution_service) override {}
|
||||
|
||||
// Returns empty if the padding type has not been negotiated.
|
||||
std::optional<PaddingType> GetProxyServerPaddingType(
|
||||
std::optional<PaddingType> GetProxyChainPaddingType(
|
||||
const ProxyChain& proxy_chain);
|
||||
|
||||
private:
|
||||
@ -66,7 +66,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
||||
HttpRequestHeaders extra_headers_;
|
||||
|
||||
// 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 {
|
||||
|
Loading…
Reference in New Issue
Block a user