Add QUIC client

This commit is contained in:
klzgrad 2018-12-12 23:22:18 -05:00
parent dddfa78243
commit fc892ef95c
6 changed files with 177 additions and 184 deletions

View File

@ -164,7 +164,8 @@ std::unique_ptr<ConnectJob> ConnectJob::CreateConnectJob(
std::move(ssl_params), delegate, nullptr /* net_log */);
}
if (proxy_server.is_http() || proxy_server.is_https()) {
if (proxy_server.is_http() || proxy_server.is_https() ||
proxy_server.is_quic()) {
return std::make_unique<HttpProxyConnectJob>(
request_priority, socket_tag, common_connect_job_params,
std::move(http_proxy_params), delegate, nullptr /* net_log */);

View File

@ -12,19 +12,18 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/threading/thread_task_runner_handle.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_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/proxy_resolution/proxy_resolution_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/spdy/spdy_session.h"
#include "net/tools/naive/http_proxy_socket.h"
#include "net/tools/naive/socks5_server_socket.h"
namespace net {
@ -37,14 +36,24 @@ constexpr int kMaxPaddingSize = 255;
NaiveConnection::NaiveConnection(
unsigned int id,
Protocol protocol,
Direction pad_direction,
const ProxyInfo& proxy_info,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
HttpNetworkSession* session,
const NetLogWithSource& net_log,
std::unique_ptr<StreamSocket> accepted_socket,
Delegate* delegate,
const NetworkTrafficAnnotationTag& traffic_annotation)
: id_(id),
protocol_(protocol),
pad_direction_(pad_direction),
proxy_info_(proxy_info),
server_ssl_config_(server_ssl_config),
proxy_ssl_config_(proxy_ssl_config),
session_(session),
net_log_(net_log),
next_state_(STATE_NONE),
delegate_(delegate),
client_socket_(std::move(accepted_socket)),
server_socket_handle_(std::make_unique<ClientSocketHandle>()),
sockets_{client_socket_.get(), nullptr},
@ -167,10 +176,30 @@ int NaiveConnection::DoConnectClientComplete(int result) {
}
int NaiveConnection::DoConnectServer() {
DCHECK(delegate_);
next_state_ = STATE_CONNECT_SERVER_COMPLETE;
return delegate_->OnConnectServer(id_, client_socket_.get(),
HostPortPair origin;
if (protocol_ == kSocks5) {
const auto* socket =
static_cast<const Socks5ServerSocket*>(client_socket_.get());
origin = socket->request_endpoint();
} else if (protocol_ == kHttp) {
const auto* socket =
static_cast<const HttpProxySocket*>(client_socket_.get());
origin = socket->request_endpoint();
}
if (origin.IsEmpty()) {
LOG(ERROR) << "Connection " << id_ << " to invalid origin";
return ERR_ADDRESS_INVALID;
}
LOG(INFO) << "Connection " << id_ << " to " << origin.ToString();
// Ignores socket limit set by socket pool for this type of socket.
return InitSocketHandleForRawConnect2(
origin, session_, LOAD_IGNORE_LIMITS, MAXIMUM_PRIORITY, proxy_info_,
server_ssl_config_, proxy_ssl_config_, PRIVACY_MODE_DISABLED, net_log_,
server_socket_handle_.get(), io_callback_);
}

View File

@ -20,14 +20,23 @@ namespace net {
class ClientSocketHandle;
class DrainableIOBuffer;
class HttpNetworkSession;
class IOBuffer;
class NetLogWithSource;
class ProxyInfo;
class StreamSocket;
struct NetworkTrafficAnnotationTag;
struct SSLConfig;
class NaiveConnection {
public:
using TimeFunc = base::TimeTicks (*)();
enum Protocol {
kSocks5,
kHttp,
};
// From this direction.
enum Direction {
kClient = 0,
@ -36,24 +45,15 @@ class NaiveConnection {
kNone = 2,
};
class Delegate {
public:
Delegate() {}
virtual ~Delegate() {}
virtual int OnConnectServer(unsigned int connection_id,
const StreamSocket* accepted_socket,
ClientSocketHandle* server_socket,
CompletionRepeatingCallback callback) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
NaiveConnection(unsigned int id,
Protocol protocol,
Direction pad_direction,
const ProxyInfo& proxy_info,
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
HttpNetworkSession* session,
const NetLogWithSource& net_log,
std::unique_ptr<StreamSocket> accepted_socket,
Delegate* delegate,
const NetworkTrafficAnnotationTag& traffic_annotation);
~NaiveConnection();
@ -97,7 +97,13 @@ class NaiveConnection {
void OnPushComplete(Direction from, Direction to, int result);
unsigned int id_;
Protocol protocol_;
Direction pad_direction_;
const ProxyInfo& proxy_info_;
const SSLConfig& server_ssl_config_;
const SSLConfig& proxy_ssl_config_;
HttpNetworkSession* session_;
const NetLogWithSource& net_log_;
CompletionRepeatingCallback io_callback_;
CompletionOnceCallback connect_callback_;
@ -105,8 +111,6 @@ class NaiveConnection {
State next_state_;
Delegate* delegate_;
std::unique_ptr<StreamSocket> client_socket_;
std::unique_ptr<ClientSocketHandle> server_socket_handle_;

View File

@ -14,32 +14,43 @@
#include "net/base/net_errors.h"
#include "net/http/http_network_session.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_list.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/server_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"
namespace net {
NaiveProxy::NaiveProxy(std::unique_ptr<ServerSocket> listen_socket,
Protocol protocol,
bool use_proxy,
NaiveConnection::Protocol protocol,
bool use_padding,
HttpNetworkSession* session,
const NetworkTrafficAnnotationTag& traffic_annotation)
: listen_socket_(std::move(listen_socket)),
protocol_(protocol),
use_proxy_(use_proxy),
use_padding_(use_padding),
session_(session),
net_log_(
NetLogWithSource::Make(session->net_log(), NetLogSourceType::NONE)),
last_id_(0),
traffic_annotation_(traffic_annotation),
weak_ptr_factory_(this) {
const auto& proxy_config = session_->proxy_resolution_service()->config();
DCHECK(proxy_config);
const ProxyList& proxy_list =
proxy_config.value().value().proxy_rules().single_proxies;
DCHECK(!proxy_list.IsEmpty());
proxy_info_.UseProxyList(proxy_list);
proxy_info_.set_traffic_annotation(
net::MutableNetworkTrafficAnnotationTag(traffic_annotation_));
HttpRequestInfo req_info;
session_->GetSSLConfig(req_info, &server_ssl_config_, &proxy_ssl_config_);
proxy_ssl_config_.disable_cert_verification_network_fetches = true;
DCHECK(listen_socket_);
// Start accepting connections in next run loop in case when delegate is not
// ready to get callbacks.
@ -79,19 +90,24 @@ void NaiveProxy::HandleAcceptResult(int result) {
void NaiveProxy::DoConnect() {
std::unique_ptr<StreamSocket> socket;
NaiveConnection::Direction pad_direction;
if (protocol_ == kSocks5) {
if (protocol_ == NaiveConnection::kSocks5) {
socket = std::make_unique<Socks5ServerSocket>(std::move(accepted_socket_),
traffic_annotation_);
pad_direction = NaiveConnection::kClient;
} else if (protocol_ == kHttp) {
} else if (protocol_ == NaiveConnection::kHttp) {
socket = std::make_unique<HttpProxySocket>(std::move(accepted_socket_),
traffic_annotation_);
pad_direction = NaiveConnection::kServer;
} else {
return;
}
if (!use_padding_) {
pad_direction = NaiveConnection::kNone;
}
auto connection_ptr = std::make_unique<NaiveConnection>(
++last_id_, pad_direction, std::move(socket), this, traffic_annotation_);
++last_id_, protocol_, pad_direction, proxy_info_, server_ssl_config_,
proxy_ssl_config_, session_, net_log_, std::move(socket),
traffic_annotation_);
auto* connection = connection_ptr.get();
connection_by_id_[connection->id()] = std::move(connection_ptr);
int result = connection->Connect(
@ -102,62 +118,8 @@ void NaiveProxy::DoConnect() {
HandleConnectResult(connection, result);
}
int NaiveProxy::OnConnectServer(unsigned int connection_id,
const StreamSocket* client_socket,
ClientSocketHandle* server_socket,
CompletionRepeatingCallback callback) {
// Ignores socket limit set by socket pool for this type of socket.
constexpr int request_load_flags = LOAD_IGNORE_LIMITS;
constexpr RequestPriority request_priority = MAXIMUM_PRIORITY;
ProxyInfo proxy_info;
SSLConfig server_ssl_config;
SSLConfig proxy_ssl_config;
if (use_proxy_) {
const auto& proxy_config = session_->proxy_resolution_service()->config();
DCHECK(proxy_config);
const ProxyList& proxy_list =
proxy_config.value().value().proxy_rules().single_proxies;
if (proxy_list.IsEmpty())
return ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
proxy_info.UseProxyList(proxy_list);
proxy_info.set_traffic_annotation(
net::MutableNetworkTrafficAnnotationTag(traffic_annotation_));
HttpRequestInfo req_info;
session_->GetSSLConfig(req_info, &server_ssl_config, &proxy_ssl_config);
proxy_ssl_config.disable_cert_verification_network_fetches = true;
} else {
proxy_info.UseDirect();
}
HostPortPair request_endpoint;
if (protocol_ == kSocks5) {
const auto* socket = static_cast<const Socks5ServerSocket*>(client_socket);
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.IsEmpty()) {
LOG(ERROR) << "Connection " << connection_id << " to invalid origin";
return ERR_ADDRESS_INVALID;
}
LOG(INFO) << "Connection " << connection_id << " to "
<< request_endpoint.ToString();
auto quic_version = quic::QUIC_VERSION_UNSUPPORTED;
return InitSocketHandleForRawConnect2(
request_endpoint, session_, request_load_flags, request_priority,
proxy_info, quic_version, server_ssl_config, proxy_ssl_config,
PRIVACY_MODE_DISABLED, net_log_, server_socket, callback);
}
void NaiveProxy::OnConnectComplete(int connection_id, int result) {
NaiveConnection* connection = FindConnection(connection_id);
void NaiveProxy::OnConnectComplete(unsigned int connection_id, int result) {
auto* connection = FindConnection(connection_id);
if (!connection)
return;
HandleConnectResult(connection, result);
@ -180,8 +142,8 @@ void NaiveProxy::DoRun(NaiveConnection* connection) {
HandleRunResult(connection, result);
}
void NaiveProxy::OnRunComplete(int connection_id, int result) {
NaiveConnection* connection = FindConnection(connection_id);
void NaiveProxy::OnRunComplete(unsigned int connection_id, int result) {
auto* connection = FindConnection(connection_id);
if (!connection)
return;
HandleRunResult(connection, result);
@ -191,7 +153,7 @@ void NaiveProxy::HandleRunResult(NaiveConnection* connection, int result) {
Close(connection->id(), result);
}
void NaiveProxy::Close(int connection_id, int reason) {
void NaiveProxy::Close(unsigned int connection_id, int reason) {
auto it = connection_by_id_.find(connection_id);
if (it == connection_by_id_.end())
return;
@ -208,19 +170,11 @@ void NaiveProxy::Close(int connection_id, int reason) {
connection_by_id_.erase(it);
}
NaiveConnection* NaiveProxy::FindConnection(int connection_id) {
NaiveConnection* NaiveProxy::FindConnection(unsigned 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 NaiveProxy::HasClosedConnection(NaiveConnection* connection) {
return FindConnection(connection->id()) != connection;
}
} // namespace net

View File

@ -13,6 +13,8 @@
#include "base/memory/weak_ptr.h"
#include "net/base/completion_repeating_callback.h"
#include "net/log/net_log_with_source.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/ssl/ssl_config.h"
#include "net/tools/naive/naive_connection.h"
namespace net {
@ -24,24 +26,14 @@ class ServerSocket;
class StreamSocket;
struct NetworkTrafficAnnotationTag;
class NaiveProxy : public NaiveConnection::Delegate {
class NaiveProxy {
public:
enum Protocol {
kSocks5,
kHttp,
};
NaiveProxy(std::unique_ptr<ServerSocket> server_socket,
Protocol protocol,
bool use_proxy,
NaiveConnection::Protocol protocol,
bool use_padding,
HttpNetworkSession* session,
const NetworkTrafficAnnotationTag& traffic_annotation);
~NaiveProxy() override;
int OnConnectServer(unsigned int connection_id,
const StreamSocket* accepted_socket,
ClientSocketHandle* server_socket,
CompletionRepeatingCallback callback) override;
~NaiveProxy();
private:
void DoAcceptLoop();
@ -49,21 +41,23 @@ class NaiveProxy : public NaiveConnection::Delegate {
void HandleAcceptResult(int result);
void DoConnect();
void OnConnectComplete(int connection_id, int result);
void OnConnectComplete(unsigned int connection_id, int result);
void HandleConnectResult(NaiveConnection* connection, int result);
void DoRun(NaiveConnection* connection);
void OnRunComplete(int connection_id, int result);
void OnRunComplete(unsigned int connection_id, int result);
void HandleRunResult(NaiveConnection* connection, int result);
void Close(int connection_id, int reason);
void Close(unsigned int connection_id, int reason);
NaiveConnection* FindConnection(int connection_id);
bool HasClosedConnection(NaiveConnection* connection);
NaiveConnection* FindConnection(unsigned int connection_id);
std::unique_ptr<ServerSocket> listen_socket_;
Protocol protocol_;
bool use_proxy_;
NaiveConnection::Protocol protocol_;
bool use_padding_;
ProxyInfo proxy_info_;
SSLConfig server_ssl_config_;
SSLConfig proxy_ssl_config_;
HttpNetworkSession* session_;
NetLogWithSource net_log_;

View File

@ -4,6 +4,7 @@
// found in the LICENSE file.
#include <cstdlib>
#include <iostream>
#include <limits>
#include <memory>
#include <string>
@ -51,6 +52,7 @@
#include "net/url_request/url_request_context_builder.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_util.h"
#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
@ -67,15 +69,16 @@ constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("naive", "");
struct Params {
net::NaiveConnection::Protocol protocol;
std::string listen_addr;
int listen_port;
net::NaiveProxy::Protocol protocol;
bool use_proxy;
bool use_padding;
std::string proxy_url;
std::string proxy_user;
std::string proxy_pass;
std::string host_resolver_rules;
logging::LoggingSettings log_settings;
base::FilePath log_path;
base::FilePath net_log_path;
base::FilePath ssl_key_path;
};
@ -112,9 +115,7 @@ std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
builder.set_net_log(net_log);
net::ProxyConfig proxy_config;
if (params.use_proxy) {
proxy_config.proxy_rules().ParseFromString(params.proxy_url);
}
auto proxy_service = net::ProxyResolutionService::CreateWithoutProxyResolver(
std::make_unique<net::ProxyConfigServiceFixed>(
net::ProxyConfigWithAnnotation(proxy_config, kTrafficAnnotation)),
@ -128,11 +129,15 @@ std::unique_ptr<net::URLRequestContext> BuildURLRequestContext(
auto context = builder.Build();
if (params.use_proxy) {
net::HttpNetworkSession* session =
context->http_transaction_factory()->GetSession();
net::HttpAuthCache* auth_cache = session->http_auth_cache();
GURL auth_origin(params.proxy_url);
if (!params.proxy_url.empty() && !params.proxy_user.empty() &&
!params.proxy_pass.empty()) {
auto* session = context->http_transaction_factory()->GetSession();
auto* auth_cache = session->http_auth_cache();
std::string proxy_url = params.proxy_url;
if (proxy_url.compare(0, 7, "quic://") == 0) {
proxy_url.replace(0, 4, "https");
}
GURL auth_origin(proxy_url);
net::AuthCredentials credentials(base::ASCIIToUTF16(params.proxy_user),
base::ASCIIToUTF16(params.proxy_pass));
auth_cache->Add(auth_origin, /*realm=*/std::string(),
@ -147,79 +152,73 @@ bool ParseCommandLineFlags(Params* params) {
const base::CommandLine& line = *base::CommandLine::ForCurrentProcess();
if (line.HasSwitch("h") || line.HasSwitch("help")) {
LOG(INFO) << "Usage: naive [options]\n"
std::cout << "Usage: naive [options]\n"
"\n"
"Options:\n"
"-h, --help Show this message\n"
"--version Print version\n"
"--addr=<address> Address to listen on (0.0.0.0)\n"
"--port=<port> Port to listen on (1080)\n"
"--proto=[socks|http] Protocol to accept (socks)\n"
"--proxy=https://<user>:<pass>@<hostname>[:<port>]\n"
" Proxy specification.\n"
"--log Log to stderr, otherwise no log\n"
"--listen=<proto>://[addr][:port]\n"
" proto: socks, http\n"
"--proxy=<proto>://[<user>:<pass>@]<hostname>[:<port>]\n"
" proto: https, quic\n"
"--padding Use padding\n"
"--log[=<path>] Log to stderr, or file\n"
"--log-net-log=<path> Save NetLog\n"
"--ssl-key-log-file=<path> Save SSL keys for Wireshark\n";
"--ssl-key-log-file=<path> Save SSL keys for Wireshark\n"
<< std::endl;
exit(EXIT_SUCCESS);
return false;
}
if (line.HasSwitch("version")) {
LOG(INFO) << "Version: " << version_info::GetVersionNumber();
std::cout << "Version: " << version_info::GetVersionNumber() << std::endl;
exit(EXIT_SUCCESS);
return false;
}
params->protocol = net::NaiveConnection::kSocks5;
params->listen_addr = "0.0.0.0";
if (line.HasSwitch("addr")) {
params->listen_addr = line.GetSwitchValueASCII("addr");
}
if (params->listen_addr.empty()) {
LOG(ERROR) << "Invalid --addr";
params->listen_port = 1080;
url::AddStandardScheme("socks", url::SCHEME_WITH_HOST_AND_PORT);
if (line.HasSwitch("listen")) {
GURL url(line.GetSwitchValueASCII("listen"));
if (url.scheme() == "socks") {
params->protocol = net::NaiveConnection::kSocks5;
params->listen_port = 1080;
} else if (url.scheme() == "http") {
params->protocol = net::NaiveConnection::kHttp;
params->listen_port = 8080;
} else {
LOG(ERROR) << "Invalid scheme in --listen";
return false;
}
params->listen_port = 1080;
if (line.HasSwitch("port")) {
if (!base::StringToInt(line.GetSwitchValueASCII("port"),
&params->listen_port)) {
LOG(ERROR) << "Invalid --port";
if (!url.host().empty()) {
params->listen_addr = url.host();
}
if (!url.port().empty()) {
if (!base::StringToInt(url.port(), &params->listen_port)) {
LOG(ERROR) << "Invalid port in --listen";
return false;
}
if (params->listen_port <= 0 ||
params->listen_port > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Invalid --port";
LOG(ERROR) << "Invalid port in --listen";
return false;
}
}
params->protocol = net::NaiveProxy::kSocks5;
if (line.HasSwitch("proto")) {
const auto& proto = line.GetSwitchValueASCII("proto");
if (proto == "socks") {
params->protocol = net::NaiveProxy::kSocks5;
} else if (proto == "http") {
params->protocol = net::NaiveProxy::kHttp;
} else {
LOG(ERROR) << "Invalid --proto";
return false;
}
}
params->use_proxy = false;
url::AddStandardScheme("quic",
url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION);
params->proxy_url = "direct://";
GURL url(line.GetSwitchValueASCII("proxy"));
if (line.HasSwitch("proxy")) {
params->use_proxy = true;
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";
if (url.scheme() != "https" && url.scheme() != "quic") {
LOG(ERROR) << "Must be HTTPS or QUIC proxy";
return false;
}
params->proxy_url = url::SchemeHostPort(url).Serialize();
@ -227,6 +226,11 @@ bool ParseCommandLineFlags(Params* params) {
params->proxy_pass = url.password();
}
params->use_padding = false;
if (line.HasSwitch("padding")) {
params->use_padding = true;
}
if (line.HasSwitch("host-resolver-rules")) {
params->host_resolver_rules =
line.GetSwitchValueASCII("host-resolver-rules");
@ -245,7 +249,14 @@ bool ParseCommandLineFlags(Params* params) {
}
if (line.HasSwitch("log")) {
params->log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
params->log_settings.logging_dest = logging::LOG_DEFAULT;
params->log_path = line.GetSwitchValuePath("log");
if (!params->log_path.empty()) {
params->log_settings.logging_dest = logging::LOG_TO_FILE;
} else if (params->log_settings.logging_dest == logging::LOG_TO_FILE) {
params->log_path = base::FilePath::FromUTF8Unsafe("naive.log");
}
params->log_settings.log_file = params->log_path.value().c_str();
} else {
params->log_settings.logging_dest = logging::LOG_NONE;
}
@ -358,6 +369,7 @@ int main(int argc, char* argv[]) {
net::NetLogCaptureMode::Default());
auto context = BuildURLRequestContext(params, &net_log);
auto* session = context->http_transaction_factory()->GetSession();
auto listen_socket =
std::make_unique<net::TCPServerSocket>(&net_log, net::NetLogSource());
@ -369,9 +381,8 @@ int main(int argc, char* argv[]) {
return EXIT_FAILURE;
}
net::NaiveProxy naive_proxy(
std::move(listen_socket), params.protocol, params.use_proxy,
context->http_transaction_factory()->GetSession(), kTrafficAnnotation);
net::NaiveProxy naive_proxy(std::move(listen_socket), params.protocol,
params.use_padding, session, kTrafficAnnotation);
base::RunLoop().Run();