Support specifying multiple proxies
This commit is contained in:
@ -64,8 +64,12 @@ Options:
|
|||||||
SOCKS-PROXY = "socks://"<HOSTNAME>[":"<PORT>]
|
SOCKS-PROXY = "socks://"<HOSTNAME>[":"<PORT>]
|
||||||
|
|
||||||
Routes traffic via the proxy chain.
|
Routes traffic via the proxy chain.
|
||||||
The default proxy is directly connection without proxying.
|
The default proxy is a direct connection without proxying.
|
||||||
The last PROXY-URI is negotiated automatically for Naive padding.
|
The last PROXY-URI is negotiated automatically for Naive padding.
|
||||||
|
|
||||||
|
If multiple proxies are specified, they must match the number of specified
|
||||||
|
LISTEN-URIs, and each LISTEN-URI is routed to the PROXY matched by position.
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
* QUIC proxies cannot follow TCP-based proxies in a proxy chain.
|
* QUIC proxies cannot follow TCP-based proxies in a proxy chain.
|
||||||
* The user needs to ensure there is no loop in the proxy chain.
|
* The user needs to ensure there is no loop in the proxy chain.
|
||||||
|
|||||||
@ -76,17 +76,13 @@ NaiveConfig::~NaiveConfig() = default;
|
|||||||
|
|
||||||
bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
||||||
if (const base::Value* v = value.Find("listen")) {
|
if (const base::Value* v = value.Find("listen")) {
|
||||||
listen.clear();
|
std::vector<std::string> listen_strs;
|
||||||
if (const std::string* str = v->GetIfString()) {
|
if (const std::string* str = v->GetIfString()) {
|
||||||
if (!listen.emplace_back().Parse(*str)) {
|
listen_strs.push_back(*str);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (const base::Value::List* strs = v->GetIfList()) {
|
} else if (const base::Value::List* strs = v->GetIfList()) {
|
||||||
for (const auto& str_e : *strs) {
|
for (const auto& str_e : *strs) {
|
||||||
if (const std::string* s = str_e.GetIfString()) {
|
if (const std::string* s = str_e.GetIfString()) {
|
||||||
if (!listen.emplace_back().Parse(*s)) {
|
listen_strs.push_back(*s);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Invalid listen element" << std::endl;
|
std::cerr << "Invalid listen element" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -96,6 +92,14 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
|||||||
std::cerr << "Invalid listen" << std::endl;
|
std::cerr << "Invalid listen" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!listen_strs.empty()) {
|
||||||
|
listen.clear();
|
||||||
|
}
|
||||||
|
for (const std::string& str : listen_strs) {
|
||||||
|
if (!listen.emplace_back().Parse(str)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const base::Value* v = value.Find("insecure-concurrency")) {
|
if (const base::Value* v = value.Find("insecure-concurrency")) {
|
||||||
@ -126,8 +130,24 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (const base::Value* v = value.Find("proxy")) {
|
if (const base::Value* v = value.Find("proxy")) {
|
||||||
|
std::vector<std::string> proxy_strs;
|
||||||
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
|
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
|
||||||
base::StringTokenizer proxy_uri_list(*str, ",");
|
proxy_strs.push_back(*str);
|
||||||
|
} else if (const base::Value::List* strs = v->GetIfList()) {
|
||||||
|
for (const auto& str_e : *strs) {
|
||||||
|
if (const std::string* s = str_e.GetIfString(); s && !s->empty()) {
|
||||||
|
proxy_strs.push_back(*s);
|
||||||
|
} else {
|
||||||
|
std::cerr << "Invalid proxy element" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Invalid proxy argument" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const std::string& str : proxy_strs) {
|
||||||
|
base::StringTokenizer proxy_uri_list(str, ",");
|
||||||
std::vector<ProxyServer> proxy_servers;
|
std::vector<ProxyServer> proxy_servers;
|
||||||
bool seen_tcp = false;
|
bool seen_tcp = false;
|
||||||
while (proxy_uri_list.GetNext()) {
|
while (proxy_uri_list.GetNext()) {
|
||||||
@ -187,6 +207,7 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ProxyChain proxy_chain;
|
||||||
if (std::any_of(proxy_servers.begin(), proxy_servers.end(),
|
if (std::any_of(proxy_servers.begin(), proxy_servers.end(),
|
||||||
[](const ProxyServer& s) { return s.is_quic(); })) {
|
[](const ProxyServer& s) { return s.is_quic(); })) {
|
||||||
proxy_chain = ProxyChain::ForIpProtection(proxy_servers);
|
proxy_chain = ProxyChain::ForIpProtection(proxy_servers);
|
||||||
@ -198,9 +219,7 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
|
|||||||
std::cerr << "Invalid proxy chain" << std::endl;
|
std::cerr << "Invalid proxy chain" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
proxy_chains.push_back(proxy_chain);
|
||||||
std::cerr << "Invalid proxy argument" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ struct NaiveConfig {
|
|||||||
HttpRequestHeaders extra_headers;
|
HttpRequestHeaders extra_headers;
|
||||||
|
|
||||||
// The last server is assumed to be Naive.
|
// The last server is assumed to be Naive.
|
||||||
ProxyChain proxy_chain = ProxyChain::Direct();
|
std::vector<ProxyChain> proxy_chains;
|
||||||
std::set<HostPortPair> origins_to_force_quic_on;
|
std::set<HostPortPair> origins_to_force_quic_on;
|
||||||
std::map<url::SchemeHostPort, AuthCredentials> auth_store;
|
std::map<url::SchemeHostPort, AuthCredentials> auth_store;
|
||||||
|
|
||||||
|
|||||||
@ -172,7 +172,8 @@ std::unique_ptr<URLRequestContext> BuildCertURLRequestContext(NetLog* net_log) {
|
|||||||
std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
||||||
const NaiveConfig& config,
|
const NaiveConfig& config,
|
||||||
scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher,
|
scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher,
|
||||||
NetLog* net_log) {
|
NetLog* net_log,
|
||||||
|
int proxy_chain_index = 0) {
|
||||||
URLRequestContextBuilder builder;
|
URLRequestContextBuilder builder;
|
||||||
|
|
||||||
builder.DisableHttpCache();
|
builder.DisableHttpCache();
|
||||||
@ -212,8 +213,13 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
|||||||
ProxyConfig proxy_config;
|
ProxyConfig proxy_config;
|
||||||
proxy_config.proxy_rules().type =
|
proxy_config.proxy_rules().type =
|
||||||
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
|
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
|
||||||
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
if (config.proxy_chains.empty()) {
|
||||||
config.proxy_chain);
|
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
||||||
|
ProxyChain::Direct());
|
||||||
|
} else {
|
||||||
|
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
|
||||||
|
config.proxy_chains.at(proxy_chain_index));
|
||||||
|
}
|
||||||
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 =
|
||||||
@ -456,14 +462,20 @@ int main(int argc, char* argv[]) {
|
|||||||
cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
|
cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
|
||||||
cert_net_fetcher->SetURLRequestContext(cert_context.get());
|
cert_net_fetcher->SetURLRequestContext(cert_context.get());
|
||||||
#endif
|
#endif
|
||||||
auto context =
|
|
||||||
net::BuildURLRequestContext(config, std::move(cert_net_fetcher), net_log);
|
if (config.proxy_chains.size() >= 2 &&
|
||||||
auto* session = context->http_transaction_factory()->GetSession();
|
config.proxy_chains.size() != config.listen.size()) {
|
||||||
|
std::cerr << "Listen addresses do not match multiple proxy chains"
|
||||||
|
<< std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<net::NaiveProxy>> naive_proxies;
|
std::vector<std::unique_ptr<net::NaiveProxy>> naive_proxies;
|
||||||
|
std::vector<std::unique_ptr<net::URLRequestContext>> contexts;
|
||||||
std::unique_ptr<net::RedirectResolver> resolver;
|
std::unique_ptr<net::RedirectResolver> resolver;
|
||||||
|
|
||||||
for (const net::NaiveListenConfig& listen_config : config.listen) {
|
for (size_t listen_i = 0; listen_i < config.listen.size(); ++listen_i) {
|
||||||
|
const net::NaiveListenConfig& listen_config = config.listen[listen_i];
|
||||||
auto listen_socket =
|
auto listen_socket =
|
||||||
std::make_unique<net::TCPServerSocket>(net_log, net::NetLogSource());
|
std::make_unique<net::TCPServerSocket>(net_log, net::NetLogSource());
|
||||||
|
|
||||||
@ -503,6 +515,15 @@ int main(int argc, char* argv[]) {
|
|||||||
config.resolver_prefix);
|
config.resolver_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.proxy_chains.size() >= 2) {
|
||||||
|
contexts.push_back(net::BuildURLRequestContext(
|
||||||
|
config, std::move(cert_net_fetcher), net_log, listen_i));
|
||||||
|
} else if (contexts.empty()) {
|
||||||
|
contexts.push_back(net::BuildURLRequestContext(
|
||||||
|
config, std::move(cert_net_fetcher), net_log));
|
||||||
|
}
|
||||||
|
auto& context = contexts.back();
|
||||||
|
auto* session = context->http_transaction_factory()->GetSession();
|
||||||
auto naive_proxy = std::make_unique<net::NaiveProxy>(
|
auto naive_proxy = std::make_unique<net::NaiveProxy>(
|
||||||
std::move(listen_socket), listen_config.protocol, listen_config.user,
|
std::move(listen_socket), listen_config.protocol, listen_config.user,
|
||||||
listen_config.pass, config.insecure_concurrency, resolver.get(),
|
listen_config.pass, config.insecure_concurrency, resolver.get(),
|
||||||
|
|||||||
@ -245,6 +245,16 @@ test_naive('Multiple listens - config file', 'http://127.0.0.1:{PORT2}',
|
|||||||
config_content='"listen":["socks://:{PORT1}", "http://:{PORT2}"],"log":""',
|
config_content='"listen":["socks://:{PORT1}", "http://:{PORT2}"],"log":""',
|
||||||
config_file='multiple-listen.json')
|
config_file='multiple-listen.json')
|
||||||
|
|
||||||
|
test_naive('Multiple proxies - command line', 'socks5h://127.0.0.1:{PORT1}',
|
||||||
|
'--log --listen=socks://:{PORT1} --listen=socks://:{PORT2} --proxy=http://127.0.0.1:{PORT3} --proxy=http://127.0.0.1:{PORT4}',
|
||||||
|
'--log --listen=http://:{PORT3} --listen=http://:{PORT4} --proxy=socks://127.0.0.1:{PORT5}',
|
||||||
|
'--log --listen=socks://:{PORT5}')
|
||||||
|
|
||||||
|
test_naive('Multiple proxies - command line', 'socks5h://127.0.0.1:{PORT2}',
|
||||||
|
'--log --listen=socks://:{PORT1} --listen=socks://:{PORT2} --proxy=http://127.0.0.1:{PORT3} --proxy=http://127.0.0.1:{PORT4}',
|
||||||
|
'--log --listen=http://:{PORT3} --listen=http://:{PORT4} --proxy=socks://127.0.0.1:{PORT5}',
|
||||||
|
'--log --listen=socks://:{PORT5}')
|
||||||
|
|
||||||
test_naive('Trivial - listen scheme only', 'socks5h://127.0.0.1:1080',
|
test_naive('Trivial - listen scheme only', 'socks5h://127.0.0.1:1080',
|
||||||
'--log --listen=socks://')
|
'--log --listen=socks://')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user