Negotiate padding capability automatically

Client: On the first connection does a full Open and detects if the
server supports padding by checking for "Padding" header in the
response. Applies padding if the server does. In the following
connections it's back to Fast Open.

Server: Detects if the client supports padding by checking for "Padding"
header in the CONNECT request. Applies padding if the client does.

Both client and server always send "Padding" headers to somewhat protect
the request and response headers' packet lengths, even if the other side
may not acknowledge padding negotiation, either due to old version or
"Padding" headers being dropped by the frontend.

The manual option --padding is removed.
This commit is contained in:
klzgrad 2020-05-25 00:30:52 +08:00
parent 9ca803e3bb
commit 5e7d36ef98
11 changed files with 384 additions and 155 deletions

View File

@ -2371,6 +2371,8 @@ if (!is_ios && !is_android) {
"tools/naive/naive_proxy.cc",
"tools/naive/naive_proxy.h",
"tools/naive/naive_proxy_bin.cc",
"tools/naive/naive_proxy_delegate.h",
"tools/naive/naive_proxy_delegate.cc",
"tools/naive/http_proxy_socket.cc",
"tools/naive/http_proxy_socket.h",
"tools/naive/redirect_resolver.h",

View File

@ -15,8 +15,10 @@
#include "base/sys_byteorder.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "net/log/net_log.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/tools/naive/naive_proxy_delegate.h"
namespace net {
@ -28,40 +30,22 @@ constexpr int kResponseHeaderSize = sizeof(kResponseHeader) - 1;
// A plain 200 is 10 bytes. Expected 48 bytes. "Padding" uses up 7 bytes.
constexpr int kMinPaddingSize = 30;
constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
bool g_nonindex_codes_initialized;
uint8_t g_nonindex_codes[17];
void InitializeNonindexCodes() {
if (g_nonindex_codes_initialized)
return;
g_nonindex_codes_initialized = true;
unsigned i = 0;
for (const auto& symbol : spdy::HpackHuffmanCodeVector()) {
if (symbol.id >= 0x20 && symbol.id <= 0x7f && symbol.length >= 8) {
g_nonindex_codes[i++] = symbol.id;
if (i >= sizeof(g_nonindex_codes))
break;
}
}
CHECK(i == sizeof(g_nonindex_codes));
}
} // namespace
HttpProxySocket::HttpProxySocket(
std::unique_ptr<StreamSocket> transport_socket,
ClientPaddingDetectorDelegate* padding_detector_delegate,
const NetworkTrafficAnnotationTag& traffic_annotation)
: io_callback_(base::BindRepeating(&HttpProxySocket::OnIOComplete,
base::Unretained(this))),
transport_(std::move(transport_socket)),
padding_detector_delegate_(padding_detector_delegate),
next_state_(STATE_NONE),
completed_handshake_(false),
was_ever_used_(false),
header_write_size_(-1),
net_log_(transport_->NetLog()),
traffic_annotation_(traffic_annotation) {
InitializeNonindexCodes();
}
traffic_annotation_(traffic_annotation) {}
HttpProxySocket::~HttpProxySocket() {
Disconnect();
@ -314,6 +298,21 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
request_endpoint_ = HostPortPair::FromString(
buffer_.substr(first_space + 1, second_space - (first_space + 1)));
auto second_line = first_line_end + 2;
HttpRequestHeaders headers;
std::string headers_str;
if (second_line < header_end) {
headers_str = buffer_.substr(second_line, header_end - second_line);
headers.AddHeadersFromString(headers_str);
}
if (headers.HasHeader("padding")) {
padding_detector_delegate_->SetClientPaddingSupport(
PaddingSupport::kCapable);
} else {
padding_detector_delegate_->SetClientPaddingSupport(
PaddingSupport::kIncapable);
}
buffer_ = buffer_.substr(header_end + 4);
next_state_ = STATE_HEADER_WRITE;
@ -329,13 +328,8 @@ int HttpProxySocket::DoHeaderWrite() {
handshake_buf_ = base::MakeRefCounted<IOBuffer>(header_write_size_);
char* p = handshake_buf_->data();
std::memcpy(p, kResponseHeader, kResponseHeaderSize);
std::memset(p + kResponseHeaderSize, g_nonindex_codes[16], padding_size);
// Prevents index reuse
uint64_t bits = base::RandUint64();
for (int i = 0; i < 16; i++) {
p[kResponseHeaderSize + i] = g_nonindex_codes[bits & 0b1111];
bits >>= 4;
}
FillNonindexHeaderValue(base::RandUint64(), p + kResponseHeaderSize,
padding_size);
std::memcpy(p + kResponseHeaderSize + padding_size, "\r\n\r\n", 4);
return transport_->Write(handshake_buf_.get(), header_write_size_,

View File

@ -26,11 +26,13 @@
namespace net {
struct NetworkTrafficAnnotationTag;
class ClientPaddingDetectorDelegate;
// This StreamSocket is used to setup a HTTP CONNECT tunnel.
class HttpProxySocket : public StreamSocket {
public:
HttpProxySocket(std::unique_ptr<StreamSocket> transport_socket,
ClientPaddingDetectorDelegate* padding_detector_delegate,
const NetworkTrafficAnnotationTag& traffic_annotation);
// On destruction Disconnect() is called.
@ -93,6 +95,7 @@ class HttpProxySocket : public StreamSocket {
// Stores the underlying socket.
std::unique_ptr<StreamSocket> transport_;
ClientPaddingDetectorDelegate* padding_detector_delegate_;
State next_state_;

View File

@ -48,8 +48,8 @@ constexpr int kMaxPaddingSize = 255;
NaiveConnection::NaiveConnection(
unsigned int id,
Protocol protocol,
Direction pad_direction,
ClientProtocol protocol,
std::unique_ptr<PaddingDetectorDelegate> padding_detector_delegate,
const ProxyInfo& proxy_info,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
@ -61,7 +61,7 @@ NaiveConnection::NaiveConnection(
const NetworkTrafficAnnotationTag& traffic_annotation)
: id_(id),
protocol_(protocol),
pad_direction_(pad_direction),
padding_detector_delegate_(std::move(padding_detector_delegate)),
proxy_info_(proxy_info),
server_ssl_config_(server_ssl_config),
proxy_ssl_config_(proxy_ssl_config),
@ -177,6 +177,17 @@ int NaiveConnection::DoConnectClientComplete(int result) {
if (result < 0)
return result;
// For proxy client sockets, padding support detection is finished after the
// first server response which means there will be one missed early pull. For
// proxy server sockets (HttpProxySocket), padding support detection is
// done during client connect, so there shouldn't be any missed early pull.
if (!padding_detector_delegate_->IsPaddingSupportKnown()) {
early_pull_pending_ = false;
early_pull_result_ = 0;
next_state_ = STATE_CONNECT_SERVER;
return OK;
}
early_pull_pending_ = true;
Pull(kClient, kServer);
if (early_pull_result_ != ERR_IO_PENDING) {
@ -194,15 +205,15 @@ int NaiveConnection::DoConnectServer() {
next_state_ = STATE_CONNECT_SERVER_COMPLETE;
HostPortPair origin;
if (protocol_ == kSocks5) {
if (protocol_ == ClientProtocol::kSocks5) {
const auto* socket =
static_cast<const Socks5ServerSocket*>(client_socket_.get());
origin = socket->request_endpoint();
} else if (protocol_ == kHttp) {
} else if (protocol_ == ClientProtocol::kHttp) {
const auto* socket =
static_cast<const HttpProxySocket*>(client_socket_.get());
origin = socket->request_endpoint();
} else if (protocol_ == kRedir) {
} else if (protocol_ == ClientProtocol::kRedir) {
#if defined(OS_LINUX)
const auto* socket =
static_cast<const TCPClientSocket*>(client_socket_.get());
@ -280,7 +291,11 @@ int NaiveConnection::Run(CompletionOnceCallback callback) {
yield_after_time_[kServer] = yield_after_time_[kClient];
can_push_to_server_ = true;
if (!early_pull_pending_) {
// early_pull_result_ == 0 means the early pull was not started because
// padding support was not yet known.
if (!early_pull_pending_ && early_pull_result_ == 0) {
Pull(kClient, kServer);
} else if (!early_pull_pending_) {
DCHECK_GT(early_pull_result_, 0);
Push(kClient, kServer, early_pull_result_);
}
@ -294,7 +309,8 @@ void NaiveConnection::Pull(Direction from, Direction to) {
return;
int read_size = kBufferSize;
if (from == pad_direction_ && num_paddings_[from] < kFirstPaddings) {
auto padding_direction = padding_detector_delegate_->GetPaddingDirection();
if (from == padding_direction && num_paddings_[from] < kFirstPaddings) {
auto buffer = base::MakeRefCounted<GrowableIOBuffer>();
buffer->SetCapacity(kBufferSize);
buffer->set_offset(kPaddingHeaderSize);
@ -320,7 +336,8 @@ void NaiveConnection::Pull(Direction from, Direction to) {
void NaiveConnection::Push(Direction from, Direction to, int size) {
int write_size = size;
int write_offset = 0;
if (from == pad_direction_ && num_paddings_[from] < kFirstPaddings) {
auto padding_direction = padding_detector_delegate_->GetPaddingDirection();
if (from == padding_direction && num_paddings_[from] < kFirstPaddings) {
// Adds padding.
++num_paddings_[from];
int padding_size = base::RandInt(0, kMaxPaddingSize);
@ -332,7 +349,7 @@ void NaiveConnection::Push(Direction from, Direction to, int size) {
p[2] = padding_size;
std::memset(p + kPaddingHeaderSize + size, 0, padding_size);
write_size = kPaddingHeaderSize + size + padding_size;
} else if (to == pad_direction_ && num_paddings_[from] < kFirstPaddings) {
} else if (to == padding_direction && num_paddings_[from] < kFirstPaddings) {
// Removes padding.
const char* p = read_buffers_[from]->data();
bool trivial_padding = false;
@ -482,7 +499,7 @@ void NaiveConnection::OnPushError(Direction from, Direction to, int error) {
void NaiveConnection::OnPullComplete(Direction from, Direction to, int result) {
if (from == kClient && early_pull_pending_) {
early_pull_pending_ = false;
early_pull_result_ = result;
early_pull_result_ = result ? result : ERR_CONNECTION_CLOSED;
}
if (result <= 0) {

View File

@ -15,6 +15,8 @@
#include "base/time/time.h"
#include "net/base/completion_once_callback.h"
#include "net/base/completion_repeating_callback.h"
#include "net/tools/naive/naive_protocol.h"
#include "net/tools/naive/naive_proxy_delegate.h"
namespace net {
@ -34,32 +36,19 @@ class NaiveConnection {
public:
using TimeFunc = base::TimeTicks (*)();
enum Protocol {
kSocks5,
kHttp,
kRedir,
};
// From this direction.
enum Direction {
kClient = 0,
kServer = 1,
kNumDirections = 2,
kNone = 2,
};
NaiveConnection(unsigned int id,
Protocol protocol,
Direction pad_direction,
const ProxyInfo& proxy_info,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
RedirectResolver* resolver,
HttpNetworkSession* session,
const NetworkIsolationKey& network_isolation_key,
const NetLogWithSource& net_log,
std::unique_ptr<StreamSocket> accepted_socket,
const NetworkTrafficAnnotationTag& traffic_annotation);
NaiveConnection(
unsigned int id,
ClientProtocol protocol,
std::unique_ptr<PaddingDetectorDelegate> padding_detector_delegate,
const ProxyInfo& proxy_info,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
RedirectResolver* resolver,
HttpNetworkSession* session,
const NetworkIsolationKey& network_isolation_key,
const NetLogWithSource& net_log,
std::unique_ptr<StreamSocket> accepted_socket,
const NetworkTrafficAnnotationTag& traffic_annotation);
~NaiveConnection();
unsigned int id() const { return id_; }
@ -102,8 +91,8 @@ class NaiveConnection {
void OnPushComplete(Direction from, Direction to, int result);
unsigned int id_;
Protocol protocol_;
Direction pad_direction_;
ClientProtocol protocol_;
std::unique_ptr<PaddingDetectorDelegate> padding_detector_delegate_;
const ProxyInfo& proxy_info_;
const SSLConfig& server_ssl_config_;
const SSLConfig& proxy_ssl_config_;

View File

@ -0,0 +1,24 @@
// Copyright 2020 klzgrad <kizdiv@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
#define NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
namespace net {
enum class ClientProtocol {
kSocks5,
kHttp,
kRedir,
};
// Adds padding for traffic from this direction.
// Removes padding for traffic from the opposite direction.
enum Direction {
kClient = 0,
kServer = 1,
kNumDirections = 2,
kNone = 2,
};
} // namespace net
#endif // NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_

View File

@ -22,20 +22,19 @@
#include "net/socket/server_socket.h"
#include "net/socket/stream_socket.h"
#include "net/tools/naive/http_proxy_socket.h"
#include "net/tools/naive/naive_proxy_delegate.h"
#include "net/tools/naive/socks5_server_socket.h"
namespace net {
NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
NaiveConnection::Protocol protocol,
bool use_padding,
ClientProtocol protocol,
int concurrency,
RedirectResolver* resolver,
HttpNetworkSession* session,
const NetworkTrafficAnnotationTag& traffic_annotation)
: listen_socket_(std::move(listen_socket)),
protocol_(protocol),
use_padding_(use_padding),
concurrency_(std::min(4, std::max(1, concurrency))),
resolver_(resolver),
session_(session),
@ -99,30 +98,33 @@ void NaiveProxy::HandleAcceptResult(int result) {
void NaiveProxy::DoConnect() {
std::unique_ptr<StreamSocket> socket;
NaiveConnection::Direction pad_direction;
if (protocol_ == NaiveConnection::kSocks5) {
auto* proxy_delegate =
static_cast<NaiveProxyDelegate*>(session_->context().proxy_delegate);
DCHECK(proxy_delegate);
DCHECK(!proxy_info_.is_empty());
const auto& proxy_server = proxy_info_.proxy_server();
auto padding_detector_delegate = std::make_unique<PaddingDetectorDelegate>(
proxy_delegate, proxy_server, protocol_);
if (protocol_ == ClientProtocol::kSocks5) {
socket = std::make_unique<Socks5ServerSocket>(std::move(accepted_socket_),
traffic_annotation_);
pad_direction = NaiveConnection::kClient;
} else if (protocol_ == NaiveConnection::kHttp) {
} else if (protocol_ == ClientProtocol::kHttp) {
socket = std::make_unique<HttpProxySocket>(std::move(accepted_socket_),
padding_detector_delegate.get(),
traffic_annotation_);
pad_direction = NaiveConnection::kServer;
} else if (protocol_ == NaiveConnection::kRedir) {
} else if (protocol_ == ClientProtocol::kRedir) {
socket = std::move(accepted_socket_);
pad_direction = NaiveConnection::kClient;
} else {
return;
}
if (!use_padding_) {
pad_direction = NaiveConnection::kNone;
}
last_id_++;
const auto& nik = network_isolation_keys_[last_id_ % concurrency_];
auto connection_ptr = std::make_unique<NaiveConnection>(
last_id_, protocol_, pad_direction, proxy_info_, server_ssl_config_,
proxy_ssl_config_, resolver_, session_, nik, net_log_, std::move(socket),
traffic_annotation_);
last_id_, protocol_, std::move(padding_detector_delegate), proxy_info_,
server_ssl_config_, proxy_ssl_config_, resolver_, session_, nik, net_log_,
std::move(socket), traffic_annotation_);
auto* connection = connection_ptr.get();
connection_by_id_[connection->id()] = std::move(connection_ptr);
int result = connection->Connect(

View File

@ -18,6 +18,7 @@
#include "net/proxy_resolution/proxy_info.h"
#include "net/ssl/ssl_config.h"
#include "net/tools/naive/naive_connection.h"
#include "net/tools/naive/naive_protocol.h"
namespace net {
@ -32,8 +33,7 @@ class RedirectResolver;
class NaiveProxy {
public:
NaiveProxy(std::unique_ptr<ServerSocket> server_socket,
NaiveConnection::Protocol protocol,
bool use_padding,
ClientProtocol protocol,
int concurrency,
RedirectResolver* resolver,
HttpNetworkSession* session,
@ -58,8 +58,7 @@ class NaiveProxy {
NaiveConnection* FindConnection(unsigned int connection_id);
std::unique_ptr<ServerSocket> listen_socket_;
NaiveConnection::Protocol protocol_;
bool use_padding_;
ClientProtocol protocol_;
int concurrency_;
ProxyInfo proxy_info_;
SSLConfig server_ssl_config_;

View File

@ -40,7 +40,6 @@
#include "net/http/http_auth_cache.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_transaction_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log.h"
@ -58,8 +57,9 @@
#include "net/socket/udp_server_socket.h"
#include "net/ssl/ssl_key_logger_impl.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/tools/naive/naive_protocol.h"
#include "net/tools/naive/naive_proxy.h"
#include "net/tools/naive/naive_proxy_delegate.h"
#include "net/tools/naive/redirect_resolver.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request_context.h"
@ -84,7 +84,6 @@ constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
struct CommandLine {
std::string listen;
std::string proxy;
bool padding;
std::string concurrency;
std::string extra_headers;
std::string host_resolver_rules;
@ -96,10 +95,9 @@ struct CommandLine {
};
struct Params {
net::NaiveConnection::Protocol protocol;
net::ClientProtocol protocol;
std::string listen_addr;
int listen_port;
bool use_padding;
int concurrency;
net::HttpRequestHeaders extra_headers;
std::string proxy_url;
@ -139,7 +137,6 @@ void GetCommandLine(const base::CommandLine& proc, CommandLine* cmdline) {
" redir (Linux only)\n"
"--proxy=<proto>://[<user>:<pass>@]<hostname>[:<port>]\n"
" proto: https, quic\n"
"--padding Use padding\n"
"--concurrency=<N> Use N connections, less secure\n"
"--extra-headers=... Extra headers split by CRLF\n"
"--host-resolver-rules=... Resolver rules\n"
@ -158,7 +155,6 @@ void GetCommandLine(const base::CommandLine& proc, CommandLine* cmdline) {
cmdline->listen = proc.GetSwitchValueASCII("listen");
cmdline->proxy = proc.GetSwitchValueASCII("proxy");
cmdline->padding = proc.HasSwitch("padding");
cmdline->concurrency = proc.GetSwitchValueASCII("concurrency");
cmdline->extra_headers = proc.GetSwitchValueASCII("extra-headers");
cmdline->host_resolver_rules =
@ -193,7 +189,6 @@ void GetCommandLineFromConfig(const base::FilePath& config_path,
if (proxy) {
cmdline->proxy = *proxy;
}
cmdline->padding = value->FindBoolKey("padding").value_or(false);
const auto* concurrency = value->FindStringKey("concurrency");
if (concurrency) {
cmdline->concurrency = *concurrency;
@ -236,7 +231,7 @@ std::string GetProxyFromURL(const GURL& url) {
}
bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
params->protocol = net::NaiveConnection::kSocks5;
params->protocol = net::ClientProtocol::kSocks5;
params->listen_addr = "0.0.0.0";
params->listen_port = 1080;
url::AddStandardScheme("socks", url::SCHEME_WITH_HOST_AND_PORT);
@ -244,14 +239,14 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
if (!cmdline.listen.empty()) {
GURL url(cmdline.listen);
if (url.scheme() == "socks") {
params->protocol = net::NaiveConnection::kSocks5;
params->protocol = net::ClientProtocol::kSocks5;
params->listen_port = 1080;
} else if (url.scheme() == "http") {
params->protocol = net::NaiveConnection::kHttp;
params->protocol = net::ClientProtocol::kHttp;
params->listen_port = 8080;
} else if (url.scheme() == "redir") {
#if defined(OS_LINUX)
params->protocol = net::NaiveConnection::kRedir;
params->protocol = net::ClientProtocol::kRedir;
params->listen_port = 1080;
#else
std::cerr << "Redir protocol only supports Linux." << std::endl;
@ -277,8 +272,6 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
}
}
url::AddStandardScheme("quic",
url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION);
params->proxy_url = "direct://";
GURL url(cmdline.proxy);
GURL::Replacements remove_auth;
@ -294,8 +287,6 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
net::GetIdentityFromURL(url, &params->proxy_user, &params->proxy_pass);
}
params->use_padding = cmdline.padding;
if (!cmdline.concurrency.empty()) {
if (!base::StringToInt(cmdline.concurrency, &params->concurrency) ||
params->concurrency < 1 || params->concurrency > 4) {
@ -310,7 +301,7 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
params->host_resolver_rules = cmdline.host_resolver_rules;
if (params->protocol == net::NaiveConnection::kRedir) {
if (params->protocol == net::ClientProtocol::kRedir) {
std::string range = "100.64.0.0/10";
if (!cmdline.resolver_range.empty())
range = cmdline.resolver_range;
@ -387,54 +378,7 @@ class PrintingLogObserver : public NetLog::ThreadSafeObserver {
};
} // namespace
class ProxyInfo;
class ProxyServer;
namespace {
class NaiveProxyDelegate : public ProxyDelegate {
public:
NaiveProxyDelegate(const Params& params) : params_(params) {
unsigned i = 0;
for (const auto& symbol : spdy::HpackHuffmanCodeVector()) {
if (symbol.id >= 0x20 && symbol.id <= 0x7f && symbol.length >= 8) {
nonindex_codes_[i++] = symbol.id;
if (i >= sizeof(nonindex_codes_))
break;
}
}
CHECK(i == sizeof(nonindex_codes_));
}
void OnResolveProxy(const GURL& url,
const std::string& method,
const ProxyRetryInfoMap& proxy_retry_info,
ProxyInfo* result) override {}
void OnFallback(const ProxyServer& bad_proxy, int net_error) override {}
void OnBeforeTunnelRequest(const ProxyServer& proxy_server,
HttpRequestHeaders* extra_headers) override {
std::string padding(base::RandInt(16, 32), nonindex_codes_[16]);
// Prevents index reuse
uint64_t bits = base::RandUint64();
for (int i = 0; i < 16; i++) {
padding[i] = nonindex_codes_[bits & 0b1111];
bits >>= 4;
}
extra_headers->SetHeader("Padding", padding);
extra_headers->MergeFrom(params_.extra_headers);
}
Error OnTunnelHeadersReceived(
const ProxyServer& proxy_server,
const HttpResponseHeaders& response_headers) override {
return OK;
}
private:
const Params& params_;
uint8_t nonindex_codes_[17];
};
std::unique_ptr<URLRequestContext> BuildCertURLRequestContext(NetLog* net_log) {
URLRequestContextBuilder builder;
@ -481,7 +425,8 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
builder.SetCertVerifier(
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(params));
builder.set_proxy_delegate(
std::make_unique<NaiveProxyDelegate>(params.extra_headers));
auto context = builder.Build();
@ -511,6 +456,8 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
} // namespace net
int main(int argc, char* argv[]) {
url::AddStandardScheme("quic",
url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION);
base::FeatureList::InitializeInstance(
"PartitionConnectionsByNetworkIsolationKey", std::string());
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
@ -605,7 +552,7 @@ int main(int argc, char* argv[]) {
<< params.listen_port;
std::unique_ptr<net::RedirectResolver> resolver;
if (params.protocol == net::NaiveConnection::kRedir) {
if (params.protocol == net::ClientProtocol::kRedir) {
auto resolver_socket =
std::make_unique<net::UDPServerSocket>(net_log, net::NetLogSource());
resolver_socket->AllowAddressReuse();
@ -628,8 +575,8 @@ int main(int argc, char* argv[]) {
}
net::NaiveProxy naive_proxy(std::move(listen_socket), params.protocol,
params.use_padding, params.concurrency,
resolver.get(), session, kTrafficAnnotation);
params.concurrency, resolver.get(), session,
kTrafficAnnotation);
base::RunLoop().Run();

View File

@ -0,0 +1,158 @@
// Copyright 2020 klzgrad <kizdiv@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/tools/naive/naive_proxy_delegate.h"
#include <string>
#include "base/logging.h"
#include "base/rand_util.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
namespace net {
namespace {
bool g_nonindex_codes_initialized;
uint8_t g_nonindex_codes[17];
} // namespace
void InitializeNonindexCodes() {
if (g_nonindex_codes_initialized)
return;
g_nonindex_codes_initialized = true;
unsigned i = 0;
for (const auto& symbol : spdy::HpackHuffmanCodeVector()) {
if (symbol.id >= 0x20 && symbol.id <= 0x7f && symbol.length >= 8) {
g_nonindex_codes[i++] = symbol.id;
if (i >= sizeof(g_nonindex_codes))
break;
}
}
CHECK(i == sizeof(g_nonindex_codes));
}
void FillNonindexHeaderValue(uint64_t unique_bits, char* buf, int len) {
DCHECK(g_nonindex_codes_initialized);
int first = len < 16 ? len : 16;
for (int i = 0; i < first; i++) {
buf[i] = g_nonindex_codes[unique_bits & 0b1111];
unique_bits >>= 4;
}
for (int i = first; i < len; i++) {
buf[i] = g_nonindex_codes[16];
}
}
NaiveProxyDelegate::NaiveProxyDelegate(const HttpRequestHeaders& extra_headers)
: extra_headers_(extra_headers) {
InitializeNonindexCodes();
}
NaiveProxyDelegate::~NaiveProxyDelegate() = default;
void NaiveProxyDelegate::OnBeforeTunnelRequest(
const ProxyServer& proxy_server,
HttpRequestHeaders* extra_headers) {
if (proxy_server.is_direct() || proxy_server.is_socks())
return;
// Sends client-side padding header regardless of server support
std::string padding(base::RandInt(16, 32), '~');
FillNonindexHeaderValue(base::RandUint64(), &padding[0], padding.size());
extra_headers->SetHeader("padding", padding);
// Enables Fast Open in H2/H3 proxy client socket once the state of server
// padding support is known.
if (padding_state_by_server_[proxy_server] != PaddingSupport::kUnknown) {
extra_headers->SetHeader("fastopen", "1");
}
extra_headers->MergeFrom(extra_headers_);
}
Error NaiveProxyDelegate::OnTunnelHeadersReceived(
const ProxyServer& proxy_server,
const HttpResponseHeaders& response_headers) {
if (proxy_server.is_direct() || proxy_server.is_socks())
return OK;
// Detects server padding support, even if it changes dynamically.
bool padding = response_headers.HasHeader("padding");
auto new_state =
padding ? PaddingSupport::kCapable : PaddingSupport::kIncapable;
auto& padding_state = padding_state_by_server_[proxy_server];
if (padding_state == PaddingSupport::kUnknown || padding_state != new_state) {
LOG(INFO) << "Padding capability of " << proxy_server.ToURI()
<< (padding ? " detected" : " undetected");
}
padding_state = new_state;
return OK;
}
PaddingSupport NaiveProxyDelegate::GetProxyServerPaddingSupport(
const ProxyServer& proxy_server) {
// Not possible to detect padding capability given underlying protocol.
if (proxy_server.is_direct() || proxy_server.is_socks())
return PaddingSupport::kIncapable;
return padding_state_by_server_[proxy_server];
}
PaddingDetectorDelegate::PaddingDetectorDelegate(
NaiveProxyDelegate* naive_proxy_delegate,
const ProxyServer& proxy_server,
ClientProtocol client_protocol)
: naive_proxy_delegate_(naive_proxy_delegate),
proxy_server_(proxy_server),
client_protocol_(client_protocol),
detected_client_padding_support_(PaddingSupport::kUnknown),
cached_server_padding_support_(PaddingSupport::kUnknown) {}
PaddingDetectorDelegate::~PaddingDetectorDelegate() = default;
bool PaddingDetectorDelegate::IsPaddingSupportKnown() {
auto c = GetClientPaddingSupport();
auto s = GetServerPaddingSupport();
return c != PaddingSupport::kUnknown && s != PaddingSupport::kUnknown;
}
Direction PaddingDetectorDelegate::GetPaddingDirection() {
auto c = GetClientPaddingSupport();
auto s = GetServerPaddingSupport();
// Padding support must be already detected at this point.
CHECK_NE(c, PaddingSupport::kUnknown);
CHECK_NE(s, PaddingSupport::kUnknown);
if (c == PaddingSupport::kCapable && s == PaddingSupport::kIncapable) {
return kServer;
}
if (c == PaddingSupport::kIncapable && s == PaddingSupport::kCapable) {
return kClient;
}
return kNone;
}
void PaddingDetectorDelegate::SetClientPaddingSupport(
PaddingSupport padding_support) {
detected_client_padding_support_ = padding_support;
}
PaddingSupport PaddingDetectorDelegate::GetClientPaddingSupport() {
// Not possible to detect padding capability given underlying protocol.
if (client_protocol_ == ClientProtocol::kSocks5) {
return PaddingSupport::kIncapable;
} else if (client_protocol_ == ClientProtocol::kRedir) {
return PaddingSupport::kIncapable;
}
return detected_client_padding_support_;
}
PaddingSupport PaddingDetectorDelegate::GetServerPaddingSupport() {
if (cached_server_padding_support_ != PaddingSupport::kUnknown)
return cached_server_padding_support_;
cached_server_padding_support_ =
naive_proxy_delegate_->GetProxyServerPaddingSupport(proxy_server_);
return cached_server_padding_support_;
}
} // namespace net

View File

@ -0,0 +1,94 @@
// Copyright 2020 klzgrad <kizdiv@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_TOOLS_NAIVE_NAIVE_PROXY_DELEGATE_H_
#define NET_TOOLS_NAIVE_NAIVE_PROXY_DELEGATE_H_
#include <cstdint>
#include <map>
#include <string>
#include "base/strings/string_piece.h"
#include "net/base/net_errors.h"
#include "net/base/proxy_delegate.h"
#include "net/base/proxy_server.h"
#include "net/proxy_resolution/proxy_retry_info.h"
#include "net/tools/naive/naive_protocol.h"
#include "url/gurl.h"
namespace net {
void InitializeNonindexCodes();
// |unique_bits| SHOULD have relatively unique values.
void FillNonindexHeaderValue(uint64_t unique_bits, char* buf, int len);
class ProxyInfo;
class HttpRequestHeaders;
class HttpResponseHeaders;
enum class PaddingSupport {
kUnknown = 0,
kCapable,
kIncapable,
};
class NaiveProxyDelegate : public ProxyDelegate {
public:
explicit NaiveProxyDelegate(const HttpRequestHeaders& extra_headers);
~NaiveProxyDelegate() override;
void OnResolveProxy(const GURL& url,
const std::string& method,
const ProxyRetryInfoMap& proxy_retry_info,
ProxyInfo* result) override {}
void OnFallback(const ProxyServer& bad_proxy, int net_error) override {}
// This only affects h2 proxy client socket.
void OnBeforeTunnelRequest(const ProxyServer& proxy_server,
HttpRequestHeaders* extra_headers) override;
Error OnTunnelHeadersReceived(
const ProxyServer& proxy_server,
const HttpResponseHeaders& response_headers) override;
PaddingSupport GetProxyServerPaddingSupport(const ProxyServer& proxy_server);
private:
const HttpRequestHeaders& extra_headers_;
std::map<ProxyServer, PaddingSupport> padding_state_by_server_;
};
class ClientPaddingDetectorDelegate {
public:
virtual ~ClientPaddingDetectorDelegate() = default;
virtual void SetClientPaddingSupport(PaddingSupport padding_support) = 0;
};
class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
public:
PaddingDetectorDelegate(NaiveProxyDelegate* naive_proxy_delegate,
const ProxyServer& proxy_server,
ClientProtocol client_protocol);
~PaddingDetectorDelegate() override;
bool IsPaddingSupportKnown();
Direction GetPaddingDirection();
void SetClientPaddingSupport(PaddingSupport padding_support) override;
private:
PaddingSupport GetClientPaddingSupport();
PaddingSupport GetServerPaddingSupport();
NaiveProxyDelegate* naive_proxy_delegate_;
const ProxyServer& proxy_server_;
ClientProtocol client_protocol_;
PaddingSupport detected_client_padding_support_;
// The result is only cached during one connection, so it's still dynamically
// updated in the following connections after server changes support.
PaddingSupport cached_server_padding_support_;
};
} // namespace net
#endif // NET_TOOLS_NAIVE_NAIVE_PROXY_DELEGATE_H_