xref: /aosp_15_r20/external/cronet/net/socket/transport_connect_job.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 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 #ifndef NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
6 #define NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
7 
8 #include <memory>
9 #include <optional>
10 #include <set>
11 #include <string>
12 #include <vector>
13 
14 #include "base/containers/flat_set.h"
15 #include "base/containers/span.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "net/base/host_port_pair.h"
21 #include "net/base/net_export.h"
22 #include "net/base/network_anonymization_key.h"
23 #include "net/dns/host_resolver.h"
24 #include "net/dns/public/host_resolver_results.h"
25 #include "net/dns/public/resolve_error_info.h"
26 #include "net/dns/public/secure_dns_policy.h"
27 #include "net/socket/connect_job.h"
28 #include "net/socket/connection_attempts.h"
29 #include "third_party/abseil-cpp/absl/types/variant.h"
30 #include "url/scheme_host_port.h"
31 
32 namespace net {
33 
34 class NetLogWithSource;
35 class SocketTag;
36 class TransportConnectSubJob;
37 
38 class NET_EXPORT_PRIVATE TransportSocketParams
39     : public base::RefCounted<TransportSocketParams> {
40  public:
41   // Representation of the destination endpoint of the transport
42   // socket/connection. Unlike ConnectJobFactory::Endpoint, this does not have a
43   // `using_ssl` field for schemeless endpoints because that has no meaning for
44   // transport parameters.
45   using Endpoint = absl::variant<url::SchemeHostPort, HostPortPair>;
46 
47   // `host_resolution_callback` will be invoked after the the hostname is
48   // resolved. `network_anonymization_key` is passed to the HostResolver to
49   // prevent cross-NAK leaks. If `host_resolution_callback` does not return OK,
50   // then the connection will be aborted with that value. `supported_alpns`
51   // specifies ALPN protocols for selecting HTTPS/SVCB records. If empty,
52   // addresses from HTTPS/SVCB records will be ignored and only A/AAAA will be
53   // used.
54   TransportSocketParams(Endpoint destination,
55                         NetworkAnonymizationKey network_anonymization_key,
56                         SecureDnsPolicy secure_dns_policy,
57                         OnHostResolutionCallback host_resolution_callback,
58                         base::flat_set<std::string> supported_alpns);
59 
60   TransportSocketParams(const TransportSocketParams&) = delete;
61   TransportSocketParams& operator=(const TransportSocketParams&) = delete;
62 
destination()63   const Endpoint& destination() const { return destination_; }
network_anonymization_key()64   const NetworkAnonymizationKey& network_anonymization_key() const {
65     return network_anonymization_key_;
66   }
secure_dns_policy()67   SecureDnsPolicy secure_dns_policy() const { return secure_dns_policy_; }
host_resolution_callback()68   const OnHostResolutionCallback& host_resolution_callback() const {
69     return host_resolution_callback_;
70   }
supported_alpns()71   const base::flat_set<std::string>& supported_alpns() const {
72     return supported_alpns_;
73   }
74 
75  private:
76   friend class base::RefCounted<TransportSocketParams>;
77   ~TransportSocketParams();
78 
79   const Endpoint destination_;
80   const NetworkAnonymizationKey network_anonymization_key_;
81   const SecureDnsPolicy secure_dns_policy_;
82   const OnHostResolutionCallback host_resolution_callback_;
83   const base::flat_set<std::string> supported_alpns_;
84 };
85 
86 // TransportConnectJob handles the host resolution necessary for socket creation
87 // and the transport (likely TCP) connect. TransportConnectJob also has fallback
88 // logic for IPv6 connect() timeouts (which may happen due to networks / routers
89 // with broken IPv6 support). Those timeouts take 20s, so rather than make the
90 // user wait 20s for the timeout to fire, we use a fallback timer
91 // (kIPv6FallbackTime) and start a connect() to a IPv4 address if the timer
92 // fires. Then we race the IPv4 connect() against the IPv6 connect() (which has
93 // a headstart) and return the one that completes first to the socket pool.
94 class NET_EXPORT_PRIVATE TransportConnectJob : public ConnectJob {
95  public:
96   class NET_EXPORT_PRIVATE Factory {
97    public:
98     Factory() = default;
99     virtual ~Factory() = default;
100 
101     virtual std::unique_ptr<TransportConnectJob> Create(
102         RequestPriority priority,
103         const SocketTag& socket_tag,
104         const CommonConnectJobParams* common_connect_job_params,
105         const scoped_refptr<TransportSocketParams>& params,
106         Delegate* delegate,
107         const NetLogWithSource* net_log);
108   };
109 
110   // In cases where both IPv6 and IPv4 addresses were returned from DNS,
111   // TransportConnectJobs will start a second connection attempt to just the
112   // IPv4 addresses after this much time. (This is "Happy Eyeballs".)
113   //
114   // TODO(willchan): Base this off RTT instead of statically setting it. Note we
115   // choose a timeout that is different from the backup connect job timer so
116   // they don't synchronize.
117   static constexpr base::TimeDelta kIPv6FallbackTime = base::Milliseconds(300);
118 
119   struct NET_EXPORT_PRIVATE EndpointResultOverride {
120     EndpointResultOverride(HostResolverEndpointResult result,
121                            std::set<std::string> dns_aliases);
122     EndpointResultOverride(EndpointResultOverride&&);
123     EndpointResultOverride(const EndpointResultOverride&);
124     ~EndpointResultOverride();
125     EndpointResultOverride& operator=(EndpointResultOverride&&) = default;
126     EndpointResultOverride& operator=(const EndpointResultOverride&) = default;
127 
128     HostResolverEndpointResult result;
129     std::set<std::string> dns_aliases;
130   };
131 
132   TransportConnectJob(RequestPriority priority,
133                       const SocketTag& socket_tag,
134                       const CommonConnectJobParams* common_connect_job_params,
135                       const scoped_refptr<TransportSocketParams>& params,
136                       Delegate* delegate,
137                       const NetLogWithSource* net_log,
138                       std::optional<EndpointResultOverride>
139                           endpoint_result_override = std::nullopt);
140 
141   TransportConnectJob(const TransportConnectJob&) = delete;
142   TransportConnectJob& operator=(const TransportConnectJob&) = delete;
143 
144   ~TransportConnectJob() override;
145 
146   // ConnectJob methods.
147   LoadState GetLoadState() const override;
148   bool HasEstablishedConnection() const override;
149   ConnectionAttempts GetConnectionAttempts() const override;
150   ResolveErrorInfo GetResolveErrorInfo() const override;
151   std::optional<HostResolverEndpointResult> GetHostResolverEndpointResult()
152       const override;
153 
154   static base::TimeDelta ConnectionTimeout();
155 
156  private:
157   friend class TransportConnectSubJob;
158 
159   enum State {
160     STATE_RESOLVE_HOST,
161     STATE_RESOLVE_HOST_COMPLETE,
162     STATE_RESOLVE_HOST_CALLBACK_COMPLETE,
163     STATE_TRANSPORT_CONNECT,
164     STATE_TRANSPORT_CONNECT_COMPLETE,
165     STATE_NONE,
166   };
167 
168   // Although it is not strictly necessary, it makes the code simpler if each
169   // subjob knows what type it is.
170   enum SubJobType { SUB_JOB_IPV4, SUB_JOB_IPV6 };
171 
172   void OnIOComplete(int result);
173   int DoLoop(int result);
174 
175   int DoResolveHost();
176   int DoResolveHostComplete(int result);
177   int DoResolveHostCallbackComplete();
178   int DoTransportConnect();
179   int DoTransportConnectComplete(int result);
180 
181   // Helper method called called when a SubJob completes, synchronously
182   // or asynchronously. Returns `ERR_IO_PENDING` if there is more work to
183   // do and another error if completed. It's up to the caller to manage
184   // advancing `DoLoop` if a value other than `ERR_IO_PENDING` is returned.
185   int HandleSubJobComplete(int result, TransportConnectSubJob* job);
186   // Called back from a SubJob when it completes. Invokes `OnIOComplete`,
187   // re-entering `DoLoop`, if there is no more work to do. Must not
188   // be called from within `DoLoop`.
189   void OnSubJobComplete(int result, TransportConnectSubJob* job);
190 
191   // Called from |fallback_timer_|.
192   void StartIPv4JobAsync();
193 
194   // Begins the host resolution and the TCP connect.  Returns OK on success
195   // and ERR_IO_PENDING if it cannot immediately service the request.
196   // Otherwise, it returns a net error code.
197   int ConnectInternal() override;
198 
199   void ChangePriorityInternal(RequestPriority priority) override;
200 
201   // Returns whether the client should be SVCB-optional when connecting to
202   // `results`.
203   bool IsSvcbOptional(
204       base::span<const HostResolverEndpointResult> results) const;
205 
206   // Returns whether `result` is usable for this connection. If `svcb_optional`
207   // is true, the non-HTTPS/SVCB fallback is allowed.
208   bool IsEndpointResultUsable(const HostResolverEndpointResult& result,
209                               bool svcb_optional) const;
210 
211   // Returns the `HostResolverEndpointResult` for the current subjobs.
212   const HostResolverEndpointResult& GetEndpointResultForCurrentSubJobs() const;
213 
214   scoped_refptr<TransportSocketParams> params_;
215   std::unique_ptr<HostResolver::ResolveHostRequest> request_;
216   std::vector<HostResolverEndpointResult> endpoint_results_;
217   size_t current_endpoint_result_ = 0;
218   std::set<std::string> dns_aliases_;
219   bool has_dns_override_ = false;
220 
221   State next_state_ = STATE_NONE;
222 
223   // The addresses are divided into IPv4 and IPv6, which are performed partially
224   // in parallel. If the list of IPv6 addresses is non-empty, then the IPv6 jobs
225   // go first, followed after `kIPv6FallbackTime` by the IPv4 addresses. The
226   // first sub-job to establish a connection wins. If one sub-job fails, the
227   // other one is launched if needed, and we wait for it to complete.
228   std::unique_ptr<TransportConnectSubJob> ipv4_job_;
229   std::unique_ptr<TransportConnectSubJob> ipv6_job_;
230 
231   base::OneShotTimer fallback_timer_;
232 
233   ResolveErrorInfo resolve_error_info_;
234   ConnectionAttempts connection_attempts_;
235 
236   base::WeakPtrFactory<TransportConnectJob> weak_ptr_factory_{this};
237 };
238 
239 }  // namespace net
240 
241 #endif  // NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
242