Bug 1667356 - Show the content of HTTPS RRs in about:networking r=necko-reviewers,fluent-reviewers,flod,dragana,webidl,asuth
authorKershaw Chang <kershaw@mozilla.com>
Wed, 21 Oct 2020 00:52:46 +0000 (2020-10-21)
changeset 553801 c0e399e7d49594e1e245e4eaa21a3bb3f6148d7b
parent 553800 7abfe9ea6def28b5140620ca38a84664195935d5
child 553802 9a84d54b203f249e80bf0f13bb6645f912e28140
push id129058
push userkjang@mozilla.com
push dateWed, 21 Oct 2020 07:46:54 +0000 (2020-10-21)
treeherderautoland@c0e399e7d495 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnecko-reviewers, fluent-reviewers, flod, dragana, webidl, asuth
bugs1667356
milestone84.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1667356 - Show the content of HTTPS RRs in about:networking r=necko-reviewers,fluent-reviewers,flod,dragana,webidl,asuth Differential Revision: https://phabricator.services.mozilla.com/D91566
dom/webidl/NetDashboard.webidl
netwerk/base/Dashboard.cpp
netwerk/base/nsIDashboard.idl
toolkit/content/aboutNetworking.html
toolkit/content/aboutNetworking.js
toolkit/locales/en-US/toolkit/about/aboutNetworking.ftl
--- a/dom/webidl/NetDashboard.webidl
+++ b/dom/webidl/NetDashboard.webidl
@@ -76,16 +76,61 @@ dictionary DNSCacheDict {
 
 [GenerateConversionToJS]
 dictionary DNSLookupDict {
   sequence<DOMString> address;
   DOMString error = "";
   boolean answer = false;
 };
 
+dictionary SVCParam {
+  unsigned short type = 0;
+};
+
+dictionary SVCParamAlpn : SVCParam {
+  DOMString alpn = "";
+};
+
+dictionary SVCParamNoDefaultAlpn : SVCParam {
+};
+
+dictionary SVCParamPort : SVCParam {
+  unsigned short port = 0;
+};
+
+dictionary SVCParamIPv4Hint : SVCParam {
+  sequence<DOMString> address;
+};
+
+dictionary SVCParamIPv6Hint : SVCParam {
+  sequence<DOMString> address;
+};
+
+dictionary SVCParamEchConfig : SVCParam {
+  DOMString echConfig = "";
+};
+
+dictionary HTTPSRecord {
+  unsigned short priority = 0;
+  DOMString targetName = "";
+  SVCParamAlpn alpn;
+  SVCParamNoDefaultAlpn noDefaultAlpn;
+  SVCParamPort port;
+  SVCParamIPv4Hint ipv4Hint;
+  SVCParamIPv6Hint ipv6Hint;
+  SVCParamEchConfig echConfig;
+};
+
+[GenerateConversionToJS]
+dictionary HTTPSRRLookupDict {
+  DOMString error = "";
+  boolean answer = false;
+  sequence<HTTPSRecord> records;
+};
+
 [GenerateConversionToJS]
 dictionary ConnStatusDict {
   DOMString status = "";
 };
 
 dictionary RcwnPerfStats {
   unsigned long avgShort = 0;
   unsigned long avgLong = 0;
--- a/netwerk/base/Dashboard.cpp
+++ b/netwerk/base/Dashboard.cpp
@@ -2,24 +2,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http:mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/NetDashboardBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/net/Dashboard.h"
 #include "mozilla/net/HttpInfo.h"
+#include "mozilla/net/HTTPSSVC.h"
 #include "mozilla/net/SocketProcessParent.h"
 #include "nsHttp.h"
 #include "nsICancelable.h"
 #include "nsIDNSListener.h"
 #include "nsIDNSService.h"
 #include "nsIDNSRecord.h"
+#include "nsIDNSByTypeRecord.h"
 #include "nsIInputStream.h"
 #include "nsINamed.h"
+#include "nsINetAddr.h"
 #include "nsISocketTransport.h"
 #include "nsProxyRelease.h"
 #include "nsSocketTransportService2.h"
 #include "nsThreadUtils.h"
 #include "nsURLHelper.h"
 #include "mozilla/Logging.h"
 #include "nsIOService.h"
 #include "../cache2/CacheFileUtils.h"
@@ -238,16 +241,17 @@ class LookupHelper final : public nsIDNS
 
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIDNSLISTENER
 
   LookupHelper() : mEventTarget{nullptr}, mStatus{NS_ERROR_NOT_INITIALIZED} {}
 
   nsresult ConstructAnswer(LookupArgument* aArgument);
+  nsresult ConstructHTTPSRRAnswer(LookupArgument* aArgument);
 
  public:
   nsCOMPtr<nsICancelable> mCancel;
   nsMainThreadPtrHandle<nsINetDashboardCallback> mCallback;
   nsIEventTarget* mEventTarget;
   nsresult mStatus;
 };
 
@@ -255,16 +259,27 @@ NS_IMPL_ISUPPORTS(LookupHelper, nsIDNSLi
 
 NS_IMETHODIMP
 LookupHelper::OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRecord,
                                nsresult aStatus) {
   MOZ_ASSERT(aRequest == mCancel);
   mCancel = nullptr;
   mStatus = aStatus;
 
+  nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord = do_QueryInterface(aRecord);
+  if (httpsRecord) {
+    RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
+    mEventTarget->Dispatch(
+        NewRunnableMethod<RefPtr<LookupArgument>>(
+            "net::LookupHelper::ConstructHTTPSRRAnswer", this,
+            &LookupHelper::ConstructHTTPSRRAnswer, arg),
+        NS_DISPATCH_NORMAL);
+    return NS_OK;
+  }
+
   RefPtr<LookupArgument> arg = new LookupArgument(aRecord, this);
   mEventTarget->Dispatch(NewRunnableMethod<RefPtr<LookupArgument>>(
                              "net::LookupHelper::ConstructAnswer", this,
                              &LookupHelper::ConstructAnswer, arg),
                          NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
@@ -303,16 +318,150 @@ nsresult LookupHelper::ConstructAnswer(L
     return NS_ERROR_FAILURE;
   }
 
   this->mCallback->OnDashboardDataAvailable(val);
 
   return NS_OK;
 }
 
+nsresult LookupHelper::ConstructHTTPSRRAnswer(LookupArgument* aArgument) {
+  nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord =
+      do_QueryInterface(aArgument->mRecord);
+
+  AutoSafeJSContext cx;
+
+  mozilla::dom::HTTPSRRLookupDict dict;
+  dict.mRecords.Construct();
+
+  Sequence<dom::HTTPSRecord>& records = dict.mRecords.Value();
+  if (NS_SUCCEEDED(mStatus) && httpsRecord) {
+    dict.mAnswer = true;
+    nsTArray<RefPtr<nsISVCBRecord>> svcbRecords;
+    httpsRecord->GetRecords(svcbRecords);
+
+    for (const auto& record : svcbRecords) {
+      dom::HTTPSRecord* nextRecord = records.AppendElement(fallible);
+      if (!nextRecord) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+
+      Unused << record->GetPriority(&nextRecord->mPriority);
+      nsCString name;
+      Unused << record->GetName(name);
+      CopyASCIItoUTF16(name, nextRecord->mTargetName);
+
+      nsTArray<RefPtr<nsISVCParam>> values;
+      Unused << record->GetValues(values);
+      if (values.IsEmpty()) {
+        continue;
+      }
+
+      for (const auto& value : values) {
+        uint16_t type;
+        Unused << value->GetType(&type);
+        switch (type) {
+          case SvcParamKeyAlpn: {
+            nextRecord->mAlpn.Construct();
+            nextRecord->mAlpn.Value().mType = type;
+            nsCOMPtr<nsISVCParamAlpn> alpnParam = do_QueryInterface(value);
+            nsCString alpnStr;
+            Unused << alpnParam->GetAlpn(alpnStr);
+            CopyASCIItoUTF16(alpnStr, nextRecord->mAlpn.Value().mAlpn);
+            break;
+          }
+          case SvcParamKeyNoDefaultAlpn: {
+            nextRecord->mNoDefaultAlpn.Construct();
+            nextRecord->mNoDefaultAlpn.Value().mType = type;
+            break;
+          }
+          case SvcParamKeyPort: {
+            nextRecord->mPort.Construct();
+            nextRecord->mPort.Value().mType = type;
+            nsCOMPtr<nsISVCParamPort> portParam = do_QueryInterface(value);
+            Unused << portParam->GetPort(&nextRecord->mPort.Value().mPort);
+            break;
+          }
+          case SvcParamKeyIpv4Hint: {
+            nextRecord->mIpv4Hint.Construct();
+            nextRecord->mIpv4Hint.Value().mType = type;
+            nsCOMPtr<nsISVCParamIPv4Hint> ipv4Param = do_QueryInterface(value);
+            nsTArray<RefPtr<nsINetAddr>> ipv4Hint;
+            Unused << ipv4Param->GetIpv4Hint(ipv4Hint);
+            if (!ipv4Hint.IsEmpty()) {
+              nextRecord->mIpv4Hint.Value().mAddress.Construct();
+              for (const auto& address : ipv4Hint) {
+                nsString* nextAddress = nextRecord->mIpv4Hint.Value()
+                                            .mAddress.Value()
+                                            .AppendElement(fallible);
+                if (!nextAddress) {
+                  return NS_ERROR_OUT_OF_MEMORY;
+                }
+
+                nsCString addressASCII;
+                Unused << address->GetAddress(addressASCII);
+                CopyASCIItoUTF16(addressASCII, *nextAddress);
+              }
+            }
+            break;
+          }
+          case SvcParamKeyIpv6Hint: {
+            nextRecord->mIpv6Hint.Construct();
+            nextRecord->mIpv6Hint.Value().mType = type;
+            nsCOMPtr<nsISVCParamIPv6Hint> ipv6Param = do_QueryInterface(value);
+            nsTArray<RefPtr<nsINetAddr>> ipv6Hint;
+            Unused << ipv6Param->GetIpv6Hint(ipv6Hint);
+            if (!ipv6Hint.IsEmpty()) {
+              nextRecord->mIpv6Hint.Value().mAddress.Construct();
+              for (const auto& address : ipv6Hint) {
+                nsString* nextAddress = nextRecord->mIpv6Hint.Value()
+                                            .mAddress.Value()
+                                            .AppendElement(fallible);
+                if (!nextAddress) {
+                  return NS_ERROR_OUT_OF_MEMORY;
+                }
+
+                nsCString addressASCII;
+                Unused << address->GetAddress(addressASCII);
+                CopyASCIItoUTF16(addressASCII, *nextAddress);
+              }
+            }
+            break;
+          }
+          case SvcParamKeyEchConfig: {
+            nextRecord->mEchConfig.Construct();
+            nextRecord->mEchConfig.Value().mType = type;
+            nsCOMPtr<nsISVCParamEchConfig> echConfigParam =
+                do_QueryInterface(value);
+            nsCString echConfigStr;
+            Unused << echConfigParam->GetEchconfig(echConfigStr);
+            CopyASCIItoUTF16(echConfigStr,
+                             nextRecord->mEchConfig.Value().mEchConfig);
+            break;
+          }
+          default:
+            break;
+        }
+      }
+    }
+  } else {
+    dict.mAnswer = false;
+    GetErrorString(mStatus, dict.mError);
+  }
+
+  JS::RootedValue val(cx);
+  if (!ToJSValue(cx, dict, &val)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  this->mCallback->OnDashboardDataAvailable(val);
+
+  return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS(Dashboard, nsIDashboard, nsIDashboardEventNotifier)
 
 Dashboard::Dashboard() { mEnableLogging = false; }
 
 NS_IMETHODIMP
 Dashboard::RequestSockets(nsINetDashboardCallback* aCallback) {
   RefPtr<SocketData> socketData = new SocketData();
   socketData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
@@ -771,16 +920,39 @@ Dashboard::RequestDNSLookup(const nsACSt
   OriginAttributes attrs;
   rv = mDnsService->AsyncResolveNative(
       aHost, nsIDNSService::RESOLVE_TYPE_DEFAULT, 0, nullptr, helper.get(),
       NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
   return rv;
 }
 
 NS_IMETHODIMP
+Dashboard::RequestDNSHTTPSRRLookup(const nsACString& aHost,
+                                   nsINetDashboardCallback* aCallback) {
+  nsresult rv;
+
+  if (!mDnsService) {
+    mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  RefPtr<LookupHelper> helper = new LookupHelper();
+  helper->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
+      "nsINetDashboardCallback", aCallback, true);
+  helper->mEventTarget = GetCurrentEventTarget();
+  OriginAttributes attrs;
+  rv = mDnsService->AsyncResolveNative(
+      aHost, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 0, nullptr, helper.get(),
+      NS_GetCurrentThread(), attrs, getter_AddRefs(helper->mCancel));
+  return rv;
+}
+
+NS_IMETHODIMP
 Dashboard::RequestRcwnStats(nsINetDashboardCallback* aCallback) {
   RefPtr<RcwnData> rcwnData = new RcwnData();
   rcwnData->mEventTarget = GetCurrentEventTarget();
   rcwnData->mCallback = new nsMainThreadPtrHolder<nsINetDashboardCallback>(
       "nsINetDashboardCallback", aCallback, true);
 
   return rcwnData->mEventTarget->Dispatch(
       NewRunnableMethod<RefPtr<RcwnData>>("net::Dashboard::GetRcwnData", this,
--- a/netwerk/base/nsIDashboard.idl
+++ b/netwerk/base/nsIDashboard.idl
@@ -45,15 +45,20 @@ interface nsIDashboard : nsISupports
 
     /* When true, the service will log websocket events */
     attribute boolean enableLogging;
 
     /* DNS resolver for host name
      * aHost: host name */
     void requestDNSLookup(in ACString aHost, in nsINetDashboardCallback cb);
 
+    /* Resolve HTTPS RRs for host name
+     * aHost: host name */
+    void requestDNSHTTPSRRLookup(in ACString aHost,
+                                 in nsINetDashboardCallback cb);
+
     /**
      * Asyncly returns stats regarding the "Race Cache With Network" feature.
      */
     void requestRcwnStats(in nsINetDashboardCallback cb);
 
     AUTF8String getLogPath();
 };
--- a/toolkit/content/aboutNetworking.html
+++ b/toolkit/content/aboutNetworking.html
@@ -143,16 +143,25 @@
               <table>
                   <thead>
                       <tr>
                           <th data-l10n-id="about-networking-dns-lookup-table-column"/>
                       </tr>
                   </thead>
                   <tbody id="dnslookuptool_content" />
               </table>
+              <hr/>
+              <table>
+                  <thead>
+                      <tr>
+                          <th data-l10n-id="about-networking-dns-https-rr-lookup-table-column"/>
+                      </tr>
+                  </thead>
+                  <tbody id="https_rr_content" />
+              </table>
           </div>
 
           <div id="rcwn" class="tab" hidden="true">
               <table>
                   <thead>
                       <tr>
                           <th data-l10n-id="about-networking-rcwn-status"/>
                           <th data-l10n-id="about-networking-total-network-requests"/>
--- a/toolkit/content/aboutNetworking.js
+++ b/toolkit/content/aboutNetworking.js
@@ -506,17 +506,22 @@ function setAutoRefreshInterval(checkBox
 // Mostly the issue is with the autorefresh checkbox.
 window.addEventListener("pageshow", function() {
   init();
 });
 
 function doLookup() {
   let host = document.getElementById("host").value;
   if (host) {
-    gDashboard.requestDNSLookup(host, displayDNSLookup);
+    try {
+      gDashboard.requestDNSLookup(host, displayDNSLookup);
+    } catch (e) {}
+    try {
+      gDashboard.requestDNSHTTPSRRLookup(host, displayHTTPSRRLookup);
+    } catch (e) {}
   }
 }
 
 function displayDNSLookup(data) {
   let cont = document.getElementById("dnslookuptool_content");
   let parent = cont.parentNode;
   let new_cont = document.createElement("tbody");
   new_cont.setAttribute("id", "dnslookuptool_content");
@@ -528,8 +533,57 @@ function displayDNSLookup(data) {
       new_cont.appendChild(row);
     }
   } else {
     new_cont.appendChild(col(data.error));
   }
 
   parent.replaceChild(new_cont, cont);
 }
+
+function displayHTTPSRRLookup(data) {
+  let cont = document.getElementById("https_rr_content");
+  let parent = cont.parentNode;
+  let new_cont = document.createElement("tbody");
+  new_cont.setAttribute("id", "https_rr_content");
+
+  if (data.answer) {
+    for (let record of data.records) {
+      let row = document.createElement("tr");
+      let alpn = record.alpn ? `alpn="${record.alpn.alpn}" ` : "";
+      let noDefaultAlpn = record.noDefaultAlpn ? "noDefaultAlpn " : "";
+      let port = record.port ? `port="${record.port.port}" ` : "";
+      let echconfig = record.echconfig
+        ? `echconfig="${record.echconfig.echconfig}" `
+        : "";
+      let ipv4hint = "";
+      let ipv6hint = "";
+      if (record.ipv4Hint) {
+        let ipv4Str = "";
+        for (let addr of record.ipv4Hint.address) {
+          ipv4Str += `${addr}, `;
+        }
+        // Remove ", " at the end.
+        ipv4Str = ipv4Str.slice(0, -2);
+        ipv4hint = `ipv4hint="${ipv4Str}" `;
+      }
+      if (record.ipv6Hint) {
+        let ipv6Str = "";
+        for (let addr of record.ipv6Hint.address) {
+          ipv6Str += `${addr}, `;
+        }
+        // Remove ", " at the end.
+        ipv6Str = ipv6Str.slice(0, -2);
+        ipv6hint = `ipv6hint="${ipv6Str}" `;
+      }
+
+      let str = `${record.priority} ${record.name} `;
+      str += `(${alpn}${noDefaultAlpn}${port}`;
+      str += `${ipv4hint}${echconfig}${ipv6hint})`;
+      row.appendChild(col(str));
+      new_cont.appendChild(row);
+    }
+  } else {
+    new_cont.appendChild(col(data.error));
+  }
+
+  parent.replaceChild(new_cont, cont);
+}
--- a/toolkit/locales/en-US/toolkit/about/aboutNetworking.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutNetworking.ftl
@@ -40,16 +40,17 @@ about-networking-current-log-modules = C
 about-networking-set-log-file = Set Log File
 about-networking-set-log-modules = Set Log Modules
 about-networking-start-logging = Start Logging
 about-networking-stop-logging = Stop Logging
 about-networking-dns-lookup = DNS Lookup
 about-networking-dns-lookup-button = Resolve
 about-networking-dns-domain = Domain:
 about-networking-dns-lookup-table-column = IPs
+about-networking-dns-https-rr-lookup-table-column = HTTP RRs
 about-networking-rcwn = RCWN Stats
 about-networking-rcwn-status = RCWN Status
 about-networking-rcwn-cache-won-count = Cache won count
 about-networking-rcwn-net-won-count = Net won count
 about-networking-total-network-requests = Total network request count
 about-networking-rcwn-operation = Cache Operation
 about-networking-rcwn-perf-open = Open
 about-networking-rcwn-perf-read = Read