xref: /aosp_15_r20/external/cronet/net/socket/socks_connect_job.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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       return LOAD_STATE_IDLE;
87   }
88 }
89 
HasEstablishedConnection() const90 bool SOCKSConnectJob::HasEstablishedConnection() const {
91   return next_state_ == STATE_SOCKS_CONNECT ||
92          next_state_ == STATE_SOCKS_CONNECT_COMPLETE;
93 }
94 
GetResolveErrorInfo() const95 ResolveErrorInfo SOCKSConnectJob::GetResolveErrorInfo() const {
96   return resolve_error_info_;
97 }
98 
HandshakeTimeoutForTesting()99 base::TimeDelta SOCKSConnectJob::HandshakeTimeoutForTesting() {
100   return kSOCKSConnectJobTimeout;
101 }
102 
OnIOComplete(int result)103 void SOCKSConnectJob::OnIOComplete(int result) {
104   int rv = DoLoop(result);
105   if (rv != ERR_IO_PENDING)
106     NotifyDelegateOfCompletion(rv);  // Deletes |this|
107 }
108 
OnConnectJobComplete(int result,ConnectJob * job)109 void SOCKSConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
110   DCHECK(transport_connect_job_);
111   DCHECK_EQ(next_state_, STATE_TRANSPORT_CONNECT_COMPLETE);
112   OnIOComplete(result);
113 }
114 
OnNeedsProxyAuth(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback,ConnectJob * job)115 void SOCKSConnectJob::OnNeedsProxyAuth(
116     const HttpResponseInfo& response,
117     HttpAuthController* auth_controller,
118     base::OnceClosure restart_with_auth_callback,
119     ConnectJob* job) {
120   // A SOCKSConnectJob can't be on top of an HttpProxyConnectJob.
121   NOTREACHED();
122 }
123 
DoLoop(int result)124 int SOCKSConnectJob::DoLoop(int result) {
125   DCHECK_NE(next_state_, STATE_NONE);
126 
127   int rv = result;
128   do {
129     State state = next_state_;
130     next_state_ = STATE_NONE;
131     switch (state) {
132       case STATE_TRANSPORT_CONNECT:
133         DCHECK_EQ(OK, rv);
134         rv = DoTransportConnect();
135         break;
136       case STATE_TRANSPORT_CONNECT_COMPLETE:
137         rv = DoTransportConnectComplete(rv);
138         break;
139       case STATE_SOCKS_CONNECT:
140         DCHECK_EQ(OK, rv);
141         rv = DoSOCKSConnect();
142         break;
143       case STATE_SOCKS_CONNECT_COMPLETE:
144         rv = DoSOCKSConnectComplete(rv);
145         break;
146       default:
147         NOTREACHED() << "bad state";
148         rv = ERR_FAILED;
149         break;
150     }
151   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
152 
153   return rv;
154 }
155 
DoTransportConnect()156 int SOCKSConnectJob::DoTransportConnect() {
157   DCHECK(!transport_connect_job_);
158 
159   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
160   transport_connect_job_ = std::make_unique<TransportConnectJob>(
161       priority(), socket_tag(), common_connect_job_params(),
162       socks_params_->transport_params(), this, &net_log());
163   return transport_connect_job_->Connect();
164 }
165 
DoTransportConnectComplete(int result)166 int SOCKSConnectJob::DoTransportConnectComplete(int result) {
167   resolve_error_info_ = transport_connect_job_->GetResolveErrorInfo();
168   if (result != OK)
169     return ERR_PROXY_CONNECTION_FAILED;
170 
171   // Start the timer to time allowed for SOCKS handshake.
172   ResetTimer(kSOCKSConnectJobTimeout);
173   next_state_ = STATE_SOCKS_CONNECT;
174   return result;
175 }
176 
DoSOCKSConnect()177 int SOCKSConnectJob::DoSOCKSConnect() {
178   next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
179 
180   // Add a SOCKS connection on top of the tcp socket.
181   if (socks_params_->is_socks_v5()) {
182     socket_ = std::make_unique<SOCKS5ClientSocket>(
183         transport_connect_job_->PassSocket(), socks_params_->destination(),
184         socks_params_->traffic_annotation());
185   } else {
186     auto socks_socket = std::make_unique<SOCKSClientSocket>(
187         transport_connect_job_->PassSocket(), socks_params_->destination(),
188         socks_params_->network_anonymization_key(), priority(), host_resolver(),
189         socks_params_->transport_params()->secure_dns_policy(),
190         socks_params_->traffic_annotation());
191     socks_socket_ptr_ = socks_socket.get();
192     socket_ = std::move(socks_socket);
193   }
194   transport_connect_job_.reset();
195   return socket_->Connect(
196       base::BindOnce(&SOCKSConnectJob::OnIOComplete, base::Unretained(this)));
197 }
198 
DoSOCKSConnectComplete(int result)199 int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
200   if (!socks_params_->is_socks_v5())
201     resolve_error_info_ = socks_socket_ptr_->GetResolveErrorInfo();
202   if (result != OK) {
203     socket_->Disconnect();
204     return result;
205   }
206 
207   SetSocket(std::move(socket_), std::nullopt /* dns_aliases */);
208   return result;
209 }
210 
ConnectInternal()211 int SOCKSConnectJob::ConnectInternal() {
212   next_state_ = STATE_TRANSPORT_CONNECT;
213   return DoLoop(OK);
214 }
215 
ChangePriorityInternal(RequestPriority priority)216 void SOCKSConnectJob::ChangePriorityInternal(RequestPriority priority) {
217   // Currently doesn't change host resolution request priority for SOCKS4 case.
218   if (transport_connect_job_)
219     transport_connect_job_->ChangePriority(priority);
220 }
221 
222 }  // namespace net
223