Bug 909152: Factor out generic logic from OCSP stapling tests to make it reusable, r=keeler, r=cviecco, r=gps
authorBrian Smith <brian@briansmith.org>
Wed, 04 Sep 2013 23:55:28 -0700
changeset 146747 ca94c896778d5d4585006c24300120ae676c4c62
parent 146746 3aa2a28398e297fcc446119d603263a89500a135
child 146748 e5e75d914d5bcd0b9c021a6851fa75f4aa6c806c
push id25270
push useremorley@mozilla.com
push dateThu, 12 Sep 2013 11:04:52 +0000
treeherdermozilla-central@b83f6d80af5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, cviecco, gps
bugs909152
milestone26.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 909152: Factor out generic logic from OCSP stapling tests to make it reusable, r=keeler, r=cviecco, r=gps
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/moz.build
security/manager/ssl/tests/unit/test_ocsp_stapling.js
security/manager/ssl/tests/unit/test_ocsp_stapling/Makefile.in
security/manager/ssl/tests/unit/test_ocsp_stapling/OCSPStaplingServer.cpp
security/manager/ssl/tests/unit/test_ocsp_stapling/cert8.db
security/manager/ssl/tests/unit/test_ocsp_stapling/gen_ocsp_certs.sh
security/manager/ssl/tests/unit/test_ocsp_stapling/key3.db
security/manager/ssl/tests/unit/test_ocsp_stapling/moz.build
security/manager/ssl/tests/unit/test_ocsp_stapling/ocsp-ca.der
security/manager/ssl/tests/unit/test_ocsp_stapling/ocsp-other-ca.der
security/manager/ssl/tests/unit/test_ocsp_stapling/secmod.db
security/manager/ssl/tests/unit/tlsserver/cert8.db
security/manager/ssl/tests/unit/tlsserver/cmd/Makefile.in
security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
security/manager/ssl/tests/unit/tlsserver/key3.db
security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
security/manager/ssl/tests/unit/tlsserver/lib/moz.build
security/manager/ssl/tests/unit/tlsserver/moz.build
security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
security/manager/ssl/tests/unit/tlsserver/secmod.db
security/manager/ssl/tests/unit/tlsserver/test-ca.der
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -4,16 +4,19 @@
  */
 "use strict";
 
 const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
 
 let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {});
+let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm");
 
 let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 function readFile(file) {
   let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
                   .createInstance(Ci.nsIFileInputStream);
   fstream.init(file, -1, 0, 0);
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
@@ -28,8 +31,242 @@ function addCertFromFile(certdb, filenam
 }
 
 function getXPCOMStatusFromNSS(offset) {
   let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"]
                            .getService(Ci.nsINSSErrorsService);
   let statusNSS = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE + offset;
   return nssErrorsService.getXPCOMFromNSSError(statusNSS);
 }
+
+function clearOCSPCache() {
+  // Open the NSS library. copied from services/crypto/modules/WeaveCrypto.js
+  let path = ctypes.libraryName("nss3");
+
+  // XXX really want to be able to pass specific dlopen flags here.
+  let nsslib;
+  try {
+    nsslib = ctypes.open(path);
+  } catch(e) {
+    // In case opening the library without a full path fails,
+    // try again with a full path.
+    let directoryService = Cc["@mozilla.org/file/directory_service;1"]
+                             .getService(Ci.nsIProperties);
+    let greDir = directoryService.get("GreD", Ci.nsILocalFile);
+    file.append(path);
+    nsslib = ctypes.open(file.path);
+  }
+
+  let SECStatus = ctypes.int;
+  let CERT_ClearOCSPCache = nsslib.declare("CERT_ClearOCSPCache",
+                                           ctypes.default_abi, SECStatus);
+  if (CERT_ClearOCSPCache() != 0) {
+    throw "Failed to clear OCSP cache";
+  }
+}
+
+
+// Set up a TLS testing environment that has a TLS server running and
+// ready to accept connections. This async function starts the server and
+// waits for the server to indicate that it is ready.
+//
+// Each test should have its own subdomain of example.com, for example
+// my-first-connection-test.example.com. The server can use the server
+// name (passed through the SNI TLS extension) to determine what behavior
+// the server side of the text should exhibit. See TLSServer.h for more
+// information on how to write the server side of tests.
+//
+// Create a new source file for your new server executable in
+// security/manager/ssl/tests/unit/tlsserver/cmd similar to the other ones in
+// that directory, and add a reference to it to the sources variable in that
+// directory's moz.build.
+//
+// Modify TEST_HARNESS_BINS in
+// testing/mochitest/Makefile.in and NO_PKG_FILES in
+// toolkit/mozapps/installer/packager.mk to make sure the new executable
+// gets included in the packages used for shipping the tests to the test
+// runners in our build/test farm. (Things will work fine locally without
+// these changes but will break on TBPL.)
+//
+// Your test script should look something like this:
+/*
+
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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/.
+"use strict";
+
+// <documentation on your test>
+
+function run_test() {
+  do_get_profile();
+  add_tls_server_setup("<test-server-name>");
+
+  add_connection_test("<test-name-1>.example.com", Cr.<expected result>,
+                      <ocsp stapling enabled>);
+  [...]
+  add_connection_test("<test-name-n>.example.com", Cr.<expected result>,
+                      <ocsp stapling enabled>);
+
+  run_next_test();
+}
+
+*/
+function add_tls_server_setup(serverBinName) {
+  add_test(function() {
+    _setupTLSServerTest(serverBinName);
+  });
+}
+
+// Add a TLS connection test case. aHost is the hostname to pass in the SNI TLS
+// extension; this should unambiguously identifiy which test is being run.
+// aExpectedResult is the expected nsresult of the connection.
+// aBeforeConnect is a callback function that takes no arguments that will be
+// called before the connection is attempted.
+// aWithSecurityInfo is a callback function that takes an
+// nsITransportSecurityInfo, which is called after the TLS handshake succeeds.
+function add_connection_test(aHost, aExpectedResult,
+                             aBeforeConnect, aWithSecurityInfo) {
+  const REMOTE_PORT = 8443;
+
+  function Connection(aHost) {
+    this.host = aHost;
+    let threadManager = Cc["@mozilla.org/thread-manager;1"]
+                          .getService(Ci.nsIThreadManager);
+    this.thread = threadManager.currentThread;
+    this.defer = Promise.defer();
+    let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
+                .getService(Ci.nsISocketTransportService);
+    this.transport = sts.createTransport(["ssl"], 1, aHost, REMOTE_PORT, null);
+    this.transport.setEventSink(this, this.thread);
+    this.inputStream = null;
+    this.outputStream = null;
+    this.connected = false;
+  }
+
+  Connection.prototype = {
+    // nsITransportEventSink
+    onTransportStatus: function(aTransport, aStatus, aProgress, aProgressMax) {
+      if (!this.connected && aStatus == Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
+        this.connected = true;
+        this.outputStream.asyncWait(this, 0, 0, this.thread);
+      }
+    },
+
+    // nsIInputStreamCallback
+    onInputStreamReady: function(aStream) {
+      try {
+        // this will throw if the stream has been closed by an error
+        let str = NetUtil.readInputStreamToString(aStream, aStream.available());
+        do_check_eq(str, "0");
+        this.inputStream.close();
+        this.outputStream.close();
+        this.result = Cr.NS_OK;
+      } catch (e) {
+        this.result = e.result;
+      }
+      this.defer.resolve(this);
+    },
+
+    // nsIOutputStreamCallback
+    onOutputStreamReady: function(aStream) {
+      let sslSocketControl = this.transport.securityInfo
+                               .QueryInterface(Ci.nsISSLSocketControl);
+      sslSocketControl.proxyStartSSL();
+      this.outputStream.write("0", 1);
+      let inStream = this.transport.openInputStream(0, 0, 0)
+                       .QueryInterface(Ci.nsIAsyncInputStream);
+      this.inputStream = inStream;
+      this.inputStream.asyncWait(this, 0, 0, this.thread);
+    },
+
+    go: function() {
+      this.outputStream = this.transport.openOutputStream(0, 0, 0)
+                            .QueryInterface(Ci.nsIAsyncOutputStream);
+      return this.defer.promise;
+    }
+  };
+
+  /* Returns a promise to connect to aHost that resolves to the result of that
+   * connection */
+  function connectTo(aHost) {
+    Services.prefs.setCharPref("network.dns.localDomains", aHost);
+    let connection = new Connection(aHost);
+    return connection.go();
+  }
+
+  add_test(function() {
+    if (aBeforeConnect) {
+      aBeforeConnect();
+    }
+    connectTo(aHost).then(function(conn) {
+      dump("hello #0\n");
+      do_check_eq(conn.result, aExpectedResult);
+      dump("hello #0.5\n");
+      if (aWithSecurityInfo) {
+        dump("hello #1\n");
+        aWithSecurityInfo(conn.transport.securityInfo
+                              .QueryInterface(Ci.nsITransportSecurityInfo));
+        dump("hello #2\n");
+      }
+      run_next_test();
+    });
+  });
+}
+
+// Do not call this directly; use add_tls_server_setup
+function _setupTLSServerTest(serverBinName)
+{
+  let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  // The trusted CA that is typically used for "good" certificates.
+  addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u");
+
+  const CALLBACK_PORT = 8444;
+
+  let directoryService = Cc["@mozilla.org/file/directory_service;1"]
+                           .getService(Ci.nsIProperties);
+  let envSvc = Cc["@mozilla.org/process/environment;1"]
+                 .getService(Ci.nsIEnvironment);
+  let greDir = directoryService.get("GreD", Ci.nsIFile);
+  envSvc.set("DYLD_LIBRARY_PATH", greDir.path);
+  envSvc.set("LD_LIBRARY_PATH", greDir.path);
+  envSvc.set("MOZ_TLS_SERVER_DEBUG_LEVEL", "3");
+  envSvc.set("MOZ_TLS_SERVER_CALLBACK_PORT", CALLBACK_PORT);
+
+  let httpServer = new HttpServer();
+  httpServer.registerPathHandler("/",
+      function handleServerCallback(aRequest, aResponse) {
+        aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+        aResponse.setHeader("Content-Type", "text/plain");
+        let responseBody = "OK!";
+        aResponse.bodyOutputStream.write(responseBody, responseBody.length);
+        do_execute_soon(function() {
+          httpServer.stop(run_next_test);
+        });
+      });
+  httpServer.start(CALLBACK_PORT);
+
+  let serverBin = directoryService.get("CurProcD", Ci.nsILocalFile);
+  serverBin.append(serverBinName + (gIsWindows ? ".exe" : ""));
+  // If we're testing locally, the above works. If not, the server executable
+  // is in another location.
+  if (!serverBin.exists()) {
+    serverBin = directoryService.get("CurWorkD", Ci.nsILocalFile);
+    while (serverBin.path.indexOf("xpcshell") != -1) {
+      serverBin = serverBin.parent;
+    }
+    serverBin.append("bin");
+    serverBin.append(serverBinName + (gIsWindows ? ".exe" : ""));
+  }
+  do_check_true(serverBin.exists());
+  let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+  process.init(serverBin);
+  let certDir = directoryService.get("CurWorkD", Ci.nsILocalFile);
+  certDir.append("tlsserver");
+  do_check_true(certDir.exists());
+  process.run(false, [certDir.path], 1);
+
+  do_register_cleanup(function() {
+    process.kill();
+  });
+}
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -1,10 +1,10 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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/.
 
-DIRS += ['test_ocsp_stapling']
+DIRS += ['tlsserver']
 
 MODULE = 'pipnss'
 
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -1,212 +1,86 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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/. */
-
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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/.
 "use strict";
 
 // In which we connect to a number of domains (as faked by a server running
 // locally) with and without OCSP stapling enabled to determine that good
 // things happen and bad things don't.
 
-let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
-let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {});
-
-let gOCSPServerProcess = null;
-let gHttpServer = null;
-
-const REMOTE_PORT = 8443;
-const CALLBACK_PORT = 8444;
-
-function Connection(aHost) {
-  this.host = aHost;
-  let threadManager = Cc["@mozilla.org/thread-manager;1"]
-                        .getService(Ci.nsIThreadManager);
-  this.thread = threadManager.currentThread;
-  this.defer = Promise.defer();
-  let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
-              .getService(Ci.nsISocketTransportService);
-  this.transport = sts.createTransport(["ssl"], 1, aHost, REMOTE_PORT, null);
-  this.transport.setEventSink(this, this.thread);
-  this.inputStream = null;
-  this.outputStream = null;
-  this.connected = false;
-}
-
-Connection.prototype = {
-  // nsITransportEventSink
-  onTransportStatus: function(aTransport, aStatus, aProgress, aProgressMax) {
-    if (!this.connected && aStatus == Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
-      this.connected = true;
-      this.outputStream.asyncWait(this, 0, 0, this.thread);
-    }
-  },
-
-  // nsIInputStreamCallback
-  onInputStreamReady: function(aStream) {
-    try {
-      // this will throw if the stream has been closed by an error
-      let str = NetUtil.readInputStreamToString(aStream, aStream.available());
-      do_check_eq(str, "0");
-      this.inputStream.close();
-      this.inputStream = null;
-      this.outputStream.close();
-      this.outputStream = null;
-      this.transport = null;
-      this.defer.resolve(Cr.NS_OK);
-    } catch (e) {
-      this.defer.resolve(e.result);
-    }
-  },
-
-  // nsIOutputStreamCallback
-  onOutputStreamReady: function(aStream) {
-    let sslSocketControl = this.transport.securityInfo
-                             .QueryInterface(Ci.nsISSLSocketControl);
-    sslSocketControl.proxyStartSSL();
-    this.outputStream.write("0", 1);
-    let inStream = this.transport.openInputStream(0, 0, 0)
-                     .QueryInterface(Ci.nsIAsyncInputStream);
-    this.inputStream = inStream;
-    this.inputStream.asyncWait(this, 0, 0, this.thread);
-  },
-
-  go: function() {
-    this.outputStream = this.transport.openOutputStream(0, 0, 0)
-                          .QueryInterface(Ci.nsIAsyncOutputStream);
-    return this.defer.promise;
-  }
-};
-
-/* Returns a promise to connect to aHost that resolves to the result of that
- * connection */
-function connectTo(aHost) {
-  Services.prefs.setCharPref("network.dns.localDomains", aHost);
-  let connection = new Connection(aHost);
-  return connection.go();
-}
-
-function add_connection_test(aHost, aExpectedResult, aStaplingEnabled) {
-  add_test(function() {
-    Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling",
-                               aStaplingEnabled);
-    do_test_pending();
-    connectTo(aHost).then(function(aResult) {
-      do_check_eq(aResult, aExpectedResult);
-      do_test_finished();
-      run_next_test();
+function add_ocsp_test(aHost, aExpectedResult, aStaplingEnabled) {
+  add_connection_test(aHost, aExpectedResult,
+    function() {
+      clearOCSPCache();
+      Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling",
+                                 aStaplingEnabled);
     });
-  });
-}
-
-function cleanup() {
-  gOCSPServerProcess.kill();
 }
 
 function run_test() {
   do_get_profile();
-  let certdb = Cc["@mozilla.org/security/x509certdb;1"]
-                 .getService(Ci.nsIX509CertDB);
-  addCertFromFile(certdb, "test_ocsp_stapling/ocsp-ca.der", "CTu,u,u");
 
-  let directoryService = Cc["@mozilla.org/file/directory_service;1"]
-                           .getService(Ci.nsIProperties);
-  let envSvc = Cc["@mozilla.org/process/environment;1"]
-                 .getService(Ci.nsIEnvironment);
-  let greDir = directoryService.get("GreD", Ci.nsIFile);
-  envSvc.set("DYLD_LIBRARY_PATH", greDir.path);
-  envSvc.set("LD_LIBRARY_PATH", greDir.path);
-  envSvc.set("OCSP_SERVER_DEBUG_LEVEL", "3");
-  envSvc.set("OCSP_SERVER_CALLBACK_PORT", CALLBACK_PORT);
-
-  gHttpServer = new HttpServer();
-  gHttpServer.registerPathHandler("/", handleServerCallback);
-  gHttpServer.start(CALLBACK_PORT);
+  add_tls_server_setup("OCSPStaplingServer");
 
-  let serverBin = directoryService.get("CurProcD", Ci.nsILocalFile);
-  serverBin.append("OCSPStaplingServer" + (gIsWindows ? ".exe" : ""));
-  // If we're testing locally, the above works. If not, the server executable
-  // is in another location.
-  if (!serverBin.exists()) {
-    serverBin = directoryService.get("CurWorkD", Ci.nsILocalFile);
-    while (serverBin.path.indexOf("xpcshell") != -1) {
-      serverBin = serverBin.parent;
-    }
-    serverBin.append("bin");
-    serverBin.append("OCSPStaplingServer" + (gIsWindows ? ".exe" : ""));
-  }
-  do_check_true(serverBin.exists());
-  gOCSPServerProcess = Cc["@mozilla.org/process/util;1"]
-                     .createInstance(Ci.nsIProcess);
-  gOCSPServerProcess.init(serverBin);
-  let ocspCertDir = directoryService.get("CurWorkD", Ci.nsILocalFile);
-  ocspCertDir.append("test_ocsp_stapling");
-  do_check_true(ocspCertDir.exists());
-  gOCSPServerProcess.run(false, [ocspCertDir.path], 1);
+  // In the absence of OCSP stapling, these should actually all work.
+  add_ocsp_test("ocsp-stapling-good.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-revoked.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-good-other-ca.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-malformed.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-srverr.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-trylater.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-needssig.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-unauthorized.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-unknown.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-good-other.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-none.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK, false);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK, false);
 
-  do_register_cleanup(cleanup);
-  do_test_pending();
-}
-
-function handleServerCallback(aRequest, aResponse) {
-  aResponse.write("OK!");
-  aResponse.seizePower();
-  aResponse.finish();
-  gHttpServer.stop(function() {});
-  run_test_body();
-}
-
-function run_test_body() {
-  // In the absence of OCSP stapling, these should actually all work.
-  add_connection_test("ocsp-stapling-good.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-revoked.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-good-other-ca.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-malformed.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-srverr.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-trylater.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-needssig.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-unauthorized.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-unknown.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-good-other.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-none.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-expired.example.com", Cr.NS_OK, false);
-  add_connection_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK, false);
   // Now test OCSP stapling
   // The following error codes are defined in security/nss/lib/util/SECerrs.h
-  add_connection_test("ocsp-stapling-good.example.com", Cr.NS_OK, true);
+
+  add_ocsp_test("ocsp-stapling-good.example.com", Cr.NS_OK, true);
+
   // SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12
-  add_connection_test("ocsp-stapling-revoked.example.com", getXPCOMStatusFromNSS(12), true);
+  add_ocsp_test("ocsp-stapling-revoked.example.com", getXPCOMStatusFromNSS(12), true);
+
   // This stapled response is from a CA that is untrusted and did not issue
   // the server's certificate.
   // SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18
-  add_connection_test("ocsp-stapling-good-other-ca.example.com", getXPCOMStatusFromNSS(18), true);
-  // Now add that CA to the trusted database. It still should not be able
-  // to sign for the ocsp response.
+  add_ocsp_test("ocsp-stapling-good-other-ca.example.com", getXPCOMStatusFromNSS(18), true);
+
+  // SEC_ERROR_BAD_DATABASE vs SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE depends on
+  // whether the CA that signed the response is a trusted CA.
   add_test(function() {
     let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                    .getService(Ci.nsIX509CertDB);
-    addCertFromFile(certdb, "test_ocsp_stapling/ocsp-other-ca.der", "CTu,u,u");
+    // Another trusted CA that shouldn't be trusted for OCSP responses, etc.
+    // for the "good" CA.
+    addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
     run_next_test();
   });
+
   // SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = (SEC_ERROR_BASE + 130)
-  add_connection_test("ocsp-stapling-good-other-ca.example.com", getXPCOMStatusFromNSS(130), true);
+  add_ocsp_test("ocsp-stapling-good-other-ca.example.com", getXPCOMStatusFromNSS(130), true);
   // SEC_ERROR_OCSP_MALFORMED_REQUEST = (SEC_ERROR_BASE + 120)
-  add_connection_test("ocsp-stapling-malformed.example.com", getXPCOMStatusFromNSS(120), true);
+  add_ocsp_test("ocsp-stapling-malformed.example.com", getXPCOMStatusFromNSS(120), true);
   // SEC_ERROR_OCSP_SERVER_ERROR = (SEC_ERROR_BASE + 121)
-  add_connection_test("ocsp-stapling-srverr.example.com", getXPCOMStatusFromNSS(121), true);
+  add_ocsp_test("ocsp-stapling-srverr.example.com", getXPCOMStatusFromNSS(121), true);
   // SEC_ERROR_OCSP_TRY_SERVER_LATER = (SEC_ERROR_BASE + 122)
-  add_connection_test("ocsp-stapling-trylater.example.com", getXPCOMStatusFromNSS(122), true);
+  add_ocsp_test("ocsp-stapling-trylater.example.com", getXPCOMStatusFromNSS(122), true);
   // SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = (SEC_ERROR_BASE + 123)
-  add_connection_test("ocsp-stapling-needssig.example.com", getXPCOMStatusFromNSS(123), true);
+  add_ocsp_test("ocsp-stapling-needssig.example.com", getXPCOMStatusFromNSS(123), true);
   // SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = (SEC_ERROR_BASE + 124)
-  add_connection_test("ocsp-stapling-unauthorized.example.com", getXPCOMStatusFromNSS(124), true);
+  add_ocsp_test("ocsp-stapling-unauthorized.example.com", getXPCOMStatusFromNSS(124), true);
   // SEC_ERROR_OCSP_UNKNOWN_CERT = (SEC_ERROR_BASE + 126)
-  add_connection_test("ocsp-stapling-unknown.example.com", getXPCOMStatusFromNSS(126), true);
-  add_connection_test("ocsp-stapling-good-other.example.com", getXPCOMStatusFromNSS(126), true);
+  add_ocsp_test("ocsp-stapling-unknown.example.com", getXPCOMStatusFromNSS(126), true);
+  add_ocsp_test("ocsp-stapling-good-other.example.com", getXPCOMStatusFromNSS(126), true);
   // SEC_ERROR_OCSP_MALFORMED_RESPONSE = (SEC_ERROR_BASE + 129)
-  add_connection_test("ocsp-stapling-none.example.com", getXPCOMStatusFromNSS(129), true);
+  add_ocsp_test("ocsp-stapling-none.example.com", getXPCOMStatusFromNSS(129), true);
   // SEC_ERROR_OCSP_OLD_RESPONSE = (SEC_ERROR_BASE + 132)
-  add_connection_test("ocsp-stapling-expired.example.com", getXPCOMStatusFromNSS(132), true);
-  add_connection_test("ocsp-stapling-expired-fresh-ca.example.com", getXPCOMStatusFromNSS(132), true);
+  add_ocsp_test("ocsp-stapling-expired.example.com", getXPCOMStatusFromNSS(132), true);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", getXPCOMStatusFromNSS(132), true);
+
   run_next_test();
-  do_test_finished();
 }
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling/Makefile.in
+++ /dev/null
@@ -1,17 +0,0 @@
-# vim: noexpandtab ts=8 sw=8
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# 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/.
-
-FAIL_ON_WARNINGS := 1
-
-include $(topsrcdir)/config/config.mk
-
-LIBS = \
-  $(NSPR_LIBS) \
-  $(NSS_LIBS) \
-  $(MOZALLOC_LIB) \
-  $(NULL)
-
-DEFINES += $(TK_CFLAGS)
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling/OCSPStaplingServer.cpp
+++ /dev/null
@@ -1,592 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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/. */
-
-// This is a standalone server that delivers various stapled OCSP responses.
-// The client is expected to connect, initiate an SSL handshake (with SNI
-// to indicate which "server" to connect to), and verify the OCSP response.
-// If all is good, the client then sends one encrypted byte and receives that
-// same byte back.
-// This server also has the ability to "call back" another process waiting on
-// it. That is, when the server is all set up and ready to receive connections,
-// it will connect to a specified port and issue a simple HTTP request.
-
-#include <stdio.h>
-#include "ScopedNSSTypes.h"
-#include "nspr.h"
-#include "nss.h"
-#include "ocsp.h"
-#include "ocspt.h"
-#include "plarenas.h"
-#include "prenv.h"
-#include "prerror.h"
-#include "prnetdb.h"
-#include "prtime.h"
-#include "ssl.h"
-#include "secerr.h"
-
-using namespace mozilla;
-
-#define LISTEN_PORT 8443
-#define DEBUG_ERRORS 1
-#define DEBUG_WARNINGS 2
-#define DEBUG_VERBOSE 3
-
-uint32_t gDebugLevel = 0;
-uint32_t gCallbackPort = 0;
-
-enum OCSPStapleResponseType
-{
-  OSRTNull = 0,
-  OSRTGood,             // the certificate is good
-  OSRTRevoked,          // the certificate has been revoked
-  OSRTUnknown,          // the responder doesn't know if the cert is good
-  OSRTGoodOtherCert,    // the response references a different certificate
-  OSRTGoodOtherCA,      // the wrong CA has signed the response
-  OSRTExpired,          // the signature on the response has expired
-  OSRTExpiredFreshCA,   // fresh signature, but old validity period
-  OSRTNone,             // no stapled response
-  OSRTMalformed,        // the response from the responder was malformed
-  OSRTSrverr,           // the response indicates there was a server error
-  OSRTTryLater,         // the responder replied with "try again later"
-  OSRTNeedsSig,         // the response needs a signature
-  OSRTUnauthorized      // the responder is not authorized for this certificate
-};
-
-struct OCSPHost
-{
-  const char *mHostName;
-  const char *mCertName;
-  OCSPStapleResponseType mOSRT;
-};
-
-const OCSPHost sOCSPHosts[] =
-{
-  { "ocsp-stapling-good.example.com", "good", OSRTGood },
-  { "ocsp-stapling-revoked.example.com", "revoked", OSRTRevoked },
-  { "ocsp-stapling-unknown.example.com", "unknown", OSRTUnknown },
-  { "ocsp-stapling-good-other.example.com", "good-other", OSRTGoodOtherCert },
-  { "ocsp-stapling-good-other-ca.example.com", "good-otherCA", OSRTGoodOtherCA },
-  { "ocsp-stapling-expired.example.com", "expired", OSRTExpired },
-  { "ocsp-stapling-expired-fresh-ca.example.com", "expired-freshCA", OSRTExpiredFreshCA },
-  { "ocsp-stapling-none.example.com", "none", OSRTNone },
-  { "ocsp-stapling-malformed.example.com", "malformed", OSRTMalformed },
-  { "ocsp-stapling-srverr.example.com", "srverr", OSRTSrverr },
-  { "ocsp-stapling-trylater.example.com", "trylater", OSRTTryLater },
-  { "ocsp-stapling-needssig.example.com", "needssig", OSRTNeedsSig },
-  { "ocsp-stapling-unauthorized.example.com", "unauthorized", OSRTUnauthorized },
-  { nullptr, nullptr, OSRTNull }
-};
-
-struct Connection
-{
-  const OCSPHost *mHost;
-  PRFileDesc *mSocket;
-  char mByte;
-
-  Connection(PRFileDesc *aSocket);
-  ~Connection();
-};
-
-Connection::Connection(PRFileDesc *aSocket)
-: mHost(nullptr)
-, mSocket(aSocket)
-, mByte(0)
-{}
-
-Connection::~Connection()
-{
-  if (mSocket) {
-    PR_Close(mSocket);
-  }
-}
-
-void
-PrintPRError(const char *aPrefix)
-{
-  const char *err = PR_ErrorToName(PR_GetError());
-  if (err) {
-    if (gDebugLevel >= DEBUG_ERRORS) {
-      fprintf(stderr, "%s: %s\n", aPrefix, err);
-    }
-  } else {
-    if (gDebugLevel >= DEBUG_ERRORS) {
-      fprintf(stderr, "%s\n", aPrefix);
-    }
-  }
-}
-
-nsresult
-SendAll(PRFileDesc *aSocket, const char *aData, size_t aDataLen)
-{
-  if (gDebugLevel >= DEBUG_VERBOSE) {
-    fprintf(stderr, "sending '%s'\n", aData);
-  }
-
-  while (aDataLen > 0) {
-    int32_t bytesSent = PR_Send(aSocket, aData, aDataLen, 0,
-                                PR_INTERVAL_NO_TIMEOUT);
-    if (bytesSent == -1) {
-      PrintPRError("PR_Send failed");
-      return NS_ERROR_FAILURE;
-    }
-
-    aDataLen -= bytesSent;
-    aData += bytesSent;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-ReplyToRequest(Connection *aConn)
-{
-  // For debugging purposes, SendAll can print out what it's sending.
-  // So, any strings we give to it to send need to be null-terminated.
-  char buf[2] = { aConn->mByte, 0 };
-  return SendAll(aConn->mSocket, buf, 1);
-}
-
-const OCSPHost *
-GetOcspHost(const char *aServerName)
-{
-  const OCSPHost *host = sOCSPHosts;
-  while (host->mHostName != nullptr &&
-         strcmp(host->mHostName, aServerName) != 0) {
-    host++;
-  }
-
-  if (!host->mHostName) {
-    fprintf(stderr, "host '%s' not in pre-defined list\n", aServerName);
-    MOZ_CRASH();
-    return nullptr;
-  }
-
-  return host;
-}
-
-nsresult
-SetupTLS(Connection *aConn, PRFileDesc *aModelSocket)
-{
-  PRFileDesc *sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket);
-  if (!sslSocket) {
-    PrintPRError("SSL_ImportFD failed");
-    return NS_ERROR_FAILURE;
-  }
-  aConn->mSocket = sslSocket;
-
-  SSL_OptionSet(sslSocket, SSL_SECURITY, true);
-  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false);
-  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true);
-
-  SSL_ResetHandshake(sslSocket, /* asServer */ 1);
-
-  return NS_OK;
-}
-
-nsresult
-ReadRequest(Connection *aConn)
-{
-  int32_t bytesRead = PR_Recv(aConn->mSocket, &aConn->mByte, 1, 0,
-                              PR_INTERVAL_NO_TIMEOUT);
-  if (bytesRead < 1) {
-    PrintPRError("PR_Recv failed");
-    return NS_ERROR_FAILURE;
-  } else {
-    if (gDebugLevel >= DEBUG_VERBOSE) {
-      fprintf(stderr, "read '0x%hhx'\n", aConn->mByte);
-    }
-  }
-  return NS_OK;
-}
-
-void
-HandleConnection(PRFileDesc *aSocket, PRFileDesc *aModelSocket)
-{
-  Connection conn(aSocket);
-  nsresult rv = SetupTLS(&conn, aModelSocket);
-  if (NS_SUCCEEDED(rv)) {
-    rv = ReadRequest(&conn);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    rv = ReplyToRequest(&conn);
-  }
-}
-
-void
-DoCallback()
-{
-  ScopedPRFileDesc socket(PR_NewTCPSocket());
-  if (!socket) {
-    PrintPRError("PR_NewTCPSocket failed");
-    return;
-  }
-
-  PRNetAddr addr;
-  PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr);
-  if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) {
-    PrintPRError("PR_Connect failed");
-    return;
-  }
-
-  const char *request = "GET / HTTP/1.0\r\n\r\n";
-  SendAll(socket, request, strlen(request));
-  char buf[4096];
-  memset(buf, 0, sizeof(buf));
-  int32_t bytesRead = PR_Recv(socket, buf, sizeof(buf) - 1, 0,
-                              PR_INTERVAL_NO_TIMEOUT);
-  if (bytesRead < 1) {
-    PrintPRError("PR_Recv failed");
-    return;
-  }
-  fprintf(stderr, "%s\n", buf);
-  memset(buf, 0, sizeof(buf));
-  bytesRead = PR_Recv(socket, buf, sizeof(buf) - 1, 0, PR_INTERVAL_NO_TIMEOUT);
-  if (bytesRead < 1) {
-    PrintPRError("PR_Recv failed");
-    return;
-  }
-  fprintf(stderr, "%s\n", buf);
-}
-
-SECItemArray *
-GetOCSPResponseForType(OCSPStapleResponseType aOSRT, CERTCertificate *aCert,
-                       PLArenaPool *aArena)
-{
-  PRTime now = PR_Now();
-  ScopedCERTOCSPCertID id(CERT_CreateOCSPCertID(aCert, now));
-  if (!id) {
-    PrintPRError("CERT_CreateOCSPCertID failed");
-    return nullptr;
-  }
-  PRTime nextUpdate = now + 10 * PR_USEC_PER_SEC;
-  PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC;
-  PRTime expiredTime = now - oneDay;
-  PRTime oldNow = now - (8 * oneDay);
-  PRTime oldNextUpdate = oldNow + 10 * PR_USEC_PER_SEC;
-  ScopedCERTCertificate othercert(PK11_FindCertFromNickname("good", nullptr));
-  ScopedCERTOCSPCertID otherid(CERT_CreateOCSPCertID(othercert, now));
-  if (!otherid) {
-    PrintPRError("CERT_CreateOCSPCertID failed");
-    return nullptr;
-  }
-  CERTOCSPSingleResponse *sr = nullptr;
-  switch (aOSRT) {
-    case OSRTGood:
-    case OSRTGoodOtherCA:
-      sr = CERT_CreateOCSPSingleResponseGood(aArena, id, now, &nextUpdate);
-      if (!sr) {
-        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
-        return nullptr;
-      }
-      break;
-    case OSRTRevoked:
-      sr = CERT_CreateOCSPSingleResponseRevoked(aArena, id, now, &nextUpdate,
-                                                expiredTime, nullptr);
-      if (!sr) {
-        PrintPRError("CERT_CreateOCSPSingleResponseRevoked failed");
-        return nullptr;
-      }
-      break;
-    case OSRTUnknown:
-      sr = CERT_CreateOCSPSingleResponseUnknown(aArena, id, now, &nextUpdate);
-      if (!sr) {
-        PrintPRError("CERT_CreateOCSPSingleResponseUnknown failed");
-        return nullptr;
-      }
-      break;
-    case OSRTExpired:
-    case OSRTExpiredFreshCA:
-      sr = CERT_CreateOCSPSingleResponseGood(aArena, id, oldNow, &oldNextUpdate);
-      if (!sr) {
-        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
-        return nullptr;
-      }
-      break;
-    case OSRTGoodOtherCert:
-      sr = CERT_CreateOCSPSingleResponseGood(aArena, otherid, now, &nextUpdate);
-      if (!sr) {
-        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
-        return nullptr;
-      }
-      break;
-    case OSRTNone:
-    case OSRTMalformed:
-    case OSRTSrverr:
-    case OSRTTryLater:
-    case OSRTNeedsSig:
-    case OSRTUnauthorized:
-      break;
-    default:
-      if (gDebugLevel >= DEBUG_ERRORS) {
-        fprintf(stderr, "bad ocsp response type: %d\n", aOSRT);
-      }
-      break;
-  }
-  ScopedCERTCertificate ca;
-  if (aOSRT == OSRTGoodOtherCA) {
-    ca = PK11_FindCertFromNickname("otherCA", nullptr);
-    if (!ca) {
-      PrintPRError("PK11_FindCertFromNickname failed");
-      return nullptr;
-    }
-  } else {
-    // XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
-    ca = CERT_FindCertIssuer(aCert, now, certUsageSSLCA);
-    if (!ca) {
-      PrintPRError("CERT_FindCertIssuer failed");
-      return nullptr;
-    }
-  }
-
-  PRTime signTime = now;
-  if (aOSRT == OSRTExpired) {
-    signTime = oldNow;
-  }
-
-  CERTOCSPSingleResponse **responses;
-  SECItem *response = nullptr;
-  switch (aOSRT) {
-    case OSRTMalformed:
-      response = CERT_CreateEncodedOCSPErrorResponse(
-        aArena, SEC_ERROR_OCSP_MALFORMED_REQUEST);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
-        return nullptr;
-      }
-      break;
-    case OSRTSrverr:
-      response = CERT_CreateEncodedOCSPErrorResponse(
-        aArena, SEC_ERROR_OCSP_SERVER_ERROR);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
-        return nullptr;
-      }
-      break;
-    case OSRTTryLater:
-      response = CERT_CreateEncodedOCSPErrorResponse(
-        aArena, SEC_ERROR_OCSP_TRY_SERVER_LATER);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
-        return nullptr;
-      }
-      break;
-    case OSRTNeedsSig:
-      response = CERT_CreateEncodedOCSPErrorResponse(
-        aArena, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
-        return nullptr;
-      }
-      break;
-    case OSRTUnauthorized:
-      response = CERT_CreateEncodedOCSPErrorResponse(
-        aArena, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
-        return nullptr;
-      }
-      break;
-    case OSRTNone:
-      break;
-    default:
-      // responses is contained in aArena and will be freed when aArena is
-      responses = PORT_ArenaNewArray(aArena, CERTOCSPSingleResponse *, 2);
-      if (!responses) {
-        PrintPRError("PORT_ArenaNewArray failed");
-        return nullptr;
-      }
-      responses[0] = sr;
-      responses[1] = nullptr;
-      response = CERT_CreateEncodedOCSPSuccessResponse(
-        aArena, ca, ocspResponderID_byName, signTime, responses, nullptr);
-      if (!response) {
-        PrintPRError("CERT_CreateEncodedOCSPSuccessResponse failed");
-        return nullptr;
-      }
-      break;
-  }
-
-  SECItemArray *arr = SECITEM_AllocArray(aArena, nullptr, 1);
-  arr->items[0].data = response ? response->data : nullptr;
-  arr->items[0].len = response ? response->len : 0;
-
-  return arr;
-}
-
-int32_t
-DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr,
-                  uint32_t aSrvNameArrSize, void *aArg)
-{
-  const OCSPHost *host = nullptr;
-  for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
-    host = GetOcspHost((const char *)aSrvNameArr[i].data);
-    if (host) {
-      break;
-    }
-  }
-
-  if (!host) {
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  if (gDebugLevel >= DEBUG_VERBOSE) {
-    fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
-  }
-
-  ScopedCERTCertificate cert(PK11_FindCertFromNickname(host->mCertName, nullptr));
-  if (!cert) {
-    PrintPRError("PK11_FindCertFromNickname failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  ScopedSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert, nullptr));
-  if (!key) {
-    PrintPRError("PK11_FindKeyByAnyCert failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  SSLKEAType certKEA = NSS_FindCertKEAType(cert);
-
-  if (SSL_ConfigSecureServer(aFd, cert, key, certKEA) != SECSuccess) {
-    PrintPRError("SSL_ConfigSecureServer failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  PLArenaPool *arena = PORT_NewArena(1024);
-  if (!arena) {
-    PrintPRError("PORT_NewArena failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-  // response is contained by the arena - freeing the arena will free it
-  SECItemArray *response = GetOCSPResponseForType(host->mOSRT, cert, arena);
-  if (!response) {
-    PORT_FreeArena(arena, PR_FALSE);
-    return SSL_SNI_SEND_ALERT;
-  }
-  // SSL_SetStapledOCSPResponses makes a deep copy of response
-  SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA);
-  PORT_FreeArena(arena, PR_FALSE);
-  if (st != SECSuccess) {
-    PrintPRError("SSL_SetStapledOCSPResponses failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  return 0;
-}
-
-void
-StartServer()
-{
-  ScopedPRFileDesc serverSocket(PR_NewTCPSocket());
-  if (!serverSocket) {
-    PrintPRError("PR_NewTCPSocket failed");
-    return;
-  }
-
-  PRSocketOptionData socketOption;
-  socketOption.option = PR_SockOpt_Reuseaddr;
-  socketOption.value.reuse_addr = true;
-  PR_SetSocketOption(serverSocket, &socketOption);
-
-  PRNetAddr serverAddr;
-  PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr);
-  if (PR_Bind(serverSocket, &serverAddr) != PR_SUCCESS) {
-    PrintPRError("PR_Bind failed");
-    return;
-  }
-
-  if (PR_Listen(serverSocket, 1) != PR_SUCCESS) {
-    PrintPRError("PR_Listen failed");
-    return;
-  }
-
-  ScopedPRFileDesc rawModelSocket(PR_NewTCPSocket());
-  if (!rawModelSocket) {
-    PrintPRError("PR_NewTCPSocket failed for rawModelSocket");
-    return;
-  }
-
-  ScopedPRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.forget()));
-  if (!modelSocket) {
-    PrintPRError("SSL_ImportFD of rawModelSocket failed");
-    return;
-  }
-
-  if (SECSuccess != SSL_SNISocketConfigHook(modelSocket, DoSNISocketConfig,
-                                            nullptr)) {
-    PrintPRError("SSL_SNISocketConfigHook failed");
-    return;
-  }
-
-  // We have to configure the server with a certificate, but it's not one
-  // we're actually going to end up using. In the SNI callback, we pick
-  // the right certificate for the connection.
-  ScopedCERTCertificate cert(PK11_FindCertFromNickname("localhost", nullptr));
-  if (!cert) {
-    PrintPRError("PK11_FindCertFromNickname failed");
-    return;
-  }
-
-  ScopedSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert, nullptr));
-  if (!key) {
-    PrintPRError("PK11_FindKeyByAnyCert failed");
-    return;
-  }
-
-  SSLKEAType certKEA = NSS_FindCertKEAType(cert);
-
-  if (SSL_ConfigSecureServer(modelSocket, cert, key, certKEA) != SECSuccess) {
-    PrintPRError("SSL_ConfigSecureServer failed");
-    return;
-  }
-
-  if (gCallbackPort != 0) {
-    DoCallback();
-  }
-
-  while (true) {
-    PRNetAddr clientAddr;
-    PRFileDesc *clientSocket = PR_Accept(serverSocket, &clientAddr,
-                                         PR_INTERVAL_NO_TIMEOUT);
-    HandleConnection(clientSocket, modelSocket);
-  }
-}
-
-int
-main(int argc, char *argv[])
-{
-  const char *debugLevel = PR_GetEnv("OCSP_SERVER_DEBUG_LEVEL");
-  if (debugLevel) {
-    gDebugLevel = atoi(debugLevel);
-  }
-
-  const char *callbackPort = PR_GetEnv("OCSP_SERVER_CALLBACK_PORT");
-  if (callbackPort) {
-    gCallbackPort = atoi(callbackPort);
-  }
-
-  if (argc != 2) {
-    fprintf(stderr, "usage: %s <NSS DB directory>\n", argv[0]);
-    return 1;
-  }
-
-  if (NSS_Init(argv[1]) != SECSuccess) {
-    PrintPRError("NSS_Init failed");
-    return 1;
-  }
-
-  if (NSS_SetDomesticPolicy() != SECSuccess) {
-    PrintPRError("NSS_SetDomesticPolicy failed");
-    return 1;
-  }
-
-  if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
-    PrintPRError("SSL_ConfigServerSessionIDCache failed");
-    return 1;
-  }
-
-  StartServer();
-
-  return 0;
-}
deleted file mode 100644
index 09aaa7a0108eea4ec6ad5c68f73f988379b9afa5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100755
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling/gen_ocsp_certs.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# 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/.
-#
-# Usage: ./gen_ocsp_certs.sh <path to objdir> <output directory>
-# e.g. (from the root of mozilla-central)
-# `./security/manager/ssl/tests/unit/test_ocsp_stapling/gen_ocsp_certs.sh \
-#  obj-x86_64-unknown-linux-gnu/ \
-#  security/manager/ssl/tests/unit/test_ocsp_stapling/`
-#
-# NB: This will cause the following files to be overwritten if they are in
-# the output directory:
-#  cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der
-
-if [ $# -ne 2 ]; then
-  echo "Usage: `basename ${0}` <path to objdir> <output directory>"
-  exit $E_BADARGS
-fi
-
-OBJDIR=${1}
-OUTPUT_DIR=${2}
-RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
-CERTUTIL="$OBJDIR/dist/bin/certutil"
-
-function check_retval {
-  retval=$?
-  if [ "$retval" -ne 0 ]; then
-    echo "failed..."
-    exit "$retval"
-  fi
-}
-
-NOISE_FILE=`mktemp`
-echo "running \"dd if=/dev/urandom of="$NOISE_FILE" bs=1024 count=8\""
-dd if=/dev/urandom of="$NOISE_FILE" bs=1024 count=1
-check_retval
-PASSWORD_FILE=`mktemp`
-
-function cleanup {
-  rm -f "$NOISE_FILE" "$PASSWORD_FILE"
-}
-
-if [ ! -f "$RUN_MOZILLA" ]; then
-  echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\'"
-  exit $E_BADARGS
-fi
-
-if [ ! -f "$CERTUTIL" ]; then
-  echo "Could not find certutil at \'$CERTUTIL\'"
-  exit $E_BADARGS
-fi
-
-if [ ! -d "$OUTPUT_DIR" ]; then
-  echo "Could not find output directory at \'$OUTPUT_DIR\'"
-  exit $E_BADARGS
-fi
-
-if [ -f "$OUTPUT_DIR/cert8.db" -o -f "$OUTPUT_DIR/key3.db" -o -f "$OUTPUT_DIR/secmod.db" ]; then
-  echo "Found pre-existing NSS DBs. Clobbering old OCSP certs."
-  rm -f "$OUTPUT_DIR/cert8.db" "$OUTPUT_DIR/key3.db" "$OUTPUT_DIR/secmod.db"
-fi
-echo "running \"$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -N -f $PASSWORD_FILE\""
-$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -N -f $PASSWORD_FILE
-check_retval
-
-COMMON_ARGS="-v 360 -w -1 -2 -z $NOISE_FILE"
-
-function make_CA {
-  CA_RESPONSES="y\n0\ny"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  DERFILE="${3}"
-
-  check_retval
-  echo "running 'echo -e \"$CA_RESPONSES\" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S -n $NICKNAME -s \"$SUBJECT\" -t CTu,u,u -x $COMMON_ARGS'"
-  echo -e "$CA_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S -n $NICKNAME -s "$SUBJECT" -t CTu,u,u -x $COMMON_ARGS
-  check_retval
-  echo "running \"$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n $NICKNAME -r > $OUTPUT_DIR/$DERFILE\""
-  $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n $NICKNAME -r > $OUTPUT_DIR/$DERFILE
-  check_retval
-}
-
-SERIALNO=1
-
-function make_cert {
-  CERT_RESPONSES="n\n\ny"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-
-  echo "running 'echo -e \"$CERT_RESPONSES\" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S -s \"$SUBJECT\" -n $NICKNAME -c $CA -t Pu,u,u -m $SERIALNO $COMMON_ARGS'"
-  echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S -s "$SUBJECT" -n $NICKNAME -c $CA -t Pu,u,u -m $SERIALNO $COMMON_ARGS
-  check_retval
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-make_CA ocspTestCA 'CN=OCSP stapling test CA' ocsp-ca.der
-make_CA otherCA 'CN=OCSP other test CA' ocsp-other-ca.der
-
-make_cert localhost 'CN=localhost' ocspTestCA
-make_cert good 'CN=ocsp-stapling-good.example.com' ocspTestCA
-make_cert revoked 'CN=ocsp-stapling-revoked.example.com' ocspTestCA
-make_cert unknown 'CN=ocsp-stapling-unknown.example.com' ocspTestCA
-make_cert good-other 'CN=ocsp-stapling-good-other.example.com' ocspTestCA
-make_cert good-otherCA 'CN=ocsp-stapling-good-other-ca.example.com' ocspTestCA
-make_cert expired 'CN=ocsp-stapling-expired.example.com' ocspTestCA
-make_cert expired-freshCA 'CN=ocsp-stapling-expired-fresh-ca.example.com' ocspTestCA
-make_cert none 'CN=ocsp-stapling-none.example.com' ocspTestCA
-make_cert malformed 'CN=ocsp-stapling-malformed.example.com' ocspTestCA
-make_cert srverr 'CN=ocsp-stapling-srverr.example.com' ocspTestCA
-make_cert trylater 'CN=ocsp-stapling-trylater.example.com' ocspTestCA
-make_cert needssig 'CN=ocsp-stapling-needssig.example.com' ocspTestCA
-make_cert unauthorized 'CN=ocsp-stapling-unauthorized.example.com' ocspTestCA
-
-cleanup
deleted file mode 100644
index 5d18ce891ac171969cfac81796ad08563ee01c43..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling/moz.build
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# 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/.
-
-MODULE = 'pipnss'
-
-sources = [
-    'OCSPStaplingServer',
-]
-
-CPP_SOURCES += [
-    '%s.cpp' % s for s in sources
-]
-
-bin_suffix = CONFIG['BIN_SUFFIX']
-SIMPLE_PROGRAMS += [
-    '%s%s' % (s, bin_suffix) for s in sources
-]
deleted file mode 100644
index d609d41573fe3fe7a2bdf5f038e3edb20f58c857..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 952dabd494534a972706e88b5042414a5578cc39..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6bad1714783fb78303e5908e9b552a172810f69c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index 0000000000000000000000000000000000000000..590f59078ca48d911a7dffe52b0b6bdc55a1560e
GIT binary patch
literal 65536
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
i00000000000000000000000000000000000p8p$!{|*`e
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/Makefile.in
@@ -0,0 +1,30 @@
+# vim: noexpandtab ts=8 sw=8
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+LIBS = \
+  $(NSPR_LIBS) \
+  $(NSS_LIBS) \
+  $(MOZALLOC_LIB) \
+  ../lib/$(LIB_PREFIX)tlsserver.$(LIB_SUFFIX) \
+  $(NULL)
+
+DEFINES += $(TK_CFLAGS)
+
+LOCAL_INCLUDES += \
+   -I$(srcdir)/../lib \
+   $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
@@ -0,0 +1,299 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+// This is a standalone server that delivers various stapled OCSP responses.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the OCSP response.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+#include "TLSServer.h"
+#include "ScopedNSSTypes.h"
+#include "ssl.h"
+#include "secerr.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+enum OCSPStapleResponseType
+{
+  OSRTNull = 0,
+  OSRTGood,             // the certificate is good
+  OSRTRevoked,          // the certificate has been revoked
+  OSRTUnknown,          // the responder doesn't know if the cert is good
+  OSRTGoodOtherCert,    // the response references a different certificate
+  OSRTGoodOtherCA,      // the wrong CA has signed the response
+  OSRTExpired,          // the signature on the response has expired
+  OSRTExpiredFreshCA,   // fresh signature, but old validity period
+  OSRTNone,             // no stapled response
+  OSRTMalformed,        // the response from the responder was malformed
+  OSRTSrverr,           // the response indicates there was a server error
+  OSRTTryLater,         // the responder replied with "try again later"
+  OSRTNeedsSig,         // the response needs a signature
+  OSRTUnauthorized      // the responder is not authorized for this certificate
+};
+
+struct OCSPHost
+{
+  const char *mHostName;
+  OCSPStapleResponseType mOSRT;
+};
+
+const OCSPHost sOCSPHosts[] =
+{
+  { "ocsp-stapling-good.example.com", OSRTGood },
+  { "ocsp-stapling-revoked.example.com", OSRTRevoked },
+  { "ocsp-stapling-unknown.example.com", OSRTUnknown },
+  { "ocsp-stapling-good-other.example.com", OSRTGoodOtherCert },
+  { "ocsp-stapling-good-other-ca.example.com", OSRTGoodOtherCA },
+  { "ocsp-stapling-expired.example.com", OSRTExpired },
+  { "ocsp-stapling-expired-fresh-ca.example.com", OSRTExpiredFreshCA },
+  { "ocsp-stapling-none.example.com", OSRTNone },
+  { "ocsp-stapling-malformed.example.com", OSRTMalformed },
+  { "ocsp-stapling-srverr.example.com", OSRTSrverr },
+  { "ocsp-stapling-trylater.example.com", OSRTTryLater },
+  { "ocsp-stapling-needssig.example.com", OSRTNeedsSig },
+  { "ocsp-stapling-unauthorized.example.com", OSRTUnauthorized },
+  { nullptr, OSRTNull }
+};
+
+SECItemArray *
+GetOCSPResponseForType(OCSPStapleResponseType aOSRT, CERTCertificate *aCert,
+                       PLArenaPool *aArena)
+{
+  PRTime now = PR_Now();
+  ScopedCERTOCSPCertID id(CERT_CreateOCSPCertID(aCert, now));
+  if (!id) {
+    PrintPRError("CERT_CreateOCSPCertID failed");
+    return nullptr;
+  }
+  PRTime nextUpdate = now + 10 * PR_USEC_PER_SEC;
+  PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC;
+  PRTime expiredTime = now - oneDay;
+  PRTime oldNow = now - (8 * oneDay);
+  PRTime oldNextUpdate = oldNow + 10 * PR_USEC_PER_SEC;
+
+  CERTOCSPSingleResponse *sr = nullptr;
+  switch (aOSRT) {
+    case OSRTGood:
+    case OSRTGoodOtherCA:
+      sr = CERT_CreateOCSPSingleResponseGood(aArena, id, now, &nextUpdate);
+      if (!sr) {
+        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
+        return nullptr;
+      }
+      id.forget(); // owned by sr now
+      break;
+    case OSRTRevoked:
+      sr = CERT_CreateOCSPSingleResponseRevoked(aArena, id, now, &nextUpdate,
+                                                expiredTime, nullptr);
+      if (!sr) {
+        PrintPRError("CERT_CreateOCSPSingleResponseRevoked failed");
+        return nullptr;
+      }
+      id.forget(); // owned by sr now
+      break;
+    case OSRTUnknown:
+      sr = CERT_CreateOCSPSingleResponseUnknown(aArena, id, now, &nextUpdate);
+      if (!sr) {
+        PrintPRError("CERT_CreateOCSPSingleResponseUnknown failed");
+        return nullptr;
+      }
+      id.forget(); // owned by sr now
+      break;
+    case OSRTExpired:
+    case OSRTExpiredFreshCA:
+      sr = CERT_CreateOCSPSingleResponseGood(aArena, id, oldNow, &oldNextUpdate);
+      if (!sr) {
+        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
+        return nullptr;
+      }
+      id.forget(); // owned by sr now
+      break;
+    case OSRTGoodOtherCert:
+    {
+      ScopedCERTCertificate otherCert(
+        PK11_FindCertFromNickname("ocspOtherEndEntity", nullptr));
+      if (!otherCert) {
+        PrintPRError("PK11_FindCertFromNickname failed");
+        return nullptr;
+      }
+
+      ScopedCERTOCSPCertID otherID(CERT_CreateOCSPCertID(otherCert, now));
+      if (!otherID) {
+        PrintPRError("CERT_CreateOCSPCertID failed");
+        return nullptr;
+      }
+      sr = CERT_CreateOCSPSingleResponseGood(aArena, otherID, now, &nextUpdate);
+      if (!sr) {
+        PrintPRError("CERT_CreateOCSPSingleResponseGood failed");
+        return nullptr;
+      }
+      otherID.forget(); // owned by sr now
+      break;
+    }
+    case OSRTNone:
+    case OSRTMalformed:
+    case OSRTSrverr:
+    case OSRTTryLater:
+    case OSRTNeedsSig:
+    case OSRTUnauthorized:
+      break;
+    default:
+      if (gDebugLevel >= DEBUG_ERRORS) {
+        fprintf(stderr, "bad ocsp response type: %d\n", aOSRT);
+      }
+      break;
+  }
+
+  ScopedCERTCertificate ca;
+  if (aOSRT == OSRTGoodOtherCA) {
+    ca = PK11_FindCertFromNickname("otherCA", nullptr);
+    if (!ca) {
+      PrintPRError("PK11_FindCertFromNickname failed");
+      return nullptr;
+    }
+  } else {
+    // XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
+    ca = CERT_FindCertIssuer(aCert, now, certUsageSSLCA);
+    if (!ca) {
+      PrintPRError("CERT_FindCertIssuer failed");
+      return nullptr;
+    }
+  }
+
+  PRTime signTime = now;
+  if (aOSRT == OSRTExpired) {
+    signTime = oldNow;
+  }
+
+  CERTOCSPSingleResponse **responses;
+  SECItem *response = nullptr;
+  switch (aOSRT) {
+    case OSRTMalformed:
+      response = CERT_CreateEncodedOCSPErrorResponse(
+        aArena, SEC_ERROR_OCSP_MALFORMED_REQUEST);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
+        return nullptr;
+      }
+      break;
+    case OSRTSrverr:
+      response = CERT_CreateEncodedOCSPErrorResponse(
+        aArena, SEC_ERROR_OCSP_SERVER_ERROR);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
+        return nullptr;
+      }
+      break;
+    case OSRTTryLater:
+      response = CERT_CreateEncodedOCSPErrorResponse(
+        aArena, SEC_ERROR_OCSP_TRY_SERVER_LATER);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
+        return nullptr;
+      }
+      break;
+    case OSRTNeedsSig:
+      response = CERT_CreateEncodedOCSPErrorResponse(
+        aArena, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
+        return nullptr;
+      }
+      break;
+    case OSRTUnauthorized:
+      response = CERT_CreateEncodedOCSPErrorResponse(
+        aArena, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPErrorResponse failed");
+        return nullptr;
+      }
+      break;
+    case OSRTNone:
+      break;
+    default:
+      // responses is contained in aArena and will be freed when aArena is
+      responses = PORT_ArenaNewArray(aArena, CERTOCSPSingleResponse *, 2);
+      if (!responses) {
+        PrintPRError("PORT_ArenaNewArray failed");
+        return nullptr;
+      }
+      responses[0] = sr;
+      responses[1] = nullptr;
+      response = CERT_CreateEncodedOCSPSuccessResponse(
+        aArena, ca, ocspResponderID_byName, signTime, responses, nullptr);
+      if (!response) {
+        PrintPRError("CERT_CreateEncodedOCSPSuccessResponse failed");
+        return nullptr;
+      }
+      break;
+  }
+
+  SECItemArray *arr = SECITEM_AllocArray(aArena, nullptr, 1);
+  arr->items[0].data = response ? response->data : nullptr;
+  arr->items[0].len = response ? response->len : 0;
+
+  return arr;
+}
+
+int32_t
+DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr,
+                  uint32_t aSrvNameArrSize, void *aArg)
+{
+  const OCSPHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
+                                       sOCSPHosts);
+  if (!host) {
+    return SSL_SNI_SEND_ALERT;
+  }
+
+  if (gDebugLevel >= DEBUG_VERBOSE) {
+    fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+  }
+
+  ScopedCERTCertificate cert;
+  SSLKEAType certKEA;
+  if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, DEFAULT_CERT_NICKNAME,
+                                                    &cert, &certKEA)) {
+    return SSL_SNI_SEND_ALERT;
+  }
+
+  PLArenaPool *arena = PORT_NewArena(1024);
+  if (!arena) {
+    PrintPRError("PORT_NewArena failed");
+    return SSL_SNI_SEND_ALERT;
+  }
+
+  // response is contained by the arena - freeing the arena will free it
+  SECItemArray *response = GetOCSPResponseForType(host->mOSRT, cert, arena);
+  if (!response) {
+    PORT_FreeArena(arena, PR_FALSE);
+    return SSL_SNI_SEND_ALERT;
+  }
+
+  // SSL_SetStapledOCSPResponses makes a deep copy of response
+  SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA);
+  PORT_FreeArena(arena, PR_FALSE);
+  if (st != SECSuccess) {
+    PrintPRError("SSL_SetStapledOCSPResponses failed");
+    return SSL_SNI_SEND_ALERT;
+  }
+
+  return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+  if (argc != 2) {
+    fprintf(stderr, "usage: %s <NSS DB directory>\n", argv[0]);
+    return 1;
+  }
+
+  return StartServer(argv[1], DoSNISocketConfig, nullptr);
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+MODULE = 'tlsserver'
+
+FAIL_ON_WARNINGS = True
+
+sources = [
+    'OCSPStaplingServer',
+]
+
+CPP_SOURCES += [
+    '%s.cpp' % s for s in sources
+]
+
+bin_suffix = CONFIG['BIN_SUFFIX']
+SIMPLE_PROGRAMS += [
+    '%s%s' % (s, bin_suffix) for s in sources
+]
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+#
+# Usage: ./generate_certs.sh <path to objdir> <output directory>
+# e.g. (from the root of mozilla-central)
+# `./security/manager/ssl/tests/unit/tlsserver/generate_certs.sh \
+#  obj-x86_64-unknown-linux-gnu/ \
+#  security/manager/ssl/tests/unit/tlsserver/`
+#
+# NB: This will cause the following files to be overwritten if they are in
+# the output directory:
+#  cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der
+set -x
+set -e
+
+if [ $# -ne 2 ]; then
+  echo "Usage: `basename ${0}` <path to objdir> <output directory>"
+  exit $E_BADARGS
+fi
+
+OBJDIR=${1}
+OUTPUT_DIR=${2}
+RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
+CERTUTIL="$OBJDIR/dist/bin/certutil"
+
+NOISE_FILE=`mktemp`
+dd if=/dev/urandom of="$NOISE_FILE" bs=1024 count=1
+PASSWORD_FILE=`mktemp`
+
+function cleanup {
+  rm -f "$NOISE_FILE" "$PASSWORD_FILE"
+}
+
+if [ ! -f "$RUN_MOZILLA" ]; then
+  echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\'"
+  exit $E_BADARGS
+fi
+
+if [ ! -f "$CERTUTIL" ]; then
+  echo "Could not find certutil at \'$CERTUTIL\'"
+  exit $E_BADARGS
+fi
+
+if [ ! -d "$OUTPUT_DIR" ]; then
+  echo "Could not find output directory at \'$OUTPUT_DIR\'"
+  exit $E_BADARGS
+fi
+
+if [ -f "$OUTPUT_DIR/cert8.db" -o -f "$OUTPUT_DIR/key3.db" -o -f "$OUTPUT_DIR/secmod.db" ]; then
+  echo "Found pre-existing NSS DBs. Clobbering old OCSP certs."
+  rm -f "$OUTPUT_DIR/cert8.db" "$OUTPUT_DIR/key3.db" "$OUTPUT_DIR/secmod.db"
+fi
+$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -N -f $PASSWORD_FILE
+
+COMMON_ARGS="-v 360 -w -1 -2 -z $NOISE_FILE"
+
+function make_CA {
+  CA_RESPONSES="y\n0\ny"
+  NICKNAME="${1}"
+  SUBJECT="${2}"
+  DERFILE="${3}"
+
+  echo -e "$CA_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S \
+                                                   -n $NICKNAME \
+                                                   -s "$SUBJECT" \
+                                                   -t "CT,," \
+                                                   -x $COMMON_ARGS
+  $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n $NICKNAME -r > $OUTPUT_DIR/$DERFILE
+}
+
+SERIALNO=1
+
+function make_cert {
+  CERT_RESPONSES="n\n\ny"
+  NICKNAME="${1}"
+  SUBJECT="${2}"
+  CA="${3}"
+  SUBJECT_ALT_NAME="${4}"
+  if [ -n "$SUBJECT_ALT_NAME" ]; then
+    SUBJECT_ALT_NAME="-8 $SUBJECT_ALT_NAME"
+  fi
+
+  echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S \
+                                                     -n $NICKNAME \
+                                                     -s "$SUBJECT" \
+                                                     $SUBJECT_ALT_NAME \
+                                                     -c $CA \
+                                                     -t ",," \
+                                                     -m $SERIALNO \
+                                                     $COMMON_ARGS
+  SERIALNO=$(($SERIALNO + 1))
+}
+
+make_CA testCA 'CN=Test CA' test-ca.der
+make_CA otherCA 'CN=Other test CA' other-test-ca.der
+make_cert localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com"
+# A cert that is like localhostAndExampleCom, but with a different serial number for
+# testing the "OCSP response is from the right issuer, but it is for the wrong cert"
+# case.
+make_cert ocspOtherEndEntity 'CN=Other Cert' testCA "localhost,*.example.com"
+
+cleanup
new file mode 100644
index 0000000000000000000000000000000000000000..bfd268e76a0f71d01ac41f6f6e400efe9a14fb5a
GIT binary patch
literal 16384
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
G+x-LmYNtp5
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
@@ -0,0 +1,328 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "TLSServer.h"
+
+#include <stdio.h>
+#include "ScopedNSSTypes.h"
+#include "nspr.h"
+#include "nss.h"
+#include "plarenas.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prnetdb.h"
+#include "prtime.h"
+#include "ssl.h"
+
+namespace mozilla { namespace test {
+
+static const uint16_t LISTEN_PORT = 8443;
+
+DebugLevel gDebugLevel = DEBUG_ERRORS;
+uint16_t gCallbackPort = 0;
+
+const char DEFAULT_CERT_NICKNAME[] = "localhostAndExampleCom";
+
+struct Connection
+{
+  PRFileDesc *mSocket;
+  char mByte;
+
+  Connection(PRFileDesc *aSocket);
+  ~Connection();
+};
+
+Connection::Connection(PRFileDesc *aSocket)
+: mSocket(aSocket)
+, mByte(0)
+{}
+
+Connection::~Connection()
+{
+  if (mSocket) {
+    PR_Close(mSocket);
+  }
+}
+
+void
+PrintPRError(const char *aPrefix)
+{
+  const char *err = PR_ErrorToName(PR_GetError());
+  if (err) {
+    if (gDebugLevel >= DEBUG_ERRORS) {
+      fprintf(stderr, "%s: %s\n", aPrefix, err);
+    }
+  } else {
+    if (gDebugLevel >= DEBUG_ERRORS) {
+      fprintf(stderr, "%s\n", aPrefix);
+    }
+  }
+}
+
+nsresult
+SendAll(PRFileDesc *aSocket, const char *aData, size_t aDataLen)
+{
+  if (gDebugLevel >= DEBUG_VERBOSE) {
+    fprintf(stderr, "sending '%s'\n", aData);
+  }
+
+  while (aDataLen > 0) {
+    int32_t bytesSent = PR_Send(aSocket, aData, aDataLen, 0,
+                                PR_INTERVAL_NO_TIMEOUT);
+    if (bytesSent == -1) {
+      PrintPRError("PR_Send failed");
+      return NS_ERROR_FAILURE;
+    }
+
+    aDataLen -= bytesSent;
+    aData += bytesSent;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+ReplyToRequest(Connection *aConn)
+{
+  // For debugging purposes, SendAll can print out what it's sending.
+  // So, any strings we give to it to send need to be null-terminated.
+  char buf[2] = { aConn->mByte, 0 };
+  return SendAll(aConn->mSocket, buf, 1);
+}
+
+nsresult
+SetupTLS(Connection *aConn, PRFileDesc *aModelSocket)
+{
+  PRFileDesc *sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket);
+  if (!sslSocket) {
+    PrintPRError("SSL_ImportFD failed");
+    return NS_ERROR_FAILURE;
+  }
+  aConn->mSocket = sslSocket;
+
+  SSL_OptionSet(sslSocket, SSL_SECURITY, true);
+  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false);
+  SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true);
+
+  SSL_ResetHandshake(sslSocket, /* asServer */ 1);
+
+  return NS_OK;
+}
+
+nsresult
+ReadRequest(Connection *aConn)
+{
+  int32_t bytesRead = PR_Recv(aConn->mSocket, &aConn->mByte, 1, 0,
+                              PR_INTERVAL_NO_TIMEOUT);
+  if (bytesRead < 0) {
+    PrintPRError("PR_Recv failed");
+    return NS_ERROR_FAILURE;
+  } else if (bytesRead == 0) {
+    PR_SetError(PR_IO_ERROR, 0);
+    PrintPRError("PR_Recv EOF in ReadRequest");
+    return NS_ERROR_FAILURE;
+  } else {
+    if (gDebugLevel >= DEBUG_VERBOSE) {
+      fprintf(stderr, "read '0x%hhx'\n", aConn->mByte);
+    }
+  }
+  return NS_OK;
+}
+
+void
+HandleConnection(PRFileDesc *aSocket, PRFileDesc *aModelSocket)
+{
+  Connection conn(aSocket);
+  nsresult rv = SetupTLS(&conn, aModelSocket);
+  if (NS_FAILED(rv)) {
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
+    PrintPRError("PR_Recv failed");
+    exit(1);
+  }
+
+  // TODO: On tests that are expected to fail (e.g. due to a revoked
+  // certificate), the client will close the connection wtihout sending us the
+  // request byte. In those cases, we should keep going. But, in the cases
+  // where the connection is supposed to suceed, we should verify that we
+  // successfully receive the request and send the response.
+  rv = ReadRequest(&conn);
+  if (NS_SUCCEEDED(rv)) {
+    rv = ReplyToRequest(&conn);
+  }
+}
+
+// returns 0 on success, non-zero on error
+int
+DoCallback()
+{
+  ScopedPRFileDesc socket(PR_NewTCPSocket());
+  if (!socket) {
+    PrintPRError("PR_NewTCPSocket failed");
+    return 1;
+  }
+
+  PRNetAddr addr;
+  PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr);
+  if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) {
+    PrintPRError("PR_Connect failed");
+    return 1;
+  }
+
+  const char *request = "GET / HTTP/1.0\r\n\r\n";
+  SendAll(socket, request, strlen(request));
+  char buf[4096];
+  memset(buf, 0, sizeof(buf));
+  int32_t bytesRead = PR_Recv(socket, buf, sizeof(buf) - 1, 0,
+                              PR_INTERVAL_NO_TIMEOUT);
+  if (bytesRead < 0) {
+    PrintPRError("PR_Recv failed 1");
+    return 1;
+  }
+  if (bytesRead == 0) {
+    fprintf(stderr, "PR_Recv eof 1\n");
+    return 1;
+  }
+  fprintf(stderr, "%s\n", buf);
+  return 0;
+}
+
+SECStatus
+ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName,
+                                /*optional*/ ScopedCERTCertificate *certOut,
+                                /*optional*/ SSLKEAType *keaOut)
+{
+  ScopedCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr));
+  if (!cert) {
+    PrintPRError("PK11_FindCertFromNickname failed");
+    return SECFailure;
+  }
+
+  ScopedSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert, nullptr));
+  if (!key) {
+    PrintPRError("PK11_FindKeyByAnyCert failed");
+    return SECFailure;
+  }
+
+  SSLKEAType certKEA = NSS_FindCertKEAType(cert);
+
+  if (SSL_ConfigSecureServer(fd, cert, key, certKEA) != SECSuccess) {
+    PrintPRError("SSL_ConfigSecureServer failed");
+    return SECFailure;
+  }
+
+  if (certOut) {
+    *certOut = cert.forget();
+  }
+
+  if (keaOut) {
+    *keaOut = certKEA;
+  }
+
+  return SECSuccess;
+}
+
+int
+StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig,
+            void *sniSocketConfigArg)
+{
+  const char *debugLevel = PR_GetEnv("MOZ_TLS_SERVER_DEBUG_LEVEL");
+  if (debugLevel) {
+    int level = atoi(debugLevel);
+    switch (level) {
+      case DEBUG_ERRORS: gDebugLevel = DEBUG_ERRORS; break;
+      case DEBUG_WARNINGS: gDebugLevel = DEBUG_WARNINGS; break;
+      case DEBUG_VERBOSE: gDebugLevel = DEBUG_VERBOSE; break;
+      default:
+        PrintPRError("invalid MOZ_TLS_SERVER_DEBUG_LEVEL");
+        return 1;
+    }
+  }
+
+  const char *callbackPort = PR_GetEnv("MOZ_TLS_SERVER_CALLBACK_PORT");
+  if (callbackPort) {
+    gCallbackPort = atoi(callbackPort);
+  }
+
+  if (NSS_Init(nssCertDBDir) != SECSuccess) {
+    PrintPRError("NSS_Init failed");
+    return 1;
+  }
+
+  if (NSS_SetDomesticPolicy() != SECSuccess) {
+    PrintPRError("NSS_SetDomesticPolicy failed");
+    return 1;
+  }
+
+  if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
+    PrintPRError("SSL_ConfigServerSessionIDCache failed");
+    return 1;
+  }
+
+  ScopedPRFileDesc serverSocket(PR_NewTCPSocket());
+  if (!serverSocket) {
+    PrintPRError("PR_NewTCPSocket failed");
+    return 1;
+  }
+
+  PRSocketOptionData socketOption;
+  socketOption.option = PR_SockOpt_Reuseaddr;
+  socketOption.value.reuse_addr = true;
+  PR_SetSocketOption(serverSocket, &socketOption);
+
+  PRNetAddr serverAddr;
+  PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr);
+  if (PR_Bind(serverSocket, &serverAddr) != PR_SUCCESS) {
+    PrintPRError("PR_Bind failed");
+    return 1;
+  }
+
+  if (PR_Listen(serverSocket, 1) != PR_SUCCESS) {
+    PrintPRError("PR_Listen failed");
+    return 1;
+  }
+
+  ScopedPRFileDesc rawModelSocket(PR_NewTCPSocket());
+  if (!rawModelSocket) {
+    PrintPRError("PR_NewTCPSocket failed for rawModelSocket");
+    return 1;
+  }
+
+  ScopedPRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.forget()));
+  if (!modelSocket) {
+    PrintPRError("SSL_ImportFD of rawModelSocket failed");
+    return 1;
+  }
+
+  if (SECSuccess != SSL_SNISocketConfigHook(modelSocket, sniSocketConfig,
+                                            sniSocketConfigArg)) {
+    PrintPRError("SSL_SNISocketConfigHook failed");
+    return 1;
+  }
+
+  // We have to configure the server with a certificate, but it's not one
+  // we're actually going to end up using. In the SNI callback, we pick
+  // the right certificate for the connection.
+  if (SECSuccess != ConfigSecureServerWithNamedCert(modelSocket,
+                                                    DEFAULT_CERT_NICKNAME,
+                                                    nullptr, nullptr)) {
+    return 1;
+  }
+
+  if (gCallbackPort != 0) {
+    if (DoCallback()) {
+      return 1;
+    }
+  }
+
+  while (true) {
+    PRNetAddr clientAddr;
+    PRFileDesc *clientSocket = PR_Accept(serverSocket, &clientAddr,
+                                         PR_INTERVAL_NO_TIMEOUT);
+    HandleConnection(clientSocket, modelSocket);
+  }
+
+  return 0;
+}
+
+} } // namespace mozilla::test
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef mozilla_test__TLSServer_h
+#define mozilla_test__TLSServer_h
+
+// This is a standalone server for testing SSL features of Gecko.
+// The client is expected to connect and initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to). If all is good, the client then
+// sends one encrypted byte and receives that same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdint.h>
+#include "prio.h"
+#include "ScopedNSSTypes.h"
+#include "secerr.h"
+#include "ssl.h"
+
+namespace mozilla { namespace test {
+
+enum DebugLevel
+{
+  DEBUG_ERRORS = 1,
+  DEBUG_WARNINGS  = 2,
+  DEBUG_VERBOSE = 3
+};
+
+extern DebugLevel gDebugLevel;
+
+void PrintPRError(const char *aPrefix);
+
+// The default certificate is trusted for localhost and *.example.com
+extern const char DEFAULT_CERT_NICKNAME[];
+
+// Pass DEFAULT_CERT_NICKNAME as certName unless you need a specific
+// certificate.
+SECStatus
+ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName,
+                                /*optional*/ ScopedCERTCertificate *cert,
+                                /*optional*/ SSLKEAType *kea);
+
+int
+StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig,
+            void *sniSocketConfigArg);
+
+template <typename Host>
+inline const Host *
+GetHostForSNI(const SECItem *aSrvNameArr, uint32_t aSrvNameArrSize,
+              const Host *hosts)
+{
+  for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+    for (const Host *host = hosts; host->mHostName; ++host) {
+      // TODO: is aSrvNameArr[i].data guaranteed to be null-terminated?
+      if (!strcmp(host->mHostName, (const char *) aSrvNameArr[i].data)) {
+        if (gDebugLevel >= DEBUG_VERBOSE) {
+          fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+        }
+        return host;
+      }
+    }
+  }
+
+  if (gDebugLevel >= DEBUG_VERBOSE) {
+    fprintf(stderr, "could not find host info from SNI\n");
+  }
+
+  PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+  return nullptr;
+}
+
+} } // namespace mozilla::test
+
+#endif // mozilla_test__TLSServer_h
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+MODULE = 'tlsserver'
+
+CPP_SOURCES += [
+    'TLSServer.cpp',
+]
+
+LIBRARY_NAME = 'tlsserver'
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+# lib must be first, because cmd depends on its output
+DIRS += ['lib', 'cmd']
+
+MODULE = 'tlsserver'
new file mode 100644
index 0000000000000000000000000000000000000000..a978b7e0d5736e931166f1e22d7c5af4899432b9
GIT binary patch
literal 452
zc$_n6Vmx5b#HhJ|nTe5!iIri_^kzN-UN%mxHjlRNyo`*jtPBPchGGUHY|No7%)-3>
zB^jwj3MHw<B?``t26E!OhQ<cwMy3Wv2IdAvQR2KN$Xq-+8|Ne2!pO?N+}O)t(Ade;
z*vN4E<%$N)jnQETHdplb`TaV?@Xg`I(%xf@Nioc|fs7F<#h1^{pZ0atjIZ2x4$jMv
zU=3m2S7W>J0PlrJzONe|Ip<Az#LaWkr0`pDFH_>eecifB-$T=%_$+E)ptbtR6S+jW
zg(-*r1+y2#s21;;RI9wrcl}B(rb{UkKI9%<%KJ4Zpqq)Ak%4hB(B%d~K)1^ZGcx{X
z;V@tWQcR2tXo15Fbg$lqiELtyyW^6bJo?L%&T&TSyr^~QjoN(tTG5U6OPyPICYMd!
zYp^4YeO=e`H4h5RY6E3iZHo>EuHu;zdiDP1qaB;fZ|j*X4pZ8@uk!5Symjw=w01l8
wsmQm#$g7LXEj|2ox_T;GTa<s+iki9GxqS_!wOAL;UVP!OXvP8dlKZu60MUV^FaQ7m
new file mode 100644
index 0000000000000000000000000000000000000000..a5f2f603a72865b8a22beb933932e44338599699
GIT binary patch
literal 16384
zc%1FpK}y3w6oBE^R%#bg-MAAOaFLy%2gs^!q$q`Yf!It-XquFniP(k5aPJjdckKnd
zf?h*vYy!1VTw0O;gU5R_6W)YbKWQF^B2pLGdlfk<N;wdzNg2|rih7kBtGqfYKYC0b
z-lSBo;@7omySNnq00000000000D!-vF7Jnn_H%pD-u0gV00000uw7GBZGU6-yH`Xc
z)$F>QTn`57GIiRdVWRrI?m(S&I_fHm@<bQsFT*Sy#p5V8foJtFH?f=h(=ZLk+W6Kc
zzSGuOKTl)lyTn>;?zHjLP#f1bdKBLWo~I^yzFrhvrdhP=R^;>O)>ES-9NR$UvnX_W
zb(L2wx*L7Uz25h3<yIW6>ziz%(+h2fCZ4%COTSLaIsB)_GAQ!tws-pJ89tT(00000
H;J^C>R^wEH
new file mode 100644
index 0000000000000000000000000000000000000000..cb247f8e985d2c0cb52aa567d8653dd65ca62805
GIT binary patch
literal 440
zc$_n6V%%cT#3;LfnTe5!iIri_^kzN-UN%mxHjlRNyo`*jtPBQ1h5`nBY|No7%);y;
zsl_D<&W;9h;=G2&2IfYl21W+v21Zfhye7z8ta=*fBb&^~%D~*%%V5yh$<)}$u*h!H
zx7R5wYW+(d_El=LcuH<NKP7l~(=}o1tKNdyk7c6&t@HdDpP9GbyMf(m|0D&G>VzFi
zCwc#NO`r8WW7<-=kDSXsboib)(Z-N`_rjzSj;)HGZ#SAQeRuBTp28JxbvACy<(BEF
zl+9z*kZltTc>cpc?coBBFK=JHN)(<bRd8b_6Eh<N<6<!b5d$Hht7U~58UM3z7_b2;
zCPoId0AU8Y*L0=2_nyP6f8H-HG2K4nx2VCxhlLg~?vIl0YluD8nXbgV+2@$2pkB$1
zaEUvre(_JI#R+_RUn8ig=@xG0S-y9<RO*JkT6uHryUN98g*{SRpm<^V+*OVaiYj-W
s`XAXIx4^%1(eZGTnVa5->1>MpF1yPyRY<pVf#7X!>+0Xj4q2}T02Sz;mjD0&