tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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