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