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 = [
|
sources = [
|
||||||
"tools/naive/naive_connection.cc",
|
"tools/naive/naive_connection.cc",
|
||||||
"tools/naive/naive_connection.h",
|
"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.cc",
|
||||||
"tools/naive/naive_proxy.h",
|
"tools/naive/naive_proxy.h",
|
||||||
"tools/naive/naive_proxy_bin.cc",
|
"tools/naive/naive_proxy_bin.cc",
|
||||||
"tools/naive/naive_proxy_delegate.h",
|
"tools/naive/naive_proxy_delegate.h",
|
||||||
"tools/naive/naive_proxy_delegate.cc",
|
"tools/naive/naive_proxy_delegate.cc",
|
||||||
"tools/naive/http_proxy_socket.cc",
|
"tools/naive/http_proxy_server_socket.cc",
|
||||||
"tools/naive/http_proxy_socket.h",
|
"tools/naive/http_proxy_server_socket.h",
|
||||||
"tools/naive/redirect_resolver.h",
|
"tools/naive/redirect_resolver.h",
|
||||||
"tools/naive/redirect_resolver.cc",
|
"tools/naive/redirect_resolver.cc",
|
||||||
"tools/naive/socks5_server_socket.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
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// 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 <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
#include "base/functional/callback_helpers.h"
|
#include "base/functional/callback_helpers.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/rand_util.h"
|
#include "base/rand_util.h"
|
||||||
|
#include "base/strings/string_split.h"
|
||||||
#include "base/sys_byteorder.h"
|
#include "base/sys_byteorder.h"
|
||||||
#include "net/base/ip_address.h"
|
#include "net/base/ip_address.h"
|
||||||
#include "net/base/net_errors.h"
|
#include "net/base/net_errors.h"
|
||||||
@ -19,6 +22,7 @@
|
|||||||
#include "net/http/http_request_headers.h"
|
#include "net/http/http_request_headers.h"
|
||||||
#include "net/log/net_log.h"
|
#include "net/log/net_log.h"
|
||||||
#include "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_constants.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"
|
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
@ -33,11 +37,12 @@ constexpr int kMinPaddingSize = 30;
|
|||||||
constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
|
constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HttpProxySocket::HttpProxySocket(
|
HttpProxyServerSocket::HttpProxyServerSocket(
|
||||||
std::unique_ptr<StreamSocket> transport_socket,
|
std::unique_ptr<StreamSocket> transport_socket,
|
||||||
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation)
|
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||||
: io_callback_(base::BindRepeating(&HttpProxySocket::OnIOComplete,
|
const std::vector<PaddingType>& supported_padding_types)
|
||||||
|
: io_callback_(base::BindRepeating(&HttpProxyServerSocket::OnIOComplete,
|
||||||
base::Unretained(this))),
|
base::Unretained(this))),
|
||||||
transport_(std::move(transport_socket)),
|
transport_(std::move(transport_socket)),
|
||||||
padding_detector_delegate_(padding_detector_delegate),
|
padding_detector_delegate_(padding_detector_delegate),
|
||||||
@ -46,17 +51,18 @@ HttpProxySocket::HttpProxySocket(
|
|||||||
was_ever_used_(false),
|
was_ever_used_(false),
|
||||||
header_write_size_(-1),
|
header_write_size_(-1),
|
||||||
net_log_(transport_->NetLog()),
|
net_log_(transport_->NetLog()),
|
||||||
traffic_annotation_(traffic_annotation) {}
|
traffic_annotation_(traffic_annotation),
|
||||||
|
supported_padding_types_(supported_padding_types) {}
|
||||||
|
|
||||||
HttpProxySocket::~HttpProxySocket() {
|
HttpProxyServerSocket::~HttpProxyServerSocket() {
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const HostPortPair& HttpProxySocket::request_endpoint() const {
|
const HostPortPair& HttpProxyServerSocket::request_endpoint() const {
|
||||||
return request_endpoint_;
|
return request_endpoint_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::Connect(CompletionOnceCallback callback) {
|
int HttpProxyServerSocket::Connect(CompletionOnceCallback callback) {
|
||||||
DCHECK(transport_);
|
DCHECK(transport_);
|
||||||
DCHECK_EQ(STATE_NONE, next_state_);
|
DCHECK_EQ(STATE_NONE, next_state_);
|
||||||
DCHECK(!user_callback_);
|
DCHECK(!user_callback_);
|
||||||
@ -75,7 +81,7 @@ int HttpProxySocket::Connect(CompletionOnceCallback callback) {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpProxySocket::Disconnect() {
|
void HttpProxyServerSocket::Disconnect() {
|
||||||
completed_handshake_ = false;
|
completed_handshake_ = false;
|
||||||
transport_->Disconnect();
|
transport_->Disconnect();
|
||||||
|
|
||||||
@ -85,23 +91,23 @@ void HttpProxySocket::Disconnect() {
|
|||||||
user_callback_.Reset();
|
user_callback_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpProxySocket::IsConnected() const {
|
bool HttpProxyServerSocket::IsConnected() const {
|
||||||
return completed_handshake_ && transport_->IsConnected();
|
return completed_handshake_ && transport_->IsConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpProxySocket::IsConnectedAndIdle() const {
|
bool HttpProxyServerSocket::IsConnectedAndIdle() const {
|
||||||
return completed_handshake_ && transport_->IsConnectedAndIdle();
|
return completed_handshake_ && transport_->IsConnectedAndIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetLogWithSource& HttpProxySocket::NetLog() const {
|
const NetLogWithSource& HttpProxyServerSocket::NetLog() const {
|
||||||
return net_log_;
|
return net_log_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpProxySocket::WasEverUsed() const {
|
bool HttpProxyServerSocket::WasEverUsed() const {
|
||||||
return was_ever_used_;
|
return was_ever_used_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpProxySocket::WasAlpnNegotiated() const {
|
bool HttpProxyServerSocket::WasAlpnNegotiated() const {
|
||||||
if (transport_) {
|
if (transport_) {
|
||||||
return transport_->WasAlpnNegotiated();
|
return transport_->WasAlpnNegotiated();
|
||||||
}
|
}
|
||||||
@ -109,7 +115,7 @@ bool HttpProxySocket::WasAlpnNegotiated() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
NextProto HttpProxySocket::GetNegotiatedProtocol() const {
|
NextProto HttpProxyServerSocket::GetNegotiatedProtocol() const {
|
||||||
if (transport_) {
|
if (transport_) {
|
||||||
return transport_->GetNegotiatedProtocol();
|
return transport_->GetNegotiatedProtocol();
|
||||||
}
|
}
|
||||||
@ -117,7 +123,7 @@ NextProto HttpProxySocket::GetNegotiatedProtocol() const {
|
|||||||
return kProtoUnknown;
|
return kProtoUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpProxySocket::GetSSLInfo(SSLInfo* ssl_info) {
|
bool HttpProxyServerSocket::GetSSLInfo(SSLInfo* ssl_info) {
|
||||||
if (transport_) {
|
if (transport_) {
|
||||||
return transport_->GetSSLInfo(ssl_info);
|
return transport_->GetSSLInfo(ssl_info);
|
||||||
}
|
}
|
||||||
@ -125,19 +131,19 @@ bool HttpProxySocket::GetSSLInfo(SSLInfo* ssl_info) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t HttpProxySocket::GetTotalReceivedBytes() const {
|
int64_t HttpProxyServerSocket::GetTotalReceivedBytes() const {
|
||||||
return transport_->GetTotalReceivedBytes();
|
return transport_->GetTotalReceivedBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpProxySocket::ApplySocketTag(const SocketTag& tag) {
|
void HttpProxyServerSocket::ApplySocketTag(const SocketTag& tag) {
|
||||||
return transport_->ApplySocketTag(tag);
|
return transport_->ApplySocketTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read is called by the transport layer above to read. This can only be done
|
// Read is called by the transport layer above to read. This can only be done
|
||||||
// if the HTTP header is complete.
|
// if the HTTP header is complete.
|
||||||
int HttpProxySocket::Read(IOBuffer* buf,
|
int HttpProxyServerSocket::Read(IOBuffer* buf,
|
||||||
int buf_len,
|
int buf_len,
|
||||||
CompletionOnceCallback callback) {
|
CompletionOnceCallback callback) {
|
||||||
DCHECK(completed_handshake_);
|
DCHECK(completed_handshake_);
|
||||||
DCHECK_EQ(STATE_NONE, next_state_);
|
DCHECK_EQ(STATE_NONE, next_state_);
|
||||||
DCHECK(!user_callback_);
|
DCHECK(!user_callback_);
|
||||||
@ -159,7 +165,7 @@ int HttpProxySocket::Read(IOBuffer* buf,
|
|||||||
|
|
||||||
int rv = transport_->Read(
|
int rv = transport_->Read(
|
||||||
buf, buf_len,
|
buf, buf_len,
|
||||||
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
|
base::BindOnce(&HttpProxyServerSocket::OnReadWriteComplete,
|
||||||
base::Unretained(this), std::move(callback)));
|
base::Unretained(this), std::move(callback)));
|
||||||
if (rv > 0)
|
if (rv > 0)
|
||||||
was_ever_used_ = true;
|
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
|
// Write is called by the transport layer. This can only be done if the
|
||||||
// SOCKS handshake is complete.
|
// HTTP CONNECT request is complete.
|
||||||
int HttpProxySocket::Write(
|
int HttpProxyServerSocket::Write(
|
||||||
IOBuffer* buf,
|
IOBuffer* buf,
|
||||||
int buf_len,
|
int buf_len,
|
||||||
CompletionOnceCallback callback,
|
CompletionOnceCallback callback,
|
||||||
@ -180,7 +186,7 @@ int HttpProxySocket::Write(
|
|||||||
|
|
||||||
int rv = transport_->Write(
|
int rv = transport_->Write(
|
||||||
buf, buf_len,
|
buf, buf_len,
|
||||||
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
|
base::BindOnce(&HttpProxyServerSocket::OnReadWriteComplete,
|
||||||
base::Unretained(this), std::move(callback)),
|
base::Unretained(this), std::move(callback)),
|
||||||
traffic_annotation);
|
traffic_annotation);
|
||||||
if (rv > 0)
|
if (rv > 0)
|
||||||
@ -188,15 +194,15 @@ int HttpProxySocket::Write(
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::SetReceiveBufferSize(int32_t size) {
|
int HttpProxyServerSocket::SetReceiveBufferSize(int32_t size) {
|
||||||
return transport_->SetReceiveBufferSize(size);
|
return transport_->SetReceiveBufferSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::SetSendBufferSize(int32_t size) {
|
int HttpProxyServerSocket::SetSendBufferSize(int32_t size) {
|
||||||
return transport_->SetSendBufferSize(size);
|
return transport_->SetSendBufferSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpProxySocket::DoCallback(int result) {
|
void HttpProxyServerSocket::DoCallback(int result) {
|
||||||
DCHECK_NE(ERR_IO_PENDING, result);
|
DCHECK_NE(ERR_IO_PENDING, result);
|
||||||
DCHECK(user_callback_);
|
DCHECK(user_callback_);
|
||||||
|
|
||||||
@ -205,7 +211,7 @@ void HttpProxySocket::DoCallback(int result) {
|
|||||||
std::move(user_callback_).Run(result);
|
std::move(user_callback_).Run(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpProxySocket::OnIOComplete(int result) {
|
void HttpProxyServerSocket::OnIOComplete(int result) {
|
||||||
DCHECK_NE(STATE_NONE, next_state_);
|
DCHECK_NE(STATE_NONE, next_state_);
|
||||||
int rv = DoLoop(result);
|
int rv = DoLoop(result);
|
||||||
if (rv != ERR_IO_PENDING) {
|
if (rv != ERR_IO_PENDING) {
|
||||||
@ -213,8 +219,8 @@ void HttpProxySocket::OnIOComplete(int result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpProxySocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
void HttpProxyServerSocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
||||||
int result) {
|
int result) {
|
||||||
DCHECK_NE(ERR_IO_PENDING, result);
|
DCHECK_NE(ERR_IO_PENDING, result);
|
||||||
DCHECK(callback);
|
DCHECK(callback);
|
||||||
|
|
||||||
@ -223,7 +229,7 @@ void HttpProxySocket::OnReadWriteComplete(CompletionOnceCallback callback,
|
|||||||
std::move(callback).Run(result);
|
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);
|
DCHECK_NE(next_state_, STATE_NONE);
|
||||||
int rv = last_io_result;
|
int rv = last_io_result;
|
||||||
do {
|
do {
|
||||||
@ -253,14 +259,50 @@ int HttpProxySocket::DoLoop(int last_io_result) {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::DoHeaderRead() {
|
int HttpProxyServerSocket::DoHeaderRead() {
|
||||||
next_state_ = STATE_HEADER_READ_COMPLETE;
|
next_state_ = STATE_HEADER_READ_COMPLETE;
|
||||||
|
|
||||||
handshake_buf_ = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
handshake_buf_ = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||||
return transport_->Read(handshake_buf_.get(), kBufferSize, io_callback_);
|
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)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -273,23 +315,21 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
|||||||
return ERR_MSG_TOO_BIG;
|
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) {
|
if (header_end == std::string::npos) {
|
||||||
next_state_ = STATE_HEADER_READ;
|
next_state_ = STATE_HEADER_READ;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpProxyClientSocket uses CONNECT for all endpoints.
|
size_t first_line_end = buffer_.find("\r\n");
|
||||||
// GET is also supported.
|
size_t first_space = buffer_.find(' ');
|
||||||
auto first_line_end = buffer_.find("\r\n");
|
|
||||||
auto first_space = buffer_.find(' ');
|
|
||||||
bool is_http_1_0 = false;
|
bool is_http_1_0 = false;
|
||||||
if (first_space == std::string::npos || first_space + 1 >= first_line_end) {
|
if (first_space == std::string::npos || first_space + 1 >= first_line_end) {
|
||||||
return ERR_INVALID_ARGUMENT;
|
return ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
if (buffer_.compare(0, first_space, HttpRequestHeaders::kConnectMethod) ==
|
if (buffer_.compare(0, first_space, HttpRequestHeaders::kConnectMethod) ==
|
||||||
0) {
|
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) {
|
if (second_space == std::string::npos || second_space >= first_line_end) {
|
||||||
LOG(WARNING) << "Invalid request: " << buffer_.substr(0, first_line_end);
|
LOG(WARNING) << "Invalid request: " << buffer_.substr(0, first_line_end);
|
||||||
return ERR_INVALID_ARGUMENT;
|
return ERR_INVALID_ARGUMENT;
|
||||||
@ -301,7 +341,7 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
|||||||
is_http_1_0 = true;
|
is_http_1_0 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto second_line = first_line_end + 2;
|
size_t second_line = first_line_end + 2;
|
||||||
HttpRequestHeaders headers;
|
HttpRequestHeaders headers;
|
||||||
std::string headers_str;
|
std::string headers_str;
|
||||||
if (second_line < header_end) {
|
if (second_line < header_end) {
|
||||||
@ -328,13 +368,11 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
|||||||
request_endpoint_.set_port(80);
|
request_endpoint_.set_port(80);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headers.HasHeader("padding")) {
|
std::optional<PaddingType> padding_type = ParsePaddingHeaders(headers);
|
||||||
padding_detector_delegate_->SetClientPaddingSupport(
|
if (!padding_type.has_value()) {
|
||||||
PaddingSupport::kCapable);
|
return ERR_INVALID_ARGUMENT;
|
||||||
} else {
|
|
||||||
padding_detector_delegate_->SetClientPaddingSupport(
|
|
||||||
PaddingSupport::kIncapable);
|
|
||||||
}
|
}
|
||||||
|
padding_detector_delegate_->SetClientPaddingType(*padding_type);
|
||||||
|
|
||||||
if (is_http_1_0) {
|
if (is_http_1_0) {
|
||||||
// Regerate http header to make sure don't leak them to end servers
|
// Regerate http header to make sure don't leak them to end servers
|
||||||
@ -361,7 +399,7 @@ int HttpProxySocket::DoHeaderReadComplete(int result) {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::DoHeaderWrite() {
|
int HttpProxyServerSocket::DoHeaderWrite() {
|
||||||
next_state_ = STATE_HEADER_WRITE_COMPLETE;
|
next_state_ = STATE_HEADER_WRITE_COMPLETE;
|
||||||
|
|
||||||
// Adds padding.
|
// Adds padding.
|
||||||
@ -378,7 +416,7 @@ int HttpProxySocket::DoHeaderWrite() {
|
|||||||
io_callback_, traffic_annotation_);
|
io_callback_, traffic_annotation_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::DoHeaderWriteComplete(int result) {
|
int HttpProxyServerSocket::DoHeaderWriteComplete(int result) {
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -391,11 +429,11 @@ int HttpProxySocket::DoHeaderWriteComplete(int result) {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::GetPeerAddress(IPEndPoint* address) const {
|
int HttpProxyServerSocket::GetPeerAddress(IPEndPoint* address) const {
|
||||||
return transport_->GetPeerAddress(address);
|
return transport_->GetPeerAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpProxySocket::GetLocalAddress(IPEndPoint* address) const {
|
int HttpProxyServerSocket::GetLocalAddress(IPEndPoint* address) const {
|
||||||
return transport_->GetLocalAddress(address);
|
return transport_->GetLocalAddress(address);
|
||||||
}
|
}
|
||||||
|
|
@ -3,13 +3,14 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#ifndef NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
|
#ifndef NET_TOOLS_NAIVE_HTTP_PROXY_SERVER_SOCKET_H_
|
||||||
#define NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
|
#define NET_TOOLS_NAIVE_HTTP_PROXY_SERVER_SOCKET_H_
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/memory/scoped_refptr.h"
|
#include "base/memory/scoped_refptr.h"
|
||||||
#include "net/base/completion_once_callback.h"
|
#include "net/base/completion_once_callback.h"
|
||||||
@ -17,27 +18,31 @@
|
|||||||
#include "net/base/host_port_pair.h"
|
#include "net/base/host_port_pair.h"
|
||||||
#include "net/base/io_buffer.h"
|
#include "net/base/io_buffer.h"
|
||||||
#include "net/base/ip_endpoint.h"
|
#include "net/base/ip_endpoint.h"
|
||||||
|
#include "net/http/http_request_headers.h"
|
||||||
#include "net/log/net_log_with_source.h"
|
#include "net/log/net_log_with_source.h"
|
||||||
#include "net/socket/connection_attempts.h"
|
#include "net/socket/connection_attempts.h"
|
||||||
#include "net/socket/next_proto.h"
|
#include "net/socket/next_proto.h"
|
||||||
#include "net/socket/stream_socket.h"
|
#include "net/socket/stream_socket.h"
|
||||||
#include "net/ssl/ssl_info.h"
|
#include "net/ssl/ssl_info.h"
|
||||||
|
#include "net/tools/naive/naive_protocol.h"
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
struct NetworkTrafficAnnotationTag;
|
struct NetworkTrafficAnnotationTag;
|
||||||
class ClientPaddingDetectorDelegate;
|
class ClientPaddingDetectorDelegate;
|
||||||
|
|
||||||
// This StreamSocket is used to setup a HTTP CONNECT tunnel.
|
// This StreamSocket is used to setup a HTTP CONNECT tunnel.
|
||||||
class HttpProxySocket : public StreamSocket {
|
class HttpProxyServerSocket : public StreamSocket {
|
||||||
public:
|
public:
|
||||||
HttpProxySocket(std::unique_ptr<StreamSocket> transport_socket,
|
HttpProxyServerSocket(
|
||||||
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
std::unique_ptr<StreamSocket> transport_socket,
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
ClientPaddingDetectorDelegate* padding_detector_delegate,
|
||||||
HttpProxySocket(const HttpProxySocket&) = delete;
|
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||||
HttpProxySocket& operator=(const HttpProxySocket&) = delete;
|
const std::vector<PaddingType>& supported_padding_types);
|
||||||
|
HttpProxyServerSocket(const HttpProxyServerSocket&) = delete;
|
||||||
|
HttpProxyServerSocket& operator=(const HttpProxyServerSocket&) = delete;
|
||||||
|
|
||||||
// On destruction Disconnect() is called.
|
// On destruction Disconnect() is called.
|
||||||
~HttpProxySocket() override;
|
~HttpProxyServerSocket() override;
|
||||||
|
|
||||||
const HostPortPair& request_endpoint() const;
|
const HostPortPair& request_endpoint() const;
|
||||||
|
|
||||||
@ -89,6 +94,9 @@ class HttpProxySocket : public StreamSocket {
|
|||||||
int DoHeaderRead();
|
int DoHeaderRead();
|
||||||
int DoHeaderReadComplete(int result);
|
int DoHeaderReadComplete(int result);
|
||||||
|
|
||||||
|
std::optional<PaddingType> ParsePaddingHeaders(
|
||||||
|
const HttpRequestHeaders& headers);
|
||||||
|
|
||||||
CompletionRepeatingCallback io_callback_;
|
CompletionRepeatingCallback io_callback_;
|
||||||
|
|
||||||
// Stores the underlying socket.
|
// Stores the underlying socket.
|
||||||
@ -116,7 +124,9 @@ class HttpProxySocket : public StreamSocket {
|
|||||||
|
|
||||||
// Traffic annotation for socket control.
|
// Traffic annotation for socket control.
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
||||||
|
|
||||||
|
std::vector<PaddingType> supported_padding_types_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace net
|
} // 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/client_socket_pool_manager.h"
|
||||||
#include "net/socket/stream_socket.h"
|
#include "net/socket/stream_socket.h"
|
||||||
#include "net/spdy/spdy_session.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/redirect_resolver.h"
|
||||||
#include "net/tools/naive/socks5_server_socket.h"
|
#include "net/tools/naive/socks5_server_socket.h"
|
||||||
#include "url/scheme_host_port.h"
|
#include "url/scheme_host_port.h"
|
||||||
@ -45,9 +46,6 @@ namespace net {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int kBufferSize = 64 * 1024;
|
constexpr int kBufferSize = 64 * 1024;
|
||||||
constexpr int kFirstPaddings = 8;
|
|
||||||
constexpr int kPaddingHeaderSize = 3;
|
|
||||||
constexpr int kMaxPaddingSize = 255;
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
NaiveConnection::NaiveConnection(
|
NaiveConnection::NaiveConnection(
|
||||||
@ -76,14 +74,12 @@ NaiveConnection::NaiveConnection(
|
|||||||
next_state_(STATE_NONE),
|
next_state_(STATE_NONE),
|
||||||
client_socket_(std::move(accepted_socket)),
|
client_socket_(std::move(accepted_socket)),
|
||||||
server_socket_handle_(std::make_unique<ClientSocketHandle>()),
|
server_socket_handle_(std::make_unique<ClientSocketHandle>()),
|
||||||
sockets_{client_socket_.get(), nullptr},
|
sockets_{nullptr, nullptr},
|
||||||
errors_{OK, OK},
|
errors_{OK, OK},
|
||||||
write_pending_{false, false},
|
write_pending_{false, false},
|
||||||
early_pull_pending_(false),
|
early_pull_pending_(false),
|
||||||
can_push_to_server_(false),
|
can_push_to_server_(false),
|
||||||
early_pull_result_(ERR_IO_PENDING),
|
early_pull_result_(ERR_IO_PENDING),
|
||||||
num_paddings_{0, 0},
|
|
||||||
read_padding_state_(STATE_READ_PAYLOAD_LENGTH_1),
|
|
||||||
full_duplex_(false),
|
full_duplex_(false),
|
||||||
time_func_(&base::TimeTicks::Now),
|
time_func_(&base::TimeTicks::Now),
|
||||||
traffic_annotation_(traffic_annotation) {
|
traffic_annotation_(traffic_annotation) {
|
||||||
@ -181,11 +177,18 @@ int NaiveConnection::DoConnectClientComplete(int result) {
|
|||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
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
|
// For proxy client sockets, padding support detection is finished after the
|
||||||
// first server response which means there will be one missed early pull. For
|
// 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.
|
// 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_pending_ = false;
|
||||||
early_pull_result_ = 0;
|
early_pull_result_ = 0;
|
||||||
next_state_ = STATE_CONNECT_SERVER;
|
next_state_ = STATE_CONNECT_SERVER;
|
||||||
@ -215,7 +218,7 @@ int NaiveConnection::DoConnectServer() {
|
|||||||
origin = socket->request_endpoint();
|
origin = socket->request_endpoint();
|
||||||
} else if (protocol_ == ClientProtocol::kHttp) {
|
} else if (protocol_ == ClientProtocol::kHttp) {
|
||||||
const auto* socket =
|
const auto* socket =
|
||||||
static_cast<const HttpProxySocket*>(client_socket_.get());
|
static_cast<const HttpProxyServerSocket*>(client_socket_.get());
|
||||||
origin = socket->request_endpoint();
|
origin = socket->request_endpoint();
|
||||||
} else if (protocol_ == ClientProtocol::kRedir) {
|
} else if (protocol_ == ClientProtocol::kRedir) {
|
||||||
#if BUILDFLAG(IS_LINUX)
|
#if BUILDFLAG(IS_LINUX)
|
||||||
@ -284,8 +287,12 @@ int NaiveConnection::DoConnectServerComplete(int result) {
|
|||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
DCHECK(server_socket_handle_->socket());
|
std::optional<PaddingType> server_padding_type =
|
||||||
sockets_[kServer] = server_socket_handle_->socket();
|
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;
|
full_duplex_ = true;
|
||||||
next_state_ = STATE_NONE;
|
next_state_ = STATE_NONE;
|
||||||
@ -331,16 +338,7 @@ void NaiveConnection::Pull(Direction from, Direction to) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int read_size = kBufferSize;
|
int read_size = kBufferSize;
|
||||||
auto padding_direction = padding_detector_delegate_->GetPaddingDirection();
|
read_buffers_[from] = base::MakeRefCounted<IOBuffer>(kBufferSize);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
DCHECK(sockets_[from]);
|
DCHECK(sockets_[from]);
|
||||||
int rv = sockets_[from]->Read(
|
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) {
|
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>(
|
write_buffers_[to] = base::MakeRefCounted<DrainableIOBuffer>(
|
||||||
std::move(read_buffers_[from]), write_offset + write_size);
|
std::move(read_buffers_[from]), size);
|
||||||
if (write_offset) {
|
|
||||||
write_buffers_[to]->DidConsume(write_offset);
|
|
||||||
}
|
|
||||||
write_pending_[to] = true;
|
write_pending_[to] = true;
|
||||||
DCHECK(sockets_[to]);
|
DCHECK(sockets_[to]);
|
||||||
int rv = sockets_[to]->Write(
|
int rv = sockets_[to]->Write(
|
||||||
write_buffers_[to].get(), write_size,
|
write_buffers_[to].get(), write_buffers_[to]->BytesRemaining(),
|
||||||
base::BindRepeating(&NaiveConnection::OnPushComplete,
|
base::BindRepeating(&NaiveConnection::OnPushComplete,
|
||||||
weak_ptr_factory_.GetWeakPtr(), from, to),
|
weak_ptr_factory_.GetWeakPtr(), from, to),
|
||||||
traffic_annotation_);
|
traffic_annotation_);
|
||||||
@ -475,7 +377,7 @@ void NaiveConnection::Disconnect(Direction side) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NaiveConnection::IsConnected(Direction side) {
|
bool NaiveConnection::IsConnected(Direction side) {
|
||||||
return sockets_[side];
|
return sockets_[side] != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NaiveConnection::OnBothDisconnected() {
|
void NaiveConnection::OnBothDisconnected() {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "base/time/time.h"
|
#include "base/time/time.h"
|
||||||
#include "net/base/completion_once_callback.h"
|
#include "net/base/completion_once_callback.h"
|
||||||
#include "net/base/completion_repeating_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_protocol.h"
|
||||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ class NaiveConnection {
|
|||||||
std::unique_ptr<StreamSocket> client_socket_;
|
std::unique_ptr<StreamSocket> client_socket_;
|
||||||
std::unique_ptr<ClientSocketHandle> server_socket_handle_;
|
std::unique_ptr<ClientSocketHandle> server_socket_handle_;
|
||||||
|
|
||||||
StreamSocket* sockets_[kNumDirections];
|
std::unique_ptr<NaivePaddingSocket> sockets_[kNumDirections];
|
||||||
scoped_refptr<IOBuffer> read_buffers_[kNumDirections];
|
scoped_refptr<IOBuffer> read_buffers_[kNumDirections];
|
||||||
scoped_refptr<DrainableIOBuffer> write_buffers_[kNumDirections];
|
scoped_refptr<DrainableIOBuffer> write_buffers_[kNumDirections];
|
||||||
int errors_[kNumDirections];
|
int errors_[kNumDirections];
|
||||||
@ -123,11 +124,6 @@ class NaiveConnection {
|
|||||||
bool can_push_to_server_;
|
bool can_push_to_server_;
|
||||||
int early_pull_result_;
|
int early_pull_result_;
|
||||||
|
|
||||||
int num_paddings_[kNumDirections];
|
|
||||||
PaddingState read_padding_state_;
|
|
||||||
int payload_length_;
|
|
||||||
int padding_length_;
|
|
||||||
|
|
||||||
bool full_duplex_;
|
bool full_duplex_;
|
||||||
|
|
||||||
TimeFunc time_func_;
|
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_
|
#ifndef NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
||||||
#define 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 {
|
namespace net {
|
||||||
enum class ClientProtocol {
|
enum class ClientProtocol {
|
||||||
kSocks5,
|
kSocks5,
|
||||||
@ -20,5 +25,39 @@ enum Direction {
|
|||||||
kNone = 2,
|
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
|
} // namespace net
|
||||||
#endif // NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
#endif // NET_TOOLS_NAIVE_NAIVE_PROTOCOL_H_
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "net/tools/naive/naive_proxy.h"
|
#include "net/tools/naive/naive_proxy.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
@ -20,7 +21,7 @@
|
|||||||
#include "net/socket/client_socket_pool_manager.h"
|
#include "net/socket/client_socket_pool_manager.h"
|
||||||
#include "net/socket/server_socket.h"
|
#include "net/socket/server_socket.h"
|
||||||
#include "net/socket/stream_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/naive_proxy_delegate.h"
|
||||||
#include "net/tools/naive/socks5_server_socket.h"
|
#include "net/tools/naive/socks5_server_socket.h"
|
||||||
|
|
||||||
@ -33,7 +34,8 @@ NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
|
|||||||
int concurrency,
|
int concurrency,
|
||||||
RedirectResolver* resolver,
|
RedirectResolver* resolver,
|
||||||
HttpNetworkSession* session,
|
HttpNetworkSession* session,
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation)
|
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||||
|
const std::vector<PaddingType>& supported_padding_types)
|
||||||
: listen_socket_(std::move(listen_socket)),
|
: listen_socket_(std::move(listen_socket)),
|
||||||
protocol_(protocol),
|
protocol_(protocol),
|
||||||
listen_user_(listen_user),
|
listen_user_(listen_user),
|
||||||
@ -44,7 +46,8 @@ NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
|
|||||||
net_log_(
|
net_log_(
|
||||||
NetLogWithSource::Make(session->net_log(), NetLogSourceType::NONE)),
|
NetLogWithSource::Make(session->net_log(), NetLogSourceType::NONE)),
|
||||||
last_id_(0),
|
last_id_(0),
|
||||||
traffic_annotation_(traffic_annotation) {
|
traffic_annotation_(traffic_annotation),
|
||||||
|
supported_padding_types_(supported_padding_types) {
|
||||||
const auto& proxy_config = static_cast<ConfiguredProxyResolutionService*>(
|
const auto& proxy_config = static_cast<ConfiguredProxyResolutionService*>(
|
||||||
session_->proxy_resolution_service())
|
session_->proxy_resolution_service())
|
||||||
->config();
|
->config();
|
||||||
@ -125,9 +128,9 @@ void NaiveProxy::DoConnect() {
|
|||||||
listen_user_, listen_pass_,
|
listen_user_, listen_pass_,
|
||||||
traffic_annotation_);
|
traffic_annotation_);
|
||||||
} else if (protocol_ == ClientProtocol::kHttp) {
|
} else if (protocol_ == ClientProtocol::kHttp) {
|
||||||
socket = std::make_unique<HttpProxySocket>(std::move(accepted_socket_),
|
socket = std::make_unique<HttpProxyServerSocket>(
|
||||||
padding_detector_delegate.get(),
|
std::move(accepted_socket_), padding_detector_delegate.get(),
|
||||||
traffic_annotation_);
|
traffic_annotation_, supported_padding_types_);
|
||||||
} else if (protocol_ == ClientProtocol::kRedir) {
|
} else if (protocol_ == ClientProtocol::kRedir) {
|
||||||
socket = std::move(accepted_socket_);
|
socket = std::move(accepted_socket_);
|
||||||
} else {
|
} else {
|
||||||
@ -135,7 +138,8 @@ void NaiveProxy::DoConnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
last_id_++;
|
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>(
|
auto connection_ptr = std::make_unique<NaiveConnection>(
|
||||||
last_id_, protocol_, std::move(padding_detector_delegate), proxy_info_,
|
last_id_, protocol_, std::move(padding_detector_delegate), proxy_info_,
|
||||||
server_ssl_config_, proxy_ssl_config_, resolver_, session_, nak, net_log_,
|
server_ssl_config_, proxy_ssl_config_, resolver_, session_, nak, net_log_,
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
// Copyright 2018 klzgrad <kizdiv@gmail.com>. All rights reserved.
|
// Copyright 2018 klzgrad <kizdiv@gmail.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#ifndef NET_TOOLS_NAIVE_NAIVE_PROXY_H_
|
#ifndef NET_TOOLS_NAIVE_NAIVE_PROXY_H_
|
||||||
#define NET_TOOLS_NAIVE_NAIVE_PROXY_H_
|
#define NET_TOOLS_NAIVE_NAIVE_PROXY_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
@ -38,7 +38,8 @@ class NaiveProxy {
|
|||||||
int concurrency,
|
int concurrency,
|
||||||
RedirectResolver* resolver,
|
RedirectResolver* resolver,
|
||||||
HttpNetworkSession* session,
|
HttpNetworkSession* session,
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation);
|
const NetworkTrafficAnnotationTag& traffic_annotation,
|
||||||
|
const std::vector<PaddingType>& supported_padding_types);
|
||||||
~NaiveProxy();
|
~NaiveProxy();
|
||||||
NaiveProxy(const NaiveProxy&) = delete;
|
NaiveProxy(const NaiveProxy&) = delete;
|
||||||
NaiveProxy& operator=(const NaiveProxy&) = delete;
|
NaiveProxy& operator=(const NaiveProxy&) = delete;
|
||||||
@ -82,6 +83,8 @@ class NaiveProxy {
|
|||||||
|
|
||||||
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
const NetworkTrafficAnnotationTag& traffic_annotation_;
|
||||||
|
|
||||||
|
std::vector<PaddingType> supported_padding_types_;
|
||||||
|
|
||||||
base::WeakPtrFactory<NaiveProxy> weak_ptr_factory_{this};
|
base::WeakPtrFactory<NaiveProxy> weak_ptr_factory_{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -426,8 +426,9 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
|
|||||||
builder.SetCertVerifier(
|
builder.SetCertVerifier(
|
||||||
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
|
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
|
||||||
|
|
||||||
builder.set_proxy_delegate(
|
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(
|
||||||
std::make_unique<NaiveProxyDelegate>(params.extra_headers));
|
params.extra_headers,
|
||||||
|
std::vector<PaddingType>{PaddingType::kVariant1, PaddingType::kNone}));
|
||||||
|
|
||||||
auto context = builder.Build();
|
auto context = builder.Build();
|
||||||
|
|
||||||
@ -584,10 +585,12 @@ int main(int argc, char* argv[]) {
|
|||||||
params.resolver_prefix);
|
params.resolver_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
net::NaiveProxy naive_proxy(std::move(listen_socket), params.protocol,
|
net::NaiveProxy naive_proxy(
|
||||||
params.listen_user, params.listen_pass,
|
std::move(listen_socket), params.protocol, params.listen_user,
|
||||||
params.concurrency, resolver.get(), session,
|
params.listen_pass, params.concurrency, resolver.get(), session,
|
||||||
kTrafficAnnotation);
|
kTrafficAnnotation,
|
||||||
|
std::vector<net::PaddingType>{net::PaddingType::kVariant1,
|
||||||
|
net::PaddingType::kNone});
|
||||||
|
|
||||||
base::RunLoop().Run();
|
base::RunLoop().Run();
|
||||||
|
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
#include "net/tools/naive/naive_proxy_delegate.h"
|
#include "net/tools/naive/naive_proxy_delegate.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/rand_util.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/base/proxy_string_util.h"
|
||||||
#include "net/http/http_request_headers.h"
|
#include "net/http/http_request_headers.h"
|
||||||
#include "net/http/http_response_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) {
|
: extra_headers_(extra_headers) {
|
||||||
InitializeNonindexCodes();
|
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;
|
NaiveProxyDelegate::~NaiveProxyDelegate() = default;
|
||||||
@ -55,48 +67,81 @@ NaiveProxyDelegate::~NaiveProxyDelegate() = default;
|
|||||||
void NaiveProxyDelegate::OnBeforeTunnelRequest(
|
void NaiveProxyDelegate::OnBeforeTunnelRequest(
|
||||||
const ProxyServer& proxy_server,
|
const ProxyServer& proxy_server,
|
||||||
HttpRequestHeaders* extra_headers) {
|
HttpRequestHeaders* extra_headers) {
|
||||||
|
// Not possible to negotiate padding capability given the underlying
|
||||||
|
// protocols.
|
||||||
if (proxy_server.is_direct() || proxy_server.is_socks())
|
if (proxy_server.is_direct() || proxy_server.is_socks())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Sends client-side padding header regardless of server support
|
// Sends client-side padding header regardless of server support
|
||||||
std::string padding(base::RandInt(16, 32), '~');
|
std::string padding(base::RandInt(16, 32), '~');
|
||||||
FillNonindexHeaderValue(base::RandUint64(), &padding[0], padding.size());
|
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
|
// Enables Fast Open in H2/H3 proxy client socket once the state of server
|
||||||
// padding support is known.
|
// 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->SetHeader("fastopen", "1");
|
||||||
}
|
}
|
||||||
extra_headers->MergeFrom(extra_headers_);
|
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(
|
Error NaiveProxyDelegate::OnTunnelHeadersReceived(
|
||||||
const ProxyServer& proxy_server,
|
const ProxyServer& proxy_server,
|
||||||
const HttpResponseHeaders& response_headers) {
|
const HttpResponseHeaders& response_headers) {
|
||||||
|
// Not possible to negotiate padding capability given the underlying
|
||||||
|
// protocols.
|
||||||
if (proxy_server.is_direct() || proxy_server.is_socks())
|
if (proxy_server.is_direct() || proxy_server.is_socks())
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
// Detects server padding support, even if it changes dynamically.
|
// Detects server padding support, even if it changes dynamically.
|
||||||
bool padding = response_headers.HasHeader("padding");
|
std::optional<PaddingType> new_padding_type =
|
||||||
auto new_state =
|
ParsePaddingHeaders(response_headers);
|
||||||
padding ? PaddingSupport::kCapable : PaddingSupport::kIncapable;
|
if (!new_padding_type.has_value()) {
|
||||||
auto& padding_state = padding_state_by_server_[proxy_server];
|
return ERR_INVALID_RESPONSE;
|
||||||
if (padding_state == PaddingSupport::kUnknown || padding_state != new_state) {
|
}
|
||||||
LOG(INFO) << "Padding capability of " << ProxyServerToProxyUri(proxy_server)
|
std::optional<PaddingType>& padding_type =
|
||||||
<< (padding ? " detected" : " undetected");
|
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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
PaddingSupport NaiveProxyDelegate::GetProxyServerPaddingSupport(
|
std::optional<PaddingType> NaiveProxyDelegate::GetProxyServerPaddingType(
|
||||||
const ProxyServer& proxy_server) {
|
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())
|
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(
|
PaddingDetectorDelegate::PaddingDetectorDelegate(
|
||||||
@ -105,55 +150,32 @@ PaddingDetectorDelegate::PaddingDetectorDelegate(
|
|||||||
ClientProtocol client_protocol)
|
ClientProtocol client_protocol)
|
||||||
: naive_proxy_delegate_(naive_proxy_delegate),
|
: naive_proxy_delegate_(naive_proxy_delegate),
|
||||||
proxy_server_(proxy_server),
|
proxy_server_(proxy_server),
|
||||||
client_protocol_(client_protocol),
|
client_protocol_(client_protocol) {}
|
||||||
detected_client_padding_support_(PaddingSupport::kUnknown),
|
|
||||||
cached_server_padding_support_(PaddingSupport::kUnknown) {}
|
|
||||||
|
|
||||||
PaddingDetectorDelegate::~PaddingDetectorDelegate() = default;
|
PaddingDetectorDelegate::~PaddingDetectorDelegate() = default;
|
||||||
|
|
||||||
bool PaddingDetectorDelegate::IsPaddingSupportKnown() {
|
void PaddingDetectorDelegate::SetClientPaddingType(PaddingType padding_type) {
|
||||||
auto c = GetClientPaddingSupport();
|
detected_client_padding_type_ = padding_type;
|
||||||
auto s = GetServerPaddingSupport();
|
|
||||||
return c != PaddingSupport::kUnknown && s != PaddingSupport::kUnknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Direction PaddingDetectorDelegate::GetPaddingDirection() {
|
std::optional<PaddingType> PaddingDetectorDelegate::GetClientPaddingType() {
|
||||||
auto c = GetClientPaddingSupport();
|
// Not possible to negotiate padding capability given the underlying
|
||||||
auto s = GetServerPaddingSupport();
|
// protocols.
|
||||||
// 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) {
|
if (client_protocol_ == ClientProtocol::kSocks5) {
|
||||||
return PaddingSupport::kIncapable;
|
return PaddingType::kNone;
|
||||||
} else if (client_protocol_ == ClientProtocol::kRedir) {
|
} 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() {
|
std::optional<PaddingType> PaddingDetectorDelegate::GetServerPaddingType() {
|
||||||
if (cached_server_padding_support_ != PaddingSupport::kUnknown)
|
if (cached_server_padding_type_.has_value())
|
||||||
return cached_server_padding_support_;
|
return cached_server_padding_type_;
|
||||||
cached_server_padding_support_ =
|
cached_server_padding_type_ =
|
||||||
naive_proxy_delegate_->GetProxyServerPaddingSupport(proxy_server_);
|
naive_proxy_delegate_->GetProxyServerPaddingType(proxy_server_);
|
||||||
return cached_server_padding_support_;
|
return cached_server_padding_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace net
|
} // namespace net
|
||||||
|
@ -6,12 +6,15 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
#include "net/base/net_errors.h"
|
#include "net/base/net_errors.h"
|
||||||
#include "net/base/proxy_delegate.h"
|
#include "net/base/proxy_delegate.h"
|
||||||
#include "net/base/proxy_server.h"
|
#include "net/base/proxy_server.h"
|
||||||
|
#include "net/http/http_request_headers.h"
|
||||||
#include "net/proxy_resolution/proxy_retry_info.h"
|
#include "net/proxy_resolution/proxy_retry_info.h"
|
||||||
#include "net/tools/naive/naive_protocol.h"
|
#include "net/tools/naive/naive_protocol.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
@ -23,18 +26,11 @@ void InitializeNonindexCodes();
|
|||||||
void FillNonindexHeaderValue(uint64_t unique_bits, char* buf, int len);
|
void FillNonindexHeaderValue(uint64_t unique_bits, char* buf, int len);
|
||||||
|
|
||||||
class ProxyInfo;
|
class ProxyInfo;
|
||||||
class HttpRequestHeaders;
|
|
||||||
class HttpResponseHeaders;
|
|
||||||
|
|
||||||
enum class PaddingSupport {
|
|
||||||
kUnknown = 0,
|
|
||||||
kCapable,
|
|
||||||
kIncapable,
|
|
||||||
};
|
|
||||||
|
|
||||||
class NaiveProxyDelegate : public ProxyDelegate {
|
class NaiveProxyDelegate : public ProxyDelegate {
|
||||||
public:
|
public:
|
||||||
explicit NaiveProxyDelegate(const HttpRequestHeaders& extra_headers);
|
NaiveProxyDelegate(const HttpRequestHeaders& extra_headers,
|
||||||
|
const std::vector<PaddingType>& supported_padding_types);
|
||||||
~NaiveProxyDelegate() override;
|
~NaiveProxyDelegate() override;
|
||||||
|
|
||||||
void OnResolveProxy(const GURL& url,
|
void OnResolveProxy(const GURL& url,
|
||||||
@ -51,18 +47,25 @@ class NaiveProxyDelegate : public ProxyDelegate {
|
|||||||
const ProxyServer& proxy_server,
|
const ProxyServer& proxy_server,
|
||||||
const HttpResponseHeaders& response_headers) override;
|
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:
|
private:
|
||||||
const HttpRequestHeaders& extra_headers_;
|
std::optional<PaddingType> ParsePaddingHeaders(
|
||||||
std::map<ProxyServer, PaddingSupport> padding_state_by_server_;
|
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 {
|
class ClientPaddingDetectorDelegate {
|
||||||
public:
|
public:
|
||||||
virtual ~ClientPaddingDetectorDelegate() = default;
|
virtual ~ClientPaddingDetectorDelegate() = default;
|
||||||
|
|
||||||
virtual void SetClientPaddingSupport(PaddingSupport padding_support) = 0;
|
virtual void SetClientPaddingType(PaddingType padding_type) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
|
class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
|
||||||
@ -72,22 +75,19 @@ class PaddingDetectorDelegate : public ClientPaddingDetectorDelegate {
|
|||||||
ClientProtocol client_protocol);
|
ClientProtocol client_protocol);
|
||||||
~PaddingDetectorDelegate() override;
|
~PaddingDetectorDelegate() override;
|
||||||
|
|
||||||
bool IsPaddingSupportKnown();
|
std::optional<PaddingType> GetClientPaddingType();
|
||||||
Direction GetPaddingDirection();
|
std::optional<PaddingType> GetServerPaddingType();
|
||||||
void SetClientPaddingSupport(PaddingSupport padding_support) override;
|
void SetClientPaddingType(PaddingType padding_type) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PaddingSupport GetClientPaddingSupport();
|
|
||||||
PaddingSupport GetServerPaddingSupport();
|
|
||||||
|
|
||||||
NaiveProxyDelegate* naive_proxy_delegate_;
|
NaiveProxyDelegate* naive_proxy_delegate_;
|
||||||
const ProxyServer& proxy_server_;
|
const ProxyServer& proxy_server_;
|
||||||
ClientProtocol client_protocol_;
|
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
|
// The result is only cached during one connection, so it's still dynamically
|
||||||
// updated in the following connections after server changes support.
|
// updated in the following connections after server changes support.
|
||||||
PaddingSupport cached_server_padding_support_;
|
std::optional<PaddingType> cached_server_padding_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace net
|
} // 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