From e9cd445c8af28ee86222c3879e41340f6ef6e16a Mon Sep 17 00:00:00 2001 From: klzgrad Date: Mon, 25 May 2020 00:30:52 +0800 Subject: [PATCH] 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. --- src/net/BUILD.gn | 2 + src/net/tools/naive/http_proxy_socket.cc | 50 +++---- src/net/tools/naive/http_proxy_socket.h | 3 + src/net/tools/naive/naive_connection.cc | 39 +++-- src/net/tools/naive/naive_connection.h | 45 +++--- src/net/tools/naive/naive_protocol.h | 24 +++ src/net/tools/naive/naive_proxy.cc | 34 +++-- src/net/tools/naive/naive_proxy.h | 7 +- src/net/tools/naive/naive_proxy_bin.cc | 83 ++-------- src/net/tools/naive/naive_proxy_delegate.cc | 158 ++++++++++++++++++++ src/net/tools/naive/naive_proxy_delegate.h | 94 ++++++++++++ 11 files changed, 384 insertions(+), 155 deletions(-) create mode 100644 src/net/tools/naive/naive_protocol.h create mode 100644 src/net/tools/naive/naive_proxy_delegate.cc create mode 100644 src/net/tools/naive/naive_proxy_delegate.h diff --git a/src/net/BUILD.gn b/src/net/BUILD.gn index e14e61cd30..3ed3b88188 100644 --- a/src/net/BUILD.gn +++ b/src/net/BUILD.gn @@ -1749,6 +1749,8 @@ executable("naive") { "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", diff --git a/src/net/tools/naive/http_proxy_socket.cc b/src/net/tools/naive/http_proxy_socket.cc index 50405c5fc4..3add402b59 100644 --- a/src/net/tools/naive/http_proxy_socket.cc +++ b/src/net/tools/naive/http_proxy_socket.cc @@ -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 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(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_, diff --git a/src/net/tools/naive/http_proxy_socket.h b/src/net/tools/naive/http_proxy_socket.h index 62d846018e..779ecfaa40 100644 --- a/src/net/tools/naive/http_proxy_socket.h +++ b/src/net/tools/naive/http_proxy_socket.h @@ -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 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 transport_; + ClientPaddingDetectorDelegate* padding_detector_delegate_; State next_state_; diff --git a/src/net/tools/naive/naive_connection.cc b/src/net/tools/naive/naive_connection.cc index a2e51c9fc2..80dca93f0e 100644 --- a/src/net/tools/naive/naive_connection.cc +++ b/src/net/tools/naive/naive_connection.cc @@ -48,8 +48,8 @@ constexpr int kMaxPaddingSize = 255; NaiveConnection::NaiveConnection( unsigned int id, - Protocol protocol, - Direction pad_direction, + ClientProtocol protocol, + std::unique_ptr 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(client_socket_.get()); origin = socket->request_endpoint(); - } else if (protocol_ == kHttp) { + } else if (protocol_ == ClientProtocol::kHttp) { const auto* socket = static_cast(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(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(); 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) { diff --git a/src/net/tools/naive/naive_connection.h b/src/net/tools/naive/naive_connection.h index d4a26e8641..d9fea531a5 100644 --- a/src/net/tools/naive/naive_connection.h +++ b/src/net/tools/naive/naive_connection.h @@ -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 accepted_socket, - const NetworkTrafficAnnotationTag& traffic_annotation); + NaiveConnection( + unsigned int id, + ClientProtocol protocol, + std::unique_ptr 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 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 padding_detector_delegate_; const ProxyInfo& proxy_info_; const SSLConfig& server_ssl_config_; const SSLConfig& proxy_ssl_config_; diff --git a/src/net/tools/naive/naive_protocol.h b/src/net/tools/naive/naive_protocol.h new file mode 100644 index 0000000000..e3e617fbfa --- /dev/null +++ b/src/net/tools/naive/naive_protocol.h @@ -0,0 +1,24 @@ +// Copyright 2020 klzgrad . 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_ diff --git a/src/net/tools/naive/naive_proxy.cc b/src/net/tools/naive/naive_proxy.cc index 097d357a15..b4d5feee68 100644 --- a/src/net/tools/naive/naive_proxy.cc +++ b/src/net/tools/naive/naive_proxy.cc @@ -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 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 socket; - NaiveConnection::Direction pad_direction; - if (protocol_ == NaiveConnection::kSocks5) { + auto* proxy_delegate = + static_cast(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( + proxy_delegate, proxy_server, protocol_); + + if (protocol_ == ClientProtocol::kSocks5) { socket = std::make_unique(std::move(accepted_socket_), traffic_annotation_); - pad_direction = NaiveConnection::kClient; - } else if (protocol_ == NaiveConnection::kHttp) { + } else if (protocol_ == ClientProtocol::kHttp) { socket = std::make_unique(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( - 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( diff --git a/src/net/tools/naive/naive_proxy.h b/src/net/tools/naive/naive_proxy.h index de7535483f..640720bcf6 100644 --- a/src/net/tools/naive/naive_proxy.h +++ b/src/net/tools/naive/naive_proxy.h @@ -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 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 listen_socket_; - NaiveConnection::Protocol protocol_; - bool use_padding_; + ClientProtocol protocol_; int concurrency_; ProxyInfo proxy_info_; SSLConfig server_ssl_config_; diff --git a/src/net/tools/naive/naive_proxy_bin.cc b/src/net/tools/naive/naive_proxy_bin.cc index 159a1d3033..9e74e08cea 100644 --- a/src/net/tools/naive/naive_proxy_bin.cc +++ b/src/net/tools/naive/naive_proxy_bin.cc @@ -39,7 +39,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" @@ -57,8 +56,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" @@ -83,7 +83,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; @@ -95,10 +94,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; @@ -136,7 +134,6 @@ void GetCommandLine(const base::CommandLine& proc, CommandLine* cmdline) { " redir (Linux only)\n" "--proxy=://[:@][:]\n" " proto: https, quic\n" - "--padding Use padding\n" "--concurrency= Use N connections, less secure\n" "--extra-headers=... Extra headers split by CRLF\n" "--host-resolver-rules=... Resolver rules\n" @@ -155,7 +152,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 = @@ -190,7 +186,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; @@ -233,7 +228,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); @@ -241,14 +236,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; @@ -274,8 +269,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; @@ -291,8 +284,6 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) { net::GetIdentityFromURL(url, ¶ms->proxy_user, ¶ms->proxy_pass); } - params->use_padding = cmdline.padding; - if (!cmdline.concurrency.empty()) { if (!base::StringToInt(cmdline.concurrency, ¶ms->concurrency) || params->concurrency < 1 || params->concurrency > 4) { @@ -307,7 +298,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; @@ -384,54 +375,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 BuildCertURLRequestContext(NetLog* net_log) { URLRequestContextBuilder builder; @@ -478,7 +422,8 @@ std::unique_ptr BuildURLRequestContext( builder.SetCertVerifier( CertVerifier::CreateDefault(std::move(cert_net_fetcher))); - builder.set_proxy_delegate(std::make_unique(params)); + builder.set_proxy_delegate( + std::make_unique(params.extra_headers)); auto context = builder.Build(); @@ -508,6 +453,8 @@ std::unique_ptr 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); @@ -599,7 +546,7 @@ int main(int argc, char* argv[]) { << params.listen_port; std::unique_ptr resolver; - if (params.protocol == net::NaiveConnection::kRedir) { + if (params.protocol == net::ClientProtocol::kRedir) { auto resolver_socket = std::make_unique(net_log, net::NetLogSource()); resolver_socket->AllowAddressReuse(); @@ -622,8 +569,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(); diff --git a/src/net/tools/naive/naive_proxy_delegate.cc b/src/net/tools/naive/naive_proxy_delegate.cc new file mode 100644 index 0000000000..0c27f8c2d2 --- /dev/null +++ b/src/net/tools/naive/naive_proxy_delegate.cc @@ -0,0 +1,158 @@ +// Copyright 2020 klzgrad . 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 + +#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 diff --git a/src/net/tools/naive/naive_proxy_delegate.h b/src/net/tools/naive/naive_proxy_delegate.h new file mode 100644 index 0000000000..43cdeeadc2 --- /dev/null +++ b/src/net/tools/naive/naive_proxy_delegate.h @@ -0,0 +1,94 @@ +// Copyright 2020 klzgrad . 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 +#include +#include + +#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 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_