зеркало из https://github.com/mozilla/gecko-dev.git
Bug 853423 - Tests to verify speculative connections are blocked for local IP addresses r=mcmanus
This commit is contained in:
Родитель
f9e5d952d5
Коммит
442ce2dc1b
|
@ -1,11 +1,89 @@
|
|||
const CC = Components.Constructor;
|
||||
/* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 sts=4 et sw=4 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"init");
|
||||
|
||||
var serv;
|
||||
var ios;
|
||||
|
||||
/** Example local IP addresses (literal IP address hostname).
|
||||
*
|
||||
* Note: for IPv6 Unique Local and Link Local, a wider range of addresses is
|
||||
* set aside than those most commonly used. Technically, link local addresses
|
||||
* include those beginning with fe80:: through febf::, although in practise
|
||||
* only fe80:: is used. Necko code blocks speculative connections for the wider
|
||||
* range; hence, this test considers that range too.
|
||||
*/
|
||||
var localIPv4Literals =
|
||||
[ // IPv4 RFC1918 \
|
||||
"10.0.0.1", "10.10.10.10", "10.255.255.255", // 10/8
|
||||
"172.16.0.1", "172.23.172.12", "172.31.255.255", // 172.16/20
|
||||
"192.168.0.1", "192.168.192.168", "192.168.255.255", // 192.168/16
|
||||
// IPv4 Link Local
|
||||
"169.254.0.1", "169.254.192.154", "169.254.255.255" // 169.254/16
|
||||
];
|
||||
var localIPv6Literals =
|
||||
[ // IPv6 Unique Local fc00::/7
|
||||
"fc00::1", "fdfe:dcba:9876:abcd:ef01:2345:6789:abcd",
|
||||
"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
// IPv6 Link Local fe80::/10
|
||||
"fe80::1", "fe80::abcd:ef01:2345:6789",
|
||||
"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
|
||||
];
|
||||
var localIPLiterals = localIPv4Literals.concat(localIPv6Literals);
|
||||
|
||||
/** Example remote IP addresses
|
||||
*
|
||||
* Note: The test environment may not have external network access, so
|
||||
* resolving hostnames may not be possible. Thus, literals are used here, and
|
||||
* should be directly converted by the stub resolver to IP addresses.
|
||||
*/
|
||||
var remoteIPv4Literals =
|
||||
[ "93.184.216.119", // example.com
|
||||
"74.125.239.130", // google.com
|
||||
"63.245.217.105", // mozilla.org
|
||||
"173.252.110.27" // facebook.com
|
||||
];
|
||||
|
||||
var remoteIPv6Literals =
|
||||
[ "2607:f8b0:4005:802::1009", // google.com
|
||||
"2620:101:8008:5::2:1", // mozilla.org
|
||||
"2a03:2880:2110:df07:face:b00c::1" // facebook.com
|
||||
];
|
||||
|
||||
var remoteIPLiterals = remoteIPv4Literals.concat(remoteIPv6Literals);
|
||||
|
||||
/** Test function list and descriptions.
|
||||
*/
|
||||
var testList =
|
||||
[ test_speculative_connect,
|
||||
test_hostnames_resolving_to_local_addresses,
|
||||
test_hostnames_resolving_to_remote_addresses,
|
||||
test_proxies_with_local_addresses,
|
||||
test_proxies_with_remote_addresses
|
||||
];
|
||||
|
||||
var testDescription =
|
||||
[ "Expect pass with localhost",
|
||||
"Expect failure with resolved local IPs",
|
||||
"Expect success with resolved remote IPs",
|
||||
"Expect failure for proxies with local IPs",
|
||||
"Expect success for proxies with remote IPs"
|
||||
];
|
||||
|
||||
var testIdx = 0;
|
||||
var hostIdx = 0;
|
||||
|
||||
|
||||
/** TestServer
|
||||
*
|
||||
* Implements nsIServerSocket for test_speculative_connect.
|
||||
*/
|
||||
function TestServer() {
|
||||
this.listener = ServerSocket(-1, true, -1);
|
||||
this.listener.asyncListen(this);
|
||||
|
@ -16,25 +94,308 @@ TestServer.prototype = {
|
|||
if (iid.equals(Ci.nsIServerSocket) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
onSocketAccepted: function(socket, trans) {
|
||||
try { this.listener.close(); } catch(e) {}
|
||||
do_check_true(true);
|
||||
do_test_finished();
|
||||
next_test();
|
||||
},
|
||||
|
||||
onStopListening: function(socket) {}
|
||||
};
|
||||
|
||||
/** TestOutputStreamCallback
|
||||
*
|
||||
* Implements nsIOutputStreamCallback for socket layer tests.
|
||||
*/
|
||||
function TestOutputStreamCallback(hostname, proxied, expectSuccess, next) {
|
||||
this.hostname = hostname;
|
||||
this.proxied = proxied;
|
||||
this.expectSuccess = expectSuccess;
|
||||
this.next = next;
|
||||
this.dummyContent = "Dummy content";
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
TestOutputStreamCallback.prototype = {
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsIOutputStreamCallback) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
onOutputStreamReady: function(stream) {
|
||||
do_check_neq(typeof(stream), undefined);
|
||||
try {
|
||||
stream.write(this.dummyContent, this.dummyContent.length);
|
||||
} catch (e) {
|
||||
// Spec Connect FAILED.
|
||||
do_check_instanceof(e, Ci.nsIException);
|
||||
if (this.expectSuccess) {
|
||||
// We may expect success, but the address could be unreachable
|
||||
// in the test environment, so expect errors.
|
||||
if (this.proxied) {
|
||||
do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
|
||||
e.result == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED);
|
||||
} else {
|
||||
do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
|
||||
e.result == Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
} else {
|
||||
// A refusal to connect speculatively should throw an error.
|
||||
do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
stream.closeWithStatus(e.result);
|
||||
this.next();
|
||||
return;
|
||||
}
|
||||
// Spec Connect SUCCEEDED.
|
||||
if (this.expectSuccess) {
|
||||
do_check_true(true, "Success for " + this.hostname);
|
||||
} else {
|
||||
do_throw("Speculative Connect should have failed for " +
|
||||
this.hostname);
|
||||
}
|
||||
stream.closeWithStatus(Cr.NS_ERROR_BINDING_ABORTED);
|
||||
this.next();
|
||||
}
|
||||
};
|
||||
|
||||
/** test_speculative_connect
|
||||
*
|
||||
* Tests a basic positive case using nsIOService.SpeculativeConnect:
|
||||
* connecting to localhost.
|
||||
*/
|
||||
function test_speculative_connect() {
|
||||
serv = new TestServer();
|
||||
URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null);
|
||||
ios.QueryInterface(Components.interfaces.nsISpeculativeConnect)
|
||||
var URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null);
|
||||
ios.QueryInterface(Ci.nsISpeculativeConnect)
|
||||
.speculativeConnect(URI, null);
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
/* Speculative connections should not be allowed for hosts with local IP
|
||||
* addresses (Bug 853423). That list includes:
|
||||
* -- IPv4 RFC1918 and Link Local Addresses.
|
||||
* -- IPv6 Unique and Link Local Addresses.
|
||||
*
|
||||
* Two tests are required:
|
||||
* 1. Verify IP Literals passed to the SpeculativeConnect API.
|
||||
* 2. Verify hostnames that need to be resolved at the socket layer.
|
||||
*/
|
||||
|
||||
/** test_hostnames_resolving_to_addresses
|
||||
*
|
||||
* Common test function for resolved hostnames. Takes a list of hosts, a
|
||||
* boolean to determine if the test is expected to succeed or fail, and a
|
||||
* function to call the next test case.
|
||||
*/
|
||||
function test_hostnames_resolving_to_addresses(host, expectSuccess, next) {
|
||||
do_print(host);
|
||||
var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
|
||||
.getService(Ci.nsISocketTransportService);
|
||||
do_check_neq(typeof(sts), undefined);
|
||||
var transport = sts.createTransport(null, 0, host, 80, null);
|
||||
do_check_neq(typeof(transport), undefined);
|
||||
|
||||
transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;
|
||||
transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1);
|
||||
do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT));
|
||||
|
||||
var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0);
|
||||
do_check_neq(typeof(outStream), undefined);
|
||||
|
||||
var callback = new TestOutputStreamCallback(host, false,
|
||||
expectSuccess,
|
||||
next);
|
||||
do_check_neq(typeof(callback), undefined);
|
||||
|
||||
// Need to get main thread pointer to ensure nsSocketTransport::AsyncWait
|
||||
// adds callback to nsOutputStreamReadyEvent on main thread, and doesn't
|
||||
// addref off the main thread.
|
||||
var gThreadManager = Cc["@mozilla.org/thread-manager;1"]
|
||||
.getService(Ci.nsIThreadManager);
|
||||
var mainThread = gThreadManager.currentThread;
|
||||
|
||||
try {
|
||||
outStream.QueryInterface(Ci.nsIAsyncOutputStream)
|
||||
.asyncWait(callback, 0, 0, mainThread);
|
||||
} catch (e) {
|
||||
do_throw("asyncWait should not fail!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test_hostnames_resolving_to_local_addresses
|
||||
*
|
||||
* Creates an nsISocketTransport and simulates a speculative connect request
|
||||
* for a hostname that resolves to a local IP address.
|
||||
* Runs asynchronously; on test success (i.e. failure to connect), the callback
|
||||
* will call this function again until all hostnames in the test list are done.
|
||||
*
|
||||
* Note: This test also uses an IP literal for the hostname. This should be ok,
|
||||
* as the socket layer will ask for the hostname to be resolved anyway, and DNS
|
||||
* code should return a numerical version of the address internally.
|
||||
*/
|
||||
function test_hostnames_resolving_to_local_addresses() {
|
||||
if (hostIdx >= localIPLiterals.length) {
|
||||
// No more local IP addresses; move on.
|
||||
next_test();
|
||||
return;
|
||||
}
|
||||
var host = localIPLiterals[hostIdx++];
|
||||
// Test another local IP address when the current one is done.
|
||||
var next = test_hostnames_resolving_to_local_addresses;
|
||||
test_hostnames_resolving_to_addresses(host, false, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* test_hostnames_resolving_to_remote_addresses
|
||||
*
|
||||
* Creates an nsISocketTransport and simulates a speculative connect request
|
||||
* for a hostname that resolves to a local IP address.
|
||||
* Runs asynchronously; on test success (i.e. failure to connect), the callback
|
||||
* will call this function again until all hostnames in the test list are done.
|
||||
*
|
||||
* Note: This test also uses an IP literal for the hostname. This should be ok,
|
||||
* as the socket layer will ask for the hostname to be resolved anyway, and DNS
|
||||
* code should return a numerical version of the address internally.
|
||||
*/
|
||||
function test_hostnames_resolving_to_remote_addresses() {
|
||||
if (hostIdx >= remoteIPLiterals.length) {
|
||||
// No more remote IP addresses; move on.
|
||||
next_test();
|
||||
return;
|
||||
}
|
||||
var host = remoteIPLiterals[hostIdx++];
|
||||
// Test another remote IP address when the current one is done.
|
||||
var next = test_hostnames_resolving_to_remote_addresses;
|
||||
test_hostnames_resolving_to_addresses(host, true, next);
|
||||
}
|
||||
|
||||
/** test_speculative_connect_with_host_list
|
||||
*
|
||||
* Common test function for resolved proxy hosts. Takes a list of hosts, a
|
||||
* boolean to determine if the test is expected to succeed or fail, and a
|
||||
* function to call the next test case.
|
||||
*/
|
||||
function test_proxies(proxyHost, expectSuccess, next) {
|
||||
do_print("Proxy: " + proxyHost);
|
||||
var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
|
||||
.getService(Ci.nsISocketTransportService);
|
||||
do_check_neq(typeof(sts), undefined);
|
||||
var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"]
|
||||
.getService();
|
||||
do_check_neq(typeof(pps), undefined);
|
||||
|
||||
var proxyInfo = pps.newProxyInfo("http", proxyHost, 8080, 0, 1, null);
|
||||
do_check_neq(typeof(proxyInfo), undefined);
|
||||
|
||||
var transport = sts.createTransport(null, 0, "dummyHost", 80, proxyInfo);
|
||||
do_check_neq(typeof(transport), undefined);
|
||||
|
||||
transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;
|
||||
|
||||
transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1);
|
||||
do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT));
|
||||
|
||||
var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0);
|
||||
do_check_neq(typeof(outStream), undefined);
|
||||
|
||||
var callback = new TestOutputStreamCallback(proxyHost, true,
|
||||
expectSuccess,
|
||||
next);
|
||||
do_check_neq(typeof(callback), undefined);
|
||||
|
||||
// Need to get main thread pointer to ensure nsSocketTransport::AsyncWait
|
||||
// adds callback to nsOutputStreamReadyEvent on main thread, and doesn't
|
||||
// addref off the main thread.
|
||||
var gThreadManager = Cc["@mozilla.org/thread-manager;1"]
|
||||
.getService(Ci.nsIThreadManager);
|
||||
var mainThread = gThreadManager.currentThread;
|
||||
|
||||
try {
|
||||
outStream.QueryInterface(Ci.nsIAsyncOutputStream)
|
||||
.asyncWait(callback, 0, 0, mainThread);
|
||||
} catch (e) {
|
||||
do_throw("asyncWait should not fail!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test_proxies_with_local_addresses
|
||||
*
|
||||
* Creates an nsISocketTransport and simulates a speculative connect request
|
||||
* for a proxy that resolves to a local IP address.
|
||||
* Runs asynchronously; on test success (i.e. failure to connect), the callback
|
||||
* will call this function again until all proxies in the test list are done.
|
||||
*
|
||||
* Note: This test also uses an IP literal for the proxy. This should be ok,
|
||||
* as the socket layer will ask for the proxy to be resolved anyway, and DNS
|
||||
* code should return a numerical version of the address internally.
|
||||
*/
|
||||
function test_proxies_with_local_addresses() {
|
||||
if (hostIdx >= localIPLiterals.length) {
|
||||
// No more local IP addresses; move on.
|
||||
next_test();
|
||||
return;
|
||||
}
|
||||
var host = localIPLiterals[hostIdx++];
|
||||
// Test another local IP address when the current one is done.
|
||||
var next = test_proxies_with_local_addresses;
|
||||
test_proxies(host, false, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* test_proxies_with_remote_addresses
|
||||
*
|
||||
* Creates an nsISocketTransport and simulates a speculative connect request
|
||||
* for a proxy that resolves to a local IP address.
|
||||
* Runs asynchronously; on test success (i.e. failure to connect), the callback
|
||||
* will call this function again until all proxies in the test list are done.
|
||||
*
|
||||
* Note: This test also uses an IP literal for the proxy. This should be ok,
|
||||
* as the socket layer will ask for the proxy to be resolved anyway, and DNS
|
||||
* code should return a numerical version of the address internally.
|
||||
*/
|
||||
function test_proxies_with_remote_addresses() {
|
||||
if (hostIdx >= remoteIPLiterals.length) {
|
||||
// No more local IP addresses; move on.
|
||||
next_test();
|
||||
return;
|
||||
}
|
||||
var host = remoteIPLiterals[hostIdx++];
|
||||
// Test another local IP address when the current one is done.
|
||||
var next = test_proxies_with_remote_addresses;
|
||||
test_proxies(host, true, next);
|
||||
}
|
||||
|
||||
/** next_test
|
||||
*
|
||||
* Calls the next test in testList. Each test is responsible for calling this
|
||||
* function when its test cases are complete.
|
||||
*/
|
||||
function next_test() {
|
||||
if (testIdx >= testList.length) {
|
||||
// No more tests; we're done.
|
||||
do_test_finished();
|
||||
return;
|
||||
}
|
||||
do_print("SpeculativeConnect: " + testDescription[testIdx]);
|
||||
hostIdx = 0;
|
||||
// Start next test in list.
|
||||
testList[testIdx++]();
|
||||
}
|
||||
|
||||
/** run_test
|
||||
*
|
||||
* Main entry function for test execution.
|
||||
*/
|
||||
function run_test() {
|
||||
ios = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
do_test_pending();
|
||||
//next_test();
|
||||
test_hostnames_resolving_to_local_addresses();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче