DnsAndConnectSocket.cpp (51698B)
1 /* vim:set ts=4 sw=2 sts=2 et cin: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // HttpLog.h should generally be included first 7 #include "HttpLog.h" 8 9 #include "ConnectionHandle.h" 10 #include "DnsAndConnectSocket.h" 11 #include "nsHttpConnection.h" 12 #include "nsIClassOfService.h" 13 #include "nsIDNSRecord.h" 14 #include "nsIInterfaceRequestorUtils.h" 15 #include "nsIHttpActivityObserver.h" 16 #include "nsSocketTransportService2.h" 17 #include "nsDNSService2.h" 18 #include "nsQueryObject.h" 19 #include "nsURLHelper.h" 20 #include "mozilla/Components.h" 21 #include "mozilla/StaticPrefs_network.h" 22 #include "mozilla/SyncRunnable.h" 23 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 24 #include "nsHttpHandler.h" 25 #include "ConnectionEntry.h" 26 #include "HttpConnectionUDP.h" 27 #include "NullHttpTransaction.h" 28 #include "nsServiceManagerUtils.h" 29 #include "mozilla/net/NeckoChannelParams.h" // For HttpActivityArgs. 30 31 // Log on level :5, instead of default :4. 32 #undef LOG 33 #define LOG(args) LOG5(args) 34 #undef LOG_ENABLED 35 #define LOG_ENABLED() LOG5_ENABLED() 36 37 namespace mozilla { 38 namespace net { 39 40 //////////////////////// DnsAndConnectSocket 41 NS_IMPL_ADDREF(DnsAndConnectSocket) 42 NS_IMPL_RELEASE(DnsAndConnectSocket) 43 44 NS_INTERFACE_MAP_BEGIN(DnsAndConnectSocket) 45 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 46 NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback) 47 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 48 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 49 NS_INTERFACE_MAP_ENTRY(nsITimerCallback) 50 NS_INTERFACE_MAP_ENTRY(nsINamed) 51 NS_INTERFACE_MAP_ENTRY(nsIDNSListener) 52 NS_INTERFACE_MAP_ENTRY_CONCRETE(DnsAndConnectSocket) 53 NS_INTERFACE_MAP_END 54 55 static void NotifyActivity(nsHttpConnectionInfo* aConnInfo, uint32_t aSubtype) { 56 HttpConnectionActivity activity( 57 aConnInfo->HashKey(), aConnInfo->GetOrigin(), aConnInfo->OriginPort(), 58 aConnInfo->EndToEndSSL(), !aConnInfo->GetEchConfig().IsEmpty(), 59 aConnInfo->IsHttp3()); 60 gHttpHandler->ObserveHttpActivityWithArgs( 61 activity, NS_ACTIVITY_TYPE_HTTP_CONNECTION, aSubtype, PR_Now(), 0, ""_ns); 62 } 63 64 DnsAndConnectSocket::DnsAndConnectSocket(nsHttpConnectionInfo* ci, 65 nsAHttpTransaction* trans, 66 uint32_t caps, bool speculative, 67 bool urgentStart) 68 : mTransaction(trans), 69 mCaps(caps), 70 mSpeculative(speculative), 71 mUrgentStart(urgentStart), 72 mConnInfo(ci) { 73 MOZ_ASSERT(ci && trans, "constructor with null arguments"); 74 LOG(("Creating DnsAndConnectSocket [this=%p trans=%p ent=%s key=%s]\n", this, 75 trans, mConnInfo->Origin(), mConnInfo->HashKey().get())); 76 77 if (mConnInfo->UsingProxy()) { 78 mIsHttp3 = mConnInfo->IsHttp3ProxyConnection(); 79 } else { 80 mIsHttp3 = mConnInfo->IsHttp3(); 81 } 82 83 MOZ_ASSERT(mConnInfo); 84 NotifyActivity(mConnInfo, 85 mSpeculative 86 ? NS_HTTP_ACTIVITY_SUBTYPE_SPECULATIVE_DNSANDSOCKET_CREATED 87 : NS_HTTP_ACTIVITY_SUBTYPE_DNSANDSOCKET_CREATED); 88 } 89 90 void DnsAndConnectSocket::CheckIsDone() { 91 MOZ_DIAGNOSTIC_ASSERT(!mPrimaryTransport.mSocketTransport); 92 MOZ_DIAGNOSTIC_ASSERT(!mPrimaryTransport.mStreamOut); 93 MOZ_DIAGNOSTIC_ASSERT(!mPrimaryTransport.mDNSRequest); 94 MOZ_DIAGNOSTIC_ASSERT(!mBackupTransport.mSocketTransport); 95 MOZ_DIAGNOSTIC_ASSERT(!mBackupTransport.mStreamOut); 96 MOZ_DIAGNOSTIC_ASSERT(!mBackupTransport.mDNSRequest); 97 } 98 99 DnsAndConnectSocket::~DnsAndConnectSocket() { 100 LOG(("Destroying DnsAndConnectSocket [this=%p]\n", this)); 101 MOZ_ASSERT(mState == DnsAndSocketState::DONE); 102 CheckIsDone(); 103 // Check in case something goes wrong that we decrease 104 // the nsHttpConnectionMgr active connection number. 105 mPrimaryTransport.MaybeSetConnectingDone(); 106 mBackupTransport.MaybeSetConnectingDone(); 107 } 108 109 nsresult DnsAndConnectSocket::Init(ConnectionEntry* ent) { 110 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 111 MOZ_ASSERT(mState == DnsAndSocketState::INIT); 112 113 if (mConnInfo->GetRoutedHost().IsEmpty()) { 114 mPrimaryTransport.mHost = mConnInfo->GetOrigin(); 115 mBackupTransport.mHost = mConnInfo->GetOrigin(); 116 } else { 117 mPrimaryTransport.mHost = mConnInfo->GetRoutedHost(); 118 mBackupTransport.mHost = mConnInfo->GetRoutedHost(); 119 } 120 121 CheckProxyConfig(); 122 123 if (!mSkipDnsResolution) { 124 nsresult rv = SetupDnsFlags(ent); 125 NS_ENSURE_SUCCESS(rv, rv); 126 } 127 return SetupEvent(SetupEvents::INIT_EVENT); 128 } 129 130 void DnsAndConnectSocket::CheckProxyConfig() { 131 if (nsCOMPtr<nsProxyInfo> proxyInfo = mConnInfo->ProxyInfo()) { 132 nsAutoCString proxyType(proxyInfo->Type()); 133 134 bool proxyTransparent = false; 135 if (proxyType.EqualsLiteral("socks") || proxyType.EqualsLiteral("socks4")) { 136 proxyTransparent = true; 137 if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) { 138 mProxyTransparentResolvesHost = true; 139 } 140 } 141 142 if (mProxyTransparentResolvesHost) { 143 // Name resolution is done on the server side. Just pretend 144 // client resolution is complete, this will get picked up later. 145 // since we don't need to do DNS now, we bypass the resolving 146 // step by initializing mNetAddr to an empty address, but we 147 // must keep the port. The SOCKS IO layer will use the hostname 148 // we send it when it's created, rather than the empty address 149 // we send with the connect call. 150 mPrimaryTransport.mSkipDnsResolution = true; 151 mBackupTransport.mSkipDnsResolution = true; 152 mSkipDnsResolution = true; 153 } 154 155 if (!proxyTransparent && !proxyInfo->Host().IsEmpty()) { 156 mProxyNotTransparent = true; 157 mPrimaryTransport.mHost = proxyInfo->Host(); 158 mBackupTransport.mHost = proxyInfo->Host(); 159 } 160 } 161 } 162 163 nsresult DnsAndConnectSocket::SetupDnsFlags(ConnectionEntry* ent) { 164 LOG(("DnsAndConnectSocket::SetupDnsFlags [this=%p] ", this)); 165 166 nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; 167 bool disableIpv6ForBackup = false; 168 if (mCaps & NS_HTTP_REFRESH_DNS) { 169 dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE; 170 } 171 if (mCaps & NS_HTTP_DISABLE_IPV4) { 172 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4; 173 } else if (mCaps & NS_HTTP_DISABLE_IPV6) { 174 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6; 175 } else if (ent->PreferenceKnown()) { 176 if (ent->mPreferIPv6) { 177 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4; 178 } else if (ent->mPreferIPv4) { 179 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6; 180 } 181 mPrimaryTransport.mRetryWithDifferentIPFamily = true; 182 mBackupTransport.mRetryWithDifferentIPFamily = true; 183 } else if (gHttpHandler->FastFallbackToIPv4()) { 184 // For backup connections, we disable IPv6. That's because some users have 185 // broken IPv6 connectivity (leading to very long timeouts), and disabling 186 // IPv6 on the backup connection gives them a much better user experience 187 // with dual-stack hosts, though they still pay the 250ms delay for each new 188 // connection. This strategy is also known as "happy eyeballs". 189 disableIpv6ForBackup = true; 190 } 191 192 if (ent->mConnInfo->HasIPHintAddress()) { 193 nsresult rv; 194 nsCOMPtr<nsIDNSService> dns; 195 dns = mozilla::components::DNS::Service(&rv); 196 if (NS_FAILED(rv)) { 197 return rv; 198 } 199 200 // The spec says: "If A and AAAA records for TargetName are locally 201 // available, the client SHOULD ignore these hints.", so we check if the DNS 202 // record is in cache before setting USE_IP_HINT_ADDRESS. 203 nsCOMPtr<nsIDNSRecord> record; 204 rv = dns->ResolveNative( 205 mPrimaryTransport.mHost, nsIDNSService::RESOLVE_OFFLINE, 206 mConnInfo->GetOriginAttributes(), getter_AddRefs(record)); 207 if (NS_FAILED(rv) || !record) { 208 LOG(("Setting Socket to use IP hint address")); 209 dnsFlags |= nsIDNSService::RESOLVE_IP_HINT; 210 } 211 } 212 213 dnsFlags |= 214 nsIDNSService::GetFlagsFromTRRMode(NS_HTTP_TRR_MODE_FROM_FLAGS(mCaps)); 215 216 // When we get here, we are not resolving using any configured proxy likely 217 // because of individual proxy setting on the request or because the host is 218 // excluded from proxying. Hence, force resolution despite global proxy-DNS 219 // configuration. 220 dnsFlags |= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS; 221 222 NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) || 223 !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4), 224 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4"); 225 226 mPrimaryTransport.mDnsFlags = dnsFlags; 227 mBackupTransport.mDnsFlags = dnsFlags; 228 if (disableIpv6ForBackup) { 229 mBackupTransport.mDnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6; 230 } 231 LOG(("DnsAndConnectSocket::SetupDnsFlags flags=%u flagsBackup=%u [this=%p]", 232 mPrimaryTransport.mDnsFlags, mBackupTransport.mDnsFlags, this)); 233 NS_ASSERTION( 234 !(mBackupTransport.mDnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) || 235 !(mBackupTransport.mDnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4), 236 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4"); 237 return NS_OK; 238 } 239 240 nsresult DnsAndConnectSocket::SetupEvent(SetupEvents event) { 241 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 242 LOG(("DnsAndConnectSocket::SetupEvent state=%d event=%d this=%p", mState, 243 event, this)); 244 nsresult rv = NS_OK; 245 switch (event) { 246 case SetupEvents::INIT_EVENT: 247 MOZ_ASSERT(mState == DnsAndSocketState::INIT); 248 rv = mPrimaryTransport.Init(this); 249 if (NS_FAILED(rv)) { 250 mState = DnsAndSocketState::DONE; 251 } else if (mPrimaryTransport.FirstResolving()) { 252 mState = DnsAndSocketState::RESOLVING; 253 } else if (!mIsHttp3 && mPrimaryTransport.ConnectingOrRetry()) { 254 mState = DnsAndSocketState::CONNECTING; 255 SetupBackupTimer(); 256 } else { 257 MOZ_ASSERT(false); 258 mState = DnsAndSocketState::DONE; 259 Abandon(); 260 rv = NS_ERROR_UNEXPECTED; 261 } 262 break; 263 case SetupEvents::RESOLVED_PRIMARY_EVENT: 264 // This event may be posted multiple times if a DNS lookup is 265 // retriggered, e.g with different parameter. 266 if (!mIsHttp3 && (mState == DnsAndSocketState::RESOLVING)) { 267 mState = DnsAndSocketState::CONNECTING; 268 SetupBackupTimer(); 269 } 270 break; 271 case SetupEvents::PRIMARY_DONE_EVENT: 272 MOZ_ASSERT((mState == DnsAndSocketState::RESOLVING) || 273 (mState == DnsAndSocketState::CONNECTING) || 274 (mState == DnsAndSocketState::ONE_CONNECTED)); 275 CancelBackupTimer(); 276 mBackupTransport.CancelDnsResolution(); 277 if (mBackupTransport.ConnectingOrRetry()) { 278 mState = DnsAndSocketState::ONE_CONNECTED; 279 } else { 280 mState = DnsAndSocketState::DONE; 281 } 282 break; 283 case SetupEvents::BACKUP_DONE_EVENT: 284 MOZ_ASSERT((mState == DnsAndSocketState::CONNECTING) || 285 (mState == DnsAndSocketState::ONE_CONNECTED)); 286 if (mPrimaryTransport.ConnectingOrRetry()) { 287 mState = DnsAndSocketState::ONE_CONNECTED; 288 } else { 289 mState = DnsAndSocketState::DONE; 290 } 291 break; 292 case SetupEvents::BACKUP_TIMER_FIRED_EVENT: 293 MOZ_ASSERT(mState == DnsAndSocketState::CONNECTING); 294 mBackupTransport.Init(this); 295 } 296 LOG(("DnsAndConnectSocket::SetupEvent state=%d", mState)); 297 298 if (mState == DnsAndSocketState::DONE) { 299 CheckIsDone(); 300 RefPtr<DnsAndConnectSocket> self(this); 301 RefPtr<ConnectionEntry> ent = 302 gHttpHandler->ConnMgr()->FindConnectionEntry(mConnInfo); 303 if (ent) { 304 ent->RemoveDnsAndConnectSocket(this, false); 305 } 306 return rv; 307 } 308 return NS_OK; 309 } 310 311 void DnsAndConnectSocket::SetupBackupTimer() { 312 uint16_t timeout = gHttpHandler->GetIdleSynTimeout(); 313 MOZ_ASSERT(!mSynTimer, "timer already initd"); 314 315 // When using Fast Open the correct transport will be setup for sure (it is 316 // guaranteed), but it can be that it will happened a bit later. 317 if (timeout && (!mSpeculative || mConnInfo->GetFallbackConnection()) && 318 !mIsHttp3) { 319 // Setup the timer that will establish a backup socket 320 // if we do not get a writable event on the main one. 321 // We do this because a lost SYN takes a very long time 322 // to repair at the TCP level. 323 // 324 // Failure to setup the timer is something we can live with, 325 // so don't return an error in that case. 326 NS_NewTimerWithCallback(getter_AddRefs(mSynTimer), this, timeout, 327 nsITimer::TYPE_ONE_SHOT); 328 LOG(("DnsAndConnectSocket::SetupBackupTimer() [this=%p]", this)); 329 } else if (timeout) { 330 LOG(("DnsAndConnectSocket::SetupBackupTimer() [this=%p], did not arm\n", 331 this)); 332 } 333 } 334 335 void DnsAndConnectSocket::CancelBackupTimer() { 336 // If the syntimer is still armed, we can cancel it because no backup 337 // socket should be formed at this point 338 if (!mSynTimer) { 339 return; 340 } 341 342 LOG(("DnsAndConnectSocket::CancelBackupTimer()")); 343 mSynTimer->Cancel(); 344 345 // Keeping the reference to the timer to remember we have already 346 // performed the backup connection. 347 } 348 349 void DnsAndConnectSocket::Abandon() { 350 LOG(("DnsAndConnectSocket::Abandon [this=%p ent=%s] %p %p %p %p", this, 351 mConnInfo->Origin(), mPrimaryTransport.mSocketTransport.get(), 352 mBackupTransport.mSocketTransport.get(), 353 mPrimaryTransport.mStreamOut.get(), mBackupTransport.mStreamOut.get())); 354 355 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 356 357 // Tell socket (and backup socket) to forget the half open socket. 358 mPrimaryTransport.Abandon(); 359 mBackupTransport.Abandon(); 360 361 // Stop the timer - we don't want any new backups. 362 CancelBackupTimer(); 363 364 mState = DnsAndSocketState::DONE; 365 } 366 367 double DnsAndConnectSocket::Duration(TimeStamp epoch) { 368 if (mPrimaryTransport.mSynStarted.IsNull()) { 369 return 0; 370 } 371 372 return (epoch - mPrimaryTransport.mSynStarted).ToMilliseconds(); 373 } 374 375 NS_IMETHODIMP // method for nsITimerCallback 376 DnsAndConnectSocket::Notify(nsITimer* timer) { 377 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 378 MOZ_ASSERT(timer == mSynTimer, "wrong timer"); 379 380 MOZ_ASSERT(!mBackupTransport.mDNSRequest); 381 MOZ_ASSERT(!mBackupTransport.mSocketTransport); 382 MOZ_ASSERT(mSynTimer); 383 384 DebugOnly<nsresult> rv = SetupEvent(BACKUP_TIMER_FIRED_EVENT); 385 MOZ_ASSERT(NS_SUCCEEDED(rv)); 386 387 // Keeping the reference to the timer to remember we have already 388 // performed the backup connection. 389 390 return NS_OK; 391 } 392 393 NS_IMETHODIMP // method for nsINamed 394 DnsAndConnectSocket::GetName(nsACString& aName) { 395 aName.AssignLiteral("DnsAndConnectSocket"); 396 return NS_OK; 397 } 398 399 NS_IMETHODIMP 400 DnsAndConnectSocket::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec, 401 nsresult status) { 402 LOG(( 403 "DnsAndConnectSocket::OnLookupComplete: this=%p mState=%d status %" PRIx32 404 ".", 405 this, mState, static_cast<uint32_t>(status))); 406 407 if (nsCOMPtr<nsIDNSAddrRecord> addrRecord = do_QueryInterface((rec))) { 408 nsIRequest::TRRMode effectivemode = nsIRequest::TRR_DEFAULT_MODE; 409 addrRecord->GetEffectiveTRRMode(&effectivemode); 410 nsITRRSkipReason::value skipReason = nsITRRSkipReason::TRR_UNSET; 411 addrRecord->GetTrrSkipReason(&skipReason); 412 if (mTransaction) { 413 mTransaction->SetTRRInfo(effectivemode, skipReason); 414 } 415 } 416 417 MOZ_DIAGNOSTIC_ASSERT(request); 418 RefPtr<DnsAndConnectSocket> deleteProtector(this); 419 420 if (!request || (!IsPrimary(request) && !IsBackup(request))) { 421 return NS_OK; 422 } 423 424 if (IsPrimary(request) && NS_SUCCEEDED(status)) { 425 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RESOLVED_HOST, 0); 426 } 427 428 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP 429 // proxy host is not found, so we fixup the error code. 430 // For SOCKS proxies (mProxyTransparent == true), the socket 431 // transport resolves the real host here, so there's no fixup 432 // (see bug 226943). 433 if (mProxyNotTransparent && (status == NS_ERROR_UNKNOWN_HOST)) { 434 status = NS_ERROR_UNKNOWN_PROXY_HOST; 435 } 436 437 nsresult rv; 438 // remember if it was primary because TransportSetups will delete the ref to 439 // the DNS request and check cannot be performed later. 440 bool isPrimary = IsPrimary(request); 441 if (isPrimary) { 442 rv = mPrimaryTransport.OnLookupComplete(this, rec, status); 443 if ((!mIsHttp3 && mPrimaryTransport.ConnectingOrRetry()) || 444 (mIsHttp3 && mPrimaryTransport.Resolved())) { 445 SetupEvent(SetupEvents::RESOLVED_PRIMARY_EVENT); 446 } 447 } else { 448 rv = mBackupTransport.OnLookupComplete(this, rec, status); 449 } 450 451 if (NS_FAILED(rv) || mIsHttp3) { 452 // If we are retrying DNS, we should not setup the connection. 453 if (mIsHttp3 && mPrimaryTransport.mState == 454 TransportSetup::TransportSetupState::RETRY_RESOLVING) { 455 LOG(("Retry DNS for Http3")); 456 return NS_OK; 457 } 458 459 // Before calling SetupConn we need to hold reference to this, i.e. a 460 // delete protector, because the corresponding ConnectionEntry may be 461 // abandoned and that will abandon this DnsAndConnectSocket. 462 SetupConn(isPrimary, rv); 463 // During a connection dispatch that will happen in SetupConn, 464 // a ConnectionEntry may be abandon and that will abandon this 465 // DnsAndConnectSocket. In that case mState will already be 466 // DnsAndSocketState::DONE and we do not need to set it again. 467 if (mState != DnsAndSocketState::DONE) { 468 if (isPrimary) { 469 SetupEvent(SetupEvents::PRIMARY_DONE_EVENT); 470 } else { 471 SetupEvent(SetupEvents::BACKUP_DONE_EVENT); 472 } 473 } 474 } 475 return NS_OK; 476 } 477 478 // method for nsIAsyncOutputStreamCallback 479 NS_IMETHODIMP 480 DnsAndConnectSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) { 481 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 482 MOZ_DIAGNOSTIC_ASSERT(mPrimaryTransport.mSocketTransport || 483 mBackupTransport.mSocketTransport); 484 MOZ_DIAGNOSTIC_ASSERT(IsPrimary(out) || IsBackup(out), "stream mismatch"); 485 486 RefPtr<ConnectionEntry> ent = 487 gHttpHandler->ConnMgr()->FindConnectionEntry(mConnInfo); 488 MOZ_DIAGNOSTIC_ASSERT(ent); 489 (void)ent; 490 491 RefPtr<DnsAndConnectSocket> deleteProtector(this); 492 493 LOG(("DnsAndConnectSocket::OnOutputStreamReady [this=%p ent=%s %s]\n", this, 494 mConnInfo->Origin(), IsPrimary(out) ? "primary" : "backup")); 495 496 // Remember if it was primary or backup reuest. 497 bool isPrimary = IsPrimary(out); 498 nsresult rv = NS_OK; 499 if (isPrimary) { 500 rv = mPrimaryTransport.CheckConnectedResult(this); 501 if (!mPrimaryTransport.DoneConnecting()) { 502 return NS_OK; 503 } 504 MOZ_ASSERT((NS_SUCCEEDED(rv) && 505 (mPrimaryTransport.mState == 506 TransportSetup::TransportSetupState::CONNECTING_DONE) && 507 mPrimaryTransport.mSocketTransport) || 508 (NS_FAILED(rv) && 509 (mPrimaryTransport.mState == 510 TransportSetup::TransportSetupState::DONE) && 511 !mPrimaryTransport.mSocketTransport)); 512 } else if (IsBackup(out)) { 513 rv = mBackupTransport.CheckConnectedResult(this); 514 if (!mBackupTransport.DoneConnecting()) { 515 return NS_OK; 516 } 517 MOZ_ASSERT((NS_SUCCEEDED(rv) && 518 (mBackupTransport.mState == 519 TransportSetup::TransportSetupState::CONNECTING_DONE) && 520 mBackupTransport.mSocketTransport) || 521 (NS_FAILED(rv) && 522 (mBackupTransport.mState == 523 TransportSetup::TransportSetupState::DONE) && 524 !mBackupTransport.mSocketTransport)); 525 } else { 526 MOZ_ASSERT(false, "unexpected stream"); 527 return NS_ERROR_UNEXPECTED; 528 } 529 530 nsresult socketStatus = out->StreamStatus(); 531 if (StaticPrefs::network_http_retry_with_another_half_open() && 532 NS_FAILED(socketStatus) && socketStatus != NS_BASE_STREAM_WOULD_BLOCK) { 533 if (isPrimary) { 534 if (mBackupTransport.mState == 535 TransportSetup::TransportSetupState::CONNECTING) { 536 mPrimaryTransport.Abandon(); 537 return NS_OK; 538 } 539 } else if (IsBackup(out)) { 540 if (mPrimaryTransport.mState == 541 TransportSetup::TransportSetupState::CONNECTING) { 542 mBackupTransport.Abandon(); 543 return NS_OK; 544 } 545 } 546 } 547 548 // Before calling SetupConn we need to hold a reference to this, i.e. a 549 // delete protector, because the corresponding ConnectionEntry may be 550 // abandoned and that will abandon this DnsAndConnectSocket. 551 rv = SetupConn(isPrimary, rv); 552 if (mState != DnsAndSocketState::DONE) { 553 // During a connection dispatch that will happen in SetupConn, 554 // a ConnectionEntry may be abandon and that will abandon this 555 // DnsAndConnectSocket. In that case mState will already be 556 // DnsAndSocketState::DONE and we do not need to set it again. 557 if (isPrimary) { 558 SetupEvent(SetupEvents::PRIMARY_DONE_EVENT); 559 } else { 560 SetupEvent(SetupEvents::BACKUP_DONE_EVENT); 561 } 562 } 563 return rv; 564 } 565 566 nsresult DnsAndConnectSocket::SetupConn(bool isPrimary, nsresult status) { 567 // assign the new socket to the http connection 568 569 RefPtr<ConnectionEntry> ent = 570 gHttpHandler->ConnMgr()->FindConnectionEntry(mConnInfo); 571 MOZ_DIAGNOSTIC_ASSERT(ent); 572 if (!ent) { 573 Abandon(); 574 return NS_OK; 575 } 576 577 RefPtr<HttpConnectionBase> conn; 578 579 nsresult rv = NS_OK; 580 if (isPrimary) { 581 rv = mPrimaryTransport.SetupConn(this, mTransaction, ent, status, mCaps, 582 getter_AddRefs(conn)); 583 } else { 584 rv = mBackupTransport.SetupConn(this, mTransaction, ent, status, mCaps, 585 getter_AddRefs(conn)); 586 } 587 588 nsCOMPtr<nsIInterfaceRequestor> callbacks; 589 mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); 590 591 if (NS_FAILED(rv)) { 592 LOG( 593 ("DnsAndConnectSocket::SetupConn " 594 "conn->init (%p) failed %" PRIx32 "\n", 595 conn.get(), static_cast<uint32_t>(rv))); 596 597 if (nsHttpTransaction* trans = mTransaction->QueryHttpTransaction()) { 598 if (mIsHttp3 && !mConnInfo->GetWebTransport() && 599 !mConnInfo->IsHttp3ProxyConnection()) { 600 trans->DisableHttp3(true); 601 gHttpHandler->ExcludeHttp3(mConnInfo); 602 } 603 // The transaction's connection info is changed after DisableHttp3(), so 604 // this is the only point we can remove this transaction from its conn 605 // entry. 606 ent->RemoveTransFromPendingQ(trans); 607 } 608 mTransaction->Close(rv); 609 610 return rv; 611 } 612 613 // This half-open socket has created a connection. This flag excludes it 614 // from counter of actual connections used for checking limits. 615 mHasConnected = true; 616 617 // if this is still in the pending list, remove it and dispatch it 618 RefPtr<PendingTransactionInfo> pendingTransInfo = 619 gHttpHandler->ConnMgr()->FindTransactionHelper(true, ent, mTransaction); 620 if (pendingTransInfo) { 621 MOZ_ASSERT(!mSpeculative, "Speculative Half Open found mTransaction"); 622 623 ent->InsertIntoActiveConns(conn); 624 if (mIsHttp3) { 625 // For WebSocket through HTTP/3 proxy, queue the transaction to be 626 // dispatched when the H3 session is connected, and use a NullTransaction 627 // to drive the H3 connection establishment. 628 // We do NOT create a ConnectionHandle for the WebSocket transaction here 629 // because it will get a tunnel connection later, and setting a 630 // ConnectionHandle now would cause it to be reclaimed when cleared. 631 nsHttpTransaction* trans = pendingTransInfo->Transaction(); 632 if (trans->IsWebsocketUpgrade()) { 633 LOG( 634 ("DnsAndConnectSocket::SetupConn WebSocket through HTTP/3 proxy, " 635 "queueing for tunnel creation after H3 connected")); 636 // Put the transaction back in the pending queue so it can be 637 // dispatched through TryDispatchTransaction when the H3 session 638 // reports it's connected 639 RefPtr<PendingTransactionInfo> newPendingInfo = 640 new PendingTransactionInfo(trans); 641 ent->InsertTransaction(newPendingInfo); 642 643 // Dispatch a NullHttpTransaction to drive the H3 proxy connection 644 // establishment 645 nsCOMPtr<nsIInterfaceRequestor> nullCallbacks; 646 trans->GetSecurityCallbacks(getter_AddRefs(nullCallbacks)); 647 RefPtr<nsAHttpTransaction> nullTrans = 648 new NullHttpTransaction(mConnInfo, nullCallbacks, mCaps); 649 rv = gHttpHandler->ConnMgr()->DispatchAbstractTransaction( 650 ent, nullTrans, mCaps, conn, 0); 651 return rv; 652 } 653 654 // Each connection must have a ConnectionHandle wrapper. 655 // In case of Http < 2 the a ConnectionHandle is created for each 656 // transaction in DispatchAbstractTransaction. 657 // In case of Http2/ and Http3, ConnectionHandle is created only once. 658 // Http2 connection always starts as http1 connection and the first 659 // transaction use DispatchAbstractTransaction to be dispatched and 660 // a ConnectionHandle is created. All consecutive transactions for 661 // Http2 use a short-cut in DispatchTransaction and call 662 // HttpConnectionBase::Activate (DispatchAbstractTransaction is never 663 // called). 664 // In case of Http3 the short-cut HttpConnectionBase::Activate is always 665 // used also for the first transaction, therefore we need to create 666 // ConnectionHandle here. 667 RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn); 668 pendingTransInfo->Transaction()->SetConnection(handle); 669 } 670 rv = gHttpHandler->ConnMgr()->DispatchTransaction( 671 ent, pendingTransInfo->Transaction(), conn); 672 } else { 673 // this transaction was dispatched off the pending q before all the 674 // sockets established themselves. 675 676 // After about 1 second allow for the possibility of restarting a 677 // transaction due to server close. Keep at sub 1 second as that is the 678 // minimum granularity we can expect a server to be timing out with. 679 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 680 if (connTCP) { 681 connTCP->SetIsReusedAfter(950); 682 } 683 684 // if we are using ssl and no other transactions are waiting right now, 685 // then form a null transaction to drive the SSL handshake to 686 // completion. Afterwards the connection will be 100% ready for the next 687 // transaction to use it. Make an exception for SSL tunneled HTTP proxy as 688 // the NullHttpTransaction does not know how to drive Connect 689 // Http3 cannot be dispatched using OnMsgReclaimConnection (see below), 690 // therefore we need to use a Nulltransaction. 691 // Ensure that the fallback transacion is always dispatched. 692 if (!connTCP || ent->mConnInfo->GetFallbackConnection() || 693 (ent->mConnInfo->FirstHopSSL() && !ent->UrgentStartQueueLength() && 694 !ent->PendingQueueLength() && !ent->mConnInfo->UsingConnect())) { 695 LOG( 696 ("DnsAndConnectSocket::SetupConn null transaction will " 697 "be used to finish SSL handshake on conn %p\n", 698 conn.get())); 699 RefPtr<nsAHttpTransaction> trans; 700 if (mTransaction->IsNullTransaction() && !mDispatchedMTransaction) { 701 // null transactions cannot be put in the entry queue, so that 702 // explains why it is not present. 703 mDispatchedMTransaction = true; 704 trans = mTransaction; 705 } else { 706 trans = new NullHttpTransaction(mConnInfo, callbacks, mCaps); 707 } 708 709 ent->InsertIntoActiveConns(conn); 710 rv = gHttpHandler->ConnMgr()->DispatchAbstractTransaction(ent, trans, 711 mCaps, conn, 0); 712 } else { 713 // otherwise just put this in the persistent connection pool 714 LOG( 715 ("DnsAndConnectSocket::SetupConn no transaction match " 716 "returning conn %p to pool\n", 717 conn.get())); 718 gHttpHandler->ConnMgr()->OnMsgReclaimConnection(conn); 719 720 // We expect that there is at least one tranasction in the pending 721 // queue that can take this connection, but it can happened that 722 // all transactions are blocked or they have took other idle 723 // connections. In that case the connection has been added to the 724 // idle queue. 725 // If the connection is in the idle queue but it is using ssl, make 726 // a nulltransaction for it to finish ssl handshake! 727 if (ent->mConnInfo->FirstHopSSL() && !ent->mConnInfo->UsingConnect()) { 728 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 729 // If RemoveIdleConnection succeeds that means that conn is in the 730 // idle queue. 731 if (connTCP && NS_SUCCEEDED(ent->RemoveIdleConnection(connTCP))) { 732 RefPtr<nsAHttpTransaction> trans; 733 if (mTransaction->IsNullTransaction() && !mDispatchedMTransaction) { 734 mDispatchedMTransaction = true; 735 trans = mTransaction; 736 } else { 737 trans = new NullHttpTransaction(ent->mConnInfo, callbacks, mCaps); 738 } 739 ent->InsertIntoActiveConns(conn); 740 rv = gHttpHandler->ConnMgr()->DispatchAbstractTransaction( 741 ent, trans, mCaps, conn, 0); 742 } 743 } 744 } 745 } 746 747 // If this halfOpenConn was speculative, but at the end the conn got a 748 // non-null transaction than this halfOpen is not speculative anymore! 749 if (conn->Transaction() && !conn->Transaction()->IsNullTransaction()) { 750 Claim(); 751 } 752 753 return rv; 754 } 755 756 // method for nsITransportEventSink 757 NS_IMETHODIMP 758 DnsAndConnectSocket::OnTransportStatus(nsITransport* trans, nsresult status, 759 int64_t progress, int64_t progressMax) { 760 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 761 762 MOZ_ASSERT(IsPrimary(trans) || IsBackup(trans)); 763 if (mTransaction) { 764 if (IsPrimary(trans) || 765 (IsBackup(trans) && (status == NS_NET_STATUS_CONNECTED_TO) && 766 mPrimaryTransport.mSocketTransport)) { 767 // Send this status event only if the transaction is still pending, 768 // i.e. it has not found a free already connected socket. 769 // Sockets in halfOpen state can only get following events: 770 // NS_NET_STATUS_CONNECTING_TO and NS_NET_STATUS_CONNECTED_TO. 771 // mBackupTransport is only started after 772 // NS_NET_STATUS_CONNECTING_TO of mSocketTransport, so ignore 773 // NS_NET_STATUS_CONNECTING_TO event for mBackupTransport and 774 // send NS_NET_STATUS_CONNECTED_TO. 775 // mBackupTransport must be connected before mSocketTransport(e.g. 776 // mPrimaryTransport.mSocketTransport != nullpttr). 777 mTransaction->OnTransportStatus(trans, status, progress); 778 } 779 } 780 781 if (status == NS_NET_STATUS_CONNECTED_TO) { 782 if (IsPrimary(trans)) { 783 mPrimaryTransport.mConnectedOK = true; 784 } else { 785 mBackupTransport.mConnectedOK = true; 786 } 787 } 788 789 // The rest of this method only applies to the primary transport 790 if (!IsPrimary(trans)) { 791 return NS_OK; 792 } 793 794 // if we are doing spdy coalescing and haven't recorded the ip address 795 // for this entry before then make the hash key if our dns lookup 796 // just completed. We can't do coalescing if using a proxy because the 797 // ip addresses are not available to the client. 798 799 nsCOMPtr<nsIDNSAddrRecord> dnsRecord( 800 do_GetInterface(mPrimaryTransport.mSocketTransport)); 801 if (status == NS_NET_STATUS_CONNECTING_TO && 802 StaticPrefs::network_http_http2_enabled() && 803 StaticPrefs::network_http_http2_coalesce_hostnames()) { 804 RefPtr<ConnectionEntry> ent = 805 gHttpHandler->ConnMgr()->FindConnectionEntry(mConnInfo); 806 MOZ_DIAGNOSTIC_ASSERT(ent); 807 if (ent) { 808 if (ent->MaybeProcessCoalescingKeys(dnsRecord)) { 809 gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(ent); 810 } 811 } 812 } 813 814 return NS_OK; 815 } 816 817 bool DnsAndConnectSocket::IsPrimary(nsITransport* trans) { 818 return trans == mPrimaryTransport.mSocketTransport; 819 } 820 821 bool DnsAndConnectSocket::IsPrimary(nsIAsyncOutputStream* out) { 822 return out == mPrimaryTransport.mStreamOut; 823 } 824 825 bool DnsAndConnectSocket::IsPrimary(nsICancelable* dnsRequest) { 826 return dnsRequest == mPrimaryTransport.mDNSRequest; 827 } 828 829 bool DnsAndConnectSocket::IsBackup(nsITransport* trans) { 830 return trans == mBackupTransport.mSocketTransport; 831 } 832 833 bool DnsAndConnectSocket::IsBackup(nsIAsyncOutputStream* out) { 834 return out == mBackupTransport.mStreamOut; 835 } 836 837 bool DnsAndConnectSocket::IsBackup(nsICancelable* dnsRequest) { 838 return dnsRequest == mBackupTransport.mDNSRequest; 839 } 840 841 // method for nsIInterfaceRequestor 842 NS_IMETHODIMP 843 DnsAndConnectSocket::GetInterface(const nsIID& iid, void** result) { 844 if (mTransaction) { 845 nsCOMPtr<nsIInterfaceRequestor> callbacks; 846 mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); 847 if (callbacks) { 848 return callbacks->GetInterface(iid, result); 849 } 850 } 851 return NS_ERROR_NO_INTERFACE; 852 } 853 854 bool DnsAndConnectSocket::AcceptsTransaction(nsHttpTransaction* trans) { 855 // When marked as urgent start, only accept urgent start marked transactions. 856 // Otherwise, accept any kind of transaction. 857 return !mUrgentStart || (trans->Caps() & nsIClassOfService::UrgentStart); 858 } 859 860 bool DnsAndConnectSocket::Claim() { 861 if (mSpeculative) { 862 mSpeculative = false; 863 mAllow1918 = true; 864 auto resetFlag = [](TransportSetup& transport) { 865 uint32_t flags; 866 if (transport.mSocketTransport && 867 NS_SUCCEEDED( 868 transport.mSocketTransport->GetConnectionFlags(&flags))) { 869 flags &= ~nsISocketTransport::DISABLE_RFC1918; 870 flags &= ~nsISocketTransport::IS_SPECULATIVE_CONNECTION; 871 transport.mSocketTransport->SetConnectionFlags(flags); 872 } 873 }; 874 resetFlag(mPrimaryTransport); 875 resetFlag(mBackupTransport); 876 877 // Http3 has its own syn-retransmission, therefore it does not need a 878 // backup connection. 879 if (mPrimaryTransport.ConnectingOrRetry() && 880 !mBackupTransport.mSocketTransport && !mSynTimer && !mIsHttp3) { 881 SetupBackupTimer(); 882 } 883 } 884 885 if (mFreeToUse) { 886 mFreeToUse = false; 887 888 if (mPrimaryTransport.mSocketTransport) { 889 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 890 if (NS_SUCCEEDED(mPrimaryTransport.mSocketTransport->GetTlsSocketControl( 891 getter_AddRefs(tlsSocketControl))) && 892 tlsSocketControl) { 893 (void)tlsSocketControl->Claim(); 894 } 895 } 896 897 return true; 898 } 899 900 return false; 901 } 902 903 void DnsAndConnectSocket::Unclaim() { 904 MOZ_ASSERT(!mSpeculative && !mFreeToUse); 905 // We will keep the backup-timer running. Most probably this halfOpen will 906 // be used by a transaction from which this transaction took the halfOpen. 907 // (this is happening because of the transaction priority.) 908 mFreeToUse = true; 909 } 910 911 void DnsAndConnectSocket::CloseTransports(nsresult error) { 912 if (mPrimaryTransport.mSocketTransport) { 913 mPrimaryTransport.mSocketTransport->Close(error); 914 } 915 if (mBackupTransport.mSocketTransport) { 916 mBackupTransport.mSocketTransport->Close(error); 917 } 918 } 919 920 DnsAndConnectSocket::TransportSetup::TransportSetup(bool isBackup) 921 : mState(TransportSetup::TransportSetupState::INIT), mIsBackup(isBackup) {} 922 923 nsresult DnsAndConnectSocket::TransportSetup::Init( 924 DnsAndConnectSocket* dnsAndSock) { 925 nsresult rv; 926 mSynStarted = TimeStamp::Now(); 927 if (mSkipDnsResolution) { 928 mState = TransportSetup::TransportSetupState::CONNECTING; 929 rv = SetupStreams(dnsAndSock); 930 } else { 931 mState = TransportSetup::TransportSetupState::RESOLVING; 932 rv = ResolveHost(dnsAndSock); 933 } 934 if (NS_FAILED(rv)) { 935 CloseAll(); 936 mState = TransportSetup::TransportSetupState::DONE; 937 } 938 return rv; 939 } 940 941 void DnsAndConnectSocket::TransportSetup::CancelDnsResolution() { 942 if (mDNSRequest) { 943 mDNSRequest->Cancel(NS_ERROR_ABORT); 944 mDNSRequest = nullptr; 945 } 946 if (mState == TransportSetup::TransportSetupState::RESOLVING) { 947 mState = TransportSetup::TransportSetupState::INIT; 948 } 949 } 950 951 void DnsAndConnectSocket::TransportSetup::Abandon() { 952 CloseAll(); 953 mState = TransportSetup::TransportSetupState::DONE; 954 } 955 956 void DnsAndConnectSocket::TransportSetup::SetConnecting() { 957 MOZ_ASSERT(!mWaitingForConnect); 958 mWaitingForConnect = true; 959 gHttpHandler->ConnMgr()->StartedConnect(); 960 } 961 962 void DnsAndConnectSocket::TransportSetup::MaybeSetConnectingDone() { 963 if (mWaitingForConnect) { 964 mWaitingForConnect = false; 965 gHttpHandler->ConnMgr()->RecvdConnect(); 966 } 967 } 968 969 void DnsAndConnectSocket::TransportSetup::CloseAll() { 970 MaybeSetConnectingDone(); 971 972 // Tell socket (and backup socket) to forget the half open socket. 973 if (mSocketTransport) { 974 mSocketTransport->SetEventSink(nullptr, nullptr); 975 mSocketTransport->SetSecurityCallbacks(nullptr); 976 mSocketTransport = nullptr; 977 } 978 979 // Tell output stream (and backup) to forget the half open socket. 980 if (mStreamOut) { 981 mStreamOut->AsyncWait(nullptr, 0, 0, nullptr); 982 mStreamOut = nullptr; 983 } 984 985 // Lose references to input stream (and backup). 986 if (mStreamIn) { 987 mStreamIn->AsyncWait(nullptr, 0, 0, nullptr); 988 mStreamIn = nullptr; 989 } 990 991 if (mDNSRequest) { 992 mDNSRequest->Cancel(NS_ERROR_ABORT); 993 mDNSRequest = nullptr; 994 } 995 996 mConnectedOK = false; 997 } 998 999 bool DnsAndConnectSocket::TransportSetup::ToggleIpFamilyFlagsIfRetryEnabled() { 1000 if (!mRetryWithDifferentIPFamily) { 1001 return false; 1002 } 1003 1004 LOG( 1005 ("DnsAndConnectSocket::TransportSetup::ToggleIpFamilyFlagsIfRetryEnabled" 1006 "[this=%p dnsFlags=%u]", 1007 this, mDnsFlags)); 1008 mRetryWithDifferentIPFamily = false; 1009 1010 // Toggle the RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4 flags in mDnsFlags 1011 // This ensures we switch the IP family for the DNS resolution 1012 mDnsFlags ^= (nsIDNSService::RESOLVE_DISABLE_IPV6 | 1013 nsIDNSService::RESOLVE_DISABLE_IPV4); 1014 1015 if ((mDnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) && 1016 (mDnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4)) { 1017 // Clear both flags to prevent an invalid state 1018 mDnsFlags &= ~(nsIDNSService::RESOLVE_DISABLE_IPV6 | 1019 nsIDNSService::RESOLVE_DISABLE_IPV4); 1020 LOG( 1021 ("DnsAndConnectSocket::TransportSetup::" 1022 "ToggleIpFamilyFlagsIfRetryEnabled " 1023 "[this=%p] both v6 and v4 are disabled", 1024 this)); 1025 MOZ_DIAGNOSTIC_CRASH("both v6 and v4 addresses are disabled"); 1026 } 1027 1028 // Indicate that the IP family preference should be reset 1029 mResetFamilyPreference = true; 1030 return true; 1031 } 1032 1033 nsresult DnsAndConnectSocket::TransportSetup::CheckConnectedResult( 1034 DnsAndConnectSocket* dnsAndSock) { 1035 mState = TransportSetup::TransportSetupState::CONNECTING_DONE; 1036 MaybeSetConnectingDone(); 1037 1038 if (mSkipDnsResolution) { 1039 return NS_OK; 1040 } 1041 bool retryDns = false; 1042 mSocketTransport->GetRetryDnsIfPossible(&retryDns); 1043 if (!retryDns) { 1044 return NS_OK; 1045 } 1046 1047 bool retry = false; 1048 if (ToggleIpFamilyFlagsIfRetryEnabled()) { 1049 retry = true; 1050 } else if (!(mDnsFlags & nsIDNSService::RESOLVE_DISABLE_TRR)) { 1051 bool trrEnabled; 1052 mDNSRecord->IsTRR(&trrEnabled); 1053 if (trrEnabled) { 1054 nsIRequest::TRRMode trrMode = nsIRequest::TRR_DEFAULT_MODE; 1055 mDNSRecord->GetEffectiveTRRMode(&trrMode); 1056 // If current trr mode is trr only, we should not retry. 1057 if (trrMode != nsIRequest::TRR_ONLY_MODE) { 1058 // Drop state to closed. This will trigger a new round of 1059 // DNS resolving. Bypass the cache this time since the 1060 // cached data came from TRR and failed already! 1061 LOG((" failed to connect with TRR enabled, try w/o\n")); 1062 mDnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR | 1063 nsIDNSService::RESOLVE_BYPASS_CACHE | 1064 nsIDNSService::RESOLVE_REFRESH_CACHE; 1065 retry = true; 1066 } 1067 } 1068 } 1069 1070 if (retry) { 1071 LOG((" retry DNS, mDnsFlags=%u", mDnsFlags)); 1072 CloseAll(); 1073 mState = TransportSetup::TransportSetupState::RETRY_RESOLVING; 1074 nsresult rv = ResolveHost(dnsAndSock); 1075 if (NS_FAILED(rv)) { 1076 CloseAll(); 1077 mState = TransportSetup::TransportSetupState::DONE; 1078 } 1079 return rv; 1080 } 1081 1082 return NS_OK; 1083 } 1084 1085 nsresult DnsAndConnectSocket::TransportSetup::SetupConn( 1086 DnsAndConnectSocket* dnsAndSock, nsAHttpTransaction* transaction, 1087 ConnectionEntry* ent, nsresult status, uint32_t cap, 1088 HttpConnectionBase** connection) { 1089 RefPtr<HttpConnectionBase> conn; 1090 if (!dnsAndSock->mIsHttp3) { 1091 conn = new nsHttpConnection(); 1092 } else { 1093 conn = new HttpConnectionUDP(); 1094 } 1095 1096 NotifyActivity(ent->mConnInfo, NS_HTTP_ACTIVITY_SUBTYPE_CONNECTION_CREATED); 1097 1098 LOG( 1099 ("DnsAndConnectSocket::SocketTransport::SetupConn " 1100 "Created new nshttpconnection %p %s\n", 1101 conn.get(), dnsAndSock->mIsHttp3 ? "using http3" : "")); 1102 1103 NullHttpTransaction* nullTrans = transaction->QueryNullTransaction(); 1104 if (nullTrans) { 1105 conn->BootstrapTimings(nullTrans->Timings()); 1106 } 1107 1108 // Some capabilities are needed before a transaction actually gets 1109 // scheduled (e.g. how to negotiate false start) 1110 conn->SetTransactionCaps(transaction->Caps()); 1111 1112 nsCOMPtr<nsIInterfaceRequestor> callbacks; 1113 transaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); 1114 nsresult rv = NS_OK; 1115 if (!dnsAndSock->mIsHttp3) { 1116 RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn); 1117 rv = 1118 connTCP->Init(ent->mConnInfo, gHttpHandler->ConnMgr()->mMaxRequestDelay, 1119 mSocketTransport, mStreamIn, mStreamOut, mConnectedOK, 1120 status, callbacks, 1121 PR_MillisecondsToInterval(static_cast<uint32_t>( 1122 (TimeStamp::Now() - mSynStarted).ToMilliseconds())), 1123 cap & NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE); 1124 } else { 1125 RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(conn); 1126 rv = connUDP->Init(ent->mConnInfo, mDNSRecord, status, callbacks, cap); 1127 if (NS_SUCCEEDED(rv)) { 1128 if (nsHttpHandler::IsHttp3Enabled() && 1129 StaticPrefs::network_http_http2_coalesce_hostnames()) { 1130 if (ent->MaybeProcessCoalescingKeys(mDNSRecord, true)) { 1131 gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(ent); 1132 } 1133 } 1134 } 1135 } 1136 1137 bool resetPreference = false; 1138 if (mResetFamilyPreference || 1139 (mSocketTransport && 1140 NS_SUCCEEDED( 1141 mSocketTransport->GetResetIPFamilyPreference(&resetPreference)) && 1142 resetPreference)) { 1143 ent->ResetIPFamilyPreference(); 1144 } 1145 1146 NetAddr peeraddr; 1147 if (mSocketTransport && 1148 NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) { 1149 ent->RecordIPFamilyPreference(peeraddr.raw.family); 1150 } 1151 1152 conn.forget(connection); 1153 mSocketTransport = nullptr; 1154 mStreamOut = nullptr; 1155 mStreamIn = nullptr; 1156 mState = TransportSetup::TransportSetupState::DONE; 1157 return rv; 1158 } 1159 1160 nsresult DnsAndConnectSocket::TransportSetup::SetupStreams( 1161 DnsAndConnectSocket* dnsAndSock) { 1162 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1163 MOZ_DIAGNOSTIC_ASSERT(!mSocketTransport); 1164 MOZ_DIAGNOSTIC_ASSERT(!mStreamOut); 1165 MOZ_DIAGNOSTIC_ASSERT(!mDNSRequest); 1166 1167 nsresult rv; 1168 nsTArray<nsCString> socketTypes; 1169 const nsHttpConnectionInfo* ci = dnsAndSock->mConnInfo; 1170 if (dnsAndSock->mIsHttp3) { 1171 socketTypes.AppendElement("quic"_ns); 1172 } else { 1173 if (ci->FirstHopSSL()) { 1174 socketTypes.AppendElement("ssl"_ns); 1175 } else { 1176 const nsCString& defaultType = gHttpHandler->DefaultSocketType(); 1177 if (!defaultType.IsVoid()) { 1178 socketTypes.AppendElement(defaultType); 1179 } 1180 } 1181 } 1182 1183 nsCOMPtr<nsISocketTransport> socketTransport; 1184 nsCOMPtr<nsISocketTransportService> sts; 1185 1186 sts = components::SocketTransport::Service(); 1187 if (!sts) { 1188 return NS_ERROR_NOT_AVAILABLE; 1189 } 1190 1191 LOG( 1192 ("DnsAndConnectSocket::SetupStreams [this=%p ent=%s] " 1193 "setup routed transport to origin %s:%d via %s:%d\n", 1194 this, ci->HashKey().get(), ci->Origin(), ci->OriginPort(), 1195 ci->RoutedHost(), ci->RoutedPort())); 1196 1197 nsCOMPtr<nsIRoutedSocketTransportService> routedSTS(do_QueryInterface(sts)); 1198 if (routedSTS) { 1199 rv = routedSTS->CreateRoutedTransport( 1200 socketTypes, ci->GetOrigin(), ci->OriginPort(), ci->GetRoutedHost(), 1201 ci->RoutedPort(), ci->ProxyInfo(), mDNSRecord, 1202 getter_AddRefs(socketTransport)); 1203 } else { 1204 if (!ci->GetRoutedHost().IsEmpty()) { 1205 // There is a route requested, but the legacy nsISocketTransportService 1206 // can't handle it. 1207 // Origin should be reachable on origin host name, so this should 1208 // not be a problem - but log it. 1209 LOG( 1210 ("DnsAndConnectSocket this=%p using legacy nsISocketTransportService " 1211 "means explicit route %s:%d will be ignored.\n", 1212 this, ci->RoutedHost(), ci->RoutedPort())); 1213 } 1214 1215 rv = sts->CreateTransport(socketTypes, ci->GetOrigin(), ci->OriginPort(), 1216 ci->ProxyInfo(), mDNSRecord, 1217 getter_AddRefs(socketTransport)); 1218 } 1219 NS_ENSURE_SUCCESS(rv, rv); 1220 1221 uint32_t tmpFlags = 0; 1222 if (dnsAndSock->mCaps & NS_HTTP_REFRESH_DNS) { 1223 tmpFlags = nsISocketTransport::BYPASS_CACHE; 1224 } 1225 1226 tmpFlags |= nsISocketTransport::GetFlagsFromTRRMode( 1227 NS_HTTP_TRR_MODE_FROM_FLAGS(dnsAndSock->mCaps)); 1228 1229 if (dnsAndSock->mCaps & NS_HTTP_LOAD_ANONYMOUS) { 1230 tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT; 1231 } 1232 1233 // When we are making a speculative connection we do not propagate all flags 1234 // in mCaps, so we need to query nsHttpConnectionInfo directly as well. 1235 if ((dnsAndSock->mCaps & NS_HTTP_LOAD_ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT) || 1236 ci->GetAnonymousAllowClientCert()) { 1237 tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT; 1238 } 1239 1240 if (ci->GetPrivate()) { 1241 tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE; 1242 } 1243 1244 (void)socketTransport->SetIsPrivate(ci->GetPrivate()); 1245 1246 if (dnsAndSock->mCaps & NS_HTTP_DISALLOW_ECH) { 1247 tmpFlags |= nsISocketTransport::DONT_TRY_ECH; 1248 } 1249 1250 if (dnsAndSock->mCaps & NS_HTTP_IS_RETRY) { 1251 tmpFlags |= nsISocketTransport::IS_RETRY; 1252 } 1253 1254 if (((dnsAndSock->mCaps & NS_HTTP_BE_CONSERVATIVE) || 1255 ci->GetBeConservative()) && 1256 gHttpHandler->ConnMgr()->BeConservativeIfProxied(ci->ProxyInfo())) { 1257 LOG(("Setting Socket to BE_CONSERVATIVE")); 1258 tmpFlags |= nsISocketTransport::BE_CONSERVATIVE; 1259 } 1260 1261 if (ci->HasIPHintAddress()) { 1262 nsCOMPtr<nsIDNSService> dns; 1263 dns = mozilla::components::DNS::Service(&rv); 1264 NS_ENSURE_SUCCESS(rv, rv); 1265 1266 // The spec says: "If A and AAAA records for TargetName are locally 1267 // available, the client SHOULD ignore these hints.", so we check if the DNS 1268 // record is in cache before setting USE_IP_HINT_ADDRESS. 1269 nsCOMPtr<nsIDNSRecord> record; 1270 rv = dns->ResolveNative(mHost, nsIDNSService::RESOLVE_OFFLINE, 1271 dnsAndSock->mConnInfo->GetOriginAttributes(), 1272 getter_AddRefs(record)); 1273 if (NS_FAILED(rv) || !record) { 1274 LOG(("Setting Socket to use IP hint address")); 1275 tmpFlags |= nsISocketTransport::USE_IP_HINT_ADDRESS; 1276 } 1277 } 1278 1279 if (mRetryWithDifferentIPFamily) { 1280 // From the same reason, let the backup socket fail faster to try the other 1281 // family. 1282 uint16_t fallbackTimeout = 1283 mIsBackup ? gHttpHandler->GetFallbackSynTimeout() : 0; 1284 if (fallbackTimeout) { 1285 socketTransport->SetTimeout(nsISocketTransport::TIMEOUT_CONNECT, 1286 fallbackTimeout); 1287 } 1288 } 1289 1290 if (!dnsAndSock->Allow1918()) { 1291 tmpFlags |= nsISocketTransport::DISABLE_RFC1918; 1292 } 1293 1294 if (dnsAndSock->mSpeculative) { 1295 tmpFlags |= nsISocketTransport::IS_SPECULATIVE_CONNECTION; 1296 } 1297 1298 socketTransport->SetConnectionFlags(tmpFlags); 1299 socketTransport->SetTlsFlags(ci->GetTlsFlags()); 1300 1301 const OriginAttributes& originAttributes = 1302 dnsAndSock->mConnInfo->GetOriginAttributes(); 1303 if (originAttributes != OriginAttributes()) { 1304 socketTransport->SetOriginAttributes(originAttributes); 1305 } 1306 1307 socketTransport->SetQoSBits(gHttpHandler->GetQoSBits()); 1308 1309 rv = socketTransport->SetEventSink(dnsAndSock, nullptr); 1310 NS_ENSURE_SUCCESS(rv, rv); 1311 1312 rv = socketTransport->SetSecurityCallbacks(dnsAndSock); 1313 NS_ENSURE_SUCCESS(rv, rv); 1314 1315 if (nsHttpHandler::EchConfigEnabled() && !ci->GetEchConfig().IsEmpty()) { 1316 MOZ_ASSERT(!dnsAndSock->mIsHttp3); 1317 LOG(("Setting ECH")); 1318 rv = socketTransport->SetEchConfig(ci->GetEchConfig()); 1319 NS_ENSURE_SUCCESS(rv, rv); 1320 1321 NotifyActivity(dnsAndSock->mConnInfo, NS_HTTP_ACTIVITY_SUBTYPE_ECH_SET); 1322 } 1323 1324 RefPtr<ConnectionEntry> ent = 1325 gHttpHandler->ConnMgr()->FindConnectionEntry(ci); 1326 MOZ_DIAGNOSTIC_ASSERT(ent); 1327 if (ent) { 1328 glean::http::connection_entry_cache_hit 1329 .EnumGet(static_cast<glean::http::ConnectionEntryCacheHitLabel>( 1330 ent->mUsedForConnection)) 1331 .Add(); 1332 ent->mUsedForConnection = true; 1333 } 1334 1335 nsCOMPtr<nsIOutputStream> sout; 1336 rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, 1337 getter_AddRefs(sout)); 1338 NS_ENSURE_SUCCESS(rv, rv); 1339 1340 nsCOMPtr<nsIInputStream> sin; 1341 rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, 1342 getter_AddRefs(sin)); 1343 NS_ENSURE_SUCCESS(rv, rv); 1344 1345 mSocketTransport = socketTransport.forget(); 1346 mStreamIn = do_QueryInterface(sin); 1347 mStreamOut = do_QueryInterface(sout); 1348 1349 rv = mStreamOut->AsyncWait(dnsAndSock, 0, 0, nullptr); 1350 if (NS_SUCCEEDED(rv)) { 1351 SetConnecting(); 1352 } 1353 1354 return rv; 1355 } 1356 1357 nsresult DnsAndConnectSocket::TransportSetup::ResolveHost( 1358 DnsAndConnectSocket* dnsAndSock) { 1359 MOZ_DIAGNOSTIC_ASSERT(!mSocketTransport); 1360 MOZ_DIAGNOSTIC_ASSERT(!mStreamOut); 1361 MOZ_DIAGNOSTIC_ASSERT(!mDNSRequest); 1362 LOG(("DnsAndConnectSocket::TransportSetup::ResolveHost [this=%p %s%s]", this, 1363 PromiseFlatCString(mHost).get(), 1364 (mDnsFlags & nsIDNSService::RESOLVE_BYPASS_CACHE) ? " bypass cache" 1365 : "")); 1366 nsCOMPtr<nsIDNSService> dns = GetOrInitDNSService(); 1367 if (!dns) { 1368 return NS_ERROR_FAILURE; 1369 } 1370 1371 if (!mIsBackup) { 1372 dnsAndSock->mTransaction->OnTransportStatus( 1373 nullptr, NS_NET_STATUS_RESOLVING_HOST, 0); 1374 } 1375 1376 nsresult rv = NS_OK; 1377 do { 1378 rv = dns->AsyncResolveNative( 1379 mHost, nsIDNSService::RESOLVE_TYPE_DEFAULT, 1380 mDnsFlags | nsIDNSService::RESOLVE_WANT_RECORD_ON_ERROR, nullptr, 1381 dnsAndSock, gSocketTransportService, 1382 dnsAndSock->mConnInfo->GetOriginAttributes(), 1383 getter_AddRefs(mDNSRequest)); 1384 } while (NS_FAILED(rv) && ShouldRetryDNS()); 1385 1386 if (NS_FAILED(rv)) { 1387 mDNSRequest = nullptr; 1388 } 1389 return rv; 1390 } 1391 1392 bool DnsAndConnectSocket::TransportSetup::ShouldRetryDNS() { 1393 if (mDnsFlags & nsIDNSService::RESOLVE_IP_HINT) { 1394 mDnsFlags &= ~nsIDNSService::RESOLVE_IP_HINT; 1395 return true; 1396 } 1397 1398 if (ToggleIpFamilyFlagsIfRetryEnabled()) { 1399 return true; 1400 } 1401 return false; 1402 } 1403 1404 nsresult DnsAndConnectSocket::TransportSetup::OnLookupComplete( 1405 DnsAndConnectSocket* dnsAndSock, nsIDNSRecord* rec, nsresult status) { 1406 mDNSRequest = nullptr; 1407 if (NS_SUCCEEDED(status)) { 1408 mDNSRecord = do_QueryInterface(rec); 1409 MOZ_ASSERT(mDNSRecord); 1410 1411 if (dnsAndSock->mIsHttp3) { 1412 mState = TransportSetup::TransportSetupState::RESOLVED; 1413 return status; 1414 } 1415 nsresult rv = SetupStreams(dnsAndSock); 1416 if (NS_SUCCEEDED(rv)) { 1417 mState = TransportSetup::TransportSetupState::CONNECTING; 1418 } else { 1419 CloseAll(); 1420 mState = TransportSetup::TransportSetupState::DONE; 1421 } 1422 return rv; 1423 } 1424 1425 // DNS lookup status failed 1426 1427 if (ShouldRetryDNS()) { 1428 mState = TransportSetup::TransportSetupState::RETRY_RESOLVING; 1429 nsresult rv = ResolveHost(dnsAndSock); 1430 if (NS_FAILED(rv)) { 1431 CloseAll(); 1432 mState = TransportSetup::TransportSetupState::DONE; 1433 } 1434 return rv; 1435 } 1436 1437 mState = TransportSetup::TransportSetupState::DONE; 1438 return status; 1439 } 1440 1441 } // namespace net 1442 } // namespace mozilla