Add server implementation and tunnel padding

This commit is contained in:
klzgrad 2018-12-08 00:51:40 -05:00
parent c3ea887de8
commit d3e3faa9c7
8 changed files with 639 additions and 39 deletions

View File

@ -0,0 +1,327 @@
// Copyright 2018 The Chromium Authors. 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
// found in the LICENSE file.
#include "net/tools/naive/http_proxy_socket.h"
#include <cstring>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/sys_byteorder.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/log/net_log.h"
namespace net {
namespace {
constexpr int kBufferSize = 64 * 1024;
constexpr size_t kMaxHeaderSize = 64 * 1024;
constexpr char kResponseHeader[] = "HTTP/1.1 200 OK\r\nPadding: ";
constexpr int kResponseHeaderSize = sizeof(kResponseHeader) - 1;
// A plain 200 is 10 bytes. Expected 48 bytes. "Padding" uses up 7 bytes.
constexpr int kMinPaddingSize = 30;
constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
} // namespace
HttpProxySocket::HttpProxySocket(
std::unique_ptr<StreamSocket> transport_socket,
const NetworkTrafficAnnotationTag& traffic_annotation)
: io_callback_(base::BindRepeating(&HttpProxySocket::OnIOComplete,
base::Unretained(this))),
transport_(std::move(transport_socket)),
next_state_(STATE_NONE),
completed_handshake_(false),
was_ever_used_(false),
header_write_size_(-1),
net_log_(transport_->NetLog()),
traffic_annotation_(traffic_annotation) {}
HttpProxySocket::~HttpProxySocket() {
Disconnect();
}
const HostPortPair& HttpProxySocket::request_endpoint() const {
return request_endpoint_;
}
int HttpProxySocket::Connect(CompletionOnceCallback callback) {
DCHECK(transport_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
// If already connected, then just return OK.
if (completed_handshake_)
return OK;
next_state_ = STATE_HEADER_READ;
buffer_.clear();
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING) {
user_callback_ = std::move(callback);
}
return rv;
}
void HttpProxySocket::Disconnect() {
completed_handshake_ = false;
transport_->Disconnect();
// Reset other states to make sure they aren't mistakenly used later.
// These are the states initialized by Connect().
next_state_ = STATE_NONE;
user_callback_.Reset();
}
bool HttpProxySocket::IsConnected() const {
return completed_handshake_ && transport_->IsConnected();
}
bool HttpProxySocket::IsConnectedAndIdle() const {
return completed_handshake_ && transport_->IsConnectedAndIdle();
}
const NetLogWithSource& HttpProxySocket::NetLog() const {
return net_log_;
}
bool HttpProxySocket::WasEverUsed() const {
return was_ever_used_;
}
bool HttpProxySocket::WasAlpnNegotiated() const {
if (transport_) {
return transport_->WasAlpnNegotiated();
}
NOTREACHED();
return false;
}
NextProto HttpProxySocket::GetNegotiatedProtocol() const {
if (transport_) {
return transport_->GetNegotiatedProtocol();
}
NOTREACHED();
return kProtoUnknown;
}
bool HttpProxySocket::GetSSLInfo(SSLInfo* ssl_info) {
if (transport_) {
return transport_->GetSSLInfo(ssl_info);
}
NOTREACHED();
return false;
}
void HttpProxySocket::GetConnectionAttempts(ConnectionAttempts* out) const {
out->clear();
}
int64_t HttpProxySocket::GetTotalReceivedBytes() const {
return transport_->GetTotalReceivedBytes();
}
void HttpProxySocket::ApplySocketTag(const SocketTag& tag) {
return transport_->ApplySocketTag(tag);
}
// Read is called by the transport layer above to read. This can only be done
// if the HTTP header is complete.
int HttpProxySocket::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
DCHECK(callback);
int rv = transport_->Read(
buf, buf_len,
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
base::Unretained(this), std::move(callback)));
if (rv > 0)
was_ever_used_ = true;
return rv;
}
// Write is called by the transport layer. This can only be done if the
// SOCKS handshake is complete.
int HttpProxySocket::Write(
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
DCHECK(callback);
int rv = transport_->Write(
buf, buf_len,
base::BindOnce(&HttpProxySocket::OnReadWriteComplete,
base::Unretained(this), std::move(callback)),
traffic_annotation);
if (rv > 0)
was_ever_used_ = true;
return rv;
}
int HttpProxySocket::SetReceiveBufferSize(int32_t size) {
return transport_->SetReceiveBufferSize(size);
}
int HttpProxySocket::SetSendBufferSize(int32_t size) {
return transport_->SetSendBufferSize(size);
}
void HttpProxySocket::DoCallback(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
DCHECK(user_callback_);
// Since Run() may result in Read being called,
// clear user_callback_ up front.
std::move(user_callback_).Run(result);
}
void HttpProxySocket::OnIOComplete(int result) {
DCHECK_NE(STATE_NONE, next_state_);
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
DoCallback(rv);
}
}
void HttpProxySocket::OnReadWriteComplete(CompletionOnceCallback callback,
int result) {
DCHECK_NE(ERR_IO_PENDING, result);
DCHECK(callback);
if (result > 0)
was_ever_used_ = true;
std::move(callback).Run(result);
}
int HttpProxySocket::DoLoop(int last_io_result) {
DCHECK_NE(next_state_, STATE_NONE);
int rv = last_io_result;
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
case STATE_HEADER_READ:
DCHECK_EQ(OK, rv);
rv = DoHeaderRead();
break;
case STATE_HEADER_READ_COMPLETE:
rv = DoHeaderReadComplete(rv);
break;
case STATE_HEADER_WRITE:
DCHECK_EQ(OK, rv);
rv = DoHeaderWrite();
break;
case STATE_HEADER_WRITE_COMPLETE:
rv = DoHeaderWriteComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_UNEXPECTED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int HttpProxySocket::DoHeaderRead() {
next_state_ = STATE_HEADER_READ_COMPLETE;
handshake_buf_ = new IOBuffer(kBufferSize);
return transport_->Read(handshake_buf_.get(), kBufferSize, io_callback_);
}
int HttpProxySocket::DoHeaderReadComplete(int result) {
if (result < 0)
return result;
if (result == 0) {
return ERR_CONNECTION_CLOSED;
}
buffer_.append(handshake_buf_->data(), result);
if (buffer_.size() > kMaxHeaderSize) {
return ERR_MSG_TOO_BIG;
}
auto header_end = buffer_.find("\r\n\r\n");
if (header_end == std::string::npos) {
next_state_ = STATE_HEADER_READ;
return OK;
}
if (header_end + 4 != buffer_.size()) {
return ERR_INVALID_ARGUMENT;
}
// HttpProxyClientSocket uses CONNECT for all endpoints.
auto first_line_end = buffer_.find("\r\n");
auto first_space = buffer_.find(' ');
if (first_space == std::string::npos || first_space + 1 >= first_line_end) {
return ERR_INVALID_ARGUMENT;
}
if (buffer_.compare(0, first_space, "CONNECT") != 0) {
return ERR_INVALID_ARGUMENT;
}
auto second_space = buffer_.find(' ', first_space + 1);
if (second_space == std::string::npos || second_space >= first_line_end) {
return ERR_INVALID_ARGUMENT;
}
request_endpoint_ = HostPortPair::FromString(
buffer_.substr(first_space + 1, second_space - (first_space + 1)));
next_state_ = STATE_HEADER_WRITE;
return OK;
}
int HttpProxySocket::DoHeaderWrite() {
next_state_ = STATE_HEADER_WRITE_COMPLETE;
// Adds padding.
int padding_size = base::RandInt(kMinPaddingSize, kMaxPaddingSize);
header_write_size_ = kResponseHeaderSize + padding_size + 4;
handshake_buf_ = new IOBuffer(header_write_size_);
char* p = handshake_buf_->data();
std::memcpy(p, kResponseHeader, kResponseHeaderSize);
std::memset(p + kResponseHeaderSize, '.', padding_size);
std::memcpy(p + kResponseHeaderSize + padding_size, "\r\n\r\n", 4);
return transport_->Write(handshake_buf_.get(), header_write_size_,
io_callback_, traffic_annotation_);
}
int HttpProxySocket::DoHeaderWriteComplete(int result) {
if (result < 0)
return result;
if (result != header_write_size_) {
return ERR_FAILED;
}
completed_handshake_ = true;
next_state_ = STATE_NONE;
return OK;
}
int HttpProxySocket::GetPeerAddress(IPEndPoint* address) const {
return transport_->GetPeerAddress(address);
}
int HttpProxySocket::GetLocalAddress(IPEndPoint* address) const {
return transport_->GetLocalAddress(address);
}
} // namespace net

View File

@ -0,0 +1,123 @@
// Copyright 2018 The Chromium Authors. 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
// found in the LICENSE file.
#ifndef NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
#define NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/ref_counted.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/ip_endpoint.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/connection_attempts.h"
#include "net/socket/next_proto.h"
#include "net/socket/stream_socket.h"
#include "net/ssl/ssl_info.h"
namespace net {
struct NetworkTrafficAnnotationTag;
// This StreamSocket is used to setup a HTTP CONNECT tunnel.
class HttpProxySocket : public StreamSocket {
public:
HttpProxySocket(std::unique_ptr<StreamSocket> transport_socket,
const NetworkTrafficAnnotationTag& traffic_annotation);
// On destruction Disconnect() is called.
~HttpProxySocket() override;
const HostPortPair& request_endpoint() const;
// StreamSocket implementation.
int Connect(CompletionOnceCallback callback) override;
void Disconnect() override;
bool IsConnected() const override;
bool IsConnectedAndIdle() const override;
const NetLogWithSource& NetLog() const override;
bool WasEverUsed() const override;
bool WasAlpnNegotiated() const override;
NextProto GetNegotiatedProtocol() const override;
bool GetSSLInfo(SSLInfo* ssl_info) override;
void GetConnectionAttempts(ConnectionAttempts* out) const override;
void ClearConnectionAttempts() override {}
void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
int64_t GetTotalReceivedBytes() const override;
void ApplySocketTag(const SocketTag& tag) override;
// Socket implementation.
int Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) override;
int Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) override;
int SetReceiveBufferSize(int32_t size) override;
int SetSendBufferSize(int32_t size) override;
int GetPeerAddress(IPEndPoint* address) const override;
int GetLocalAddress(IPEndPoint* address) const override;
private:
enum State {
STATE_HEADER_READ,
STATE_HEADER_READ_COMPLETE,
STATE_HEADER_WRITE,
STATE_HEADER_WRITE_COMPLETE,
STATE_NONE,
};
void DoCallback(int result);
void OnIOComplete(int result);
void OnReadWriteComplete(CompletionOnceCallback callback, int result);
int DoLoop(int last_io_result);
int DoHeaderWrite();
int DoHeaderWriteComplete(int result);
int DoHeaderRead();
int DoHeaderReadComplete(int result);
CompletionRepeatingCallback io_callback_;
// Stores the underlying socket.
std::unique_ptr<StreamSocket> transport_;
State next_state_;
// Stores the callback to the layer above, called on completing Connect().
CompletionOnceCallback user_callback_;
// This IOBuffer is used by the class to read and write
// SOCKS handshake data. The length contains the expected size to
// read or write.
scoped_refptr<IOBuffer> handshake_buf_;
std::string buffer_;
bool completed_handshake_;
bool was_ever_used_;
int header_write_size_;
HostPortPair request_endpoint_;
NetLogWithSource net_log_;
// Traffic annotation for socket control.
const NetworkTrafficAnnotationTag& traffic_annotation_;
DISALLOW_COPY_AND_ASSIGN(HttpProxySocket);
};
} // namespace net
#endif // NET_TOOLS_NAIVE_HTTP_PROXY_SOCKET_H_

View File

@ -5,11 +5,13 @@
#include "net/tools/naive/naive_connection.h" #include "net/tools/naive/naive_connection.h"
#include <cstring>
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/rand_util.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
@ -28,15 +30,20 @@
namespace net { namespace net {
namespace { namespace {
static const int kBufferSize = 64 * 1024; constexpr int kBufferSize = 64 * 1024;
constexpr int kFirstPaddings = 4;
constexpr int kPaddingHeaderSize = 3;
constexpr int kMaxPaddingSize = 255;
} // namespace } // namespace
NaiveConnection::NaiveConnection( NaiveConnection::NaiveConnection(
unsigned int id, unsigned int id,
Direction pad_direction,
std::unique_ptr<StreamSocket> accepted_socket, std::unique_ptr<StreamSocket> accepted_socket,
Delegate* delegate, Delegate* delegate,
const NetworkTrafficAnnotationTag& traffic_annotation) const NetworkTrafficAnnotationTag& traffic_annotation)
: id_(id), : id_(id),
pad_direction_(pad_direction),
next_state_(STATE_NONE), next_state_(STATE_NONE),
delegate_(delegate), delegate_(delegate),
client_socket_(std::move(accepted_socket)), client_socket_(std::move(accepted_socket)),
@ -47,6 +54,8 @@ NaiveConnection::NaiveConnection(
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) {
@ -212,10 +221,20 @@ void NaiveConnection::Pull(Direction from, Direction to) {
if (errors_[kClient] < 0 || errors_[kServer] < 0) if (errors_[kClient] < 0 || errors_[kServer] < 0)
return; return;
read_buffers_[from] = new IOBuffer(kBufferSize); int read_size = kBufferSize;
if (from == pad_direction_ && num_paddings_[from] < kFirstPaddings) {
auto* buffer = new GrowableIOBuffer;
buffer->SetCapacity(kBufferSize);
buffer->set_offset(kPaddingHeaderSize);
read_buffers_[from] = buffer;
read_size = kBufferSize - kPaddingHeaderSize - kMaxPaddingSize;
} else {
read_buffers_[from] = new IOBuffer(kBufferSize);
}
DCHECK(sockets_[from]); DCHECK(sockets_[from]);
int rv = sockets_[from]->Read( int rv = sockets_[from]->Read(
read_buffers_[from].get(), kBufferSize, read_buffers_[from].get(), read_size,
base::BindRepeating(&NaiveConnection::OnPullComplete, base::BindRepeating(&NaiveConnection::OnPullComplete,
weak_ptr_factory_.GetWeakPtr(), from, to)); weak_ptr_factory_.GetWeakPtr(), from, to));
@ -227,11 +246,107 @@ 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) {
write_buffers_[to] = new DrainableIOBuffer(read_buffers_[from].get(), size); int write_size = size;
int write_offset = 0;
if (from == pad_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 == pad_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 = new 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] = new DrainableIOBuffer(read_buffers_[from].get(),
write_offset + write_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(), size, write_buffers_[to].get(), write_size,
base::BindRepeating(&NaiveConnection::OnPushComplete, base::BindRepeating(&NaiveConnection::OnPushComplete,
weak_ptr_factory_.GetWeakPtr(), from, to), weak_ptr_factory_.GetWeakPtr(), from, to),
traffic_annotation_); traffic_annotation_);
@ -310,7 +425,7 @@ void NaiveConnection::OnPullComplete(Direction from, Direction to, int result) {
} }
void NaiveConnection::OnPushComplete(Direction from, Direction to, int result) { void NaiveConnection::OnPushComplete(Direction from, Direction to, int result) {
if (result >= 0) { if (result >= 0 && write_buffers_[to] != nullptr) {
bytes_passed_without_yielding_[from] += result; bytes_passed_without_yielding_[from] += result;
write_buffers_[to]->DidConsume(result); write_buffers_[to]->DidConsume(result);
int size = write_buffers_[to]->BytesRemaining(); int size = write_buffers_[to]->BytesRemaining();

View File

@ -7,6 +7,7 @@
#define NET_TOOLS_NAIVE_NAIVE_CONNECTION_H_ #define NET_TOOLS_NAIVE_NAIVE_CONNECTION_H_
#include <memory> #include <memory>
#include <string>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
@ -27,6 +28,14 @@ class NaiveConnection {
public: public:
using TimeFunc = base::TimeTicks (*)(); using TimeFunc = base::TimeTicks (*)();
// From this direction.
enum Direction {
kClient = 0,
kServer = 1,
kNumDirections = 2,
kNone = 2,
};
class Delegate { class Delegate {
public: public:
Delegate() {} Delegate() {}
@ -42,6 +51,7 @@ class NaiveConnection {
}; };
NaiveConnection(unsigned int id, NaiveConnection(unsigned int id,
Direction pad_direction,
std::unique_ptr<StreamSocket> accepted_socket, std::unique_ptr<StreamSocket> accepted_socket,
Delegate* delegate, Delegate* delegate,
const NetworkTrafficAnnotationTag& traffic_annotation); const NetworkTrafficAnnotationTag& traffic_annotation);
@ -61,11 +71,12 @@ class NaiveConnection {
STATE_NONE, STATE_NONE,
}; };
// From this direction. enum PaddingState {
enum Direction { STATE_READ_PAYLOAD_LENGTH_1,
kClient = 0, STATE_READ_PAYLOAD_LENGTH_2,
kServer = 1, STATE_READ_PADDING_LENGTH,
kNumDirections = 2, STATE_READ_PAYLOAD,
STATE_READ_PADDING,
}; };
void DoCallback(int result); void DoCallback(int result);
@ -86,6 +97,7 @@ class NaiveConnection {
void OnPushComplete(Direction from, Direction to, int result); void OnPushComplete(Direction from, Direction to, int result);
unsigned int id_; unsigned int id_;
Direction pad_direction_;
CompletionRepeatingCallback io_callback_; CompletionRepeatingCallback io_callback_;
CompletionOnceCallback connect_callback_; CompletionOnceCallback connect_callback_;
@ -110,6 +122,11 @@ 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_;

View File

@ -20,6 +20,8 @@
#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/third_party/quic/core/quic_versions.h"
#include "net/tools/naive/http_proxy_socket.h"
#include "net/tools/naive/socks5_server_socket.h" #include "net/tools/naive/socks5_server_socket.h"
namespace net { namespace net {
@ -75,13 +77,20 @@ void NaiveProxy::HandleAcceptResult(int result) {
void NaiveProxy::DoConnect() { void NaiveProxy::DoConnect() {
std::unique_ptr<StreamSocket> socket; std::unique_ptr<StreamSocket> socket;
NaiveConnection::Direction pad_direction;
if (protocol_ == kSocks5) { if (protocol_ == kSocks5) {
socket = std::make_unique<Socks5ServerSocket>(std::move(accepted_socket_)); socket = std::make_unique<Socks5ServerSocket>(std::move(accepted_socket_),
traffic_annotation_);
pad_direction = NaiveConnection::kClient;
} else if (protocol_ == kHttp) {
socket = std::make_unique<HttpProxySocket>(std::move(accepted_socket_),
traffic_annotation_);
pad_direction = NaiveConnection::kServer;
} else { } else {
return; return;
} }
auto connection_ptr = std::make_unique<NaiveConnection>( auto connection_ptr = std::make_unique<NaiveConnection>(
++last_id_, std::move(socket), this, traffic_annotation_); ++last_id_, pad_direction, std::move(socket), this, traffic_annotation_);
auto* connection = connection_ptr.get(); auto* connection = connection_ptr.get();
connection_by_id_[connection->id()] = std::move(connection_ptr); connection_by_id_[connection->id()] = std::move(connection_ptr);
int result = connection->Connect( int result = connection->Connect(
@ -118,25 +127,32 @@ int NaiveProxy::OnConnectServer(unsigned int connection_id,
HttpRequestInfo req_info; HttpRequestInfo req_info;
session_->GetSSLConfig(req_info, &server_ssl_config, &proxy_ssl_config); session_->GetSSLConfig(req_info, &server_ssl_config, &proxy_ssl_config);
proxy_ssl_config.disable_cert_verification_network_fetches = true; proxy_ssl_config.disable_cert_verification_network_fetches = true;
} else {
proxy_info.UseDirect();
} }
HostPortPair request_endpoint; HostPortPair request_endpoint;
if (protocol_ == kSocks5) { if (protocol_ == kSocks5) {
const auto* socket = static_cast<const Socks5ServerSocket*>(client_socket); const auto* socket = static_cast<const Socks5ServerSocket*>(client_socket);
request_endpoint = socket->request_endpoint(); request_endpoint = socket->request_endpoint();
} else if (protocol_ == kHttp) {
const auto* socket = static_cast<const HttpProxySocket*>(client_socket);
request_endpoint = socket->request_endpoint();
} }
if (request_endpoint.port() == 0) { if (request_endpoint.IsEmpty()) {
LOG(ERROR) << "Connection " << connection_id << " has invalid upstream"; LOG(ERROR) << "Connection " << connection_id << " to invalid origin";
return ERR_ADDRESS_INVALID; return ERR_ADDRESS_INVALID;
} }
LOG(INFO) << "Connection " << connection_id << " to " LOG(INFO) << "Connection " << connection_id << " to "
<< request_endpoint.ToString(); << request_endpoint.ToString();
return InitSocketHandleForRawConnect( auto quic_version = quic::QUIC_VERSION_UNSUPPORTED;
return InitSocketHandleForRawConnect2(
request_endpoint, session_, request_load_flags, request_priority, request_endpoint, session_, request_load_flags, request_priority,
proxy_info, server_ssl_config, proxy_ssl_config, PRIVACY_MODE_DISABLED, proxy_info, quic_version, server_ssl_config, proxy_ssl_config,
net_log_, server_socket, callback); PRIVACY_MODE_DISABLED, net_log_, server_socket, callback);
} }
void NaiveProxy::OnConnectComplete(int connection_id, int result) { void NaiveProxy::OnConnectComplete(int connection_id, int result) {

View File

@ -108,17 +108,19 @@ std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
net::NetLog* net_log) { net::NetLog* net_log) {
net::URLRequestContextBuilder builder; net::URLRequestContextBuilder builder;
builder.DisableHttpCache();
builder.set_net_log(net_log);
net::ProxyConfig proxy_config; net::ProxyConfig proxy_config;
proxy_config.proxy_rules().ParseFromString(params.proxy_url); if (params.use_proxy) {
proxy_config.proxy_rules().ParseFromString(params.proxy_url);
}
auto proxy_service = net::ProxyResolutionService::CreateWithoutProxyResolver( auto proxy_service = net::ProxyResolutionService::CreateWithoutProxyResolver(
std::make_unique<net::ProxyConfigServiceFixed>( std::make_unique<net::ProxyConfigServiceFixed>(
net::ProxyConfigWithAnnotation(proxy_config, kTrafficAnnotation)), net::ProxyConfigWithAnnotation(proxy_config, kTrafficAnnotation)),
net_log); net_log);
proxy_service->ForceReloadProxyConfig(); proxy_service->ForceReloadProxyConfig();
builder.set_proxy_resolution_service(std::move(proxy_service)); builder.set_proxy_resolution_service(std::move(proxy_service));
builder.DisableHttpCache();
builder.set_net_log(net_log);
if (!params.host_resolver_rules.empty()) { if (!params.host_resolver_rules.empty()) {
builder.set_host_mapping_rules(params.host_resolver_rules); builder.set_host_mapping_rules(params.host_resolver_rules);
@ -126,15 +128,17 @@ std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
auto context = builder.Build(); auto context = builder.Build();
net::HttpNetworkSession* session = if (params.use_proxy) {
context->http_transaction_factory()->GetSession(); net::HttpNetworkSession* session =
net::HttpAuthCache* auth_cache = session->http_auth_cache(); context->http_transaction_factory()->GetSession();
GURL auth_origin(params.proxy_url); net::HttpAuthCache* auth_cache = session->http_auth_cache();
net::AuthCredentials credentials(base::ASCIIToUTF16(params.proxy_user), GURL auth_origin(params.proxy_url);
base::ASCIIToUTF16(params.proxy_pass)); net::AuthCredentials credentials(base::ASCIIToUTF16(params.proxy_user),
auth_cache->Add(auth_origin, /*realm=*/std::string(), base::ASCIIToUTF16(params.proxy_pass));
net::HttpAuth::AUTH_SCHEME_BASIC, /*challenge=*/"Basic", auth_cache->Add(auth_origin, /*realm=*/std::string(),
credentials, /*path=*/"/"); net::HttpAuth::AUTH_SCHEME_BASIC, /*challenge=*/"Basic",
credentials, /*path=*/"/");
}
return context; return context;
} }
@ -202,6 +206,7 @@ bool ParseCommandLineFlags(Params* params) {
} }
} }
params->use_proxy = false;
GURL url(line.GetSwitchValueASCII("proxy")); GURL url(line.GetSwitchValueASCII("proxy"));
if (line.HasSwitch("proxy")) { if (line.HasSwitch("proxy")) {
params->use_proxy = true; params->use_proxy = true;

View File

@ -33,13 +33,9 @@ const char Socks5ServerSocket::kReplyCommandNotSupported = '\x07';
static_assert(sizeof(struct in_addr) == 4, "incorrect system size of IPv4"); static_assert(sizeof(struct in_addr) == 4, "incorrect system size of IPv4");
static_assert(sizeof(struct in6_addr) == 16, "incorrect system size of IPv6"); static_assert(sizeof(struct in6_addr) == 16, "incorrect system size of IPv6");
namespace {
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("naive", "");
} // namespace
Socks5ServerSocket::Socks5ServerSocket( Socks5ServerSocket::Socks5ServerSocket(
std::unique_ptr<StreamSocket> transport_socket) std::unique_ptr<StreamSocket> transport_socket,
const NetworkTrafficAnnotationTag& traffic_annotation)
: io_callback_(base::BindRepeating(&Socks5ServerSocket::OnIOComplete, : io_callback_(base::BindRepeating(&Socks5ServerSocket::OnIOComplete,
base::Unretained(this))), base::Unretained(this))),
transport_(std::move(transport_socket)), transport_(std::move(transport_socket)),
@ -51,7 +47,7 @@ Socks5ServerSocket::Socks5ServerSocket(
read_header_size_(kReadHeaderSize), read_header_size_(kReadHeaderSize),
was_ever_used_(false), was_ever_used_(false),
net_log_(transport_->NetLog()), net_log_(transport_->NetLog()),
traffic_annotation_(kTrafficAnnotation) {} traffic_annotation_(traffic_annotation) {}
Socks5ServerSocket::~Socks5ServerSocket() { Socks5ServerSocket::~Socks5ServerSocket() {
Disconnect(); Disconnect();

View File

@ -31,7 +31,8 @@ struct NetworkTrafficAnnotationTag;
// Currently no SOCKSv5 authentication is supported. // Currently no SOCKSv5 authentication is supported.
class Socks5ServerSocket : public StreamSocket { class Socks5ServerSocket : public StreamSocket {
public: public:
explicit Socks5ServerSocket(std::unique_ptr<StreamSocket> transport_socket); Socks5ServerSocket(std::unique_ptr<StreamSocket> transport_socket,
const NetworkTrafficAnnotationTag& traffic_annotation);
// On destruction Disconnect() is called. // On destruction Disconnect() is called.
~Socks5ServerSocket() override; ~Socks5ServerSocket() override;