diff --git a/src/net/BUILD.gn b/src/net/BUILD.gn index 4e1d1b9e85..395c867064 100644 --- a/src/net/BUILD.gn +++ b/src/net/BUILD.gn @@ -1737,6 +1737,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_