// Copyright (c) 2011 The Chromium Authors. 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/socket/socks_client_socket_pool.h" #include "base/time.h" #include "base/values.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_base.h" #include "net/socket/socks5_client_socket.h" #include "net/socket/socks_client_socket.h" #include "net/socket/transport_client_socket_pool.h" namespace net { SOCKSSocketParams::SOCKSSocketParams( const scoped_refptr<TransportSocketParams>& proxy_server, bool socks_v5, const HostPortPair& host_port_pair, RequestPriority priority, const GURL& referrer) : transport_params_(proxy_server), destination_(host_port_pair), socks_v5_(socks_v5) { if (transport_params_) ignore_limits_ = transport_params_->ignore_limits(); else ignore_limits_ = false; // The referrer is used by the DNS prefetch system to correlate resolutions // with the page that triggered them. It doesn't impact the actual addresses // that we resolve to. destination_.set_referrer(referrer); destination_.set_priority(priority); } #ifdef ANDROID bool SOCKSSocketParams::getUID(uid_t *uid) const { if (transport_params_) return transport_params_->getUID(uid); else return false; } void SOCKSSocketParams::setUID(uid_t uid) { if (transport_params_) return transport_params_->setUID(uid); } #endif SOCKSSocketParams::~SOCKSSocketParams() {} // SOCKSConnectJobs will time out after this many seconds. Note this is on // top of the timeout for the transport socket. static const int kSOCKSConnectJobTimeoutInSeconds = 30; SOCKSConnectJob::SOCKSConnectJob( const std::string& group_name, const scoped_refptr<SOCKSSocketParams>& socks_params, const base::TimeDelta& timeout_duration, TransportClientSocketPool* transport_pool, HostResolver* host_resolver, Delegate* delegate, NetLog* net_log) : ConnectJob(group_name, timeout_duration, delegate, BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), socks_params_(socks_params), transport_pool_(transport_pool), resolver_(host_resolver), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, &SOCKSConnectJob::OnIOComplete)) { } SOCKSConnectJob::~SOCKSConnectJob() { // We don't worry about cancelling the tcp socket since the destructor in // scoped_ptr<ClientSocketHandle> transport_socket_handle_ will take care of // it. } LoadState SOCKSConnectJob::GetLoadState() const { switch (next_state_) { case STATE_TRANSPORT_CONNECT: case STATE_TRANSPORT_CONNECT_COMPLETE: return transport_socket_handle_->GetLoadState(); case STATE_SOCKS_CONNECT: case STATE_SOCKS_CONNECT_COMPLETE: return LOAD_STATE_CONNECTING; default: NOTREACHED(); return LOAD_STATE_IDLE; } } void SOCKSConnectJob::OnIOComplete(int result) { int rv = DoLoop(result); if (rv != ERR_IO_PENDING) NotifyDelegateOfCompletion(rv); // Deletes |this| } int SOCKSConnectJob::DoLoop(int result) { DCHECK_NE(next_state_, STATE_NONE); int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_TRANSPORT_CONNECT: DCHECK_EQ(OK, rv); rv = DoTransportConnect(); break; case STATE_TRANSPORT_CONNECT_COMPLETE: rv = DoTransportConnectComplete(rv); break; case STATE_SOCKS_CONNECT: DCHECK_EQ(OK, rv); rv = DoSOCKSConnect(); break; case STATE_SOCKS_CONNECT_COMPLETE: rv = DoSOCKSConnectComplete(rv); break; default: NOTREACHED() << "bad state"; rv = ERR_FAILED; break; } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); return rv; } int SOCKSConnectJob::DoTransportConnect() { next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; transport_socket_handle_.reset(new ClientSocketHandle()); return transport_socket_handle_->Init(group_name(), socks_params_->transport_params(), socks_params_->destination().priority(), &callback_, transport_pool_, net_log()); } int SOCKSConnectJob::DoTransportConnectComplete(int result) { if (result != OK) return ERR_PROXY_CONNECTION_FAILED; // Reset the timer to just the length of time allowed for SOCKS handshake // so that a fast TCP connection plus a slow SOCKS failure doesn't take // longer to timeout than it should. ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds)); next_state_ = STATE_SOCKS_CONNECT; return result; } int SOCKSConnectJob::DoSOCKSConnect() { next_state_ = STATE_SOCKS_CONNECT_COMPLETE; // Add a SOCKS connection on top of the tcp socket. if (socks_params_->is_socks_v5()) { socket_.reset(new SOCKS5ClientSocket(transport_socket_handle_.release(), socks_params_->destination())); } else { socket_.reset(new SOCKSClientSocket(transport_socket_handle_.release(), socks_params_->destination(), resolver_)); } #ifdef ANDROID uid_t calling_uid = 0; bool valid_uid = socks_params_->transport_params()->getUID(&calling_uid); #endif return socket_->Connect(&callback_ #ifdef ANDROID , socks_params_->ignore_limits() , valid_uid , calling_uid #endif ); } int SOCKSConnectJob::DoSOCKSConnectComplete(int result) { if (result != OK) { socket_->Disconnect(); return result; } set_socket(socket_.release()); return result; } int SOCKSConnectJob::ConnectInternal() { next_state_ = STATE_TRANSPORT_CONNECT; return DoLoop(OK); } ConnectJob* SOCKSClientSocketPool::SOCKSConnectJobFactory::NewConnectJob( const std::string& group_name, const PoolBase::Request& request, ConnectJob::Delegate* delegate) const { return new SOCKSConnectJob(group_name, request.params(), ConnectionTimeout(), transport_pool_, host_resolver_, delegate, net_log_); } base::TimeDelta SOCKSClientSocketPool::SOCKSConnectJobFactory::ConnectionTimeout() const { return transport_pool_->ConnectionTimeout() + base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds); } SOCKSClientSocketPool::SOCKSClientSocketPool( int max_sockets, int max_sockets_per_group, ClientSocketPoolHistograms* histograms, HostResolver* host_resolver, TransportClientSocketPool* transport_pool, NetLog* net_log) : transport_pool_(transport_pool), base_(max_sockets, max_sockets_per_group, histograms, base::TimeDelta::FromSeconds( ClientSocketPool::unused_idle_socket_timeout()), base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout), new SOCKSConnectJobFactory(transport_pool, host_resolver, net_log)) { } SOCKSClientSocketPool::~SOCKSClientSocketPool() {} int SOCKSClientSocketPool::RequestSocket(const std::string& group_name, const void* socket_params, RequestPriority priority, ClientSocketHandle* handle, CompletionCallback* callback, const BoundNetLog& net_log) { const scoped_refptr<SOCKSSocketParams>* casted_socket_params = static_cast<const scoped_refptr<SOCKSSocketParams>*>(socket_params); return base_.RequestSocket(group_name, *casted_socket_params, priority, handle, callback, net_log); } void SOCKSClientSocketPool::RequestSockets( const std::string& group_name, const void* params, int num_sockets, const BoundNetLog& net_log) { const scoped_refptr<SOCKSSocketParams>* casted_params = static_cast<const scoped_refptr<SOCKSSocketParams>*>(params); base_.RequestSockets(group_name, *casted_params, num_sockets, net_log); } void SOCKSClientSocketPool::CancelRequest(const std::string& group_name, ClientSocketHandle* handle) { base_.CancelRequest(group_name, handle); } void SOCKSClientSocketPool::ReleaseSocket(const std::string& group_name, ClientSocket* socket, int id) { base_.ReleaseSocket(group_name, socket, id); } void SOCKSClientSocketPool::Flush() { base_.Flush(); } void SOCKSClientSocketPool::CloseIdleSockets() { base_.CloseIdleSockets(); } int SOCKSClientSocketPool::IdleSocketCount() const { return base_.idle_socket_count(); } int SOCKSClientSocketPool::IdleSocketCountInGroup( const std::string& group_name) const { return base_.IdleSocketCountInGroup(group_name); } LoadState SOCKSClientSocketPool::GetLoadState( const std::string& group_name, const ClientSocketHandle* handle) const { return base_.GetLoadState(group_name, handle); } DictionaryValue* SOCKSClientSocketPool::GetInfoAsValue( const std::string& name, const std::string& type, bool include_nested_pools) const { DictionaryValue* dict = base_.GetInfoAsValue(name, type); if (include_nested_pools) { ListValue* list = new ListValue(); list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool", "transport_socket_pool", false)); dict->Set("nested_pools", list); } return dict; } base::TimeDelta SOCKSClientSocketPool::ConnectionTimeout() const { return base_.ConnectionTimeout(); } ClientSocketPoolHistograms* SOCKSClientSocketPool::histograms() const { return base_.histograms(); }; } // namespace net