Bug 1507139 - Fix h2 push for esni r=bagder
authorDragana Damjanovic <dd.mozilla@gmail.com>
Thu, 15 Nov 2018 13:10:54 +0000
changeset 503053 7155e69bc42c3613901dd356371e5529bf85e855
parent 503052 56f220f7a98f9ca941912ed1b9e69518f49c0d79
child 503054 80ebeb322e14528b47e152cd306795745b4d6b5b
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder
bugs1507139
milestone65.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 1507139 - Fix h2 push for esni r=bagder Differential Revision: https://phabricator.services.mozilla.com/D11881
netwerk/dns/TRR.cpp
netwerk/test/unit/test_esni_dns_fetch.js
testing/xpcshell/moz-http2/moz-http2.js
--- a/netwerk/dns/TRR.cpp
+++ b/netwerk/dns/TRR.cpp
@@ -426,20 +426,25 @@ TRR::ReceivePush(nsIHttpChannel *pushed,
 
   PRNetAddr tempAddr;
   if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
       (PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // literal
     LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
     return NS_ERROR_UNEXPECTED;
   }
 
+  if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && (mType != TRRTYPE_TXT)) {
+    LOG(("TRR::ReceivePush unknown type %d\n", mType));
+    return NS_ERROR_UNEXPECTED;
+  }
+
   RefPtr<nsHostRecord> hostRecord;
   nsresult rv;
   rv = mHostResolver->GetHostRecord(mHost,
-                                    pushedRec->type,
+                                    (mType != TRRTYPE_TXT) ? 0 : nsIDNSService::RESOLVE_TYPE_TXT,
                                     pushedRec->flags, pushedRec->af,
                                     pushedRec->pb,
                                     pushedRec->originSuffix,
                                     getter_AddRefs(hostRecord));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
--- a/netwerk/test/unit/test_esni_dns_fetch.js
+++ b/netwerk/test/unit/test_esni_dns_fetch.js
@@ -15,42 +15,38 @@ function run_test() {
   h2Port = env.get("MOZHTTP2_PORT");
   Assert.notEqual(h2Port, null);
   Assert.notEqual(h2Port, "");
 
   // Set to allow the cert presented by our H2 server
   do_get_profile();
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
 
-  prefs.setBoolPref("network.security.esni.enabled", true);
+  prefs.setBoolPref("network.security.esni.enabled", false);
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2", true);
   // the TRR server is on 127.0.0.1
   prefs.setCharPref("network.trr.bootstrapAddress", "127.0.0.1");
 
-  // use the h2 server as DOH provider
-  prefs.setCharPref("network.trr.uri", "https://foo.example.com:" + h2Port + "/esni-dns");
   // make all native resolve calls "secretly" resolve localhost instead
   prefs.setBoolPref("network.dns.native-is-localhost", true);
 
   // 0 - off, 1 - race, 2 TRR first, 3 TRR only, 4 shadow
   prefs.setIntPref("network.trr.mode", 2); // TRR first
   prefs.setBoolPref("network.trr.wait-for-portal", false);
   // don't confirm that TRR is working, just go!
   prefs.setCharPref("network.trr.confirmationNS", "skip");
 
   // The moz-http2 cert is for foo.example.com and is signed by CA.cert.der
   // so add that cert to the trust list as a signing cert.  // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
       .getService(Ci.nsIX509CertDB);
   addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
   do_test_pending();
-
-
-  listen = dns.asyncResolveByType("_esni.example.com", dns.RESOLVE_TYPE_TXT, 0, listenerFine, mainThread, defaultOriginAttributes);
+  run_dns_tests();
 }
 
 registerCleanupFunction(() => {
   prefs.clearUserPref("network.security.esni.enabled");
   prefs.clearUserPref("network.http.spdy.enabled");
   prefs.clearUserPref("network.http.spdy.enabled.http2");
   prefs.clearUserPref("network.dns.localDomains");
   prefs.clearUserPref("network.dns.native-is-localhost");
@@ -78,28 +74,96 @@ function readFile(file) {
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
   let der = readFile(certFile);
   certdb.addCert(der, trustString);
 }
 
 var test_answer="bXkgdm9pY2UgaXMgbXkgcGFzc3dvcmQ=";
+var test_answer_addr="127.0.0.1";
 
 // check that we do lookup by type fine
-var listenerFine = {
+var listenerEsni = {
   onLookupByTypeComplete: function(inRequest, inRecord, inStatus) {
     if (inRequest == listen) {
       Assert.ok(!inStatus);
       var answer = inRecord.getRecordsAsOneString();
       Assert.equal(answer, test_answer);
       do_test_finished();
+      run_dns_tests();
     }
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsIDNSListener) ||
-        aIID.equals(Ci.nsISupports)) {
+      aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+// check that we do lookup for A record is fine
+var listenerAddr = {
+  onLookupComplete: function(inRequest, inRecord, inStatus) {
+    if (inRequest == listen) {
+      Assert.ok(!inStatus);
+      var answer = inRecord.getNextAddrAsString();
+      Assert.equal(answer, test_answer_addr);
+      do_test_finished();
+      run_dns_tests();
+    }
+  },
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.nsIDNSListener) ||
+      aIID.equals(Ci.nsISupports)) {
       return this;
     }
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 };
 
+function testEsniRequest()
+{
+  // use the h2 server as DOH provider
+  prefs.setCharPref("network.trr.uri", "https://foo.example.com:" + h2Port + "/esni-dns");
+  listen = dns.asyncResolveByType("_esni.example.com", dns.RESOLVE_TYPE_TXT, 0, listenerEsni, mainThread, defaultOriginAttributes);
+}
+
+// verify esni record pushed on a A record request
+function testEsniPushPart1()
+{
+  prefs.setCharPref("network.trr.uri", "https://foo.example.com:" + h2Port + "/esni-dns-push");
+  listen = dns.asyncResolve("_esni_push.example.com", 0, listenerAddr, mainThread, defaultOriginAttributes);
+}
+
+// verify the esni pushed record
+function testEsniPushPart2()
+{
+  // At this point the second host name should've been pushed and we can resolve it using
+  // cache only. Set back the URI to a path that fails.
+  prefs.setCharPref("network.trr.uri", "https://foo.example.com:" + h2Port + "/404");
+  listen = dns.asyncResolveByType("_esni_push.example.com", dns.RESOLVE_TYPE_TXT, 0, listenerEsni, mainThread, defaultOriginAttributes);
+}
+
+function testsDone()
+{
+  do_test_finished();
+  do_test_finished();
+}
+
+var tests = [testEsniRequest,
+             testEsniPushPart1,
+             testEsniPushPart2,
+             testsDone
+            ];
+var current_test = 0;
+
+function run_dns_tests()
+{
+  if (current_test < tests.length) {
+    dump("starting test " + current_test + "\n");
+    do_test_pending();
+    tests[current_test++]();
+  }
+}
+
+
--- a/testing/xpcshell/moz-http2/moz-http2.js
+++ b/testing/xpcshell/moz-http2/moz-http2.js
@@ -760,17 +760,17 @@ function handleRequest(req, res) {
 
   // for use with test_esni_dns_fetch.js
   else if (u.pathname === "/esni-dns") {
     content = new Buffer("0000" +
                          "8180" +
                          "0001" + // QDCOUNT
                          "0001" + // ANCOUNT
                          "00000000" + // NSCOUNT + ARCOUNT
-                         "055F65736E69076578616D706C6503636F6D00" + // esni.example.com
+                         "055F65736E69076578616D706C6503636F6D00" + // _esni.example.com
                          "00100001" + // question type (TXT) + question class (IN)
 
                          "C00C" + // name pointer to .example.com
                          "0010" + // type (TXT)
                          "0001" + // class
                          "00000037" + // TTL
                          "0021" + // RDLENGTH
                          "2062586B67646D39705932556761584D6762586B676347467A63336476636D513D", // esni keys.
@@ -779,16 +779,48 @@ function handleRequest(req, res) {
     res.setHeader('Content-Type', 'application/dns-message');
     res.setHeader('Content-Length', content.length);
     res.writeHead(200);
     res.write(content);
     res.end("");
     return;
   }
 
+  // for use with test_esni_dns_fetch.js
+  else if (u.pathname === "/esni-dns-push") {
+    // _esni_push.example.com has A entry 127.0.0.1
+    var content= new Buffer("0000010000010001000000000A5F65736E695F70757368076578616D706C6503636F6D0000010001C00C000100010000003700047F000001", "hex");
+
+    // _esni_push.example.com has TXT entry 2062586B67646D39705932556761584D6762586B676347467A63336476636D513D
+    var pcontent= new Buffer("0000818000010001000000000A5F65736E695F70757368076578616D706C6503636F6D0000100001C00C001000010000003700212062586B67646D39705932556761584D6762586B676347467A63336476636D513D", "hex");
+
+    push = res.push({
+      hostname: 'foo.example.com:' + serverPort,
+      port: serverPort,
+      path: '/dns-pushed-response?dns=AAABAAABAAAAAAABCl9lc25pX3B1c2gHZXhhbXBsZQNjb20AABAAAQAAKRAAAAAAAAAIAAgABAABAAA',
+      method: 'GET',
+      headers: {
+        'accept' : 'application/dns-message'
+      }
+    });
+    push.writeHead(200, {
+      'content-type': 'application/dns-message',
+      'pushed' : 'yes',
+      'content-length' : pcontent.length,
+      'X-Connection-Http2': 'yes'
+    });
+    push.end(pcontent);
+    res.setHeader('Content-Type', 'application/dns-message');
+    res.setHeader('Content-Length', content.length);
+    res.writeHead(200);
+    res.write(content);
+    res.end("");
+    return;
+  }
+
   else if (u.pathname === "/.well-known/http-opportunistic") {
     res.setHeader('Cache-Control', 'no-cache');
     res.setHeader('Content-Type', 'application/json');
     res.writeHead(200, "OK");
     res.end('{"http://' + req.headers['host'] + '": { "tls-ports": [' + serverPort + '] }}');
     return;
   }