1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/socket/socks_connect_job.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/functional/bind.h"
11 #include "net/base/net_errors.h"
12 #include "net/log/net_log_source_type.h"
13 #include "net/log/net_log_with_source.h"
14 #include "net/socket/client_socket_factory.h"
15 #include "net/socket/client_socket_handle.h"
16 #include "net/socket/connect_job_params.h"
17 #include "net/socket/socks5_client_socket.h"
18 #include "net/socket/socks_client_socket.h"
19 #include "net/socket/transport_connect_job.h"
20
21 namespace net {
22
23 // SOCKSConnectJobs will time out if the SOCKS handshake takes longer than this.
24 static constexpr base::TimeDelta kSOCKSConnectJobTimeout = base::Seconds(30);
25
SOCKSSocketParams(ConnectJobParams nested_params,bool socks_v5,const HostPortPair & host_port_pair,const NetworkAnonymizationKey & network_anonymization_key,const NetworkTrafficAnnotationTag & traffic_annotation)26 SOCKSSocketParams::SOCKSSocketParams(
27 ConnectJobParams nested_params,
28 bool socks_v5,
29 const HostPortPair& host_port_pair,
30 const NetworkAnonymizationKey& network_anonymization_key,
31 const NetworkTrafficAnnotationTag& traffic_annotation)
32 : transport_params_(nested_params.take_transport()),
33 destination_(host_port_pair),
34 socks_v5_(socks_v5),
35 network_anonymization_key_(network_anonymization_key),
36 traffic_annotation_(traffic_annotation) {}
37
38 SOCKSSocketParams::~SOCKSSocketParams() = default;
39
Create(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SOCKSSocketParams> socks_params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)40 std::unique_ptr<SOCKSConnectJob> SOCKSConnectJob::Factory::Create(
41 RequestPriority priority,
42 const SocketTag& socket_tag,
43 const CommonConnectJobParams* common_connect_job_params,
44 scoped_refptr<SOCKSSocketParams> socks_params,
45 ConnectJob::Delegate* delegate,
46 const NetLogWithSource* net_log) {
47 return std::make_unique<SOCKSConnectJob>(
48 priority, socket_tag, common_connect_job_params, std::move(socks_params),
49 delegate, net_log);
50 }
51
SOCKSConnectJob(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SOCKSSocketParams> socks_params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)52 SOCKSConnectJob::SOCKSConnectJob(
53 RequestPriority priority,
54 const SocketTag& socket_tag,
55 const CommonConnectJobParams* common_connect_job_params,
56 scoped_refptr<SOCKSSocketParams> socks_params,
57 ConnectJob::Delegate* delegate,
58 const NetLogWithSource* net_log)
59 : ConnectJob(priority,
60 socket_tag,
61 base::TimeDelta(),
62 common_connect_job_params,
63 delegate,
64 net_log,
65 NetLogSourceType::SOCKS_CONNECT_JOB,
66 NetLogEventType::SOCKS_CONNECT_JOB_CONNECT),
67 socks_params_(std::move(socks_params)) {}
68
~SOCKSConnectJob()69 SOCKSConnectJob::~SOCKSConnectJob() {
70 // In the case the job was canceled, need to delete nested job first to
71 // correctly order NetLog events.
72 transport_connect_job_.reset();
73 }
74
GetLoadState() const75 LoadState SOCKSConnectJob::GetLoadState() const {
76 switch (next_state_) {
77 case STATE_TRANSPORT_CONNECT:
78 return LOAD_STATE_IDLE;
79 case STATE_TRANSPORT_CONNECT_COMPLETE:
80 return transport_connect_job_->GetLoadState();
81 case STATE_SOCKS_CONNECT:
82 case STATE_SOCKS_CONNECT_COMPLETE:
83 return LOAD_STATE_CONNECTING;
84 default:
85 NOTREACHED();
86 }
87 }
88
HasEstablishedConnection() const89 bool SOCKSConnectJob::HasEstablishedConnection() const {
90 return next_state_ == STATE_SOCKS_CONNECT ||
91 next_state_ == STATE_SOCKS_CONNECT_COMPLETE;
92 }
93
GetResolveErrorInfo() const94 ResolveErrorInfo SOCKSConnectJob::GetResolveErrorInfo() const {
95 return resolve_error_info_;
96 }
97
HandshakeTimeoutForTesting()98 base::TimeDelta SOCKSConnectJob::HandshakeTimeoutForTesting() {
99 return kSOCKSConnectJobTimeout;
100 }
101
OnIOComplete(int result)102 void SOCKSConnectJob::OnIOComplete(int result) {
103 int rv = DoLoop(result);
104 if (rv != ERR_IO_PENDING)
105 NotifyDelegateOfCompletion(rv); // Deletes |this|
106 }
107
OnConnectJobComplete(int result,ConnectJob * job)108 void SOCKSConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
109 DCHECK(transport_connect_job_);
110 DCHECK_EQ(next_state_, STATE_TRANSPORT_CONNECT_COMPLETE);
111 OnIOComplete(result);
112 }
113
OnNeedsProxyAuth(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback,ConnectJob * job)114 void SOCKSConnectJob::OnNeedsProxyAuth(
115 const HttpResponseInfo& response,
116 HttpAuthController* auth_controller,
117 base::OnceClosure restart_with_auth_callback,
118 ConnectJob* job) {
119 // A SOCKSConnectJob can't be on top of an HttpProxyConnectJob.
120 NOTREACHED();
121 }
122
DoLoop(int result)123 int SOCKSConnectJob::DoLoop(int result) {
124 DCHECK_NE(next_state_, STATE_NONE);
125
126 int rv = result;
127 do {
128 State state = next_state_;
129 next_state_ = STATE_NONE;
130 switch (state) {
131 case STATE_TRANSPORT_CONNECT:
132 DCHECK_EQ(OK, rv);
133 rv = DoTransportConnect();
134 break;
135 case STATE_TRANSPORT_CONNECT_COMPLETE:
136 rv = DoTransportConnectComplete(rv);
137 break;
138 case STATE_SOCKS_CONNECT:
139 DCHECK_EQ(OK, rv);
140 rv = DoSOCKSConnect();
141 break;
142 case STATE_SOCKS_CONNECT_COMPLETE:
143 rv = DoSOCKSConnectComplete(rv);
144 break;
145 default:
146 NOTREACHED() << "bad state";
147 }
148 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
149
150 return rv;
151 }
152
DoTransportConnect()153 int SOCKSConnectJob::DoTransportConnect() {
154 DCHECK(!transport_connect_job_);
155
156 next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
157 transport_connect_job_ = std::make_unique<TransportConnectJob>(
158 priority(), socket_tag(), common_connect_job_params(),
159 socks_params_->transport_params(), this, &net_log());
160 return transport_connect_job_->Connect();
161 }
162
DoTransportConnectComplete(int result)163 int SOCKSConnectJob::DoTransportConnectComplete(int result) {
164 resolve_error_info_ = transport_connect_job_->GetResolveErrorInfo();
165 if (result != OK)
166 return ERR_PROXY_CONNECTION_FAILED;
167
168 // Start the timer to time allowed for SOCKS handshake.
169 ResetTimer(kSOCKSConnectJobTimeout);
170 next_state_ = STATE_SOCKS_CONNECT;
171 return result;
172 }
173
DoSOCKSConnect()174 int SOCKSConnectJob::DoSOCKSConnect() {
175 next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
176
177 // Add a SOCKS connection on top of the tcp socket.
178 if (socks_params_->is_socks_v5()) {
179 socket_ = std::make_unique<SOCKS5ClientSocket>(
180 transport_connect_job_->PassSocket(), socks_params_->destination(),
181 socks_params_->traffic_annotation());
182 } else {
183 auto socks_socket = std::make_unique<SOCKSClientSocket>(
184 transport_connect_job_->PassSocket(), socks_params_->destination(),
185 socks_params_->network_anonymization_key(), priority(), host_resolver(),
186 socks_params_->transport_params()->secure_dns_policy(),
187 socks_params_->traffic_annotation());
188 socks_socket_ptr_ = socks_socket.get();
189 socket_ = std::move(socks_socket);
190 }
191 transport_connect_job_.reset();
192 return socket_->Connect(
193 base::BindOnce(&SOCKSConnectJob::OnIOComplete, base::Unretained(this)));
194 }
195
DoSOCKSConnectComplete(int result)196 int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
197 if (!socks_params_->is_socks_v5())
198 resolve_error_info_ = socks_socket_ptr_->GetResolveErrorInfo();
199 if (result != OK) {
200 socket_->Disconnect();
201 return result;
202 }
203
204 SetSocket(std::move(socket_), std::nullopt /* dns_aliases */);
205 return result;
206 }
207
ConnectInternal()208 int SOCKSConnectJob::ConnectInternal() {
209 next_state_ = STATE_TRANSPORT_CONNECT;
210 return DoLoop(OK);
211 }
212
ChangePriorityInternal(RequestPriority priority)213 void SOCKSConnectJob::ChangePriorityInternal(RequestPriority priority) {
214 // Currently doesn't change host resolution request priority for SOCKS4 case.
215 if (transport_connect_job_)
216 transport_connect_job_->ChangePriority(priority);
217 }
218
219 } // namespace net
220