Add initial implementation of Naive client

This commit is contained in:
klzgrad 2018-01-20 12:15:45 -05:00
parent 1ad9649508
commit 239467420b
8 changed files with 1647 additions and 0 deletions

View File

@ -429,6 +429,11 @@ EVENT_TYPE(SOCKS_HOSTNAME_TOO_BIG)
EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_GREETING) EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_GREETING)
EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_HANDSHAKE) EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_HANDSHAKE)
EVENT_TYPE(SOCKS_NO_REQUESTED_AUTH)
EVENT_TYPE(SOCKS_NO_ACCEPTABLE_AUTH)
EVENT_TYPE(SOCKS_ZERO_LENGTH_DOMAIN)
EVENT_TYPE(SOCKS_UNEXPECTED_COMMAND)
// This event indicates that a bad version number was received in the // This event indicates that a bad version number was received in the
// proxy server's response. The extra parameters show its value: // proxy server's response. The extra parameters show its value:
// { // {

View File

@ -0,0 +1,146 @@
// 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/naive_client.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/net_errors.h"
#include "net/http/http_network_session.h"
#include "net/socket/server_socket.h"
#include "net/socket/stream_socket.h"
#include "net/tools/naive/naive_client_connection.h"
#include "net/tools/naive/socks5_server_socket.h"
namespace net {
NaiveClient::NaiveClient(std::unique_ptr<ServerSocket> server_socket,
HttpNetworkSession* session)
: server_socket_(std::move(server_socket)),
session_(session),
last_id_(0),
weak_ptr_factory_(this) {
DCHECK(server_socket_);
// Start accepting connections in next run loop in case when delegate is not
// ready to get callbacks.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&NaiveClient::DoAcceptLoop,
weak_ptr_factory_.GetWeakPtr()));
}
NaiveClient::~NaiveClient() = default;
void NaiveClient::DoAcceptLoop() {
int result;
do {
result = server_socket_->Accept(&accepted_socket_,
base::Bind(&NaiveClient::OnAcceptComplete,
weak_ptr_factory_.GetWeakPtr()));
if (result == ERR_IO_PENDING)
return;
HandleAcceptResult(result);
} while (result == OK);
}
void NaiveClient::OnAcceptComplete(int result) {
HandleAcceptResult(result);
if (result == OK)
DoAcceptLoop();
}
void NaiveClient::HandleAcceptResult(int result) {
if (result != OK) {
LOG(ERROR) << "Accept error: rv=" << result;
return;
}
DoConnect();
}
void NaiveClient::DoConnect() {
auto connection_ptr = std::make_unique<NaiveClientConnection>(
++last_id_, std::move(accepted_socket_), session_);
auto* connection = connection_ptr.get();
connection_by_id_[connection->id()] = std::move(connection_ptr);
int result = connection->Connect(base::Bind(&NaiveClient::OnConnectComplete,
weak_ptr_factory_.GetWeakPtr(),
connection->id()));
if (result == ERR_IO_PENDING)
return;
HandleConnectResult(connection, result);
}
void NaiveClient::OnConnectComplete(int connection_id, int result) {
NaiveClientConnection* connection = FindConnection(connection_id);
if (!connection)
return;
HandleConnectResult(connection, result);
}
void NaiveClient::HandleConnectResult(NaiveClientConnection* connection,
int result) {
if (result != OK) {
Close(connection->id());
return;
}
DoRun(connection);
}
void NaiveClient::DoRun(NaiveClientConnection* connection) {
int result = connection->Run(base::Bind(&NaiveClient::OnRunComplete,
weak_ptr_factory_.GetWeakPtr(),
connection->id()));
if (result == ERR_IO_PENDING)
return;
HandleRunResult(connection, result);
}
void NaiveClient::OnRunComplete(int connection_id, int result) {
NaiveClientConnection* connection = FindConnection(connection_id);
if (!connection)
return;
HandleRunResult(connection, result);
}
void NaiveClient::HandleRunResult(NaiveClientConnection* connection,
int result) {
LOG(INFO) << "Connection " << connection->id()
<< " ended: " << ErrorToString(result);
Close(connection->id());
}
void NaiveClient::Close(int connection_id) {
auto it = connection_by_id_.find(connection_id);
if (it == connection_by_id_.end())
return;
// The call stack might have callbacks which still have the pointer of
// connection. Instead of referencing connection with ID all the time,
// destroys the connection in next run loop to make sure any pending
// callbacks in the call stack return.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
std::move(it->second));
connection_by_id_.erase(it);
}
NaiveClientConnection* NaiveClient::FindConnection(int connection_id) {
auto it = connection_by_id_.find(connection_id);
if (it == connection_by_id_.end())
return nullptr;
return it->second.get();
}
// This is called after any delegate callbacks are called to check if Close()
// has been called during callback processing. Using the pointer of connection,
// |connection| is safe here because Close() deletes the connection in next run
// loop.
bool NaiveClient::HasClosedConnection(NaiveClientConnection* connection) {
return FindConnection(connection->id()) != connection;
}
} // namespace net

View File

@ -0,0 +1,62 @@
// 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_NAIVE_CLIENT_H_
#define NET_TOOLS_NAIVE_NAIVE_CLIENT_H_
#include <map>
#include <memory>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
namespace net {
class HttpNetworkSession;
class ServerSocket;
class StreamSocket;
class NaiveClientConnection;
class NaiveClient {
public:
NaiveClient(std::unique_ptr<ServerSocket> server_socket,
HttpNetworkSession* session);
~NaiveClient();
private:
void DoAcceptLoop();
void OnAcceptComplete(int result);
void HandleAcceptResult(int result);
void DoConnect();
void OnConnectComplete(int connection_id, int result);
void HandleConnectResult(NaiveClientConnection* connection, int result);
void DoRun(NaiveClientConnection* connection);
void OnRunComplete(int connection_id, int result);
void HandleRunResult(NaiveClientConnection* connection, int result);
void Close(int connection_id);
NaiveClientConnection* FindConnection(int connection_id);
bool HasClosedConnection(NaiveClientConnection* connection);
std::unique_ptr<ServerSocket> server_socket_;
HttpNetworkSession* session_;
unsigned int last_id_;
std::unique_ptr<StreamSocket> accepted_socket_;
std::map<unsigned int, std::unique_ptr<NaiveClientConnection>>
connection_by_id_;
base::WeakPtrFactory<NaiveClient> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NaiveClient);
};
} // namespace net
#endif // NET_TOOLS_NAIVE_NAIVE_CLIENT_H_

View File

@ -0,0 +1,325 @@
// 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 <cstdlib>
#include <limits>
#include <memory>
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_info.h"
#include "base/task_scheduler/task_scheduler.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/auth.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_cache.h"
#include "net/http/http_network_session.h"
#include "net/http/http_transaction_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log.h"
#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_entry.h"
#include "net/log/net_log_source.h"
#include "net/log/net_log_util.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/tcp_server_socket.h"
#include "net/tools/naive/naive_client.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
#endif
namespace {
constexpr int kListenBackLog = 512;
constexpr int kDefaultMaxSocketsPerPool = 256;
constexpr int kDefaultMaxSocketsPerGroup = 255;
constexpr int kExpectedMaxUsers = 8;
std::unique_ptr<base::Value> GetConstants(
const base::CommandLine::StringType& command_line_string) {
auto constants_dict = net::GetNetConstants();
DCHECK(constants_dict);
// Add a dictionary with the version of the client and its command line
// arguments.
auto dict = std::make_unique<base::DictionaryValue>();
// We have everything we need to send the right values.
std::string os_type = base::StringPrintf(
"%s: %s (%s)", base::SysInfo::OperatingSystemName().c_str(),
base::SysInfo::OperatingSystemVersion().c_str(),
base::SysInfo::OperatingSystemArchitecture().c_str());
dict->SetString("os_type", os_type);
dict->SetString("command_line", command_line_string);
constants_dict->Set("clientInfo", std::move(dict));
return std::move(constants_dict);
}
// Builds a URLRequestContext assuming there's only a single loop.
std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
const std::string& proxy_url,
const std::string& proxy_user,
const std::string& proxy_pass,
net::NetLog* net_log) {
net::URLRequestContextBuilder builder;
net::ProxyConfig proxy_config;
proxy_config.proxy_rules().ParseFromString(proxy_url);
auto proxy_service = net::ProxyService::CreateWithoutProxyResolver(
std::make_unique<net::ProxyConfigServiceFixed>(proxy_config), net_log);
proxy_service->ForceReloadProxyConfig();
builder.set_proxy_service(std::move(proxy_service));
builder.DisableHttpCache();
builder.set_net_log(net_log);
auto context = builder.Build();
net::HttpNetworkSession* session =
context->http_transaction_factory()->GetSession();
net::HttpAuthCache* auth_cache = session->http_auth_cache();
GURL auth_origin(proxy_url);
net::AuthCredentials credentials(base::ASCIIToUTF16(proxy_user),
base::ASCIIToUTF16(proxy_pass));
auth_cache->Add(auth_origin, /*realm=*/std::string(),
net::HttpAuth::AUTH_SCHEME_BASIC, /*challenge=*/"Basic",
credentials, /*path=*/"/");
return context;
}
struct Params {
std::string listen_addr;
int listen_port;
std::string proxy_url;
std::string proxy_user;
std::string proxy_pass;
logging::LoggingSettings log_settings;
base::FilePath net_log_path;
base::FilePath ssl_key_path;
};
bool ParseCommandLineFlags(Params* params) {
const base::CommandLine& line = *base::CommandLine::ForCurrentProcess();
if (line.HasSwitch("h") || line.HasSwitch("help")) {
LOG(INFO) << "Usage: naive_client [options]\n"
"\n"
"Options:\n"
"-h, --help Show this help message and exit\n"
"--addr=<address> Address to listen on\n"
"--port=<port> Port to listen on\n"
"--proxy=https://<user>:<pass>@<domain>[:port]\n"
" Proxy specification\n"
"--log Log to stderr, otherwise no log\n"
"--log-net-log=<path> Save NetLog\n"
"--ssl-key-log-file=<path> Save SSL keys for Wireshark\n";
exit(EXIT_SUCCESS);
return false;
}
if (!line.HasSwitch("addr")) {
LOG(ERROR) << "Missing --addr";
return false;
}
params->listen_addr = line.GetSwitchValueASCII("addr");
if (params->listen_addr.empty()) {
LOG(ERROR) << "Invalid --port";
return false;
}
if (!line.HasSwitch("port")) {
LOG(ERROR) << "Missing --port";
return false;
}
if (!base::StringToInt(line.GetSwitchValueASCII("port"),
&params->listen_port)) {
LOG(ERROR) << "Invalid --port";
return false;
}
if (params->listen_port <= 0 ||
params->listen_port > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Invalid --port";
return false;
}
if (!line.HasSwitch("proxy")) {
LOG(ERROR) << "Missing --proxy";
return false;
}
GURL url(line.GetSwitchValueASCII("proxy"));
if (!url.is_valid()) {
LOG(ERROR) << "Invalid proxy URL";
return false;
}
if (url.scheme() != "https") {
LOG(ERROR) << "Must be HTTPS proxy";
return false;
}
if (url.username().empty() || url.password().empty()) {
LOG(ERROR) << "Missing user or pass";
return false;
}
params->proxy_url = url::SchemeHostPort(url).Serialize();
params->proxy_user = url.username();
params->proxy_pass = url.password();
if (line.HasSwitch("log")) {
params->log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
} else {
params->log_settings.logging_dest = logging::LOG_NONE;
}
if (line.HasSwitch("log-net-log")) {
params->net_log_path = line.GetSwitchValuePath("log-net-log");
}
if (line.HasSwitch("ssl-key-log-file")) {
params->ssl_key_path = line.GetSwitchValuePath("ssl-key-log-file");
}
return true;
}
// NetLog::ThreadSafeObserver implementation that simply prints events
// to the logs.
class PrintingLogObserver : public net::NetLog::ThreadSafeObserver {
public:
PrintingLogObserver() = default;
~PrintingLogObserver() override {
// This is guaranteed to be safe as this program is single threaded.
net_log()->RemoveObserver(this);
}
// NetLog::ThreadSafeObserver implementation:
void OnAddEntry(const net::NetLogEntry& entry) override {
switch (entry.type()) {
case net::NetLogEventType::SOCKET_POOL_STALLED_MAX_SOCKETS:
case net::NetLogEventType::SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP:
case net::NetLogEventType::
HTTP2_SESSION_STREAM_STALLED_BY_SESSION_SEND_WINDOW:
case net::NetLogEventType::
HTTP2_SESSION_STREAM_STALLED_BY_STREAM_SEND_WINDOW:
case net::NetLogEventType::HTTP2_SESSION_STALLED_MAX_STREAMS:
case net::NetLogEventType::HTTP2_STREAM_FLOW_CONTROL_UNSTALLED:
break;
default:
return;
}
const char* const source_type =
net::NetLog::SourceTypeToString(entry.source().type);
const char* const event_type = net::NetLog::EventTypeToString(entry.type());
const char* const event_phase =
net::NetLog::EventPhaseToString(entry.phase());
auto params = entry.ParametersToValue();
std::string params_str;
if (params.get()) {
base::JSONWriter::Write(*params, &params_str);
params_str.insert(0, ": ");
}
LOG(INFO) << source_type << "(" << entry.source().id << "): " << event_type
<< ": " << event_phase << params_str;
}
private:
DISALLOW_COPY_AND_ASSIGN(PrintingLogObserver);
};
} // namespace
int main(int argc, char* argv[]) {
base::TaskScheduler::CreateAndStartWithDefaultParams("");
base::AtExitManager exit_manager;
base::MessageLoopForIO main_loop;
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool pool;
#endif
base::CommandLine::Init(argc, argv);
Params params;
if (!ParseCommandLineFlags(&params)) {
return EXIT_FAILURE;
}
net::ClientSocketPoolManager::set_max_sockets_per_pool(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerGroup * kExpectedMaxUsers);
CHECK(logging::InitLogging(params.log_settings));
if (!params.ssl_key_path.empty()) {
net::SSLClientSocket::SetSSLKeyLogFile(params.ssl_key_path);
}
// The declaration order for net_log and printing_log_observer is
// important. The destructor of PrintingLogObserver removes itself
// from net_log, so net_log must be available for entire lifetime of
// printing_log_observer.
net::NetLog net_log;
std::unique_ptr<net::FileNetLogObserver> observer;
if (!params.net_log_path.empty()) {
const auto& cmdline =
base::CommandLine::ForCurrentProcess()->GetCommandLineString();
observer = net::FileNetLogObserver::CreateUnbounded(params.net_log_path,
GetConstants(cmdline));
observer->StartObserving(&net_log, net::NetLogCaptureMode::Default());
}
PrintingLogObserver printing_log_observer;
net_log.AddObserver(&printing_log_observer,
net::NetLogCaptureMode::Default());
auto context = BuildURLRequestContext(params.proxy_url, params.proxy_user,
params.proxy_pass, &net_log);
auto server_socket =
std::make_unique<net::TCPServerSocket>(&net_log, net::NetLogSource());
int result = server_socket->ListenWithAddressAndPort(
params.listen_addr, params.listen_port, kListenBackLog);
if (result != net::OK) {
LOG(ERROR) << "Failed to listen: " << result;
return EXIT_FAILURE;
}
net::NaiveClient naive_client(
std::move(server_socket),
context->http_transaction_factory()->GetSession());
base::RunLoop().Run();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,272 @@
// 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/naive_client_connection.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/privacy_mode.h"
#include "net/http/http_network_session.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_list.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/stream_socket.h"
#include "net/tools/naive/socks5_server_socket.h"
namespace net {
namespace {
static const int kBufferSize = 64 * 1024;
}
NaiveClientConnection::NaiveClientConnection(
int id,
std::unique_ptr<StreamSocket> accepted_socket,
HttpNetworkSession* session)
: id_(id),
next_state_(STATE_NONE),
session_(session),
net_log_(
NetLogWithSource::Make(session->net_log(), NetLogSourceType::NONE)),
client_socket_(
std::make_unique<Socks5ServerSocket>(std::move(accepted_socket))),
server_socket_handle_(std::make_unique<ClientSocketHandle>()),
client_error_(OK),
server_error_(OK),
full_duplex_(false),
weak_ptr_factory_(this) {
io_callback_ = base::Bind(&NaiveClientConnection::OnIOComplete,
weak_ptr_factory_.GetWeakPtr());
}
NaiveClientConnection::~NaiveClientConnection() {
Disconnect();
}
int NaiveClientConnection::Connect(const CompletionCallback& callback) {
DCHECK(client_socket_);
DCHECK_EQ(next_state_, STATE_NONE);
DCHECK(connect_callback_.is_null());
if (full_duplex_)
return OK;
next_state_ = STATE_CONNECT_CLIENT;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING) {
connect_callback_ = callback;
}
return rv;
}
void NaiveClientConnection::Disconnect() {
full_duplex_ = false;
client_socket_->Disconnect();
if (server_socket_handle_->socket())
server_socket_handle_->socket()->Disconnect();
next_state_ = STATE_NONE;
connect_callback_.Reset();
run_callback_.Reset();
}
void NaiveClientConnection::DoCallback(int result) {
DCHECK_NE(result, ERR_IO_PENDING);
DCHECK(!connect_callback_.is_null());
// Since Run() may result in Read being called,
// clear connect_callback_ up front.
base::ResetAndReturn(&connect_callback_).Run(result);
}
void NaiveClientConnection::OnIOComplete(int result) {
DCHECK_NE(next_state_, STATE_NONE);
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
DoCallback(rv);
}
}
int NaiveClientConnection::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_CONNECT_CLIENT:
DCHECK_EQ(rv, OK);
rv = DoConnectClient();
break;
case STATE_CONNECT_CLIENT_COMPLETE:
rv = DoConnectClientComplete(rv);
break;
case STATE_CONNECT_SERVER:
DCHECK_EQ(rv, OK);
rv = DoConnectServer();
case STATE_CONNECT_SERVER_COMPLETE:
rv = DoConnectServerComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_UNEXPECTED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int NaiveClientConnection::DoConnectClient() {
next_state_ = STATE_CONNECT_CLIENT_COMPLETE;
return client_socket_->Connect(&request_endpoint_, io_callback_);
}
int NaiveClientConnection::DoConnectClientComplete(int result) {
if (result < 0)
return result;
next_state_ = STATE_CONNECT_SERVER;
return OK;
}
int NaiveClientConnection::DoConnectServer() {
ProxyInfo proxy_info;
const ProxyList& proxy_list =
session_->proxy_service()->config().proxy_rules().single_proxies;
if (proxy_list.IsEmpty())
return ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
proxy_info.UseProxyList(proxy_list);
HttpRequestInfo req_info;
SSLConfig server_ssl_config;
SSLConfig proxy_ssl_config;
session_->GetSSLConfig(req_info, &server_ssl_config, &proxy_ssl_config);
proxy_ssl_config.rev_checking_enabled = false;
next_state_ = STATE_CONNECT_SERVER_COMPLETE;
DCHECK_NE(request_endpoint_.port(), 0);
LOG(INFO) << "Connection " << id_ << " to " << request_endpoint_.ToString();
return InitSocketHandleForRawConnect(
request_endpoint_, session_, proxy_info, server_ssl_config,
proxy_ssl_config, PRIVACY_MODE_DISABLED, net_log_,
server_socket_handle_.get(), io_callback_);
}
int NaiveClientConnection::DoConnectServerComplete(int result) {
if (result < 0)
return result;
full_duplex_ = true;
next_state_ = STATE_NONE;
return OK;
}
int NaiveClientConnection::Run(const CompletionCallback& callback) {
DCHECK(client_socket_);
DCHECK(server_socket_handle_->socket());
DCHECK_EQ(next_state_, STATE_NONE);
DCHECK(connect_callback_.is_null());
run_callback_ = callback;
Pull(client_socket_.get(), server_socket_handle_->socket());
Pull(server_socket_handle_->socket(), client_socket_.get());
return ERR_IO_PENDING;
}
void NaiveClientConnection::Pull(StreamSocket* from, StreamSocket* to) {
if (client_error_ < 0 || server_error_ < 0)
return;
auto buffer = base::MakeRefCounted<IOBuffer>(kBufferSize);
int rv =
from->Read(buffer.get(), kBufferSize,
base::Bind(&NaiveClientConnection::OnReadComplete,
weak_ptr_factory_.GetWeakPtr(), from, to, buffer));
if (rv != ERR_IO_PENDING)
OnReadComplete(from, to, buffer, rv);
}
void NaiveClientConnection::Push(StreamSocket* from,
StreamSocket* to,
scoped_refptr<IOBuffer> buffer,
int size) {
if (client_error_ < 0 || server_error_ < 0)
return;
auto drainable = base::MakeRefCounted<DrainableIOBuffer>(buffer.get(), size);
int rv = to->Write(
drainable.get(), size,
base::Bind(&NaiveClientConnection::OnWriteComplete,
weak_ptr_factory_.GetWeakPtr(), from, to, drainable));
if (rv != ERR_IO_PENDING)
OnWriteComplete(from, to, drainable, rv);
}
void NaiveClientConnection::OnIOError(StreamSocket* socket, int error) {
if (socket == client_socket_.get()) {
if (client_error_ == OK) {
base::ResetAndReturn(&run_callback_).Run(error);
}
client_error_ = error;
return;
}
if (socket == server_socket_handle_->socket()) {
if (server_error_ == OK) {
base::ResetAndReturn(&run_callback_).Run(error);
}
server_error_ = error;
return;
}
}
void NaiveClientConnection::OnReadComplete(StreamSocket* from,
StreamSocket* to,
scoped_refptr<IOBuffer> buffer,
int result) {
if (result <= 0) {
OnIOError(from, result ? result : ERR_CONNECTION_CLOSED);
return;
}
Push(from, to, buffer, result);
}
void NaiveClientConnection::OnWriteComplete(
StreamSocket* from,
StreamSocket* to,
scoped_refptr<DrainableIOBuffer> drainable,
int result) {
if (result < 0) {
OnIOError(to, result);
return;
}
drainable->DidConsume(result);
int size = drainable->BytesRemaining();
if (size > 0) {
Push(from, to, drainable.get(), size);
return;
}
Pull(from, to);
}
} // namespace net

View File

@ -0,0 +1,100 @@
// 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_NAIVE_CLIENT_CONNECTION_H_
#define NET_TOOLS_NAIVE_NAIVE_CLIENT_CONNECTION_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "net/base/completion_callback.h"
#include "net/base/host_port_pair.h"
#include "net/log/net_log_with_source.h"
#include "net/proxy/proxy_info.h"
#include "net/ssl/ssl_config.h"
namespace net {
class ClientSocketHandle;
class DrainableIOBuffer;
class HttpNetworkSession;
class IOBuffer;
class Socks5ServerSocket;
class StreamSocket;
class NaiveClientConnection {
public:
NaiveClientConnection(int id,
std::unique_ptr<StreamSocket> accepted_socket,
HttpNetworkSession* session);
~NaiveClientConnection();
int id() const { return id_; }
int Connect(const CompletionCallback& callback);
void Disconnect();
int Run(const CompletionCallback& callback);
private:
enum State {
STATE_CONNECT_CLIENT,
STATE_CONNECT_CLIENT_COMPLETE,
STATE_CONNECT_SERVER,
STATE_CONNECT_SERVER_COMPLETE,
STATE_NONE,
};
void DoCallback(int result);
void OnIOComplete(int result);
int DoLoop(int last_io_result);
int DoConnectClient();
int DoConnectClientComplete(int result);
int DoConnectServer();
int DoConnectServerComplete(int result);
void Pull(StreamSocket* from, StreamSocket* to);
void Push(StreamSocket* from,
StreamSocket* to,
scoped_refptr<IOBuffer> buffer,
int size);
void OnIOError(StreamSocket* socket, int error);
void OnReadComplete(StreamSocket* from,
StreamSocket* to,
scoped_refptr<IOBuffer> buffer,
int result);
void OnWriteComplete(StreamSocket* from,
StreamSocket* to,
scoped_refptr<DrainableIOBuffer> drainable,
int result);
int id_;
CompletionCallback io_callback_;
CompletionCallback connect_callback_;
CompletionCallback run_callback_;
State next_state_;
HttpNetworkSession* session_;
NetLogWithSource net_log_;
HostPortPair request_endpoint_;
std::unique_ptr<Socks5ServerSocket> client_socket_;
std::unique_ptr<StreamSocket> server_socket_;
std::unique_ptr<ClientSocketHandle> server_socket_handle_;
int client_error_;
int server_error_;
bool full_duplex_;
base::WeakPtrFactory<NaiveClientConnection> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NaiveClientConnection);
};
} // namespace net
#endif // NET_TOOLS_NAIVE_NAIVE_CLIENT_CONNECTION_H_

View File

@ -0,0 +1,565 @@
// 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/socks5_server_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/sys_byteorder.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/log/net_log.h"
#include "net/log/net_log_event_type.h"
namespace net {
const unsigned int Socks5ServerSocket::kGreetReadHeaderSize = 2;
const unsigned int Socks5ServerSocket::kReadHeaderSize = 5;
const char Socks5ServerSocket::kSOCKS5Version = '\x05';
const char Socks5ServerSocket::kSOCKS5Reserved = '\x00';
const char Socks5ServerSocket::kAuthMethodNone = '\x00';
const char Socks5ServerSocket::kAuthMethodNoAcceptable = '\xff';
const char Socks5ServerSocket::kReplySuccess = '\x00';
const char Socks5ServerSocket::kReplyCommandNotSupported = '\x07';
static_assert(sizeof(struct in_addr) == 4, "incorrect system size of IPv4");
static_assert(sizeof(struct in6_addr) == 16, "incorrect system size of IPv6");
Socks5ServerSocket::Socks5ServerSocket(
std::unique_ptr<StreamSocket> transport_socket)
: io_callback_(base::Bind(&Socks5ServerSocket::OnIOComplete,
base::Unretained(this))),
transport_(std::move(transport_socket)),
next_state_(STATE_NONE),
completed_handshake_(false),
bytes_received_(0),
bytes_sent_(0),
greet_read_header_size_(kGreetReadHeaderSize),
read_header_size_(kReadHeaderSize),
was_ever_used_(false),
net_log_(transport_->NetLog()) {}
Socks5ServerSocket::~Socks5ServerSocket() {
Disconnect();
}
int Socks5ServerSocket::Connect(const CompletionCallback& callback) {
DCHECK(transport_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
// If already connected, then just return OK.
if (completed_handshake_)
return OK;
net_log_.BeginEvent(NetLogEventType::SOCKS5_CONNECT);
next_state_ = STATE_GREET_READ;
buffer_.clear();
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING) {
user_callback_ = callback;
} else {
net_log_.EndEventWithNetErrorCode(NetLogEventType::SOCKS5_CONNECT, rv);
}
return rv;
}
int Socks5ServerSocket::Connect(HostPortPair* request_endpoint,
const CompletionCallback& callback) {
int result =
Connect(base::Bind(&Socks5ServerSocket::DoCallbackReturnRequest,
base::Unretained(this), request_endpoint, callback));
if (result == OK)
*request_endpoint = host_port_pair_;
return result;
}
void Socks5ServerSocket::DoCallbackReturnRequest(
HostPortPair* request_endpoint,
const CompletionCallback& callback,
int result) {
if (result == OK)
*request_endpoint = host_port_pair_;
callback.Run(result);
}
void Socks5ServerSocket::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 Socks5ServerSocket::IsConnected() const {
return completed_handshake_ && transport_->IsConnected();
}
bool Socks5ServerSocket::IsConnectedAndIdle() const {
return completed_handshake_ && transport_->IsConnectedAndIdle();
}
const NetLogWithSource& Socks5ServerSocket::NetLog() const {
return net_log_;
}
void Socks5ServerSocket::SetSubresourceSpeculation() {
if (transport_) {
transport_->SetSubresourceSpeculation();
} else {
NOTREACHED();
}
}
void Socks5ServerSocket::SetOmniboxSpeculation() {
if (transport_) {
transport_->SetOmniboxSpeculation();
} else {
NOTREACHED();
}
}
bool Socks5ServerSocket::WasEverUsed() const {
return was_ever_used_;
}
bool Socks5ServerSocket::WasAlpnNegotiated() const {
if (transport_) {
return transport_->WasAlpnNegotiated();
}
NOTREACHED();
return false;
}
NextProto Socks5ServerSocket::GetNegotiatedProtocol() const {
if (transport_) {
return transport_->GetNegotiatedProtocol();
}
NOTREACHED();
return kProtoUnknown;
}
bool Socks5ServerSocket::GetSSLInfo(SSLInfo* ssl_info) {
if (transport_) {
return transport_->GetSSLInfo(ssl_info);
}
NOTREACHED();
return false;
}
void Socks5ServerSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
out->clear();
}
int64_t Socks5ServerSocket::GetTotalReceivedBytes() const {
return transport_->GetTotalReceivedBytes();
}
// Read is called by the transport layer above to read. This can only be done
// if the SOCKS handshake is complete.
int Socks5ServerSocket::Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
DCHECK(!callback.is_null());
int rv = transport_->Read(buf, buf_len,
base::Bind(&Socks5ServerSocket::OnReadWriteComplete,
base::Unretained(this), 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 Socks5ServerSocket::Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
DCHECK(completed_handshake_);
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(user_callback_.is_null());
DCHECK(!callback.is_null());
int rv =
transport_->Write(buf, buf_len,
base::Bind(&Socks5ServerSocket::OnReadWriteComplete,
base::Unretained(this), callback));
if (rv > 0)
was_ever_used_ = true;
return rv;
}
int Socks5ServerSocket::SetReceiveBufferSize(int32_t size) {
return transport_->SetReceiveBufferSize(size);
}
int Socks5ServerSocket::SetSendBufferSize(int32_t size) {
return transport_->SetSendBufferSize(size);
}
void Socks5ServerSocket::DoCallback(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
DCHECK(!user_callback_.is_null());
// Since Run() may result in Read being called,
// clear user_callback_ up front.
base::ResetAndReturn(&user_callback_).Run(result);
}
void Socks5ServerSocket::OnIOComplete(int result) {
DCHECK_NE(STATE_NONE, next_state_);
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING) {
net_log_.EndEvent(NetLogEventType::SOCKS5_CONNECT);
DoCallback(rv);
}
}
void Socks5ServerSocket::OnReadWriteComplete(const CompletionCallback& callback,
int result) {
DCHECK_NE(ERR_IO_PENDING, result);
DCHECK(!callback.is_null());
if (result > 0)
was_ever_used_ = true;
callback.Run(result);
}
int Socks5ServerSocket::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_GREET_READ:
DCHECK_EQ(OK, rv);
net_log_.BeginEvent(NetLogEventType::SOCKS5_GREET_READ);
rv = DoGreetRead();
break;
case STATE_GREET_READ_COMPLETE:
rv = DoGreetReadComplete(rv);
net_log_.EndEventWithNetErrorCode(NetLogEventType::SOCKS5_GREET_WRITE,
rv);
break;
case STATE_GREET_WRITE:
DCHECK_EQ(OK, rv);
net_log_.BeginEvent(NetLogEventType::SOCKS5_GREET_READ);
rv = DoGreetWrite();
break;
case STATE_GREET_WRITE_COMPLETE:
rv = DoGreetWriteComplete(rv);
net_log_.EndEventWithNetErrorCode(NetLogEventType::SOCKS5_GREET_READ,
rv);
break;
case STATE_HANDSHAKE_READ:
DCHECK_EQ(OK, rv);
net_log_.BeginEvent(NetLogEventType::SOCKS5_HANDSHAKE_READ);
rv = DoHandshakeRead();
break;
case STATE_HANDSHAKE_READ_COMPLETE:
rv = DoHandshakeReadComplete(rv);
net_log_.EndEventWithNetErrorCode(
NetLogEventType::SOCKS5_HANDSHAKE_READ, rv);
break;
case STATE_HANDSHAKE_WRITE:
DCHECK_EQ(OK, rv);
net_log_.BeginEvent(NetLogEventType::SOCKS5_HANDSHAKE_WRITE);
rv = DoHandshakeWrite();
break;
case STATE_HANDSHAKE_WRITE_COMPLETE:
rv = DoHandshakeWriteComplete(rv);
net_log_.EndEventWithNetErrorCode(
NetLogEventType::SOCKS5_HANDSHAKE_WRITE, rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_UNEXPECTED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int Socks5ServerSocket::DoGreetRead() {
next_state_ = STATE_GREET_READ_COMPLETE;
if (buffer_.empty()) {
DCHECK_EQ(0U, bytes_received_);
DCHECK_EQ(kGreetReadHeaderSize, greet_read_header_size_);
}
int handshake_buf_len = greet_read_header_size_ - bytes_received_;
DCHECK_LT(0, handshake_buf_len);
handshake_buf_ = new IOBuffer(handshake_buf_len);
return transport_->Read(handshake_buf_.get(), handshake_buf_len,
io_callback_);
}
int Socks5ServerSocket::DoGreetReadComplete(int result) {
if (result < 0)
return result;
if (result == 0) {
net_log_.AddEvent(
NetLogEventType::SOCKS_UNEXPECTEDLY_CLOSED_DURING_GREETING);
return ERR_SOCKS_CONNECTION_FAILED;
}
bytes_received_ += result;
buffer_.append(handshake_buf_->data(), result);
// When the first few bytes are read, check how many more are required
// and accordingly increase them
if (bytes_received_ == kGreetReadHeaderSize) {
if (buffer_[0] != kSOCKS5Version) {
net_log_.AddEvent(NetLogEventType::SOCKS_UNEXPECTED_VERSION,
NetLog::IntCallback("version", buffer_[0]));
return ERR_SOCKS_CONNECTION_FAILED;
}
if (buffer_[1] == 0) {
net_log_.AddEvent(NetLogEventType::SOCKS_NO_REQUESTED_AUTH);
return ERR_SOCKS_CONNECTION_FAILED;
}
greet_read_header_size_ += buffer_[1];
next_state_ = STATE_GREET_READ;
return OK;
}
if (bytes_received_ == greet_read_header_size_) {
void* match = std::memchr(&buffer_[kGreetReadHeaderSize], kAuthMethodNone,
greet_read_header_size_ - kGreetReadHeaderSize);
if (match) {
auth_method_ = kAuthMethodNone;
} else {
auth_method_ = kAuthMethodNoAcceptable;
}
buffer_.clear();
next_state_ = STATE_GREET_WRITE;
return OK;
}
next_state_ = STATE_GREET_READ;
return OK;
}
int Socks5ServerSocket::DoGreetWrite() {
if (buffer_.empty()) {
const char write_data[] = {kSOCKS5Version, auth_method_};
buffer_ = std::string(write_data, arraysize(write_data));
bytes_sent_ = 0;
}
next_state_ = STATE_GREET_WRITE_COMPLETE;
int handshake_buf_len = buffer_.size() - bytes_sent_;
DCHECK_LT(0, handshake_buf_len);
handshake_buf_ = new IOBuffer(handshake_buf_len);
std::memcpy(handshake_buf_->data(), &buffer_.data()[bytes_sent_],
handshake_buf_len);
return transport_->Write(handshake_buf_.get(), handshake_buf_len,
io_callback_);
}
int Socks5ServerSocket::DoGreetWriteComplete(int result) {
if (result < 0)
return result;
bytes_sent_ += result;
if (bytes_sent_ == buffer_.size()) {
buffer_.clear();
bytes_received_ = 0;
if (auth_method_ != kAuthMethodNoAcceptable) {
next_state_ = STATE_HANDSHAKE_READ;
} else {
net_log_.AddEvent(NetLogEventType::SOCKS_NO_ACCEPTABLE_AUTH);
return ERR_SOCKS_CONNECTION_FAILED;
}
} else {
next_state_ = STATE_GREET_WRITE;
}
return OK;
}
int Socks5ServerSocket::DoHandshakeRead() {
next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
if (buffer_.empty()) {
DCHECK_EQ(0U, bytes_received_);
DCHECK_EQ(kReadHeaderSize, read_header_size_);
}
int handshake_buf_len = read_header_size_ - bytes_received_;
DCHECK_LT(0, handshake_buf_len);
handshake_buf_ = new IOBuffer(handshake_buf_len);
return transport_->Read(handshake_buf_.get(), handshake_buf_len,
io_callback_);
}
int Socks5ServerSocket::DoHandshakeReadComplete(int result) {
if (result < 0)
return result;
// The underlying socket closed unexpectedly.
if (result == 0) {
net_log_.AddEvent(
NetLogEventType::SOCKS_UNEXPECTEDLY_CLOSED_DURING_HANDSHAKE);
return ERR_SOCKS_CONNECTION_FAILED;
}
buffer_.append(handshake_buf_->data(), result);
bytes_received_ += result;
// When the first few bytes are read, check how many more are required
// and accordingly increase them
if (bytes_received_ == kReadHeaderSize) {
if (buffer_[0] != kSOCKS5Version || buffer_[2] != kSOCKS5Reserved) {
net_log_.AddEvent(NetLogEventType::SOCKS_UNEXPECTED_VERSION,
NetLog::IntCallback("version", buffer_[0]));
return ERR_SOCKS_CONNECTION_FAILED;
}
SocksCommandType command = static_cast<SocksCommandType>(buffer_[1]);
if (command == kCommandConnect) {
// The proxy replies with success immediately without first connecting
// to the requested endpoint.
reply_ = kReplySuccess;
} else if (command == kCommandBind || command == kCommandUDPAssociate) {
reply_ = kReplyCommandNotSupported;
} else {
net_log_.AddEvent(NetLogEventType::SOCKS_UNEXPECTED_COMMAND,
NetLog::IntCallback("commmand", buffer_[1]));
return ERR_SOCKS_CONNECTION_FAILED;
}
// We check the type of IP/Domain the server returns and accordingly
// increase the size of the request. For domains, we need to read the
// size of the domain, so the initial request size is upto the domain
// size. Since for IPv4/IPv6 the size is fixed and hence no 'size' is
// read, we substract 1 byte from the additional request size.
address_type_ = static_cast<SocksEndPointAddressType>(buffer_[3]);
if (address_type_ == kEndPointDomain) {
address_size_ = static_cast<uint8_t>(buffer_[4]);
if (address_size_ == 0) {
net_log_.AddEvent(NetLogEventType::SOCKS_ZERO_LENGTH_DOMAIN);
return ERR_SOCKS_CONNECTION_FAILED;
}
} else if (address_type_ == kEndPointResolvedIPv4) {
address_size_ = sizeof(struct in_addr);
--read_header_size_;
} else if (address_type_ == kEndPointResolvedIPv6) {
address_size_ = sizeof(struct in6_addr);
--read_header_size_;
} else {
// Aborts connection on unspecified address type.
net_log_.AddEvent(NetLogEventType::SOCKS_UNKNOWN_ADDRESS_TYPE,
NetLog::IntCallback("address_type", buffer_[3]));
return ERR_SOCKS_CONNECTION_FAILED;
}
read_header_size_ += address_size_ + sizeof(uint16_t);
next_state_ = STATE_HANDSHAKE_READ;
return OK;
}
// When the final bytes are read, setup handshake.
if (bytes_received_ == read_header_size_) {
size_t port_start = read_header_size_ - sizeof(uint16_t);
uint16_t port_net;
std::memcpy(&port_net, &buffer_[port_start], sizeof(uint16_t));
uint16_t port_host = base::NetToHost16(port_net);
size_t address_start = port_start - address_size_;
if (address_type_ == kEndPointDomain) {
std::string domain(&buffer_[address_start], address_size_);
host_port_pair_ = HostPortPair(domain, port_host);
} else {
IPAddress ip_addr(
reinterpret_cast<const uint8_t*>(&buffer_[address_start]),
address_size_);
IPEndPoint endpoint(ip_addr, port_host);
host_port_pair_ = HostPortPair::FromIPEndPoint(endpoint);
}
buffer_.clear();
next_state_ = STATE_HANDSHAKE_WRITE;
return OK;
}
next_state_ = STATE_HANDSHAKE_READ;
return OK;
}
// Writes the SOCKS handshake data to the underlying socket connection.
int Socks5ServerSocket::DoHandshakeWrite() {
next_state_ = STATE_HANDSHAKE_WRITE_COMPLETE;
if (buffer_.empty()) {
const char write_data[] = {
kSOCKS5Version,
reply_,
kSOCKS5Reserved,
kEndPointResolvedIPv4,
0x00, 0x00, 0x00, 0x00, // BND.ADDR
0x00, 0x00, // BND.PORT
};
buffer_ = std::string(write_data, arraysize(write_data));
bytes_sent_ = 0;
}
int handshake_buf_len = buffer_.size() - bytes_sent_;
DCHECK_LT(0, handshake_buf_len);
handshake_buf_ = new IOBuffer(handshake_buf_len);
std::memcpy(handshake_buf_->data(), &buffer_[bytes_sent_], handshake_buf_len);
return transport_->Write(handshake_buf_.get(), handshake_buf_len,
io_callback_);
}
int Socks5ServerSocket::DoHandshakeWriteComplete(int result) {
if (result < 0)
return result;
// We ignore the case when result is 0, since the underlying Write
// may return spurious writes while waiting on the socket.
bytes_sent_ += result;
if (bytes_sent_ == buffer_.size()) {
buffer_.clear();
if (reply_ == kReplySuccess) {
completed_handshake_ = true;
next_state_ = STATE_NONE;
} else {
net_log_.AddEvent(NetLogEventType::SOCKS_SERVER_ERROR,
NetLog::IntCallback("error_code", reply_));
return ERR_SOCKS_CONNECTION_FAILED;
}
} else if (bytes_sent_ < buffer_.size()) {
next_state_ = STATE_HANDSHAKE_WRITE;
} else {
NOTREACHED();
}
return OK;
}
int Socks5ServerSocket::GetPeerAddress(IPEndPoint* address) const {
return transport_->GetPeerAddress(address);
}
int Socks5ServerSocket::GetLocalAddress(IPEndPoint* address) const {
return transport_->GetLocalAddress(address);
}
} // namespace net

View File

@ -0,0 +1,172 @@
// 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_SOCKS5_SERVER_SOCKET_H_
#define NET_TOOLS_NAIVE_SOCKS5_SERVER_SOCKET_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "net/base/completion_callback.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_export.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 {
// This StreamSocket is used to setup a SOCKSv5 handshake with a socks client.
// Currently no SOCKSv5 authentication is supported.
class NET_EXPORT_PRIVATE Socks5ServerSocket : public StreamSocket {
public:
explicit Socks5ServerSocket(std::unique_ptr<StreamSocket> transport_socket);
// On destruction Disconnect() is called.
~Socks5ServerSocket() override;
int Connect(HostPortPair* request_endpoint,
const CompletionCallback& callback);
// StreamSocket implementation.
// Does the SOCKS handshake and completes the protocol.
int Connect(const CompletionCallback& callback) override;
void Disconnect() override;
bool IsConnected() const override;
bool IsConnectedAndIdle() const override;
const NetLogWithSource& NetLog() const override;
void SetSubresourceSpeculation() override;
void SetOmniboxSpeculation() 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;
// Socket implementation.
int Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) override;
int Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) 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_GREET_READ,
STATE_GREET_READ_COMPLETE,
STATE_GREET_WRITE,
STATE_GREET_WRITE_COMPLETE,
STATE_HANDSHAKE_WRITE,
STATE_HANDSHAKE_WRITE_COMPLETE,
STATE_HANDSHAKE_READ,
STATE_HANDSHAKE_READ_COMPLETE,
STATE_NONE,
};
// Addressing type that can be specified in requests or responses.
enum SocksEndPointAddressType {
kEndPointDomain = 0x03,
kEndPointResolvedIPv4 = 0x01,
kEndPointResolvedIPv6 = 0x04,
};
enum SocksCommandType {
kCommandConnect = 0x01,
kCommandBind = 0x02,
kCommandUDPAssociate = 0x03,
};
static const unsigned int kGreetReadHeaderSize;
static const unsigned int kReadHeaderSize;
static const char kSOCKS5Version;
static const char kSOCKS5Reserved;
static const char kAuthMethodNone;
static const char kAuthMethodNoAcceptable;
static const char kReplySuccess;
static const char kReplyCommandNotSupported;
void DoCallback(int result);
void DoCallbackReturnRequest(HostPortPair* request_endpoint,
const CompletionCallback& callback,
int result);
void OnIOComplete(int result);
void OnReadWriteComplete(const CompletionCallback& callback, int result);
int DoLoop(int last_io_result);
int DoGreetWrite();
int DoGreetWriteComplete(int result);
int DoGreetRead();
int DoGreetReadComplete(int result);
int DoHandshakeRead();
int DoHandshakeReadComplete(int result);
int DoHandshakeWrite();
int DoHandshakeWriteComplete(int result);
CompletionCallback 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().
CompletionCallback 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_;
// While writing, this buffer stores the complete write handshake data.
// While reading, it stores the handshake information received so far.
std::string buffer_;
// This becomes true when the SOCKS handshake has completed and the
// overlying connection is free to communicate.
bool completed_handshake_;
// These contain the bytes received / sent by the SOCKS handshake.
size_t bytes_received_;
size_t bytes_sent_;
size_t greet_read_header_size_;
size_t read_header_size_;
bool was_ever_used_;
SocksEndPointAddressType address_type_;
int address_size_;
char auth_method_;
char reply_;
HostPortPair host_port_pair_;
NetLogWithSource net_log_;
DISALLOW_COPY_AND_ASSIGN(Socks5ServerSocket);
};
} // namespace net
#endif // NET_TOOLS_NAIVE_SOCKS5_SERVER_SOCKET_H_