mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2025-02-26 20:03:26 +03:00
Refactor
This commit is contained in:
parent
c53143ba5c
commit
4b250049af
@ -1767,13 +1767,19 @@ executable("naive") {
|
||||
sources = [
|
||||
"tools/naive/naive_connection.cc",
|
||||
"tools/naive/naive_connection.h",
|
||||
"tools/naive/naive_padding_framer.cc",
|
||||
"tools/naive/naive_padding_framer.h",
|
||||
"tools/naive/naive_padding_socket.cc",
|
||||
"tools/naive/naive_padding_socket.h",
|
||||
"tools/naive/naive_protocol.cc",
|
||||
"tools/naive/naive_protocol.h",
|
||||
"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/http_proxy_server_socket.cc",
|
||||
"tools/naive/http_proxy_server_socket.h",
|
||||
"tools/naive/redirect_resolver.h",
|
||||
"tools/naive/redirect_resolver.cc",
|
||||
"tools/naive/socks5_server_socket.cc",
|
||||
|
@ -3,15 +3,18 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/tools/naive/http_proxy_socket.h"
|
||||
#include "net/tools/naive/http_proxy_server_socket.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/sys_byteorder.h"
|
||||
#include "net/base/ip_address.h"
|
||||
#include "net/base/net_errors.h"
|
||||
@ -19,6 +22,7 @@
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/log/net_log.h"
|
||||
#include "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_constants.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||
|
||||
namespace net {
|
||||
@ -33,11 +37,12 @@ constexpr int kMinPaddingSize = 30;
|
||||
constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
|
||||
} // namespace
|
||||
|
||||
HttpProxySocket::HttpProxySocket(
|
||||
HttpProxyServerSocket::HttpProxyServerSocket(
|
||||
std::unique_ptr<StreamSocket> transport_socket,
|
||||
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation)
|
||||
: io_callback_(base::BindRepeating(&HttpProxySocket::OnIOComplete,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const std::vector<PaddingType>& supported_padding_types)
|
||||
: io_callback_(base::BindRepeating(&HttpProxyServerSocket::OnIOComplete,
|
||||
base::Unretained(this))),
|
||||
transport_(std::move(transport_socket)),
|
||||
padding_detector_delegate_(padding_detector_delegate),
|
||||
@ -46,17 +51,18 @@ HttpProxySocket::HttpProxySocket(
|
||||
was_ever_used_(false),
|
||||
header_write_size_(-1),
|
||||
net_log_(transport_->NetLog()),
|
||||
traffic_annotation_(traffic_annotation) {}
|
||||
traffic_annotation_(traffic_annotation),
|
||||
supported_padding_types_(supported_padding_types) {}
|
||||
|
||||
HttpProxySocket::~HttpProxySocket() {
|
||||
HttpProxyServerSocket::~HttpProxyServerSocket() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
const HostPortPair& HttpProxySocket::request_endpoint() const {
|
||||
const HostPortPair& HttpProxyServerSocket::request_endpoint() const {
|
||||
return request_endpoint_;
|
||||
}
|
||||
|
||||
int HttpProxySocket::Connect(CompletionOnceCallback callback) {
|
||||
int HttpProxyServerSocket::Connect(CompletionOnceCallback callback) {
|
||||
DCHECK(transport_);
|
||||
DCHECK_EQ(STATE_NONE, next_state_);
|
||||
DCHECK(!user_callback_);
|
||||
@ -75,7 +81,7 @@ int HttpProxySocket::Connect(CompletionOnceCallback callback) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
void HttpProxySocket::Disconnect() {
|
||||
void HttpProxyServerSocket::Disconnect() {
|
||||
completed_handshake_ = false;
|
||||
transport_->Disconnect();
|
||||
|
||||
@ -85,23 +91,23 @@ void HttpProxySocket::Disconnect() {
|
||||
user_callback_.Reset();
|
||||
}
|
||||
|
||||
bool HttpProxySocket::IsConnected() const {
|
||||
bool HttpProxyServerSocket::IsConnected() const {
|
||||
return completed_handshake_ && transport_->IsConnected();
|
||||
}
|
||||
|
||||
bool HttpProxySocket::IsConnectedAndIdle() const {
|
||||
bool HttpProxyServerSocket::IsConnectedAndIdle() const {
|
||||
return completed_handshake_ && transport_->IsConnectedAndIdle();
|
||||
}
|
||||
|
||||
const NetLogWithSource& HttpProxySocket::NetLog() const {
|
||||
const NetLogWithSource& HttpProxyServerSocket::NetLog() const {
|
||||
return net_log_;
|
||||
}
|
||||
|
||||
bool HttpProxySocket::WasEverUsed() const {
|
||||
bool HttpProxyServerSocket::WasEverUsed() const {
|
||||
return was_ever_used_;
|
||||
}
|
||||
|
||||
bool HttpProxySocket::WasAlpnNegotiated() const {
|
||||
bool HttpProxyServerSocket::WasAlpnNegotiated() const {
|
||||
if (transport_) {
|
||||
return transport_->WasAlpnNegotiated();
|
||||
}
|
||||
@ -109,7 +115,7 @@ bool HttpProxySocket::WasAlpnNegotiated() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
NextProto HttpProxySocket::GetNegotiatedProtocol() const {
|
||||
NextProto HttpProxyServerSocket::GetNegotiatedProtocol() const {
|
||||
if (transport_) {
|
||||
return transport_->GetNegotiatedProtocol();
|
||||
}
|
||||
@ -117,7 +123,7 @@ NextProto HttpProxySocket::GetNegotiatedProtocol() const {
|
||||
return kProtoUnknown;
|
||||
}
|
||||
|
||||
bool HttpProxySocket::GetSSLInfo(SSLInfo* ssl_info) {
|
||||
bool HttpProxyServerSocket::GetSSLInfo(SSLInfo* ssl_info) {
|
||||
if (transport_) {
|
||||
return transport_->GetSSLInfo(ssl_info);
|
||||
}
|
||||
@ -125,19 +131,19 @@ bool HttpProxySocket::GetSSLInfo(SSLInfo* ssl_info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t HttpProxySocket::GetTotalReceivedBytes() const {
|
||||
int64_t HttpProxyServerSocket::GetTotalReceivedBytes() const {
|
||||
return transport_->GetTotalReceivedBytes();
|
||||
}
|
||||
|
||||
void HttpProxySocket::ApplySocketTag(const SocketTag& tag) {
|
||||
void HttpProxyServerSocket::ApplySocketTag(const SocketTag& tag) {
|
||||
return transport_->ApplySocketTag(tag);
|
||||
}
|
||||
|
||||
// Read is called by the transport layer above to read. This can only be done
|
||||
// if the HTTP header is complete.
|
||||
int HttpProxySocket::Read(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback) {
|
||||
int HttpProxyServerSocket::Read(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback) {
|
||||
DCHECK(completed_handshake_);
|
||||
DCHECK_EQ(STATE_NONE, next_state_);
|
||||
DCHECK(!user_callback_);
|
||||
@ -159,7 +165,7 @@ int HttpProxySocket::Read(IOBuffer* buf,
|
||||
|
||||
int rv = transport_->Read(
|
||||
buf, buf_len,
|
||||
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
|
||||
base::BindOnce(&HttpProxyServerSocket::OnReadWriteComplete,
|
||||
base::Unretained(this), std::move(callback)));
|
||||
if (rv > 0)
|
||||
was_ever_used_ = true;
|
||||
@ -167,8 +173,8 @@ int HttpProxySocket::Read(IOBuffer* buf,
|
||||
}
|
||||
|
||||
// Write is called by the transport layer. This can only be done if the
|
||||
// SOCKS handshake is complete.
|
||||
int HttpProxySocket::Write(
|
||||
// HTTP CONNECT request is complete.
|
||||
int HttpProxyServerSocket::Write(
|
||||
IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
@ -180,7 +186,7 @@ int HttpProxySocket::Write(
|
||||
|
||||
int rv = transport_->Write(
|
||||
buf, buf_len,
|
||||
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
|
||||
base::BindOnce(&HttpProxyServerSocket::OnReadWriteComplete,
|
||||
base::Unretained(this), std::move(callback)),
|
||||
traffic_annotation);
|
||||
if (rv > 0)
|
||||
@ -188,15 +194,15 @@ int HttpProxySocket::Write(
|
||||
return rv;
|
||||
}
|
||||
|
||||
int HttpProxySocket::SetReceiveBufferSize(int32_t size) {
|
||||
int HttpProxyServerSocket::SetReceiveBufferSize(int32_t size) {
|
||||
return transport_->SetReceiveBufferSize(size);
|
||||
}
|
||||
|
||||
int HttpProxySocket::SetSendBufferSize(int32_t size) {
|
||||
int HttpProxyServerSocket::SetSendBufferSize(int32_t size) {
|
||||
return transport_->SetSendBufferSize(size);
|
||||
}
|
||||
|
||||
void HttpProxySocket::DoCallback(int result) {
|
||||
void HttpProxyServerSocket::DoCallback(int result) {
|
||||
DCHECK_NE(ERR_IO_PENDING, result);
|
||||
DCHECK(user_callback_);
|
||||
|
||||
@ -205,7 +211,7 @@ void HttpProxySocket::DoCallback(int result) {
|
||||
std::move(user_callback_).Run(result);
|
||||
}
|
||||
|
||||
void HttpProxySocket::OnIOComplete(int result) {
|
||||
void HttpProxyServerSocket::OnIOComplete(int result) {
|
||||
DCHECK_NE(STATE_NONE, next_state_);
|
||||
int rv = DoLoop(result);
|
||||
if (rv != ERR_IO_PENDING) {
|
||||
@ -213,8 +219,8 @@ void HttpProxySocket::OnIOComplete(int result) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpProxySocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
||||
int result) {
|
||||
void HttpProxyServerSocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
||||
int result) {
|
||||
DCHECK_NE(ERR_IO_PENDING, result);
|
||||
DCHECK(callback);
|
||||
|
||||
@ -223,7 +229,7 @@ void HttpProxySocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
||||
std::move(callback).Run(result);
|
||||
}
|
||||
|
||||
int HttpProxySocket::DoLoop(int last_io_result) {
|
||||
int HttpProxyServerSocket::DoLoop(int last_io_result) {
|
||||
DCHECK_NE(next_state_, STATE_NONE);
|
||||
int rv = last_io_result;
|
||||
do {
|
||||
@ -253,14 +259,50 @@ int HttpProxySocket::DoLoop(int last_io_result) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
int HttpProxySocket::DoHeaderRead() {
|
||||
int HttpProxyServerSocket::DoHeaderRead() {
|
||||
next_state_ = STATE_HEADER_READ_COMPLETE;
|
||||
|
||||
handshake_buf_ = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||
return transport_->Read(handshake_buf_.get(), kBufferSize, io_callback_);
|
||||
}
|
||||
|
||||
int HttpProxySocket::DoHeaderReadComplete(int result) {
|
||||
std::optional<PaddingType> HttpProxyServerSocket::ParsePaddingHeaders(
|
||||
const HttpRequestHeaders& headers) {
|
||||
bool has_padding = headers.HasHeader(kPaddingHeader);
|
||||
std::string padding_type_request;
|
||||
bool has_padding_type_request =
|
||||
headers.GetHeader(kPaddingTypeRequestHeader, &padding_type_request);
|
||||
|
||||
if (!has_padding_type_request) {
|
||||
// Backward compatibility with before kVariant1 when the padding-version
|
||||
// header does not exist.
|
||||
if (has_padding) {
|
||||
return PaddingType::kVariant1;
|
||||
} else {
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<base::StringPiece> padding_type_strs = base::SplitStringPiece(
|
||||
padding_type_request, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
|
||||
for (base::StringPiece padding_type_str : padding_type_strs) {
|
||||
std::optional<PaddingType> padding_type =
|
||||
ParsePaddingType(padding_type_str);
|
||||
if (!padding_type.has_value()) {
|
||||
LOG(ERROR) << "Invalid padding type: " << padding_type_str;
|
||||
return std::nullopt;
|
||||
}
|
||||
if (std::find(supported_padding_types_.begin(),
|
||||
supported_padding_types_.end(),
|
||||
*padding_type) != supported_padding_types_.end()) {
|
||||
return padding_type;
|
||||
}
|
||||
}
|
||||
LOG(ERROR) << "No padding type is supported: " << padding_type_request;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int HttpProxyServerSocket::DoHeaderReadComplete(int result) {
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
@ -273,23 +315,21 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
||||
return ERR_MSG_TOO_BIG;
|
||||
}
|
||||
|
||||
auto header_end = buffer_.find("\r\n\r\n");
|
||||
size_t header_end = buffer_.find("\r\n\r\n");
|
||||
if (header_end == std::string::npos) {
|
||||
next_state_ = STATE_HEADER_READ;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// HttpProxyClientSocket uses CONNECT for all endpoints.
|
||||
// GET is also supported.
|
||||
auto first_line_end = buffer_.find("\r\n");
|
||||
auto first_space = buffer_.find(' ');
|
||||
size_t first_line_end = buffer_.find("\r\n");
|
||||
size_t first_space = buffer_.find(' ');
|
||||
bool is_http_1_0 = false;
|
||||
if (first_space == std::string::npos || first_space + 1 >= first_line_end) {
|
||||
return ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (buffer_.compare(0, first_space, HttpRequestHeaders::kConnectMethod) ==
|
||||
0) {
|
||||
auto second_space = buffer_.find(' ', first_space + 1);
|
||||
size_t second_space = buffer_.find(' ', first_space + 1);
|
||||
if (second_space == std::string::npos || second_space >= first_line_end) {
|
||||
LOG(WARNING) << "Invalid request: " << buffer_.substr(0, first_line_end);
|
||||
return ERR_INVALID_ARGUMENT;
|
||||
@ -301,7 +341,7 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
||||
is_http_1_0 = true;
|
||||
}
|
||||
|
||||
auto second_line = first_line_end + 2;
|
||||
size_t second_line = first_line_end + 2;
|
||||
HttpRequestHeaders headers;
|
||||
std::string headers_str;
|
||||
if (second_line < header_end) {
|
||||
@ -328,13 +368,11 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
||||
request_endpoint_.set_port(80);
|
||||
}
|
||||
|
||||
if (headers.HasHeader("padding")) {
|
||||
padding_detector_delegate_->SetClientPaddingSupport(
|
||||
PaddingSupport::kCapable);
|
||||
} else {
|
||||
padding_detector_delegate_->SetClientPaddingSupport(
|
||||
PaddingSupport::kIncapable);
|
||||
std::optional<PaddingType> padding_type = ParsePaddingHeaders(headers);
|
||||
if (!padding_type.has_value()) {
|
||||
return ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
padding_detector_delegate_->SetClientPaddingType(*padding_type);
|
||||
|
||||
if (is_http_1_0) {
|
||||
// Regerate http header to make sure don't leak them to end servers
|
||||
@ -361,7 +399,7 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
int HttpProxySocket::DoHeaderWrite() {
|
||||
int HttpProxyServerSocket::DoHeaderWrite() {
|
||||
next_state_ = STATE_HEADER_WRITE_COMPLETE;
|
||||
|
||||
// Adds padding.
|
||||
@ -378,7 +416,7 @@ int HttpProxySocket::DoHeaderWrite() {
|
||||
io_callback_, traffic_annotation_);
|
||||
}
|
||||
|
||||
int HttpProxySocket::DoHeaderWriteComplete(int result) {
|
||||
int HttpProxyServerSocket::DoHeaderWriteComplete(int result) {
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
@ -391,11 +429,11 @@ int HttpProxySocket::DoHeaderWriteComplete(int result) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
int HttpProxySocket::GetPeerAddress(IPEndPoint* address) const {
|
||||
int HttpProxyServerSocket::GetPeerAddress(IPEndPoint* address) const {
|
||||
return transport_->GetPeerAddress(address);
|
||||
}
|
||||
|
||||
int HttpProxySocket::GetLocalAddress(IPEndPoint* address) const {
|
||||
int HttpProxyServerSocket::GetLocalAddress(IPEndPoint* address) const {
|
||||
return transport_->GetLocalAddress(address);
|
||||
}
|
||||
|
@ -3,13 +3,14 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
|
||||
#define NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
|
||||
#ifndef NET_TOOLS_NAIVE_HTTP_PROXY_SERVER_SOCKET_H_
|
||||
#define NET_TOOLS_NAIVE_HTTP_PROXY_SERVER_SOCKET_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "net/base/completion_once_callback.h"
|
||||
@ -17,27 +18,31 @@
|
||||
#include "net/base/host_port_pair.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/ip_endpoint.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/log/net_log_with_source.h"
|
||||
#include "net/socket/connection_attempts.h"
|
||||
#include "net/socket/next_proto.h"
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/ssl/ssl_info.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
|
||||
namespace net {
|
||||
struct NetworkTrafficAnnotationTag;
|
||||
class ClientPaddingDetectorDelegate;
|
||||
|
||||
// This StreamSocket is used to setup a HTTP CONNECT tunnel.
|
||||
class HttpProxySocket : public StreamSocket {
|
||||
class HttpProxyServerSocket : public StreamSocket {
|
||||
public:
|
||||
HttpProxySocket(std::unique_ptr<StreamSocket> transport_socket,
|
||||
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
HttpProxySocket(const HttpProxySocket&) = delete;
|
||||
HttpProxySocket& operator=(const HttpProxySocket&) = delete;
|
||||
HttpProxyServerSocket(
|
||||
std::unique_ptr<StreamSocket> transport_socket,
|
||||
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const std::vector<PaddingType>& supported_padding_types);
|
||||
HttpProxyServerSocket(const HttpProxyServerSocket&) = delete;
|
||||
HttpProxyServerSocket& operator=(const HttpProxyServerSocket&) = delete;
|
||||
|
||||
// On destruction Disconnect() is called.
|
||||
~HttpProxySocket() override;
|
||||
~HttpProxyServerSocket() override;
|
||||
|
||||
const HostPortPair& request_endpoint() const;
|
||||
|
||||
@ -89,6 +94,9 @@ class HttpProxySocket : public StreamSocket {
|
||||
int DoHeaderRead();
|
||||
int DoHeaderReadComplete(int result);
|
||||
|
||||
std::optional<PaddingType> ParsePaddingHeaders(
|
||||
const HttpRequestHeaders& headers);
|
||||
|
||||
CompletionRepeatingCallback io_callback_;
|
||||
|
||||
// Stores the underlying socket.
|
||||
@ -116,7 +124,9 @@ class HttpProxySocket : public StreamSocket {
|
||||
|
||||
// Traffic annotation for socket control.
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
||||
|
||||
std::vector<PaddingType> supported_padding_types_;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
#endif // NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
|
||||
#endif // NET_TOOLS_NAIVE_HTTP_PROXY_SERVER_SOCKET_H_
|
@ -26,7 +26,8 @@
|
||||
#include "net/socket/client_socket_pool_manager.h"
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/spdy/spdy_session.h"
|
||||
#include "net/tools/naive/http_proxy_socket.h"
|
||||
#include "net/tools/naive/http_proxy_server_socket.h"
|
||||
#include "net/tools/naive/naive_padding_socket.h"
|
||||
#include "net/tools/naive/redirect_resolver.h"
|
||||
#include "net/tools/naive/socks5_server_socket.h"
|
||||
#include "url/scheme_host_port.h"
|
||||
@ -45,9 +46,6 @@ namespace net {
|
||||
|
||||
namespace {
|
||||
constexpr int kBufferSize = 64 * 1024;
|
||||
constexpr int kFirstPaddings = 8;
|
||||
constexpr int kPaddingHeaderSize = 3;
|
||||
constexpr int kMaxPaddingSize = 255;
|
||||
} // namespace
|
||||
|
||||
NaiveConnection::NaiveConnection(
|
||||
@ -76,14 +74,12 @@ NaiveConnection::NaiveConnection(
|
||||
next_state_(STATE_NONE),
|
||||
client_socket_(std::move(accepted_socket)),
|
||||
server_socket_handle_(std::make_unique<ClientSocketHandle>()),
|
||||
sockets_{client_socket_.get(), nullptr},
|
||||
sockets_{nullptr, nullptr},
|
||||
errors_{OK, OK},
|
||||
write_pending_{false, false},
|
||||
early_pull_pending_(false),
|
||||
can_push_to_server_(false),
|
||||
early_pull_result_(ERR_IO_PENDING),
|
||||
num_paddings_{0, 0},
|
||||
read_padding_state_(STATE_READ_PAYLOAD_LENGTH_1),
|
||||
full_duplex_(false),
|
||||
time_func_(&base::TimeTicks::Now),
|
||||
traffic_annotation_(traffic_annotation) {
|
||||
@ -181,11 +177,18 @@ int NaiveConnection::DoConnectClientComplete(int result) {
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
std::optional<PaddingType> client_padding_type =
|
||||
padding_detector_delegate_->GetClientPaddingType();
|
||||
CHECK(client_padding_type.has_value());
|
||||
|
||||
sockets_[kClient] = std::make_unique<NaivePaddingSocket>(
|
||||
client_socket_.get(), *client_padding_type, kClient);
|
||||
|
||||
// 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
|
||||
// proxy server sockets (HttpProxyServerSocket), padding support detection is
|
||||
// done during client connect, so there shouldn't be any missed early pull.
|
||||
if (!padding_detector_delegate_->IsPaddingSupportKnown()) {
|
||||
if (!padding_detector_delegate_->GetServerPaddingType().has_value()) {
|
||||
early_pull_pending_ = false;
|
||||
early_pull_result_ = 0;
|
||||
next_state_ = STATE_CONNECT_SERVER;
|
||||
@ -215,7 +218,7 @@ int NaiveConnection::DoConnectServer() {
|
||||
origin = socket->request_endpoint();
|
||||
} else if (protocol_ == ClientProtocol::kHttp) {
|
||||
const auto* socket =
|
||||
static_cast<const HttpProxySocket*>(client_socket_.get());
|
||||
static_cast<const HttpProxyServerSocket*>(client_socket_.get());
|
||||
origin = socket->request_endpoint();
|
||||
} else if (protocol_ == ClientProtocol::kRedir) {
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
@ -284,8 +287,12 @@ int NaiveConnection::DoConnectServerComplete(int result) {
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
DCHECK(server_socket_handle_->socket());
|
||||
sockets_[kServer] = server_socket_handle_->socket();
|
||||
std::optional<PaddingType> server_padding_type =
|
||||
padding_detector_delegate_->GetServerPaddingType();
|
||||
CHECK(server_padding_type.has_value());
|
||||
|
||||
sockets_[kServer] = std::make_unique<NaivePaddingSocket>(
|
||||
server_socket_handle_->socket(), *server_padding_type, kServer);
|
||||
|
||||
full_duplex_ = true;
|
||||
next_state_ = STATE_NONE;
|
||||
@ -331,16 +338,7 @@ void NaiveConnection::Pull(Direction from, Direction to) {
|
||||
return;
|
||||
|
||||
int read_size = kBufferSize;
|
||||
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);
|
||||
read_buffers_[from] = buffer;
|
||||
read_size = kBufferSize - kPaddingHeaderSize - kMaxPaddingSize;
|
||||
} else {
|
||||
read_buffers_[from] = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||
}
|
||||
read_buffers_[from] = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||
|
||||
DCHECK(sockets_[from]);
|
||||
int rv = sockets_[from]->Read(
|
||||
@ -356,108 +354,12 @@ void NaiveConnection::Pull(Direction from, Direction to) {
|
||||
}
|
||||
|
||||
void NaiveConnection::Push(Direction from, Direction to, int size) {
|
||||
int write_size = size;
|
||||
int write_offset = 0;
|
||||
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);
|
||||
auto* buffer = static_cast<GrowableIOBuffer*>(read_buffers_[from].get());
|
||||
buffer->set_offset(0);
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(buffer->data());
|
||||
p[0] = size / 256;
|
||||
p[1] = size % 256;
|
||||
p[2] = padding_size;
|
||||
std::memset(p + kPaddingHeaderSize + size, 0, padding_size);
|
||||
write_size = kPaddingHeaderSize + size + padding_size;
|
||||
} else if (to == padding_direction && num_paddings_[from] < kFirstPaddings) {
|
||||
// Removes padding.
|
||||
const char* p = read_buffers_[from]->data();
|
||||
bool trivial_padding = false;
|
||||
if (read_padding_state_ == STATE_READ_PAYLOAD_LENGTH_1 &&
|
||||
size >= kPaddingHeaderSize) {
|
||||
int payload_size =
|
||||
static_cast<uint8_t>(p[0]) * 256 + static_cast<uint8_t>(p[1]);
|
||||
int padding_size = static_cast<uint8_t>(p[2]);
|
||||
if (size == kPaddingHeaderSize + payload_size + padding_size) {
|
||||
write_size = payload_size;
|
||||
write_offset = kPaddingHeaderSize;
|
||||
++num_paddings_[from];
|
||||
trivial_padding = true;
|
||||
}
|
||||
}
|
||||
if (!trivial_padding) {
|
||||
auto unpadded_buffer = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||
char* unpadded_ptr = unpadded_buffer->data();
|
||||
for (int i = 0; i < size;) {
|
||||
if (num_paddings_[from] >= kFirstPaddings &&
|
||||
read_padding_state_ == STATE_READ_PAYLOAD_LENGTH_1) {
|
||||
std::memcpy(unpadded_ptr, p + i, size - i);
|
||||
unpadded_ptr += size - i;
|
||||
break;
|
||||
}
|
||||
int copy_size;
|
||||
switch (read_padding_state_) {
|
||||
case STATE_READ_PAYLOAD_LENGTH_1:
|
||||
payload_length_ = static_cast<uint8_t>(p[i]);
|
||||
++i;
|
||||
read_padding_state_ = STATE_READ_PAYLOAD_LENGTH_2;
|
||||
break;
|
||||
case STATE_READ_PAYLOAD_LENGTH_2:
|
||||
payload_length_ =
|
||||
payload_length_ * 256 + static_cast<uint8_t>(p[i]);
|
||||
++i;
|
||||
read_padding_state_ = STATE_READ_PADDING_LENGTH;
|
||||
break;
|
||||
case STATE_READ_PADDING_LENGTH:
|
||||
padding_length_ = static_cast<uint8_t>(p[i]);
|
||||
++i;
|
||||
read_padding_state_ = STATE_READ_PAYLOAD;
|
||||
break;
|
||||
case STATE_READ_PAYLOAD:
|
||||
if (payload_length_ <= size - i) {
|
||||
copy_size = payload_length_;
|
||||
read_padding_state_ = STATE_READ_PADDING;
|
||||
} else {
|
||||
copy_size = size - i;
|
||||
}
|
||||
std::memcpy(unpadded_ptr, p + i, copy_size);
|
||||
unpadded_ptr += copy_size;
|
||||
i += copy_size;
|
||||
payload_length_ -= copy_size;
|
||||
break;
|
||||
case STATE_READ_PADDING:
|
||||
if (padding_length_ <= size - i) {
|
||||
copy_size = padding_length_;
|
||||
read_padding_state_ = STATE_READ_PAYLOAD_LENGTH_1;
|
||||
++num_paddings_[from];
|
||||
} else {
|
||||
copy_size = size - i;
|
||||
}
|
||||
i += copy_size;
|
||||
padding_length_ -= copy_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_size = unpadded_ptr - unpadded_buffer->data();
|
||||
read_buffers_[from] = unpadded_buffer;
|
||||
}
|
||||
if (write_size == 0) {
|
||||
OnPushComplete(from, to, OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
write_buffers_[to] = base::MakeRefCounted<DrainableIOBuffer>(
|
||||
std::move(read_buffers_[from]), write_offset + write_size);
|
||||
if (write_offset) {
|
||||
write_buffers_[to]->DidConsume(write_offset);
|
||||
}
|
||||
std::move(read_buffers_[from]), size);
|
||||
write_pending_[to] = true;
|
||||
DCHECK(sockets_[to]);
|
||||
int rv = sockets_[to]->Write(
|
||||
write_buffers_[to].get(), write_size,
|
||||
write_buffers_[to].get(), write_buffers_[to]->BytesRemaining(),
|
||||
base::BindRepeating(&NaiveConnection::OnPushComplete,
|
||||
weak_ptr_factory_.GetWeakPtr(), from, to),
|
||||
traffic_annotation_);
|
||||
@ -475,7 +377,7 @@ void NaiveConnection::Disconnect(Direction side) {
|
||||
}
|
||||
|
||||
bool NaiveConnection::IsConnected(Direction side) {
|
||||
return sockets_[side];
|
||||
return sockets_[side] != nullptr;
|
||||
}
|
||||
|
||||
void NaiveConnection::OnBothDisconnected() {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "base/time/time.h"
|
||||
#include "net/base/completion_once_callback.h"
|
||||
#include "net/base/completion_repeating_callback.h"
|
||||
#include "net/tools/naive/naive_padding_socket.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||
|
||||
@ -111,7 +112,7 @@ class NaiveConnection {
|
||||
std::unique_ptr<StreamSocket> client_socket_;
|
||||
std::unique_ptr<ClientSocketHandle> server_socket_handle_;
|
||||
|
||||
StreamSocket* sockets_[kNumDirections];
|
||||
std::unique_ptr<NaivePaddingSocket> sockets_[kNumDirections];
|
||||
scoped_refptr<IOBuffer> read_buffers_[kNumDirections];
|
||||
scoped_refptr<DrainableIOBuffer> write_buffers_[kNumDirections];
|
||||
int errors_[kNumDirections];
|
||||
@ -123,11 +124,6 @@ class NaiveConnection {
|
||||
bool can_push_to_server_;
|
||||
int early_pull_result_;
|
||||
|
||||
int num_paddings_[kNumDirections];
|
||||
PaddingState read_padding_state_;
|
||||
int payload_length_;
|
||||
int padding_length_;
|
||||
|
||||
bool full_duplex_;
|
||||
|
||||
TimeFunc time_func_;
|
||||
|
117
src/net/tools/naive/naive_padding_framer.cc
Normal file
117
src/net/tools/naive/naive_padding_framer.cc
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2023 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_padding_framer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace net {
|
||||
NaivePaddingFramer::NaivePaddingFramer(std::optional<int> max_read_frames)
|
||||
: max_read_frames_(max_read_frames) {
|
||||
if (max_read_frames.has_value()) {
|
||||
CHECK_GE(*max_read_frames, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int NaivePaddingFramer::Read(const char* padded,
|
||||
int padded_len,
|
||||
char* payload_buf,
|
||||
int payload_buf_capacity) {
|
||||
// This check guarantees write_ptr does not overflow.
|
||||
CHECK_GE(payload_buf_capacity, padded_len);
|
||||
|
||||
char* write_ptr = payload_buf;
|
||||
while (padded_len > 0) {
|
||||
int copy_size;
|
||||
switch (state_) {
|
||||
case ReadState::kPayloadLength1:
|
||||
if (max_read_frames_.has_value() &&
|
||||
num_read_frames_ >= *max_read_frames_) {
|
||||
std::memcpy(write_ptr, padded, padded_len);
|
||||
padded += padded_len;
|
||||
write_ptr += padded_len;
|
||||
padded_len = 0;
|
||||
break;
|
||||
}
|
||||
read_payload_length_ = static_cast<uint8_t>(padded[0]);
|
||||
++padded;
|
||||
--padded_len;
|
||||
state_ = ReadState::kPayloadLength2;
|
||||
break;
|
||||
case ReadState::kPayloadLength2:
|
||||
read_payload_length_ =
|
||||
read_payload_length_ * 256 + static_cast<uint8_t>(padded[0]);
|
||||
++padded;
|
||||
--padded_len;
|
||||
state_ = ReadState::kPaddingLength1;
|
||||
break;
|
||||
case ReadState::kPaddingLength1:
|
||||
read_padding_length_ = static_cast<uint8_t>(padded[0]);
|
||||
++padded;
|
||||
--padded_len;
|
||||
state_ = ReadState::kPayload;
|
||||
break;
|
||||
case ReadState::kPayload:
|
||||
copy_size = std::min(read_payload_length_, padded_len);
|
||||
read_payload_length_ -= copy_size;
|
||||
if (read_payload_length_ == 0) {
|
||||
state_ = ReadState::kPadding;
|
||||
}
|
||||
|
||||
std::memcpy(write_ptr, padded, copy_size);
|
||||
padded += copy_size;
|
||||
write_ptr += copy_size;
|
||||
padded_len -= copy_size;
|
||||
break;
|
||||
case ReadState::kPadding:
|
||||
copy_size = std::min(read_padding_length_, padded_len);
|
||||
read_padding_length_ -= copy_size;
|
||||
if (read_padding_length_ == 0) {
|
||||
if (num_read_frames_ < std::numeric_limits<int>::max() - 1) {
|
||||
++num_read_frames_;
|
||||
}
|
||||
state_ = ReadState::kPayloadLength1;
|
||||
}
|
||||
|
||||
padded += copy_size;
|
||||
padded_len -= copy_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return write_ptr - payload_buf;
|
||||
}
|
||||
|
||||
int NaivePaddingFramer::Write(const char* payload_buf,
|
||||
int payload_buf_len,
|
||||
int padding_size,
|
||||
char* padded,
|
||||
int padded_capacity,
|
||||
int& payload_consumed_len) {
|
||||
CHECK_GE(payload_buf_len, 0);
|
||||
CHECK_LE(padding_size, max_padding_size());
|
||||
CHECK_GE(padding_size, 0);
|
||||
|
||||
payload_consumed_len = std::min(
|
||||
payload_buf_len, padded_capacity - frame_header_size() - padding_size);
|
||||
int padded_buf_len =
|
||||
frame_header_size() + payload_consumed_len + padding_size;
|
||||
|
||||
padded[0] = payload_consumed_len / 256;
|
||||
padded[1] = payload_consumed_len % 256;
|
||||
padded[2] = padding_size;
|
||||
std::memcpy(padded + frame_header_size(), payload_buf, payload_consumed_len);
|
||||
std::memset(padded + frame_header_size() + payload_consumed_len, '\0',
|
||||
padding_size);
|
||||
|
||||
if (num_written_frames_ < std::numeric_limits<int>::max() - 1) {
|
||||
++num_written_frames_;
|
||||
}
|
||||
return padded_buf_len;
|
||||
}
|
||||
} // namespace net
|
72
src/net/tools/naive/naive_padding_framer.h
Normal file
72
src/net/tools/naive/naive_padding_framer.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2023 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 <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace net {
|
||||
|
||||
// struct PaddedFrame {
|
||||
// uint16_t payload_size; // big-endian
|
||||
// uint8_t padding_size; // big-endian
|
||||
// uint8_t payload[payload_size];
|
||||
// uint8_t zeros[padding_size];
|
||||
// };
|
||||
class NaivePaddingFramer {
|
||||
public:
|
||||
// `max_read_frames`: Assumes the byte stream stops using the padding
|
||||
// framing after `max_read_frames` frames. If -1, it means
|
||||
// the byte stream always uses the padding framing.
|
||||
explicit NaivePaddingFramer(std::optional<int> max_read_frames);
|
||||
|
||||
int max_payload_size() const { return std::numeric_limits<uint16_t>::max(); }
|
||||
|
||||
int max_padding_size() const { return std::numeric_limits<uint8_t>::max(); }
|
||||
|
||||
int frame_header_size() const { return 3; }
|
||||
|
||||
int num_read_frames() const { return num_read_frames_; }
|
||||
|
||||
int num_written_frames() const { return num_written_frames_; }
|
||||
|
||||
// Reads `padded` for `padded_len` bytes and extracts unpadded payload to
|
||||
// `payload_buf`.
|
||||
// Returns the number of payload bytes extracted.
|
||||
// Returning zero indicates a pure padding instead of EOF.
|
||||
int Read(const char* padded,
|
||||
int padded_len,
|
||||
char* payload_buf,
|
||||
int payload_buf_capacity);
|
||||
|
||||
// Writes `payload_buf` for up to `payload_buf_len` bytes into `padded`.
|
||||
// Returns the number of padded bytes written.
|
||||
// If the padded bytes would exceed `padded_capacity`, the payload is
|
||||
// truncated to `payload_consumed_len`.
|
||||
int Write(const char* payload_buf,
|
||||
int payload_buf_len,
|
||||
int padding_size,
|
||||
char* padded,
|
||||
int padded_capacity,
|
||||
int& payload_consumed_len);
|
||||
|
||||
private:
|
||||
enum class ReadState {
|
||||
kPayloadLength1,
|
||||
kPayloadLength2,
|
||||
kPaddingLength1,
|
||||
kPayload,
|
||||
kPadding,
|
||||
};
|
||||
|
||||
std::optional<int> max_read_frames_;
|
||||
|
||||
ReadState state_ = ReadState::kPayloadLength1;
|
||||
int read_payload_length_ = 0;
|
||||
int read_padding_length_ = 0;
|
||||
int num_read_frames_ = 0;
|
||||
|
||||
int num_written_frames_ = 0;
|
||||
};
|
||||
} // namespace net
|
271
src/net/tools/naive/naive_padding_socket.cc
Normal file
271
src/net/tools/naive/naive_padding_socket.cc
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2023 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_padding_socket.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "base/rand_util.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
constexpr int kMaxBufferSize = 64 * 1024;
|
||||
constexpr int kFirstPaddings = 8;
|
||||
} // namespace
|
||||
|
||||
NaivePaddingSocket::NaivePaddingSocket(StreamSocket* transport_socket,
|
||||
PaddingType padding_type,
|
||||
Direction direction)
|
||||
: transport_socket_(transport_socket),
|
||||
padding_type_(padding_type),
|
||||
direction_(direction),
|
||||
read_buf_(base::MakeRefCounted<IOBuffer>(kMaxBufferSize)),
|
||||
framer_(kFirstPaddings) {}
|
||||
|
||||
NaivePaddingSocket::~NaivePaddingSocket() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
void NaivePaddingSocket::Disconnect() {
|
||||
transport_socket_->Disconnect();
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::Read(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
|
||||
switch (padding_type_) {
|
||||
case PaddingType::kNone:
|
||||
return ReadNoPadding(buf, buf_len, std::move(callback));
|
||||
case PaddingType::kVariant1:
|
||||
if (framer_.num_read_frames() < kFirstPaddings) {
|
||||
return ReadPaddingV1(buf, buf_len, std::move(callback));
|
||||
} else {
|
||||
return ReadNoPadding(buf, buf_len, std::move(callback));
|
||||
}
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::ReadNoPadding(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback) {
|
||||
int rv = transport_socket_->Read(
|
||||
buf, buf_len,
|
||||
base::BindOnce(&NaivePaddingSocket::OnReadNoPaddingComplete,
|
||||
base::Unretained(this), std::move(callback)));
|
||||
return rv;
|
||||
}
|
||||
|
||||
void NaivePaddingSocket::OnReadNoPaddingComplete(
|
||||
CompletionOnceCallback callback,
|
||||
int rv) {
|
||||
DCHECK_NE(ERR_IO_PENDING, rv);
|
||||
DCHECK(callback);
|
||||
|
||||
std::move(callback).Run(rv);
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::ReadPaddingV1(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback) {
|
||||
DCHECK(!callback.is_null());
|
||||
DCHECK(read_user_buf_ == nullptr);
|
||||
|
||||
// Truncates user requested buf len if it is too large for the padding
|
||||
// buffer.
|
||||
buf_len = std::min(buf_len, kMaxBufferSize);
|
||||
read_user_buf_ = buf;
|
||||
read_user_buf_len_ = buf_len;
|
||||
|
||||
int rv = ReadPaddingV1Payload();
|
||||
|
||||
if (rv == ERR_IO_PENDING) {
|
||||
read_callback_ = std::move(callback);
|
||||
return rv;
|
||||
}
|
||||
|
||||
read_user_buf_ = nullptr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void NaivePaddingSocket::OnReadPaddingV1Complete(int rv) {
|
||||
DCHECK_NE(ERR_IO_PENDING, rv);
|
||||
DCHECK(read_callback_);
|
||||
DCHECK(read_user_buf_ != nullptr);
|
||||
|
||||
if (rv > 0) {
|
||||
rv = framer_.Read(read_buf_->data(), rv, read_user_buf_->data(),
|
||||
read_user_buf_len_);
|
||||
if (rv == 0) {
|
||||
rv = ReadPaddingV1Payload();
|
||||
if (rv == ERR_IO_PENDING)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Must reset read_user_buf_ before invoking read_callback_, which may reenter
|
||||
// Read().
|
||||
read_user_buf_ = nullptr;
|
||||
|
||||
std::move(read_callback_).Run(rv);
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::ReadPaddingV1Payload() {
|
||||
for (;;) {
|
||||
int rv = transport_socket_->Read(
|
||||
read_buf_.get(), read_user_buf_len_,
|
||||
base::BindOnce(&NaivePaddingSocket::OnReadPaddingV1Complete,
|
||||
base::Unretained(this)));
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
rv = framer_.Read(read_buf_->data(), rv, read_user_buf_->data(),
|
||||
read_user_buf_len_);
|
||||
if (rv > 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::Write(
|
||||
IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
switch (padding_type_) {
|
||||
case PaddingType::kNone:
|
||||
return WriteNoPadding(buf, buf_len, std::move(callback),
|
||||
traffic_annotation);
|
||||
case PaddingType::kVariant1:
|
||||
if (framer_.num_written_frames() < kFirstPaddings) {
|
||||
return WritePaddingV1(buf, buf_len, std::move(callback),
|
||||
traffic_annotation);
|
||||
} else {
|
||||
return WriteNoPadding(buf, buf_len, std::move(callback),
|
||||
traffic_annotation);
|
||||
}
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::WriteNoPadding(
|
||||
IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
return transport_socket_->Write(
|
||||
buf, buf_len,
|
||||
base::BindOnce(&NaivePaddingSocket::OnWriteNoPaddingComplete,
|
||||
base::Unretained(this), std::move(callback),
|
||||
traffic_annotation),
|
||||
traffic_annotation);
|
||||
}
|
||||
|
||||
void NaivePaddingSocket::OnWriteNoPaddingComplete(
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
int rv) {
|
||||
DCHECK_NE(ERR_IO_PENDING, rv);
|
||||
DCHECK(callback);
|
||||
|
||||
std::move(callback).Run(rv);
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::WritePaddingV1(
|
||||
IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
DCHECK(write_buf_ == nullptr);
|
||||
|
||||
auto padded = base::MakeRefCounted<IOBuffer>(kMaxBufferSize);
|
||||
int padding_size;
|
||||
if (direction_ == kServer) {
|
||||
if (buf_len < 100) {
|
||||
padding_size = base::RandInt(framer_.max_padding_size() - buf_len,
|
||||
framer_.max_padding_size());
|
||||
} else {
|
||||
padding_size = base::RandInt(0, framer_.max_padding_size());
|
||||
}
|
||||
} else {
|
||||
padding_size = base::RandInt(0, framer_.max_padding_size());
|
||||
}
|
||||
int write_buf_len =
|
||||
framer_.Write(buf->data(), buf_len, padding_size, padded->data(),
|
||||
kMaxBufferSize, write_user_payload_len_);
|
||||
// Using DrainableIOBuffer here because we do not want to
|
||||
// repeatedly encode the padding frames when short writes happen.
|
||||
write_buf_ =
|
||||
base::MakeRefCounted<DrainableIOBuffer>(std::move(padded), write_buf_len);
|
||||
|
||||
int rv = WritePaddingV1Drain(traffic_annotation);
|
||||
if (rv == ERR_IO_PENDING) {
|
||||
write_callback_ = std::move(callback);
|
||||
return rv;
|
||||
}
|
||||
|
||||
write_buf_ = nullptr;
|
||||
write_user_payload_len_ = 0;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void NaivePaddingSocket::OnWritePaddingV1Complete(
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
int rv) {
|
||||
DCHECK_NE(ERR_IO_PENDING, rv);
|
||||
DCHECK(write_callback_);
|
||||
DCHECK(write_buf_ != nullptr);
|
||||
|
||||
if (rv > 0) {
|
||||
write_buf_->DidConsume(rv);
|
||||
rv = WritePaddingV1Drain(traffic_annotation);
|
||||
if (rv == ERR_IO_PENDING)
|
||||
return;
|
||||
}
|
||||
|
||||
// Must reset these before invoking write_callback_, which may reenter
|
||||
// Write().
|
||||
write_buf_ = nullptr;
|
||||
write_user_payload_len_ = 0;
|
||||
|
||||
std::move(write_callback_).Run(rv);
|
||||
}
|
||||
|
||||
int NaivePaddingSocket::WritePaddingV1Drain(
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
DCHECK(write_buf_ != nullptr);
|
||||
|
||||
while (write_buf_->BytesRemaining() > 0) {
|
||||
int remaining = write_buf_->BytesRemaining();
|
||||
if (direction_ == kServer && write_user_payload_len_ > 400 &&
|
||||
write_user_payload_len_ < 1024) {
|
||||
remaining = std::min(remaining, base::RandInt(200, 300));
|
||||
}
|
||||
int rv = transport_socket_->Write(
|
||||
write_buf_.get(), remaining,
|
||||
base::BindOnce(&NaivePaddingSocket::OnWritePaddingV1Complete,
|
||||
base::Unretained(this), traffic_annotation),
|
||||
traffic_annotation);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
write_buf_->DidConsume(rv);
|
||||
}
|
||||
// Synchronously drained the buffer.
|
||||
return write_user_payload_len_;
|
||||
}
|
||||
|
||||
} // namespace net
|
105
src/net/tools/naive/naive_padding_socket.h
Normal file
105
src/net/tools/naive/naive_padding_socket.h
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2023 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_PADDING_SOCKET_H_
|
||||
#define NET_TOOLS_NAIVE_NAIVE_PADDING_SOCKET_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "net/base/address_list.h"
|
||||
#include "net/base/completion_once_callback.h"
|
||||
#include "net/base/completion_repeating_callback.h"
|
||||
#include "net/base/host_port_pair.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/tools/naive/naive_padding_framer.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class NaivePaddingSocket {
|
||||
public:
|
||||
NaivePaddingSocket(StreamSocket* transport_socket,
|
||||
PaddingType padding_type,
|
||||
Direction direction);
|
||||
|
||||
NaivePaddingSocket(const NaivePaddingSocket&) = delete;
|
||||
NaivePaddingSocket& operator=(const NaivePaddingSocket&) = delete;
|
||||
|
||||
// On destruction Disconnect() is called.
|
||||
~NaivePaddingSocket();
|
||||
|
||||
void Disconnect();
|
||||
|
||||
int Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
|
||||
|
||||
int Write(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
|
||||
private:
|
||||
int ReadNoPadding(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback);
|
||||
int WriteNoPadding(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
void OnReadNoPaddingComplete(CompletionOnceCallback callback, int rv);
|
||||
void OnWriteNoPaddingComplete(
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
int rv);
|
||||
|
||||
int ReadPaddingV1(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback);
|
||||
int WritePaddingV1(IOBuffer* buf,
|
||||
int buf_len,
|
||||
CompletionOnceCallback callback,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
void OnReadPaddingV1Complete(int rv);
|
||||
void OnWritePaddingV1Complete(
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
int rv);
|
||||
|
||||
// Exhausts synchronous reads if it is a pure padding
|
||||
// so this does not return zero for non-EOF condition.
|
||||
int ReadPaddingV1Payload();
|
||||
|
||||
int WritePaddingV1Drain(
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
|
||||
// Stores the underlying socket.
|
||||
// Non-owning because this socket does not take part in the client socket pool
|
||||
// handling and making it owning the transport socket may interfere badly
|
||||
// with the client socket pool.
|
||||
StreamSocket* transport_socket_;
|
||||
|
||||
PaddingType padding_type_;
|
||||
Direction direction_;
|
||||
|
||||
IOBuffer* read_user_buf_ = nullptr;
|
||||
int read_user_buf_len_ = 0;
|
||||
CompletionOnceCallback read_callback_;
|
||||
scoped_refptr<IOBuffer> read_buf_;
|
||||
|
||||
int write_user_payload_len_ = 0;
|
||||
CompletionOnceCallback write_callback_;
|
||||
scoped_refptr<DrainableIOBuffer> write_buf_;
|
||||
|
||||
NaivePaddingFramer framer_;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_TOOLS_NAIVE_NAIVE_PADDING_SOCKET_H_
|
44
src/net/tools/naive/naive_protocol.cc
Normal file
44
src/net/tools/naive/naive_protocol.cc
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2023 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_protocol.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
|
||||
namespace net {
|
||||
std::optional<PaddingType> ParsePaddingType(base::StringPiece str) {
|
||||
if (str == "0") {
|
||||
return PaddingType::kNone;
|
||||
} else if (str == "1") {
|
||||
return PaddingType::kVariant1;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ToString(PaddingType value) {
|
||||
switch (value) {
|
||||
case PaddingType::kNone:
|
||||
return "0";
|
||||
case PaddingType::kVariant1:
|
||||
return "1";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char* ToReadableString(PaddingType value) {
|
||||
switch (value) {
|
||||
case PaddingType::kNone:
|
||||
return "None";
|
||||
case PaddingType::kVariant1:
|
||||
return "Variant1";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace net
|
@ -4,6 +4,11 @@
|
||||
#ifndef NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
||||
#define NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/strings/string_piece_forward.h"
|
||||
|
||||
namespace net {
|
||||
enum class ClientProtocol {
|
||||
kSocks5,
|
||||
@ -20,5 +25,39 @@ enum Direction {
|
||||
kNone = 2,
|
||||
};
|
||||
|
||||
enum class PaddingType {
|
||||
// Wire format: "0".
|
||||
kNone = 0,
|
||||
|
||||
// Pads the first 8 reads and writes with padding bytes of random size
|
||||
// uniformly distributed in [0, 255].
|
||||
// struct PaddedFrame {
|
||||
// uint8_t original_data_size_high; // original_data_size / 256
|
||||
// uint8_t original_data_size_low; // original_data_size % 256
|
||||
// uint8_t padding_size;
|
||||
// uint8_t original_data[original_data_size];
|
||||
// uint8_t zeros[padding_size];
|
||||
// };
|
||||
// Wire format: "1".
|
||||
kVariant1 = 1,
|
||||
};
|
||||
|
||||
// Returns empty if `str` is invalid.
|
||||
std::optional<PaddingType> ParsePaddingType(base::StringPiece str);
|
||||
|
||||
const char* ToString(PaddingType value);
|
||||
|
||||
const char* ToReadableString(PaddingType value);
|
||||
|
||||
constexpr const char* kPaddingHeader = "padding";
|
||||
|
||||
// Contains a comma separated list of requested padding types.
|
||||
// Preferred types come first.
|
||||
constexpr const char* kPaddingTypeRequestHeader = "padding-type-request";
|
||||
|
||||
// Contains a single number representing the negotiated padding type.
|
||||
// Must be one of PaddingType.
|
||||
constexpr const char* kPaddingTypeReplyHeader = "padding-type-reply";
|
||||
|
||||
} // namespace net
|
||||
#endif // NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "net/tools/naive/naive_proxy.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
@ -20,7 +21,7 @@
|
||||
#include "net/socket/client_socket_pool_manager.h"
|
||||
#include "net/socket/server_socket.h"
|
||||
#include "net/socket/stream_socket.h"
|
||||
#include "net/tools/naive/http_proxy_socket.h"
|
||||
#include "net/tools/naive/http_proxy_server_socket.h"
|
||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||
#include "net/tools/naive/socks5_server_socket.h"
|
||||
|
||||
@ -33,7 +34,8 @@ NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
|
||||
int concurrency,
|
||||
RedirectResolver* resolver,
|
||||
HttpNetworkSession* session,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation)
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const std::vector<PaddingType>& supported_padding_types)
|
||||
: listen_socket_(std::move(listen_socket)),
|
||||
protocol_(protocol),
|
||||
listen_user_(listen_user),
|
||||
@ -44,7 +46,8 @@ NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
|
||||
net_log_(
|
||||
NetLogWithSource::Make(session->net_log(), NetLogSourceType::NONE)),
|
||||
last_id_(0),
|
||||
traffic_annotation_(traffic_annotation) {
|
||||
traffic_annotation_(traffic_annotation),
|
||||
supported_padding_types_(supported_padding_types) {
|
||||
const auto& proxy_config = static_cast<ConfiguredProxyResolutionService*>(
|
||||
session_->proxy_resolution_service())
|
||||
->config();
|
||||
@ -125,9 +128,9 @@ void NaiveProxy::DoConnect() {
|
||||
listen_user_, listen_pass_,
|
||||
traffic_annotation_);
|
||||
} else if (protocol_ == ClientProtocol::kHttp) {
|
||||
socket = std::make_unique<HttpProxySocket>(std::move(accepted_socket_),
|
||||
padding_detector_delegate.get(),
|
||||
traffic_annotation_);
|
||||
socket = std::make_unique<HttpProxyServerSocket>(
|
||||
std::move(accepted_socket_), padding_detector_delegate.get(),
|
||||
traffic_annotation_, supported_padding_types_);
|
||||
} else if (protocol_ == ClientProtocol::kRedir) {
|
||||
socket = std::move(accepted_socket_);
|
||||
} else {
|
||||
@ -135,7 +138,8 @@ void NaiveProxy::DoConnect() {
|
||||
}
|
||||
|
||||
last_id_++;
|
||||
const auto& nak = network_anonymization_keys_[last_id_ % concurrency_];
|
||||
int tunnel_session_id = last_id_ % concurrency_;
|
||||
const auto& nak = network_anonymization_keys_[tunnel_session_id];
|
||||
auto connection_ptr = std::make_unique<NaiveConnection>(
|
||||
last_id_, protocol_, std::move(padding_detector_delegate), proxy_info_,
|
||||
server_ssl_config_, proxy_ssl_config_, resolver_, session_, nak, net_log_,
|
||||
|
@ -2,12 +2,12 @@
|
||||
// Copyright 2018 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_H_
|
||||
#define NET_TOOLS_NAIVE_NAIVE_PROXY_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
@ -38,7 +38,8 @@ class NaiveProxy {
|
||||
int concurrency,
|
||||
RedirectResolver* resolver,
|
||||
HttpNetworkSession* session,
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||
const std::vector<PaddingType>& supported_padding_types);
|
||||
~NaiveProxy();
|
||||
NaiveProxy(const NaiveProxy&) = delete;
|
||||
NaiveProxy& operator=(const NaiveProxy&) = delete;
|
||||
@ -82,6 +83,8 @@ class NaiveProxy {
|
||||
|
||||
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
||||
|
||||
std::vector<PaddingType> supported_padding_types_;
|
||||
|
||||
base::WeakPtrFactory<NaiveProxy> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
|
@ -426,8 +426,9 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
||||
builder.SetCertVerifier(
|
||||
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
|
||||
|
||||
builder.set_proxy_delegate(
|
||||
std::make_unique<NaiveProxyDelegate>(params.extra_headers));
|
||||
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(
|
||||
params.extra_headers,
|
||||
std::vector<PaddingType>{PaddingType::kVariant1, PaddingType::kNone}));
|
||||
|
||||
auto context = builder.Build();
|
||||
|
||||
@ -584,10 +585,12 @@ int main(int argc, char* argv[]) {
|
||||
params.resolver_prefix);
|
||||
}
|
||||
|
||||
net::NaiveProxy naive_proxy(std::move(listen_socket), params.protocol,
|
||||
params.listen_user, params.listen_pass,
|
||||
params.concurrency, resolver.get(), session,
|
||||
kTrafficAnnotation);
|
||||
net::NaiveProxy naive_proxy(
|
||||
std::move(listen_socket), params.protocol, params.listen_user,
|
||||
params.listen_pass, params.concurrency, resolver.get(), session,
|
||||
kTrafficAnnotation,
|
||||
std::vector<net::PaddingType>{net::PaddingType::kVariant1,
|
||||
net::PaddingType::kNone});
|
||||
|
||||
base::RunLoop().Run();
|
||||
|
||||
|
@ -3,10 +3,13 @@
|
||||
// found in the LICENSE file.
|
||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/strings/string_piece_forward.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "net/base/proxy_string_util.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
@ -45,9 +48,18 @@ void FillNonindexHeaderValue(uint64_t unique_bits, char* buf, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
NaiveProxyDelegate::NaiveProxyDelegate(const HttpRequestHeaders& extra_headers)
|
||||
NaiveProxyDelegate::NaiveProxyDelegate(
|
||||
const HttpRequestHeaders& extra_headers,
|
||||
const std::vector<PaddingType>& supported_padding_types)
|
||||
: extra_headers_(extra_headers) {
|
||||
InitializeNonindexCodes();
|
||||
|
||||
std::vector<base::StringPiece> padding_type_strs;
|
||||
for (PaddingType padding_type : supported_padding_types) {
|
||||
padding_type_strs.push_back(ToString(padding_type));
|
||||
}
|
||||
extra_headers_.SetHeader(kPaddingTypeRequestHeader,
|
||||
base::JoinString(padding_type_strs, ", "));
|
||||
}
|
||||
|
||||
NaiveProxyDelegate::~NaiveProxyDelegate() = default;
|
||||
@ -55,48 +67,81 @@ NaiveProxyDelegate::~NaiveProxyDelegate() = default;
|
||||
void NaiveProxyDelegate::OnBeforeTunnelRequest(
|
||||
const ProxyServer& proxy_server,
|
||||
HttpRequestHeaders* extra_headers) {
|
||||
// Not possible to negotiate padding capability given the underlying
|
||||
// protocols.
|
||||
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);
|
||||
extra_headers->SetHeader(kPaddingHeader, 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) {
|
||||
if (padding_type_by_server_[proxy_server].has_value()) {
|
||||
extra_headers->SetHeader("fastopen", "1");
|
||||
}
|
||||
extra_headers->MergeFrom(extra_headers_);
|
||||
}
|
||||
|
||||
std::optional<PaddingType> NaiveProxyDelegate::ParsePaddingHeaders(
|
||||
const HttpResponseHeaders& headers) {
|
||||
bool has_padding = headers.HasHeader(kPaddingHeader);
|
||||
std::string padding_type_reply;
|
||||
bool has_padding_type_reply =
|
||||
headers.GetNormalizedHeader(kPaddingTypeReplyHeader, &padding_type_reply);
|
||||
|
||||
if (!has_padding_type_reply) {
|
||||
// Backward compatibility with before kVariant1 when the padding-version
|
||||
// header does not exist.
|
||||
if (has_padding) {
|
||||
return PaddingType::kVariant1;
|
||||
} else {
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
std::optional<PaddingType> padding_type =
|
||||
ParsePaddingType(padding_type_reply);
|
||||
if (!padding_type.has_value()) {
|
||||
LOG(ERROR) << "Received invalid padding type: " << padding_type_reply;
|
||||
}
|
||||
return padding_type;
|
||||
}
|
||||
|
||||
Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
||||
const ProxyServer& proxy_server,
|
||||
const HttpResponseHeaders& response_headers) {
|
||||
// Not possible to negotiate padding capability given the underlying
|
||||
// protocols.
|
||||
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 " << ProxyServerToProxyUri(proxy_server)
|
||||
<< (padding ? " detected" : " undetected");
|
||||
std::optional<PaddingType> new_padding_type =
|
||||
ParsePaddingHeaders(response_headers);
|
||||
if (!new_padding_type.has_value()) {
|
||||
return ERR_INVALID_RESPONSE;
|
||||
}
|
||||
std::optional<PaddingType>& padding_type =
|
||||
padding_type_by_server_[proxy_server];
|
||||
if (!padding_type.has_value() || padding_type != new_padding_type) {
|
||||
LOG(INFO) << ProxyServerToProxyUri(proxy_server)
|
||||
<< " negotiated padding type: "
|
||||
<< ToReadableString(*new_padding_type);
|
||||
padding_type = new_padding_type;
|
||||
}
|
||||
padding_state = new_state;
|
||||
return OK;
|
||||
}
|
||||
|
||||
PaddingSupport NaiveProxyDelegate::GetProxyServerPaddingSupport(
|
||||
std::optional<PaddingType> NaiveProxyDelegate::GetProxyServerPaddingType(
|
||||
const ProxyServer& proxy_server) {
|
||||
// Not possible to detect padding capability given underlying protocol.
|
||||
// Not possible to negotiate padding capability given the underlying
|
||||
// protocols.
|
||||
if (proxy_server.is_direct() || proxy_server.is_socks())
|
||||
return PaddingSupport::kIncapable;
|
||||
return PaddingType::kNone;
|
||||
|
||||
return padding_state_by_server_[proxy_server];
|
||||
return padding_type_by_server_[proxy_server];
|
||||
}
|
||||
|
||||
PaddingDetectorDelegate::PaddingDetectorDelegate(
|
||||
@ -105,55 +150,32 @@ PaddingDetectorDelegate::PaddingDetectorDelegate(
|
||||
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) {}
|
||||
client_protocol_(client_protocol) {}
|
||||
|
||||
PaddingDetectorDelegate::~PaddingDetectorDelegate() = default;
|
||||
|
||||
bool PaddingDetectorDelegate::IsPaddingSupportKnown() {
|
||||
auto c = GetClientPaddingSupport();
|
||||
auto s = GetServerPaddingSupport();
|
||||
return c != PaddingSupport::kUnknown && s != PaddingSupport::kUnknown;
|
||||
void PaddingDetectorDelegate::SetClientPaddingType(PaddingType padding_type) {
|
||||
detected_client_padding_type_ = padding_type;
|
||||
}
|
||||
|
||||
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.
|
||||
std::optional<PaddingType> PaddingDetectorDelegate::GetClientPaddingType() {
|
||||
// Not possible to negotiate padding capability given the underlying
|
||||
// protocols.
|
||||
if (client_protocol_ == ClientProtocol::kSocks5) {
|
||||
return PaddingSupport::kIncapable;
|
||||
return PaddingType::kNone;
|
||||
} else if (client_protocol_ == ClientProtocol::kRedir) {
|
||||
return PaddingSupport::kIncapable;
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
|
||||
return detected_client_padding_support_;
|
||||
return detected_client_padding_type_;
|
||||
}
|
||||
|
||||
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_;
|
||||
std::optional<PaddingType> PaddingDetectorDelegate::GetServerPaddingType() {
|
||||
if (cached_server_padding_type_.has_value())
|
||||
return cached_server_padding_type_;
|
||||
cached_server_padding_type_ =
|
||||
naive_proxy_delegate_->GetProxyServerPaddingType(proxy_server_);
|
||||
return cached_server_padding_type_;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -6,12 +6,15 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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/http/http_request_headers.h"
|
||||
#include "net/proxy_resolution/proxy_retry_info.h"
|
||||
#include "net/tools/naive/naive_protocol.h"
|
||||
#include "url/gurl.h"
|
||||
@ -23,18 +26,11 @@ void InitializeNonindexCodes();
|
||||
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(const HttpRequestHeaders& extra_headers,
|
||||
const std::vector<PaddingType>& supported_padding_types);
|
||||
~NaiveProxyDelegate() override;
|
||||
|
||||
void OnResolveProxy(const GURL& url,
|
||||
@ -51,18 +47,25 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
||||
const ProxyServer& proxy_server,
|
||||
const HttpResponseHeaders& response_headers) override;
|
||||
|
||||
PaddingSupport GetProxyServerPaddingSupport(const ProxyServer& proxy_server);
|
||||
// Returns empty if the padding type has not been negotiated.
|
||||
std::optional<PaddingType> GetProxyServerPaddingType(
|
||||
const ProxyServer& proxy_server);
|
||||
|
||||
private:
|
||||
const HttpRequestHeaders& extra_headers_;
|
||||
std::map<ProxyServer, PaddingSupport> padding_state_by_server_;
|
||||
std::optional<PaddingType> ParsePaddingHeaders(
|
||||
const HttpResponseHeaders& headers);
|
||||
|
||||
HttpRequestHeaders extra_headers_;
|
||||
|
||||
// Empty value means padding type has not been negotiated.
|
||||
std::map<ProxyServer, std::optional<PaddingType>> padding_type_by_server_;
|
||||
};
|
||||
|
||||
class ClientPaddingDetectorDelegate {
|
||||
public:
|
||||
virtual ~ClientPaddingDetectorDelegate() = default;
|
||||
|
||||
virtual void SetClientPaddingSupport(PaddingSupport padding_support) = 0;
|
||||
virtual void SetClientPaddingType(PaddingType padding_type) = 0;
|
||||
};
|
||||
|
||||
class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
|
||||
@ -72,22 +75,19 @@ class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
|
||||
ClientProtocol client_protocol);
|
||||
~PaddingDetectorDelegate() override;
|
||||
|
||||
bool IsPaddingSupportKnown();
|
||||
Direction GetPaddingDirection();
|
||||
void SetClientPaddingSupport(PaddingSupport padding_support) override;
|
||||
std::optional<PaddingType> GetClientPaddingType();
|
||||
std::optional<PaddingType> GetServerPaddingType();
|
||||
void SetClientPaddingType(PaddingType padding_type) override;
|
||||
|
||||
private:
|
||||
PaddingSupport GetClientPaddingSupport();
|
||||
PaddingSupport GetServerPaddingSupport();
|
||||
|
||||
NaiveProxyDelegate* naive_proxy_delegate_;
|
||||
const ProxyServer& proxy_server_;
|
||||
ClientProtocol client_protocol_;
|
||||
|
||||
PaddingSupport detected_client_padding_support_;
|
||||
std::optional<PaddingType> detected_client_padding_type_;
|
||||
// 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_;
|
||||
std::optional<PaddingType> cached_server_padding_type_;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
10
tools/list-tls-sni-stream.sh
Executable file
10
tools/list-tls-sni-stream.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
if [ ! "$1" ]; then
|
||||
echo "Usage: $0 PCAP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
file="$1"
|
||||
# Remember to disable segmentation offload so pcap files won't capture packets larger than MTU:
|
||||
# sudo ethtool --offload eth0 gso off gro off
|
||||
tshark -2 -r "$file" -R tls.handshake.extensions_server_name -T fields -e tls.handshake.extensions_server_name -e tcp.stream
|
118
tools/parse-pcap-stream.py
Executable file
118
tools/parse-pcap-stream.py
Executable file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
|
||||
class TlsStreamParser:
|
||||
STATE_CONTENT_TYPE = 0
|
||||
STATE_VERSION_BYTE0 = 1
|
||||
STATE_VERSION_BYTE1 = 2
|
||||
STATE_LENGTH_BYTE0 = 3
|
||||
STATE_LENGTH_BYTE1 = 4
|
||||
STATE_DATA = 5
|
||||
|
||||
TLS_HEADER_SIZE = 5
|
||||
|
||||
def __init__(self):
|
||||
self.state = self.STATE_CONTENT_TYPE
|
||||
self.current_length = None
|
||||
self.current_remaining = None
|
||||
|
||||
def read(self, data):
|
||||
record_parts = []
|
||||
i = 0
|
||||
tls_consumed_bytes = 0
|
||||
while i < len(data):
|
||||
if self.state == self.STATE_CONTENT_TYPE:
|
||||
# TODO: add content type description
|
||||
content_type = data[i]
|
||||
self.state = self.STATE_VERSION_BYTE0
|
||||
i += 1
|
||||
tls_consumed_bytes += 1
|
||||
elif self.state == self.STATE_VERSION_BYTE0:
|
||||
self.state = self.STATE_VERSION_BYTE1
|
||||
i += 1
|
||||
tls_consumed_bytes += 1
|
||||
elif self.state == self.STATE_VERSION_BYTE1:
|
||||
self.state = self.STATE_LENGTH_BYTE0
|
||||
i += 1
|
||||
tls_consumed_bytes += 1
|
||||
elif self.state == self.STATE_LENGTH_BYTE0:
|
||||
self.current_length = data[i]
|
||||
self.state = self.STATE_LENGTH_BYTE1
|
||||
i += 1
|
||||
tls_consumed_bytes += 1
|
||||
elif self.state == self.STATE_LENGTH_BYTE1:
|
||||
self.current_length = self.current_length * 256 + data[i]
|
||||
self.current_remaining = self.current_length
|
||||
self.state = self.STATE_DATA
|
||||
i += 1
|
||||
tls_consumed_bytes += 1
|
||||
elif self.state == self.STATE_DATA:
|
||||
consume_data = min(self.current_remaining, len(data) - i)
|
||||
self.current_remaining -= consume_data
|
||||
i += consume_data
|
||||
tls_consumed_bytes += consume_data
|
||||
if self.current_remaining == 0:
|
||||
record_parts.append(
|
||||
(tls_consumed_bytes, self.TLS_HEADER_SIZE + self.current_length))
|
||||
tls_consumed_bytes = 0
|
||||
self.current_length = None
|
||||
self.state = self.STATE_CONTENT_TYPE
|
||||
if tls_consumed_bytes:
|
||||
if self.current_length is None:
|
||||
record_parts.append((tls_consumed_bytes, '?'))
|
||||
else:
|
||||
record_parts.append(
|
||||
(tls_consumed_bytes, self.TLS_HEADER_SIZE + self.current_length))
|
||||
return record_parts
|
||||
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print(f'Usage: {sys.argv[0]} PCAP_FILE STREAM_ID')
|
||||
os.exit(1)
|
||||
|
||||
file = sys.argv[1]
|
||||
stream_id = sys.argv[2]
|
||||
result = subprocess.run(['tshark', '-2', '-r', file, '-q', '-z',
|
||||
f'follow,tcp,yaml,{stream_id}'], capture_output=True, check=True, text=True)
|
||||
|
||||
follow_result = yaml.safe_load(result.stdout)
|
||||
LOCAL_PEER = 0
|
||||
REMOTE_PEER = 1
|
||||
assert follow_result['peers'][REMOTE_PEER][
|
||||
'port'] == 443, f"assuming the remote peer is the TLS server: {follow_result['peers']}"
|
||||
packets = follow_result['packets']
|
||||
|
||||
upload_stream = TlsStreamParser()
|
||||
download_stream = TlsStreamParser()
|
||||
rtt = packets[1]['timestamp'] - packets[0]['timestamp']
|
||||
time_unit = rtt / 2
|
||||
local_timestamp_first = packets[0]['timestamp']
|
||||
mitm_timestamp_first = local_timestamp_first + rtt / 4
|
||||
min_mitm_timestamp_up = packets[0]['timestamp']
|
||||
min_mitm_timestamp_down = packets[0]['timestamp']
|
||||
for packet in packets:
|
||||
local_timestamp = packet['timestamp']
|
||||
|
||||
data = packet['data']
|
||||
if packet['peer'] == LOCAL_PEER:
|
||||
mitm_timestamp = local_timestamp + time_unit / 2
|
||||
mitm_timestamp = max(mitm_timestamp, min_mitm_timestamp_up)
|
||||
min_mitm_timestamp_up = mitm_timestamp
|
||||
|
||||
timestamp = (mitm_timestamp - mitm_timestamp_first) / time_unit
|
||||
record_parts = upload_stream.read(data)
|
||||
print('%.3f' % timestamp, len(data), ','.join(
|
||||
f'{i}/{j}' for i, j in record_parts))
|
||||
elif packet['peer'] == REMOTE_PEER:
|
||||
mitm_timestamp = local_timestamp - time_unit / 2
|
||||
mitm_timestamp = max(mitm_timestamp, min_mitm_timestamp_down)
|
||||
min_mitm_timestamp_down = mitm_timestamp
|
||||
|
||||
timestamp = (mitm_timestamp - mitm_timestamp_first) / time_unit
|
||||
record_parts = download_stream.read(data)
|
||||
print('%.3f' % timestamp, -len(data),
|
||||
','.join(f'{i}/{j}' for i, j in record_parts))
|
16
tools/sample-traffic.sh
Executable file
16
tools/sample-traffic.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
if [ ! "$1" ]; then
|
||||
echo "Usage: $0 IFACE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
iface="$1"
|
||||
sudo echo
|
||||
|
||||
sudo tcpdump -i "$1" -s0 -w microsoft-direct.pcap &
|
||||
chromium --user-data-dir=$(mktemp -d) https://www.microsoft.com/en-us/
|
||||
sudo pkill tcpdump
|
||||
|
||||
sudo tcpdump -i "$1" -s0 -w microsoft-proxy.pcap &
|
||||
chromium --proxy-server=socks5://127.0.0.1:1080 --user-data-dir=$(mktemp -d) https://www.microsoft.com/en-us/
|
||||
sudo pkill tcpdump &
|
Loading…
x
Reference in New Issue
Block a user